UNPKG

3.42 MBJavaScriptView Raw
1/**
2* plotly.js (cartesian) v2.4.1
3* Copyright 2012-2021, 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 .3s ease 0s;-moz-transition:background-color .3s ease 0s;-ms-transition:background-color .3s ease 0s;-o-transition:background-color .3s ease 0s;transition:background-color .3s ease 0s;",
43 "X .modebar--hover>:not(.watermark)": "opacity:0;-webkit-transition:opacity .3s ease 0s;-moz-transition:opacity .3s ease 0s;-ms-transition:opacity .3s ease 0s;-o-transition:opacity .3s ease 0s;transition:opacity .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:#fff;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:#fff;",
60 "X .select-outline-2": "stroke:#000;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,.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:.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":287}],2:[function(_dereq_,module,exports){
76'use strict';
77
78module.exports = _dereq_('../src/transforms/aggregate');
79
80},{"../src/transforms/aggregate":544}],3:[function(_dereq_,module,exports){
81'use strict';
82
83module.exports = _dereq_('../src/traces/bar');
84
85},{"../src/traces/bar":394}],4:[function(_dereq_,module,exports){
86'use strict';
87
88module.exports = _dereq_('../src/traces/box');
89
90},{"../src/traces/box":409}],5:[function(_dereq_,module,exports){
91'use strict';
92
93module.exports = _dereq_('../src/components/calendars');
94
95},{"../src/components/calendars":155}],6:[function(_dereq_,module,exports){
96'use strict';
97
98module.exports = _dereq_('../src/traces/contour');
99
100},{"../src/traces/contour":429}],7:[function(_dereq_,module,exports){
101'use strict';
102
103module.exports = _dereq_('../src/core');
104
105},{"../src/core":269}],8:[function(_dereq_,module,exports){
106'use strict';
107
108module.exports = _dereq_('../src/transforms/filter');
109
110},{"../src/transforms/filter":545}],9:[function(_dereq_,module,exports){
111'use strict';
112
113module.exports = _dereq_('../src/transforms/groupby');
114
115},{"../src/transforms/groupby":546}],10:[function(_dereq_,module,exports){
116'use strict';
117
118module.exports = _dereq_('../src/traces/heatmap');
119
120},{"../src/traces/heatmap":445}],11:[function(_dereq_,module,exports){
121'use strict';
122
123module.exports = _dereq_('../src/traces/histogram');
124
125},{"../src/traces/histogram":463}],12:[function(_dereq_,module,exports){
126'use strict';
127
128module.exports = _dereq_('../src/traces/histogram2d');
129
130},{"../src/traces/histogram2d":469}],13:[function(_dereq_,module,exports){
131'use strict';
132
133module.exports = _dereq_('../src/traces/histogram2dcontour');
134
135},{"../src/traces/histogram2dcontour":473}],14:[function(_dereq_,module,exports){
136'use strict';
137
138module.exports = _dereq_('../src/traces/image');
139
140},{"../src/traces/image":481}],15:[function(_dereq_,module,exports){
141'use strict';
142
143var Plotly = _dereq_('./core');
144
145Plotly.register([
146 // traces
147 _dereq_('./bar'),
148 _dereq_('./box'),
149 _dereq_('./heatmap'),
150 _dereq_('./histogram'),
151 _dereq_('./histogram2d'),
152 _dereq_('./histogram2dcontour'),
153 _dereq_('./contour'),
154 _dereq_('./scatterternary'),
155 _dereq_('./violin'),
156 _dereq_('./image'),
157 _dereq_('./pie'),
158
159 // transforms
160 _dereq_('./aggregate'),
161 _dereq_('./filter'),
162 _dereq_('./groupby'),
163 _dereq_('./sort'),
164
165 // components
166 _dereq_('./calendars'),
167]);
168
169module.exports = Plotly;
170
171},{"./aggregate":2,"./bar":3,"./box":4,"./calendars":5,"./contour":6,"./core":7,"./filter":8,"./groupby":9,"./heatmap":10,"./histogram":11,"./histogram2d":12,"./histogram2dcontour":13,"./image":14,"./pie":16,"./scatterternary":17,"./sort":18,"./violin":19}],16:[function(_dereq_,module,exports){
172'use strict';
173
174module.exports = _dereq_('../src/traces/pie');
175
176},{"../src/traces/pie":490}],17:[function(_dereq_,module,exports){
177'use strict';
178
179module.exports = _dereq_('../src/traces/scatterternary');
180
181},{"../src/traces/scatterternary":531}],18:[function(_dereq_,module,exports){
182'use strict';
183
184module.exports = _dereq_('../src/transforms/sort');
185
186},{"../src/transforms/sort":548}],19:[function(_dereq_,module,exports){
187'use strict';
188
189module.exports = _dereq_('../src/traces/violin');
190
191},{"../src/traces/violin":539}],20:[function(_dereq_,module,exports){
192!function() {
193 var d3 = {
194 version: "3.8.0"
195 };
196 var d3_arraySlice = [].slice, d3_array = function(list) {
197 return d3_arraySlice.call(list);
198 };
199 var d3_document = self.document;
200 function d3_documentElement(node) {
201 return node && (node.ownerDocument || node.document || node).documentElement;
202 }
203 function d3_window(node) {
204 return node && (node.ownerDocument && node.ownerDocument.defaultView || node.document && node || node.defaultView);
205 }
206 if (d3_document) {
207 try {
208 d3_array(d3_document.documentElement.childNodes)[0].nodeType;
209 } catch (e) {
210 d3_array = function(list) {
211 var i = list.length, array = new Array(i);
212 while (i--) array[i] = list[i];
213 return array;
214 };
215 }
216 }
217 if (!Date.now) Date.now = function() {
218 return +new Date();
219 };
220 if (d3_document) {
221 try {
222 d3_document.createElement("DIV").style.setProperty("opacity", 0, "");
223 } catch (error) {
224 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;
225 d3_element_prototype.setAttribute = function(name, value) {
226 d3_element_setAttribute.call(this, name, value + "");
227 };
228 d3_element_prototype.setAttributeNS = function(space, local, value) {
229 d3_element_setAttributeNS.call(this, space, local, value + "");
230 };
231 d3_style_prototype.setProperty = function(name, value, priority) {
232 d3_style_setProperty.call(this, name, value + "", priority);
233 };
234 }
235 }
236 d3.ascending = d3_ascending;
237 function d3_ascending(a, b) {
238 return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
239 }
240 d3.descending = function(a, b) {
241 return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
242 };
243 d3.min = function(array, f) {
244 var i = -1, n = array.length, a, b;
245 if (arguments.length === 1) {
246 while (++i < n) if ((b = array[i]) != null && b >= b) {
247 a = b;
248 break;
249 }
250 while (++i < n) if ((b = array[i]) != null && a > b) a = b;
251 } else {
252 while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) {
253 a = b;
254 break;
255 }
256 while (++i < n) if ((b = f.call(array, array[i], i)) != null && a > b) a = b;
257 }
258 return a;
259 };
260 d3.max = function(array, f) {
261 var i = -1, n = array.length, a, b;
262 if (arguments.length === 1) {
263 while (++i < n) if ((b = array[i]) != null && b >= b) {
264 a = b;
265 break;
266 }
267 while (++i < n) if ((b = array[i]) != null && b > a) a = b;
268 } else {
269 while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) {
270 a = b;
271 break;
272 }
273 while (++i < n) if ((b = f.call(array, array[i], i)) != null && b > a) a = b;
274 }
275 return a;
276 };
277 d3.extent = function(array, f) {
278 var i = -1, n = array.length, a, b, c;
279 if (arguments.length === 1) {
280 while (++i < n) if ((b = array[i]) != null && b >= b) {
281 a = c = b;
282 break;
283 }
284 while (++i < n) if ((b = array[i]) != null) {
285 if (a > b) a = b;
286 if (c < b) c = b;
287 }
288 } else {
289 while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) {
290 a = c = b;
291 break;
292 }
293 while (++i < n) if ((b = f.call(array, array[i], i)) != null) {
294 if (a > b) a = b;
295 if (c < b) c = b;
296 }
297 }
298 return [ a, c ];
299 };
300 function d3_number(x) {
301 return x === null ? NaN : +x;
302 }
303 function d3_numeric(x) {
304 return !isNaN(x);
305 }
306 d3.sum = function(array, f) {
307 var s = 0, n = array.length, a, i = -1;
308 if (arguments.length === 1) {
309 while (++i < n) if (d3_numeric(a = +array[i])) s += a;
310 } else {
311 while (++i < n) if (d3_numeric(a = +f.call(array, array[i], i))) s += a;
312 }
313 return s;
314 };
315 d3.mean = function(array, f) {
316 var s = 0, n = array.length, a, i = -1, j = n;
317 if (arguments.length === 1) {
318 while (++i < n) if (d3_numeric(a = d3_number(array[i]))) s += a; else --j;
319 } else {
320 while (++i < n) if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) s += a; else --j;
321 }
322 if (j) return s / j;
323 };
324 d3.quantile = function(values, p) {
325 var H = (values.length - 1) * p + 1, h = Math.floor(H), v = +values[h - 1], e = H - h;
326 return e ? v + e * (values[h] - v) : v;
327 };
328 d3.median = function(array, f) {
329 var numbers = [], n = array.length, a, i = -1;
330 if (arguments.length === 1) {
331 while (++i < n) if (d3_numeric(a = d3_number(array[i]))) numbers.push(a);
332 } else {
333 while (++i < n) if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) numbers.push(a);
334 }
335 if (numbers.length) return d3.quantile(numbers.sort(d3_ascending), .5);
336 };
337 d3.variance = function(array, f) {
338 var n = array.length, m = 0, a, d, s = 0, i = -1, j = 0;
339 if (arguments.length === 1) {
340 while (++i < n) {
341 if (d3_numeric(a = d3_number(array[i]))) {
342 d = a - m;
343 m += d / ++j;
344 s += d * (a - m);
345 }
346 }
347 } else {
348 while (++i < n) {
349 if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) {
350 d = a - m;
351 m += d / ++j;
352 s += d * (a - m);
353 }
354 }
355 }
356 if (j > 1) return s / (j - 1);
357 };
358 d3.deviation = function() {
359 var v = d3.variance.apply(this, arguments);
360 return v ? Math.sqrt(v) : v;
361 };
362 function d3_bisector(compare) {
363 return {
364 left: function(a, x, lo, hi) {
365 if (arguments.length < 3) lo = 0;
366 if (arguments.length < 4) hi = a.length;
367 while (lo < hi) {
368 var mid = lo + hi >>> 1;
369 if (compare(a[mid], x) < 0) lo = mid + 1; else hi = mid;
370 }
371 return lo;
372 },
373 right: function(a, x, lo, hi) {
374 if (arguments.length < 3) lo = 0;
375 if (arguments.length < 4) hi = a.length;
376 while (lo < hi) {
377 var mid = lo + hi >>> 1;
378 if (compare(a[mid], x) > 0) hi = mid; else lo = mid + 1;
379 }
380 return lo;
381 }
382 };
383 }
384 var d3_bisect = d3_bisector(d3_ascending);
385 d3.bisectLeft = d3_bisect.left;
386 d3.bisect = d3.bisectRight = d3_bisect.right;
387 d3.bisector = function(f) {
388 return d3_bisector(f.length === 1 ? function(d, x) {
389 return d3_ascending(f(d), x);
390 } : f);
391 };
392 d3.shuffle = function(array, i0, i1) {
393 if ((m = arguments.length) < 3) {
394 i1 = array.length;
395 if (m < 2) i0 = 0;
396 }
397 var m = i1 - i0, t, i;
398 while (m) {
399 i = Math.random() * m-- | 0;
400 t = array[m + i0], array[m + i0] = array[i + i0], array[i + i0] = t;
401 }
402 return array;
403 };
404 d3.permute = function(array, indexes) {
405 var i = indexes.length, permutes = new Array(i);
406 while (i--) permutes[i] = array[indexes[i]];
407 return permutes;
408 };
409 d3.pairs = function(array) {
410 var i = 0, n = array.length - 1, p0, p1 = array[0], pairs = new Array(n < 0 ? 0 : n);
411 while (i < n) pairs[i] = [ p0 = p1, p1 = array[++i] ];
412 return pairs;
413 };
414 d3.transpose = function(matrix) {
415 if (!(n = matrix.length)) return [];
416 for (var i = -1, m = d3.min(matrix, d3_transposeLength), transpose = new Array(m); ++i < m; ) {
417 for (var j = -1, n, row = transpose[i] = new Array(n); ++j < n; ) {
418 row[j] = matrix[j][i];
419 }
420 }
421 return transpose;
422 };
423 function d3_transposeLength(d) {
424 return d.length;
425 }
426 d3.zip = function() {
427 return d3.transpose(arguments);
428 };
429 d3.keys = function(map) {
430 var keys = [];
431 for (var key in map) keys.push(key);
432 return keys;
433 };
434 d3.values = function(map) {
435 var values = [];
436 for (var key in map) values.push(map[key]);
437 return values;
438 };
439 d3.entries = function(map) {
440 var entries = [];
441 for (var key in map) entries.push({
442 key: key,
443 value: map[key]
444 });
445 return entries;
446 };
447 d3.merge = function(arrays) {
448 var n = arrays.length, m, i = -1, j = 0, merged, array;
449 while (++i < n) j += arrays[i].length;
450 merged = new Array(j);
451 while (--n >= 0) {
452 array = arrays[n];
453 m = array.length;
454 while (--m >= 0) {
455 merged[--j] = array[m];
456 }
457 }
458 return merged;
459 };
460 var abs = Math.abs;
461 d3.range = function(start, stop, step) {
462 if (arguments.length < 3) {
463 step = 1;
464 if (arguments.length < 2) {
465 stop = start;
466 start = 0;
467 }
468 }
469 if ((stop - start) / step === Infinity) throw new Error("infinite range");
470 var range = [], k = d3_range_integerScale(abs(step)), i = -1, j;
471 start *= k, stop *= k, step *= k;
472 if (step < 0) while ((j = start + step * ++i) > stop) range.push(j / k); else while ((j = start + step * ++i) < stop) range.push(j / k);
473 return range;
474 };
475 function d3_range_integerScale(x) {
476 var k = 1;
477 while (x * k % 1) k *= 10;
478 return k;
479 }
480 function d3_class(ctor, properties) {
481 for (var key in properties) {
482 Object.defineProperty(ctor.prototype, key, {
483 value: properties[key],
484 enumerable: false
485 });
486 }
487 }
488 d3.map = function(object, f) {
489 var map = new d3_Map();
490 if (object instanceof d3_Map) {
491 object.forEach(function(key, value) {
492 map.set(key, value);
493 });
494 } else if (Array.isArray(object)) {
495 var i = -1, n = object.length, o;
496 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);
497 } else {
498 for (var key in object) map.set(key, object[key]);
499 }
500 return map;
501 };
502 function d3_Map() {
503 this._ = Object.create(null);
504 }
505 var d3_map_proto = "__proto__", d3_map_zero = "\x00";
506 d3_class(d3_Map, {
507 has: d3_map_has,
508 get: function(key) {
509 return this._[d3_map_escape(key)];
510 },
511 set: function(key, value) {
512 return this._[d3_map_escape(key)] = value;
513 },
514 remove: d3_map_remove,
515 keys: d3_map_keys,
516 values: function() {
517 var values = [];
518 for (var key in this._) values.push(this._[key]);
519 return values;
520 },
521 entries: function() {
522 var entries = [];
523 for (var key in this._) entries.push({
524 key: d3_map_unescape(key),
525 value: this._[key]
526 });
527 return entries;
528 },
529 size: d3_map_size,
530 empty: d3_map_empty,
531 forEach: function(f) {
532 for (var key in this._) f.call(this, d3_map_unescape(key), this._[key]);
533 }
534 });
535 function d3_map_escape(key) {
536 return (key += "") === d3_map_proto || key[0] === d3_map_zero ? d3_map_zero + key : key;
537 }
538 function d3_map_unescape(key) {
539 return (key += "")[0] === d3_map_zero ? key.slice(1) : key;
540 }
541 function d3_map_has(key) {
542 return d3_map_escape(key) in this._;
543 }
544 function d3_map_remove(key) {
545 return (key = d3_map_escape(key)) in this._ && delete this._[key];
546 }
547 function d3_map_keys() {
548 var keys = [];
549 for (var key in this._) keys.push(d3_map_unescape(key));
550 return keys;
551 }
552 function d3_map_size() {
553 var size = 0;
554 for (var key in this._) ++size;
555 return size;
556 }
557 function d3_map_empty() {
558 for (var key in this._) return false;
559 return true;
560 }
561 d3.nest = function() {
562 var nest = {}, keys = [], sortKeys = [], sortValues, rollup;
563 function map(mapType, array, depth) {
564 if (depth >= keys.length) return rollup ? rollup.call(nest, array) : sortValues ? array.sort(sortValues) : array;
565 var i = -1, n = array.length, key = keys[depth++], keyValue, object, setter, valuesByKey = new d3_Map(), values;
566 while (++i < n) {
567 if (values = valuesByKey.get(keyValue = key(object = array[i]))) {
568 values.push(object);
569 } else {
570 valuesByKey.set(keyValue, [ object ]);
571 }
572 }
573 if (mapType) {
574 object = mapType();
575 setter = function(keyValue, values) {
576 object.set(keyValue, map(mapType, values, depth));
577 };
578 } else {
579 object = {};
580 setter = function(keyValue, values) {
581 object[keyValue] = map(mapType, values, depth);
582 };
583 }
584 valuesByKey.forEach(setter);
585 return object;
586 }
587 function entries(map, depth) {
588 if (depth >= keys.length) return map;
589 var array = [], sortKey = sortKeys[depth++];
590 map.forEach(function(key, keyMap) {
591 array.push({
592 key: key,
593 values: entries(keyMap, depth)
594 });
595 });
596 return sortKey ? array.sort(function(a, b) {
597 return sortKey(a.key, b.key);
598 }) : array;
599 }
600 nest.map = function(array, mapType) {
601 return map(mapType, array, 0);
602 };
603 nest.entries = function(array) {
604 return entries(map(d3.map, array, 0), 0);
605 };
606 nest.key = function(d) {
607 keys.push(d);
608 return nest;
609 };
610 nest.sortKeys = function(order) {
611 sortKeys[keys.length - 1] = order;
612 return nest;
613 };
614 nest.sortValues = function(order) {
615 sortValues = order;
616 return nest;
617 };
618 nest.rollup = function(f) {
619 rollup = f;
620 return nest;
621 };
622 return nest;
623 };
624 d3.set = function(array) {
625 var set = new d3_Set();
626 if (array) for (var i = 0, n = array.length; i < n; ++i) set.add(array[i]);
627 return set;
628 };
629 function d3_Set() {
630 this._ = Object.create(null);
631 }
632 d3_class(d3_Set, {
633 has: d3_map_has,
634 add: function(key) {
635 this._[d3_map_escape(key += "")] = true;
636 return key;
637 },
638 remove: d3_map_remove,
639 values: d3_map_keys,
640 size: d3_map_size,
641 empty: d3_map_empty,
642 forEach: function(f) {
643 for (var key in this._) f.call(this, d3_map_unescape(key));
644 }
645 });
646 d3.behavior = {};
647 function d3_identity(d) {
648 return d;
649 }
650 d3.rebind = function(target, source) {
651 var i = 1, n = arguments.length, method;
652 while (++i < n) target[method = arguments[i]] = d3_rebind(target, source, source[method]);
653 return target;
654 };
655 function d3_rebind(target, source, method) {
656 return function() {
657 var value = method.apply(source, arguments);
658 return value === source ? target : value;
659 };
660 }
661 function d3_vendorSymbol(object, name) {
662 if (name in object) return name;
663 name = name.charAt(0).toUpperCase() + name.slice(1);
664 for (var i = 0, n = d3_vendorPrefixes.length; i < n; ++i) {
665 var prefixName = d3_vendorPrefixes[i] + name;
666 if (prefixName in object) return prefixName;
667 }
668 }
669 var d3_vendorPrefixes = [ "webkit", "ms", "moz", "Moz", "o", "O" ];
670 function d3_noop() {}
671 d3.dispatch = function() {
672 var dispatch = new d3_dispatch(), i = -1, n = arguments.length;
673 while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch);
674 return dispatch;
675 };
676 function d3_dispatch() {}
677 d3_dispatch.prototype.on = function(type, listener) {
678 var i = type.indexOf("."), name = "";
679 if (i >= 0) {
680 name = type.slice(i + 1);
681 type = type.slice(0, i);
682 }
683 if (type) return arguments.length < 2 ? this[type].on(name) : this[type].on(name, listener);
684 if (arguments.length === 2) {
685 if (listener == null) for (type in this) {
686 if (this.hasOwnProperty(type)) this[type].on(name, null);
687 }
688 return this;
689 }
690 };
691 function d3_dispatch_event(dispatch) {
692 var listeners = [], listenerByName = new d3_Map();
693 function event() {
694 var z = listeners, i = -1, n = z.length, l;
695 while (++i < n) if (l = z[i].on) l.apply(this, arguments);
696 return dispatch;
697 }
698 event.on = function(name, listener) {
699 var l = listenerByName.get(name), i;
700 if (arguments.length < 2) return l && l.on;
701 if (l) {
702 l.on = null;
703 listeners = listeners.slice(0, i = listeners.indexOf(l)).concat(listeners.slice(i + 1));
704 listenerByName.remove(name);
705 }
706 if (listener) listeners.push(listenerByName.set(name, {
707 on: listener
708 }));
709 return dispatch;
710 };
711 return event;
712 }
713 d3.event = null;
714 function d3_eventPreventDefault() {
715 d3.event.preventDefault();
716 }
717 function d3_eventSource() {
718 var e = d3.event, s;
719 while (s = e.sourceEvent) e = s;
720 return e;
721 }
722 function d3_eventDispatch(target) {
723 var dispatch = new d3_dispatch(), i = 0, n = arguments.length;
724 while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch);
725 dispatch.of = function(thiz, argumentz) {
726 return function(e1) {
727 try {
728 var e0 = e1.sourceEvent = d3.event;
729 e1.target = target;
730 d3.event = e1;
731 dispatch[e1.type].apply(thiz, argumentz);
732 } finally {
733 d3.event = e0;
734 }
735 };
736 };
737 return dispatch;
738 }
739 d3.requote = function(s) {
740 return s.replace(d3_requote_re, "\\$&");
741 };
742 var d3_requote_re = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;
743 var d3_subclass = {}.__proto__ ? function(object, prototype) {
744 object.__proto__ = prototype;
745 } : function(object, prototype) {
746 for (var property in prototype) object[property] = prototype[property];
747 };
748 function d3_selection(groups) {
749 d3_subclass(groups, d3_selectionPrototype);
750 return groups;
751 }
752 var d3_select = function(s, n) {
753 return n.querySelector(s);
754 }, d3_selectAll = function(s, n) {
755 return n.querySelectorAll(s);
756 }, d3_selectMatches = function(n, s) {
757 var d3_selectMatcher = n.matches || n[d3_vendorSymbol(n, "matchesSelector")];
758 d3_selectMatches = function(n, s) {
759 return d3_selectMatcher.call(n, s);
760 };
761 return d3_selectMatches(n, s);
762 };
763 if (typeof Sizzle === "function") {
764 d3_select = function(s, n) {
765 return Sizzle(s, n)[0] || null;
766 };
767 d3_selectAll = Sizzle;
768 d3_selectMatches = Sizzle.matchesSelector;
769 }
770 d3.selection = function() {
771 return d3.select(d3_document.documentElement);
772 };
773 var d3_selectionPrototype = d3.selection.prototype = [];
774 d3_selectionPrototype.select = function(selector) {
775 var subgroups = [], subgroup, subnode, group, node;
776 selector = d3_selection_selector(selector);
777 for (var j = -1, m = this.length; ++j < m; ) {
778 subgroups.push(subgroup = []);
779 subgroup.parentNode = (group = this[j]).parentNode;
780 for (var i = -1, n = group.length; ++i < n; ) {
781 if (node = group[i]) {
782 subgroup.push(subnode = selector.call(node, node.__data__, i, j));
783 if (subnode && "__data__" in node) subnode.__data__ = node.__data__;
784 } else {
785 subgroup.push(null);
786 }
787 }
788 }
789 return d3_selection(subgroups);
790 };
791 function d3_selection_selector(selector) {
792 return typeof selector === "function" ? selector : function() {
793 return d3_select(selector, this);
794 };
795 }
796 d3_selectionPrototype.selectAll = function(selector) {
797 var subgroups = [], subgroup, node;
798 selector = d3_selection_selectorAll(selector);
799 for (var j = -1, m = this.length; ++j < m; ) {
800 for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
801 if (node = group[i]) {
802 subgroups.push(subgroup = d3_array(selector.call(node, node.__data__, i, j)));
803 subgroup.parentNode = node;
804 }
805 }
806 }
807 return d3_selection(subgroups);
808 };
809 function d3_selection_selectorAll(selector) {
810 return typeof selector === "function" ? selector : function() {
811 return d3_selectAll(selector, this);
812 };
813 }
814 var d3_nsXhtml = "http://www.w3.org/1999/xhtml";
815 var d3_nsPrefix = {
816 svg: "http://www.w3.org/2000/svg",
817 xhtml: d3_nsXhtml,
818 xlink: "http://www.w3.org/1999/xlink",
819 xml: "http://www.w3.org/XML/1998/namespace",
820 xmlns: "http://www.w3.org/2000/xmlns/"
821 };
822 d3.ns = {
823 prefix: d3_nsPrefix,
824 qualify: function(name) {
825 var i = name.indexOf(":"), prefix = name;
826 if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") name = name.slice(i + 1);
827 return d3_nsPrefix.hasOwnProperty(prefix) ? {
828 space: d3_nsPrefix[prefix],
829 local: name
830 } : name;
831 }
832 };
833 d3_selectionPrototype.attr = function(name, value) {
834 if (arguments.length < 2) {
835 if (typeof name === "string") {
836 var node = this.node();
837 name = d3.ns.qualify(name);
838 return name.local ? node.getAttributeNS(name.space, name.local) : node.getAttribute(name);
839 }
840 for (value in name) this.each(d3_selection_attr(value, name[value]));
841 return this;
842 }
843 return this.each(d3_selection_attr(name, value));
844 };
845 function d3_selection_attr(name, value) {
846 name = d3.ns.qualify(name);
847 function attrNull() {
848 this.removeAttribute(name);
849 }
850 function attrNullNS() {
851 this.removeAttributeNS(name.space, name.local);
852 }
853 function attrConstant() {
854 this.setAttribute(name, value);
855 }
856 function attrConstantNS() {
857 this.setAttributeNS(name.space, name.local, value);
858 }
859 function attrFunction() {
860 var x = value.apply(this, arguments);
861 if (x == null) this.removeAttribute(name); else this.setAttribute(name, x);
862 }
863 function attrFunctionNS() {
864 var x = value.apply(this, arguments);
865 if (x == null) this.removeAttributeNS(name.space, name.local); else this.setAttributeNS(name.space, name.local, x);
866 }
867 return value == null ? name.local ? attrNullNS : attrNull : typeof value === "function" ? name.local ? attrFunctionNS : attrFunction : name.local ? attrConstantNS : attrConstant;
868 }
869 function d3_collapse(s) {
870 return s.trim().replace(/\s+/g, " ");
871 }
872 d3_selectionPrototype.classed = function(name, value) {
873 if (arguments.length < 2) {
874 if (typeof name === "string") {
875 var node = this.node(), n = (name = d3_selection_classes(name)).length, i = -1;
876 if (value = node.classList) {
877 while (++i < n) if (!value.contains(name[i])) return false;
878 } else {
879 value = node.getAttribute("class");
880 while (++i < n) if (!d3_selection_classedRe(name[i]).test(value)) return false;
881 }
882 return true;
883 }
884 for (value in name) this.each(d3_selection_classed(value, name[value]));
885 return this;
886 }
887 return this.each(d3_selection_classed(name, value));
888 };
889 function d3_selection_classedRe(name) {
890 return new RegExp("(?:^|\\s+)" + d3.requote(name) + "(?:\\s+|$)", "g");
891 }
892 function d3_selection_classes(name) {
893 return (name + "").trim().split(/^|\s+/);
894 }
895 function d3_selection_classed(name, value) {
896 name = d3_selection_classes(name).map(d3_selection_classedName);
897 var n = name.length;
898 function classedConstant() {
899 var i = -1;
900 while (++i < n) name[i](this, value);
901 }
902 function classedFunction() {
903 var i = -1, x = value.apply(this, arguments);
904 while (++i < n) name[i](this, x);
905 }
906 return typeof value === "function" ? classedFunction : classedConstant;
907 }
908 function d3_selection_classedName(name) {
909 var re = d3_selection_classedRe(name);
910 return function(node, value) {
911 if (c = node.classList) return value ? c.add(name) : c.remove(name);
912 var c = node.getAttribute("class") || "";
913 if (value) {
914 re.lastIndex = 0;
915 if (!re.test(c)) node.setAttribute("class", d3_collapse(c + " " + name));
916 } else {
917 node.setAttribute("class", d3_collapse(c.replace(re, " ")));
918 }
919 };
920 }
921 d3_selectionPrototype.style = function(name, value, priority) {
922 var n = arguments.length;
923 if (n < 3) {
924 if (typeof name !== "string") {
925 if (n < 2) value = "";
926 for (priority in name) this.each(d3_selection_style(priority, name[priority], value));
927 return this;
928 }
929 if (n < 2) {
930 var node = this.node();
931 return d3_window(node).getComputedStyle(node, null).getPropertyValue(name);
932 }
933 priority = "";
934 }
935 return this.each(d3_selection_style(name, value, priority));
936 };
937 function d3_selection_style(name, value, priority) {
938 function styleNull() {
939 this.style.removeProperty(name);
940 }
941 function styleConstant() {
942 this.style.setProperty(name, value, priority);
943 }
944 function styleFunction() {
945 var x = value.apply(this, arguments);
946 if (x == null) this.style.removeProperty(name); else this.style.setProperty(name, x, priority);
947 }
948 return value == null ? styleNull : typeof value === "function" ? styleFunction : styleConstant;
949 }
950 d3_selectionPrototype.property = function(name, value) {
951 if (arguments.length < 2) {
952 if (typeof name === "string") return this.node()[name];
953 for (value in name) this.each(d3_selection_property(value, name[value]));
954 return this;
955 }
956 return this.each(d3_selection_property(name, value));
957 };
958 function d3_selection_property(name, value) {
959 function propertyNull() {
960 delete this[name];
961 }
962 function propertyConstant() {
963 this[name] = value;
964 }
965 function propertyFunction() {
966 var x = value.apply(this, arguments);
967 if (x == null) delete this[name]; else this[name] = x;
968 }
969 return value == null ? propertyNull : typeof value === "function" ? propertyFunction : propertyConstant;
970 }
971 d3_selectionPrototype.text = function(value) {
972 return arguments.length ? this.each(typeof value === "function" ? function() {
973 var v = value.apply(this, arguments);
974 this.textContent = v == null ? "" : v;
975 } : value == null ? function() {
976 this.textContent = "";
977 } : function() {
978 this.textContent = value;
979 }) : this.node().textContent;
980 };
981 d3_selectionPrototype.html = function(value) {
982 return arguments.length ? this.each(typeof value === "function" ? function() {
983 var v = value.apply(this, arguments);
984 this.innerHTML = v == null ? "" : v;
985 } : value == null ? function() {
986 this.innerHTML = "";
987 } : function() {
988 this.innerHTML = value;
989 }) : this.node().innerHTML;
990 };
991 d3_selectionPrototype.append = function(name) {
992 name = d3_selection_creator(name);
993 return this.select(function() {
994 return this.appendChild(name.apply(this, arguments));
995 });
996 };
997 function d3_selection_creator(name) {
998 function create() {
999 var document = this.ownerDocument, namespace = this.namespaceURI;
1000 return namespace === d3_nsXhtml && document.documentElement.namespaceURI === d3_nsXhtml ? document.createElement(name) : document.createElementNS(namespace, name);
1001 }
1002 function createNS() {
1003 return this.ownerDocument.createElementNS(name.space, name.local);
1004 }
1005 return typeof name === "function" ? name : (name = d3.ns.qualify(name)).local ? createNS : create;
1006 }
1007 d3_selectionPrototype.insert = function(name, before) {
1008 name = d3_selection_creator(name);
1009 before = d3_selection_selector(before);
1010 return this.select(function() {
1011 return this.insertBefore(name.apply(this, arguments), before.apply(this, arguments) || null);
1012 });
1013 };
1014 d3_selectionPrototype.remove = function() {
1015 return this.each(d3_selectionRemove);
1016 };
1017 function d3_selectionRemove() {
1018 var parent = this.parentNode;
1019 if (parent) parent.removeChild(this);
1020 }
1021 d3_selectionPrototype.data = function(value, key) {
1022 var i = -1, n = this.length, group, node;
1023 if (!arguments.length) {
1024 value = new Array(n = (group = this[0]).length);
1025 while (++i < n) {
1026 if (node = group[i]) {
1027 value[i] = node.__data__;
1028 }
1029 }
1030 return value;
1031 }
1032 function bind(group, groupData) {
1033 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;
1034 if (key) {
1035 var nodeByKeyValue = new d3_Map(), keyValues = new Array(n), keyValue;
1036 for (i = -1; ++i < n; ) {
1037 if (node = group[i]) {
1038 if (nodeByKeyValue.has(keyValue = key.call(node, node.__data__, i))) {
1039 exitNodes[i] = node;
1040 } else {
1041 nodeByKeyValue.set(keyValue, node);
1042 }
1043 keyValues[i] = keyValue;
1044 }
1045 }
1046 for (i = -1; ++i < m; ) {
1047 if (!(node = nodeByKeyValue.get(keyValue = key.call(groupData, nodeData = groupData[i], i)))) {
1048 enterNodes[i] = d3_selection_dataNode(nodeData);
1049 } else if (node !== true) {
1050 updateNodes[i] = node;
1051 node.__data__ = nodeData;
1052 }
1053 nodeByKeyValue.set(keyValue, true);
1054 }
1055 for (i = -1; ++i < n; ) {
1056 if (i in keyValues && nodeByKeyValue.get(keyValues[i]) !== true) {
1057 exitNodes[i] = group[i];
1058 }
1059 }
1060 } else {
1061 for (i = -1; ++i < n0; ) {
1062 node = group[i];
1063 nodeData = groupData[i];
1064 if (node) {
1065 node.__data__ = nodeData;
1066 updateNodes[i] = node;
1067 } else {
1068 enterNodes[i] = d3_selection_dataNode(nodeData);
1069 }
1070 }
1071 for (;i < m; ++i) {
1072 enterNodes[i] = d3_selection_dataNode(groupData[i]);
1073 }
1074 for (;i < n; ++i) {
1075 exitNodes[i] = group[i];
1076 }
1077 }
1078 enterNodes.update = updateNodes;
1079 enterNodes.parentNode = updateNodes.parentNode = exitNodes.parentNode = group.parentNode;
1080 enter.push(enterNodes);
1081 update.push(updateNodes);
1082 exit.push(exitNodes);
1083 }
1084 var enter = d3_selection_enter([]), update = d3_selection([]), exit = d3_selection([]);
1085 if (typeof value === "function") {
1086 while (++i < n) {
1087 bind(group = this[i], value.call(group, group.parentNode.__data__, i));
1088 }
1089 } else {
1090 while (++i < n) {
1091 bind(group = this[i], value);
1092 }
1093 }
1094 update.enter = function() {
1095 return enter;
1096 };
1097 update.exit = function() {
1098 return exit;
1099 };
1100 return update;
1101 };
1102 function d3_selection_dataNode(data) {
1103 return {
1104 __data__: data
1105 };
1106 }
1107 d3_selectionPrototype.datum = function(value) {
1108 return arguments.length ? this.property("__data__", value) : this.property("__data__");
1109 };
1110 d3_selectionPrototype.filter = function(filter) {
1111 var subgroups = [], subgroup, group, node;
1112 if (typeof filter !== "function") filter = d3_selection_filter(filter);
1113 for (var j = 0, m = this.length; j < m; j++) {
1114 subgroups.push(subgroup = []);
1115 subgroup.parentNode = (group = this[j]).parentNode;
1116 for (var i = 0, n = group.length; i < n; i++) {
1117 if ((node = group[i]) && filter.call(node, node.__data__, i, j)) {
1118 subgroup.push(node);
1119 }
1120 }
1121 }
1122 return d3_selection(subgroups);
1123 };
1124 function d3_selection_filter(selector) {
1125 return function() {
1126 return d3_selectMatches(this, selector);
1127 };
1128 }
1129 d3_selectionPrototype.order = function() {
1130 for (var j = -1, m = this.length; ++j < m; ) {
1131 for (var group = this[j], i = group.length - 1, next = group[i], node; --i >= 0; ) {
1132 if (node = group[i]) {
1133 if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next);
1134 next = node;
1135 }
1136 }
1137 }
1138 return this;
1139 };
1140 d3_selectionPrototype.sort = function(comparator) {
1141 comparator = d3_selection_sortComparator.apply(this, arguments);
1142 for (var j = -1, m = this.length; ++j < m; ) this[j].sort(comparator);
1143 return this.order();
1144 };
1145 function d3_selection_sortComparator(comparator) {
1146 if (!arguments.length) comparator = d3_ascending;
1147 return function(a, b) {
1148 return a && b ? comparator(a.__data__, b.__data__) : !a - !b;
1149 };
1150 }
1151 d3_selectionPrototype.each = function(callback) {
1152 return d3_selection_each(this, function(node, i, j) {
1153 callback.call(node, node.__data__, i, j);
1154 });
1155 };
1156 function d3_selection_each(groups, callback) {
1157 for (var j = 0, m = groups.length; j < m; j++) {
1158 for (var group = groups[j], i = 0, n = group.length, node; i < n; i++) {
1159 if (node = group[i]) callback(node, i, j);
1160 }
1161 }
1162 return groups;
1163 }
1164 d3_selectionPrototype.call = function(callback) {
1165 var args = d3_array(arguments);
1166 callback.apply(args[0] = this, args);
1167 return this;
1168 };
1169 d3_selectionPrototype.empty = function() {
1170 return !this.node();
1171 };
1172 d3_selectionPrototype.node = function() {
1173 for (var j = 0, m = this.length; j < m; j++) {
1174 for (var group = this[j], i = 0, n = group.length; i < n; i++) {
1175 var node = group[i];
1176 if (node) return node;
1177 }
1178 }
1179 return null;
1180 };
1181 d3_selectionPrototype.size = function() {
1182 var n = 0;
1183 d3_selection_each(this, function() {
1184 ++n;
1185 });
1186 return n;
1187 };
1188 function d3_selection_enter(selection) {
1189 d3_subclass(selection, d3_selection_enterPrototype);
1190 return selection;
1191 }
1192 var d3_selection_enterPrototype = [];
1193 d3.selection.enter = d3_selection_enter;
1194 d3.selection.enter.prototype = d3_selection_enterPrototype;
1195 d3_selection_enterPrototype.append = d3_selectionPrototype.append;
1196 d3_selection_enterPrototype.empty = d3_selectionPrototype.empty;
1197 d3_selection_enterPrototype.node = d3_selectionPrototype.node;
1198 d3_selection_enterPrototype.call = d3_selectionPrototype.call;
1199 d3_selection_enterPrototype.size = d3_selectionPrototype.size;
1200 d3_selection_enterPrototype.select = function(selector) {
1201 var subgroups = [], subgroup, subnode, upgroup, group, node;
1202 for (var j = -1, m = this.length; ++j < m; ) {
1203 upgroup = (group = this[j]).update;
1204 subgroups.push(subgroup = []);
1205 subgroup.parentNode = group.parentNode;
1206 for (var i = -1, n = group.length; ++i < n; ) {
1207 if (node = group[i]) {
1208 subgroup.push(upgroup[i] = subnode = selector.call(group.parentNode, node.__data__, i, j));
1209 subnode.__data__ = node.__data__;
1210 } else {
1211 subgroup.push(null);
1212 }
1213 }
1214 }
1215 return d3_selection(subgroups);
1216 };
1217 d3_selection_enterPrototype.insert = function(name, before) {
1218 if (arguments.length < 2) before = d3_selection_enterInsertBefore(this);
1219 return d3_selectionPrototype.insert.call(this, name, before);
1220 };
1221 function d3_selection_enterInsertBefore(enter) {
1222 var i0, j0;
1223 return function(d, i, j) {
1224 var group = enter[j].update, n = group.length, node;
1225 if (j != j0) j0 = j, i0 = 0;
1226 if (i >= i0) i0 = i + 1;
1227 while (!(node = group[i0]) && ++i0 < n) ;
1228 return node;
1229 };
1230 }
1231 d3.select = function(node) {
1232 var group;
1233 if (typeof node === "string") {
1234 group = [ d3_select(node, d3_document) ];
1235 group.parentNode = d3_document.documentElement;
1236 } else {
1237 group = [ node ];
1238 group.parentNode = d3_documentElement(node);
1239 }
1240 return d3_selection([ group ]);
1241 };
1242 d3.selectAll = function(nodes) {
1243 var group;
1244 if (typeof nodes === "string") {
1245 group = d3_array(d3_selectAll(nodes, d3_document));
1246 group.parentNode = d3_document.documentElement;
1247 } else {
1248 group = d3_array(nodes);
1249 group.parentNode = null;
1250 }
1251 return d3_selection([ group ]);
1252 };
1253 d3_selectionPrototype.on = function(type, listener, capture) {
1254 var n = arguments.length;
1255 if (n < 3) {
1256 if (typeof type !== "string") {
1257 if (n < 2) listener = false;
1258 for (capture in type) this.each(d3_selection_on(capture, type[capture], listener));
1259 return this;
1260 }
1261 if (n < 2) return (n = this.node()["__on" + type]) && n._;
1262 capture = false;
1263 }
1264 return this.each(d3_selection_on(type, listener, capture));
1265 };
1266 function d3_selection_on(type, listener, capture) {
1267 var name = "__on" + type, i = type.indexOf("."), wrap = d3_selection_onListener;
1268 if (i > 0) type = type.slice(0, i);
1269 var filter = d3_selection_onFilters.get(type);
1270 if (filter) type = filter, wrap = d3_selection_onFilter;
1271 function onRemove() {
1272 var l = this[name];
1273 if (l) {
1274 this.removeEventListener(type, l, l.$);
1275 delete this[name];
1276 }
1277 }
1278 function onAdd() {
1279 var l = wrap(listener, d3_array(arguments));
1280 onRemove.call(this);
1281 this.addEventListener(type, this[name] = l, l.$ = capture);
1282 l._ = listener;
1283 }
1284 function removeAll() {
1285 var re = new RegExp("^__on([^.]+)" + d3.requote(type) + "$"), match;
1286 for (var name in this) {
1287 if (match = name.match(re)) {
1288 var l = this[name];
1289 this.removeEventListener(match[1], l, l.$);
1290 delete this[name];
1291 }
1292 }
1293 }
1294 return i ? listener ? onAdd : onRemove : listener ? d3_noop : removeAll;
1295 }
1296 var d3_selection_onFilters = d3.map({
1297 mouseenter: "mouseover",
1298 mouseleave: "mouseout"
1299 });
1300 if (d3_document) {
1301 d3_selection_onFilters.forEach(function(k) {
1302 if ("on" + k in d3_document) d3_selection_onFilters.remove(k);
1303 });
1304 }
1305 function d3_selection_onListener(listener, argumentz) {
1306 return function(e) {
1307 var o = d3.event;
1308 d3.event = e;
1309 argumentz[0] = this.__data__;
1310 try {
1311 listener.apply(this, argumentz);
1312 } finally {
1313 d3.event = o;
1314 }
1315 };
1316 }
1317 function d3_selection_onFilter(listener, argumentz) {
1318 var l = d3_selection_onListener(listener, argumentz);
1319 return function(e) {
1320 var target = this, related = e.relatedTarget;
1321 if (!related || related !== target && !(related.compareDocumentPosition(target) & 8)) {
1322 l.call(target, e);
1323 }
1324 };
1325 }
1326 var d3_event_dragSelect, d3_event_dragId = 0;
1327 function d3_event_dragSuppress(node) {
1328 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);
1329 if (d3_event_dragSelect == null) {
1330 d3_event_dragSelect = "onselectstart" in node ? false : d3_vendorSymbol(node.style, "userSelect");
1331 }
1332 if (d3_event_dragSelect) {
1333 var style = d3_documentElement(node).style, select = style[d3_event_dragSelect];
1334 style[d3_event_dragSelect] = "none";
1335 }
1336 return function(suppressClick) {
1337 w.on(name, null);
1338 if (d3_event_dragSelect) style[d3_event_dragSelect] = select;
1339 if (suppressClick) {
1340 var off = function() {
1341 w.on(click, null);
1342 };
1343 w.on(click, function() {
1344 d3_eventPreventDefault();
1345 off();
1346 }, true);
1347 setTimeout(off, 0);
1348 }
1349 };
1350 }
1351 d3.mouse = function(container) {
1352 return d3_mousePoint(container, d3_eventSource());
1353 };
1354 var d3_mouse_bug44083 = this.navigator && /WebKit/.test(this.navigator.userAgent) ? -1 : 0;
1355 function d3_mousePoint(container, e) {
1356 if (e.changedTouches) e = e.changedTouches[0];
1357 var svg = container.ownerSVGElement || container;
1358 if (svg.createSVGPoint) {
1359 var point = svg.createSVGPoint();
1360 if (d3_mouse_bug44083 < 0) {
1361 var window = d3_window(container);
1362 if (window.scrollX || window.scrollY) {
1363 svg = d3.select("body").append("svg").style({
1364 position: "absolute",
1365 top: 0,
1366 left: 0,
1367 margin: 0,
1368 padding: 0,
1369 border: "none"
1370 }, "important");
1371 var ctm = svg[0][0].getScreenCTM();
1372 d3_mouse_bug44083 = !(ctm.f || ctm.e);
1373 svg.remove();
1374 }
1375 }
1376 if (d3_mouse_bug44083) point.x = e.pageX, point.y = e.pageY; else point.x = e.clientX,
1377 point.y = e.clientY;
1378 point = point.matrixTransform(container.getScreenCTM().inverse());
1379 return [ point.x, point.y ];
1380 }
1381 var rect = container.getBoundingClientRect();
1382 return [ e.clientX - rect.left - container.clientLeft, e.clientY - rect.top - container.clientTop ];
1383 }
1384 d3.touch = function(container, touches, identifier) {
1385 if (arguments.length < 3) identifier = touches, touches = d3_eventSource().changedTouches;
1386 if (touches) for (var i = 0, n = touches.length, touch; i < n; ++i) {
1387 if ((touch = touches[i]).identifier === identifier) {
1388 return d3_mousePoint(container, touch);
1389 }
1390 }
1391 };
1392 d3.behavior.drag = function() {
1393 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");
1394 function drag() {
1395 this.on("mousedown.drag", mousedown).on("touchstart.drag", touchstart);
1396 }
1397 function dragstart(id, position, subject, move, end) {
1398 return function() {
1399 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);
1400 if (origin) {
1401 dragOffset = origin.apply(that, arguments);
1402 dragOffset = [ dragOffset.x - position0[0], dragOffset.y - position0[1] ];
1403 } else {
1404 dragOffset = [ 0, 0 ];
1405 }
1406 dispatch({
1407 type: "dragstart"
1408 });
1409 function moved() {
1410 var position1 = position(parent, dragId), dx, dy;
1411 if (!position1) return;
1412 dx = position1[0] - position0[0];
1413 dy = position1[1] - position0[1];
1414 dragged |= dx | dy;
1415 position0 = position1;
1416 dispatch({
1417 type: "drag",
1418 x: position1[0] + dragOffset[0],
1419 y: position1[1] + dragOffset[1],
1420 dx: dx,
1421 dy: dy
1422 });
1423 }
1424 function ended() {
1425 if (!position(parent, dragId)) return;
1426 dragSubject.on(move + dragName, null).on(end + dragName, null);
1427 dragRestore(dragged);
1428 dispatch({
1429 type: "dragend"
1430 });
1431 }
1432 };
1433 }
1434 drag.origin = function(x) {
1435 if (!arguments.length) return origin;
1436 origin = x;
1437 return drag;
1438 };
1439 return d3.rebind(drag, event, "on");
1440 };
1441 function d3_behavior_dragTouchId() {
1442 return d3.event.changedTouches[0].identifier;
1443 }
1444 d3.touches = function(container, touches) {
1445 if (arguments.length < 2) touches = d3_eventSource().touches;
1446 return touches ? d3_array(touches).map(function(touch) {
1447 var point = d3_mousePoint(container, touch);
1448 point.identifier = touch.identifier;
1449 return point;
1450 }) : [];
1451 };
1452 var ε = 1e-6, ε2 = ε * ε, π = Math.PI, τ = 2 * π, τε = τ - ε, halfπ = π / 2, d3_radians = π / 180, d3_degrees = 180 / π;
1453 function d3_sgn(x) {
1454 return x > 0 ? 1 : x < 0 ? -1 : 0;
1455 }
1456 function d3_cross2d(a, b, c) {
1457 return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
1458 }
1459 function d3_acos(x) {
1460 return x > 1 ? 0 : x < -1 ? π : Math.acos(x);
1461 }
1462 function d3_asin(x) {
1463 return x > 1 ? halfπ : x < -1 ? -halfπ : Math.asin(x);
1464 }
1465 function d3_sinh(x) {
1466 return ((x = Math.exp(x)) - 1 / x) / 2;
1467 }
1468 function d3_cosh(x) {
1469 return ((x = Math.exp(x)) + 1 / x) / 2;
1470 }
1471 function d3_tanh(x) {
1472 return ((x = Math.exp(2 * x)) - 1) / (x + 1);
1473 }
1474 function d3_haversin(x) {
1475 return (x = Math.sin(x / 2)) * x;
1476 }
1477 var ρ = Math.SQRT2, ρ2 = 2, ρ4 = 4;
1478 d3.interpolateZoom = function(p0, p1) {
1479 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;
1480 if (d2 < ε2) {
1481 S = Math.log(w1 / w0) / ρ;
1482 i = function(t) {
1483 return [ ux0 + t * dx, uy0 + t * dy, w0 * Math.exp(ρ * t * S) ];
1484 };
1485 } else {
1486 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);
1487 S = (r1 - r0) / ρ;
1488 i = function(t) {
1489 var s = t * S, coshr0 = d3_cosh(r0), u = w0 / (ρ2 * d1) * (coshr0 * d3_tanh(ρ * s + r0) - d3_sinh(r0));
1490 return [ ux0 + u * dx, uy0 + u * dy, w0 * coshr0 / d3_cosh(ρ * s + r0) ];
1491 };
1492 }
1493 i.duration = S * 1e3;
1494 return i;
1495 };
1496 d3.behavior.zoom = function() {
1497 var view = {
1498 x: 0,
1499 y: 0,
1500 k: 1
1501 }, 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;
1502 if (!d3_behavior_zoomWheel) {
1503 d3_behavior_zoomWheel = "onwheel" in d3_document ? (d3_behavior_zoomDelta = function() {
1504 return -d3.event.deltaY * (d3.event.deltaMode ? 120 : 1);
1505 }, "wheel") : "onmousewheel" in d3_document ? (d3_behavior_zoomDelta = function() {
1506 return d3.event.wheelDelta;
1507 }, "mousewheel") : (d3_behavior_zoomDelta = function() {
1508 return -d3.event.detail;
1509 }, "MozMousePixelScroll");
1510 }
1511 function zoom(g) {
1512 g.on(mousedown, mousedowned).on(d3_behavior_zoomWheel + ".zoom", mousewheeled).on("dblclick.zoom", dblclicked).on(touchstart, touchstarted);
1513 }
1514 zoom.event = function(g) {
1515 g.each(function() {
1516 var dispatch = event.of(this, arguments), view1 = view;
1517 if (d3_transitionInheritId) {
1518 d3.select(this).transition().each("start.zoom", function() {
1519 view = this.__chart__ || {
1520 x: 0,
1521 y: 0,
1522 k: 1
1523 };
1524 zoomstarted(dispatch);
1525 }).tween("zoom:zoom", function() {
1526 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 ]);
1527 return function(t) {
1528 var l = i(t), k = dx / l[2];
1529 this.__chart__ = view = {
1530 x: cx - l[0] * k,
1531 y: cy - l[1] * k,
1532 k: k
1533 };
1534 zoomed(dispatch);
1535 };
1536 }).each("interrupt.zoom", function() {
1537 zoomended(dispatch);
1538 }).each("end.zoom", function() {
1539 zoomended(dispatch);
1540 });
1541 } else {
1542 this.__chart__ = view;
1543 zoomstarted(dispatch);
1544 zoomed(dispatch);
1545 zoomended(dispatch);
1546 }
1547 });
1548 };
1549 zoom.translate = function(_) {
1550 if (!arguments.length) return [ view.x, view.y ];
1551 view = {
1552 x: +_[0],
1553 y: +_[1],
1554 k: view.k
1555 };
1556 rescale();
1557 return zoom;
1558 };
1559 zoom.scale = function(_) {
1560 if (!arguments.length) return view.k;
1561 view = {
1562 x: view.x,
1563 y: view.y,
1564 k: null
1565 };
1566 scaleTo(+_);
1567 rescale();
1568 return zoom;
1569 };
1570 zoom.scaleExtent = function(_) {
1571 if (!arguments.length) return scaleExtent;
1572 scaleExtent = _ == null ? d3_behavior_zoomInfinity : [ +_[0], +_[1] ];
1573 return zoom;
1574 };
1575 zoom.center = function(_) {
1576 if (!arguments.length) return center;
1577 center = _ && [ +_[0], +_[1] ];
1578 return zoom;
1579 };
1580 zoom.size = function(_) {
1581 if (!arguments.length) return size;
1582 size = _ && [ +_[0], +_[1] ];
1583 return zoom;
1584 };
1585 zoom.duration = function(_) {
1586 if (!arguments.length) return duration;
1587 duration = +_;
1588 return zoom;
1589 };
1590 zoom.x = function(z) {
1591 if (!arguments.length) return x1;
1592 x1 = z;
1593 x0 = z.copy();
1594 view = {
1595 x: 0,
1596 y: 0,
1597 k: 1
1598 };
1599 return zoom;
1600 };
1601 zoom.y = function(z) {
1602 if (!arguments.length) return y1;
1603 y1 = z;
1604 y0 = z.copy();
1605 view = {
1606 x: 0,
1607 y: 0,
1608 k: 1
1609 };
1610 return zoom;
1611 };
1612 function location(p) {
1613 return [ (p[0] - view.x) / view.k, (p[1] - view.y) / view.k ];
1614 }
1615 function point(l) {
1616 return [ l[0] * view.k + view.x, l[1] * view.k + view.y ];
1617 }
1618 function scaleTo(s) {
1619 view.k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], s));
1620 }
1621 function translateTo(p, l) {
1622 l = point(l);
1623 view.x += p[0] - l[0];
1624 view.y += p[1] - l[1];
1625 }
1626 function zoomTo(that, p, l, k) {
1627 that.__chart__ = {
1628 x: view.x,
1629 y: view.y,
1630 k: view.k
1631 };
1632 scaleTo(Math.pow(2, k));
1633 translateTo(center0 = p, l);
1634 that = d3.select(that);
1635 if (duration > 0) that = that.transition().duration(duration);
1636 that.call(zoom.event);
1637 }
1638 function rescale() {
1639 if (x1) x1.domain(x0.range().map(function(x) {
1640 return (x - view.x) / view.k;
1641 }).map(x0.invert));
1642 if (y1) y1.domain(y0.range().map(function(y) {
1643 return (y - view.y) / view.k;
1644 }).map(y0.invert));
1645 }
1646 function zoomstarted(dispatch) {
1647 if (!zooming++) dispatch({
1648 type: "zoomstart"
1649 });
1650 }
1651 function zoomed(dispatch) {
1652 rescale();
1653 dispatch({
1654 type: "zoom",
1655 scale: view.k,
1656 translate: [ view.x, view.y ]
1657 });
1658 }
1659 function zoomended(dispatch) {
1660 if (!--zooming) dispatch({
1661 type: "zoomend"
1662 }), center0 = null;
1663 }
1664 function mousedowned() {
1665 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);
1666 d3_selection_interrupt.call(that);
1667 zoomstarted(dispatch);
1668 function moved() {
1669 dragged = 1;
1670 translateTo(d3.mouse(that), location0);
1671 zoomed(dispatch);
1672 }
1673 function ended() {
1674 subject.on(mousemove, null).on(mouseup, null);
1675 dragRestore(dragged);
1676 zoomended(dispatch);
1677 }
1678 }
1679 function touchstarted() {
1680 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);
1681 started();
1682 zoomstarted(dispatch);
1683 subject.on(mousedown, null).on(touchstart, started);
1684 function relocate() {
1685 var touches = d3.touches(that);
1686 scale0 = view.k;
1687 touches.forEach(function(t) {
1688 if (t.identifier in locations0) locations0[t.identifier] = location(t);
1689 });
1690 return touches;
1691 }
1692 function started() {
1693 var target = d3.event.target;
1694 d3.select(target).on(touchmove, moved).on(touchend, ended);
1695 targets.push(target);
1696 var changed = d3.event.changedTouches;
1697 for (var i = 0, n = changed.length; i < n; ++i) {
1698 locations0[changed[i].identifier] = null;
1699 }
1700 var touches = relocate(), now = Date.now();
1701 if (touches.length === 1) {
1702 if (now - touchtime < 500) {
1703 var p = touches[0];
1704 zoomTo(that, p, locations0[p.identifier], Math.floor(Math.log(view.k) / Math.LN2) + 1);
1705 d3_eventPreventDefault();
1706 }
1707 touchtime = now;
1708 } else if (touches.length > 1) {
1709 var p = touches[0], q = touches[1], dx = p[0] - q[0], dy = p[1] - q[1];
1710 distance0 = dx * dx + dy * dy;
1711 }
1712 }
1713 function moved() {
1714 var touches = d3.touches(that), p0, l0, p1, l1;
1715 d3_selection_interrupt.call(that);
1716 for (var i = 0, n = touches.length; i < n; ++i, l1 = null) {
1717 p1 = touches[i];
1718 if (l1 = locations0[p1.identifier]) {
1719 if (l0) break;
1720 p0 = p1, l0 = l1;
1721 }
1722 }
1723 if (l1) {
1724 var distance1 = (distance1 = p1[0] - p0[0]) * distance1 + (distance1 = p1[1] - p0[1]) * distance1, scale1 = distance0 && Math.sqrt(distance1 / distance0);
1725 p0 = [ (p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2 ];
1726 l0 = [ (l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2 ];
1727 scaleTo(scale1 * scale0);
1728 }
1729 touchtime = null;
1730 translateTo(p0, l0);
1731 zoomed(dispatch);
1732 }
1733 function ended() {
1734 if (d3.event.touches.length) {
1735 var changed = d3.event.changedTouches;
1736 for (var i = 0, n = changed.length; i < n; ++i) {
1737 delete locations0[changed[i].identifier];
1738 }
1739 for (var identifier in locations0) {
1740 return void relocate();
1741 }
1742 }
1743 d3.selectAll(targets).on(zoomName, null);
1744 subject.on(mousedown, mousedowned).on(touchstart, touchstarted);
1745 dragRestore();
1746 zoomended(dispatch);
1747 }
1748 }
1749 function mousewheeled() {
1750 var dispatch = event.of(this, arguments);
1751 if (mousewheelTimer) clearTimeout(mousewheelTimer); else d3_selection_interrupt.call(this),
1752 translate0 = location(center0 = center || d3.mouse(this)), zoomstarted(dispatch);
1753 mousewheelTimer = setTimeout(function() {
1754 mousewheelTimer = null;
1755 zoomended(dispatch);
1756 }, 50);
1757 d3_eventPreventDefault();
1758 scaleTo(Math.pow(2, d3_behavior_zoomDelta() * .002) * view.k);
1759 translateTo(center0, translate0);
1760 zoomed(dispatch);
1761 }
1762 function dblclicked() {
1763 var p = d3.mouse(this), k = Math.log(view.k) / Math.LN2;
1764 zoomTo(this, p, location(p), d3.event.shiftKey ? Math.ceil(k) - 1 : Math.floor(k) + 1);
1765 }
1766 return d3.rebind(zoom, event, "on");
1767 };
1768 var d3_behavior_zoomInfinity = [ 0, Infinity ], d3_behavior_zoomDelta, d3_behavior_zoomWheel;
1769 d3.color = d3_color;
1770 function d3_color() {}
1771 d3_color.prototype.toString = function() {
1772 return this.rgb() + "";
1773 };
1774 d3.hsl = d3_hsl;
1775 function d3_hsl(h, s, l) {
1776 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);
1777 }
1778 var d3_hslPrototype = d3_hsl.prototype = new d3_color();
1779 d3_hslPrototype.brighter = function(k) {
1780 k = Math.pow(.7, arguments.length ? k : 1);
1781 return new d3_hsl(this.h, this.s, this.l / k);
1782 };
1783 d3_hslPrototype.darker = function(k) {
1784 k = Math.pow(.7, arguments.length ? k : 1);
1785 return new d3_hsl(this.h, this.s, k * this.l);
1786 };
1787 d3_hslPrototype.rgb = function() {
1788 return d3_hsl_rgb(this.h, this.s, this.l);
1789 };
1790 function d3_hsl_rgb(h, s, l) {
1791 var m1, m2;
1792 h = isNaN(h) ? 0 : (h %= 360) < 0 ? h + 360 : h;
1793 s = isNaN(s) ? 0 : s < 0 ? 0 : s > 1 ? 1 : s;
1794 l = l < 0 ? 0 : l > 1 ? 1 : l;
1795 m2 = l <= .5 ? l * (1 + s) : l + s - l * s;
1796 m1 = 2 * l - m2;
1797 function v(h) {
1798 if (h > 360) h -= 360; else if (h < 0) h += 360;
1799 if (h < 60) return m1 + (m2 - m1) * h / 60;
1800 if (h < 180) return m2;
1801 if (h < 240) return m1 + (m2 - m1) * (240 - h) / 60;
1802 return m1;
1803 }
1804 function vv(h) {
1805 return Math.round(v(h) * 255);
1806 }
1807 return new d3_rgb(vv(h + 120), vv(h), vv(h - 120));
1808 }
1809 d3.hcl = d3_hcl;
1810 function d3_hcl(h, c, l) {
1811 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);
1812 }
1813 var d3_hclPrototype = d3_hcl.prototype = new d3_color();
1814 d3_hclPrototype.brighter = function(k) {
1815 return new d3_hcl(this.h, this.c, Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)));
1816 };
1817 d3_hclPrototype.darker = function(k) {
1818 return new d3_hcl(this.h, this.c, Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)));
1819 };
1820 d3_hclPrototype.rgb = function() {
1821 return d3_hcl_lab(this.h, this.c, this.l).rgb();
1822 };
1823 function d3_hcl_lab(h, c, l) {
1824 if (isNaN(h)) h = 0;
1825 if (isNaN(c)) c = 0;
1826 return new d3_lab(l, Math.cos(h *= d3_radians) * c, Math.sin(h) * c);
1827 }
1828 d3.lab = d3_lab;
1829 function d3_lab(l, a, b) {
1830 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);
1831 }
1832 var d3_lab_K = 18;
1833 var d3_lab_X = .95047, d3_lab_Y = 1, d3_lab_Z = 1.08883;
1834 var d3_labPrototype = d3_lab.prototype = new d3_color();
1835 d3_labPrototype.brighter = function(k) {
1836 return new d3_lab(Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)), this.a, this.b);
1837 };
1838 d3_labPrototype.darker = function(k) {
1839 return new d3_lab(Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)), this.a, this.b);
1840 };
1841 d3_labPrototype.rgb = function() {
1842 return d3_lab_rgb(this.l, this.a, this.b);
1843 };
1844 function d3_lab_rgb(l, a, b) {
1845 var y = (l + 16) / 116, x = y + a / 500, z = y - b / 200;
1846 x = d3_lab_xyz(x) * d3_lab_X;
1847 y = d3_lab_xyz(y) * d3_lab_Y;
1848 z = d3_lab_xyz(z) * d3_lab_Z;
1849 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));
1850 }
1851 function d3_lab_hcl(l, a, b) {
1852 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);
1853 }
1854 function d3_lab_xyz(x) {
1855 return x > .206893034 ? x * x * x : (x - 4 / 29) / 7.787037;
1856 }
1857 function d3_xyz_lab(x) {
1858 return x > .008856 ? Math.pow(x, 1 / 3) : 7.787037 * x + 4 / 29;
1859 }
1860 function d3_xyz_rgb(r) {
1861 return Math.round(255 * (r <= .00304 ? 12.92 * r : 1.055 * Math.pow(r, 1 / 2.4) - .055));
1862 }
1863 d3.rgb = d3_rgb;
1864 function d3_rgb(r, g, b) {
1865 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);
1866 }
1867 function d3_rgbNumber(value) {
1868 return new d3_rgb(value >> 16, value >> 8 & 255, value & 255);
1869 }
1870 function d3_rgbString(value) {
1871 return d3_rgbNumber(value) + "";
1872 }
1873 var d3_rgbPrototype = d3_rgb.prototype = new d3_color();
1874 d3_rgbPrototype.brighter = function(k) {
1875 k = Math.pow(.7, arguments.length ? k : 1);
1876 var r = this.r, g = this.g, b = this.b, i = 30;
1877 if (!r && !g && !b) return new d3_rgb(i, i, i);
1878 if (r && r < i) r = i;
1879 if (g && g < i) g = i;
1880 if (b && b < i) b = i;
1881 return new d3_rgb(Math.min(255, r / k), Math.min(255, g / k), Math.min(255, b / k));
1882 };
1883 d3_rgbPrototype.darker = function(k) {
1884 k = Math.pow(.7, arguments.length ? k : 1);
1885 return new d3_rgb(k * this.r, k * this.g, k * this.b);
1886 };
1887 d3_rgbPrototype.hsl = function() {
1888 return d3_rgb_hsl(this.r, this.g, this.b);
1889 };
1890 d3_rgbPrototype.toString = function() {
1891 return "#" + d3_rgb_hex(this.r) + d3_rgb_hex(this.g) + d3_rgb_hex(this.b);
1892 };
1893 function d3_rgb_hex(v) {
1894 return v < 16 ? "0" + Math.max(0, v).toString(16) : Math.min(255, v).toString(16);
1895 }
1896 function d3_rgb_parse(format, rgb, hsl) {
1897 var r = 0, g = 0, b = 0, m1, m2, color;
1898 m1 = /([a-z]+)\((.*)\)/.exec(format = format.toLowerCase());
1899 if (m1) {
1900 m2 = m1[2].split(",");
1901 switch (m1[1]) {
1902 case "hsl":
1903 {
1904 return hsl(parseFloat(m2[0]), parseFloat(m2[1]) / 100, parseFloat(m2[2]) / 100);
1905 }
1906
1907 case "rgb":
1908 {
1909 return rgb(d3_rgb_parseNumber(m2[0]), d3_rgb_parseNumber(m2[1]), d3_rgb_parseNumber(m2[2]));
1910 }
1911 }
1912 }
1913 if (color = d3_rgb_names.get(format)) {
1914 return rgb(color.r, color.g, color.b);
1915 }
1916 if (format != null && format.charAt(0) === "#" && !isNaN(color = parseInt(format.slice(1), 16))) {
1917 if (format.length === 4) {
1918 r = (color & 3840) >> 4;
1919 r = r >> 4 | r;
1920 g = color & 240;
1921 g = g >> 4 | g;
1922 b = color & 15;
1923 b = b << 4 | b;
1924 } else if (format.length === 7) {
1925 r = (color & 16711680) >> 16;
1926 g = (color & 65280) >> 8;
1927 b = color & 255;
1928 }
1929 }
1930 return rgb(r, g, b);
1931 }
1932 function d3_rgb_hsl(r, g, b) {
1933 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;
1934 if (d) {
1935 s = l < .5 ? d / (max + min) : d / (2 - max - min);
1936 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;
1937 h *= 60;
1938 } else {
1939 h = NaN;
1940 s = l > 0 && l < 1 ? 0 : h;
1941 }
1942 return new d3_hsl(h, s, l);
1943 }
1944 function d3_rgb_lab(r, g, b) {
1945 r = d3_rgb_xyz(r);
1946 g = d3_rgb_xyz(g);
1947 b = d3_rgb_xyz(b);
1948 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);
1949 return d3_lab(116 * y - 16, 500 * (x - y), 200 * (y - z));
1950 }
1951 function d3_rgb_xyz(r) {
1952 return (r /= 255) <= .04045 ? r / 12.92 : Math.pow((r + .055) / 1.055, 2.4);
1953 }
1954 function d3_rgb_parseNumber(c) {
1955 var f = parseFloat(c);
1956 return c.charAt(c.length - 1) === "%" ? Math.round(f * 2.55) : f;
1957 }
1958 var d3_rgb_names = d3.map({
1959 aliceblue: 15792383,
1960 antiquewhite: 16444375,
1961 aqua: 65535,
1962 aquamarine: 8388564,
1963 azure: 15794175,
1964 beige: 16119260,
1965 bisque: 16770244,
1966 black: 0,
1967 blanchedalmond: 16772045,
1968 blue: 255,
1969 blueviolet: 9055202,
1970 brown: 10824234,
1971 burlywood: 14596231,
1972 cadetblue: 6266528,
1973 chartreuse: 8388352,
1974 chocolate: 13789470,
1975 coral: 16744272,
1976 cornflowerblue: 6591981,
1977 cornsilk: 16775388,
1978 crimson: 14423100,
1979 cyan: 65535,
1980 darkblue: 139,
1981 darkcyan: 35723,
1982 darkgoldenrod: 12092939,
1983 darkgray: 11119017,
1984 darkgreen: 25600,
1985 darkgrey: 11119017,
1986 darkkhaki: 12433259,
1987 darkmagenta: 9109643,
1988 darkolivegreen: 5597999,
1989 darkorange: 16747520,
1990 darkorchid: 10040012,
1991 darkred: 9109504,
1992 darksalmon: 15308410,
1993 darkseagreen: 9419919,
1994 darkslateblue: 4734347,
1995 darkslategray: 3100495,
1996 darkslategrey: 3100495,
1997 darkturquoise: 52945,
1998 darkviolet: 9699539,
1999 deeppink: 16716947,
2000 deepskyblue: 49151,
2001 dimgray: 6908265,
2002 dimgrey: 6908265,
2003 dodgerblue: 2003199,
2004 firebrick: 11674146,
2005 floralwhite: 16775920,
2006 forestgreen: 2263842,
2007 fuchsia: 16711935,
2008 gainsboro: 14474460,
2009 ghostwhite: 16316671,
2010 gold: 16766720,
2011 goldenrod: 14329120,
2012 gray: 8421504,
2013 green: 32768,
2014 greenyellow: 11403055,
2015 grey: 8421504,
2016 honeydew: 15794160,
2017 hotpink: 16738740,
2018 indianred: 13458524,
2019 indigo: 4915330,
2020 ivory: 16777200,
2021 khaki: 15787660,
2022 lavender: 15132410,
2023 lavenderblush: 16773365,
2024 lawngreen: 8190976,
2025 lemonchiffon: 16775885,
2026 lightblue: 11393254,
2027 lightcoral: 15761536,
2028 lightcyan: 14745599,
2029 lightgoldenrodyellow: 16448210,
2030 lightgray: 13882323,
2031 lightgreen: 9498256,
2032 lightgrey: 13882323,
2033 lightpink: 16758465,
2034 lightsalmon: 16752762,
2035 lightseagreen: 2142890,
2036 lightskyblue: 8900346,
2037 lightslategray: 7833753,
2038 lightslategrey: 7833753,
2039 lightsteelblue: 11584734,
2040 lightyellow: 16777184,
2041 lime: 65280,
2042 limegreen: 3329330,
2043 linen: 16445670,
2044 magenta: 16711935,
2045 maroon: 8388608,
2046 mediumaquamarine: 6737322,
2047 mediumblue: 205,
2048 mediumorchid: 12211667,
2049 mediumpurple: 9662683,
2050 mediumseagreen: 3978097,
2051 mediumslateblue: 8087790,
2052 mediumspringgreen: 64154,
2053 mediumturquoise: 4772300,
2054 mediumvioletred: 13047173,
2055 midnightblue: 1644912,
2056 mintcream: 16121850,
2057 mistyrose: 16770273,
2058 moccasin: 16770229,
2059 navajowhite: 16768685,
2060 navy: 128,
2061 oldlace: 16643558,
2062 olive: 8421376,
2063 olivedrab: 7048739,
2064 orange: 16753920,
2065 orangered: 16729344,
2066 orchid: 14315734,
2067 palegoldenrod: 15657130,
2068 palegreen: 10025880,
2069 paleturquoise: 11529966,
2070 palevioletred: 14381203,
2071 papayawhip: 16773077,
2072 peachpuff: 16767673,
2073 peru: 13468991,
2074 pink: 16761035,
2075 plum: 14524637,
2076 powderblue: 11591910,
2077 purple: 8388736,
2078 rebeccapurple: 6697881,
2079 red: 16711680,
2080 rosybrown: 12357519,
2081 royalblue: 4286945,
2082 saddlebrown: 9127187,
2083 salmon: 16416882,
2084 sandybrown: 16032864,
2085 seagreen: 3050327,
2086 seashell: 16774638,
2087 sienna: 10506797,
2088 silver: 12632256,
2089 skyblue: 8900331,
2090 slateblue: 6970061,
2091 slategray: 7372944,
2092 slategrey: 7372944,
2093 snow: 16775930,
2094 springgreen: 65407,
2095 steelblue: 4620980,
2096 tan: 13808780,
2097 teal: 32896,
2098 thistle: 14204888,
2099 tomato: 16737095,
2100 turquoise: 4251856,
2101 violet: 15631086,
2102 wheat: 16113331,
2103 white: 16777215,
2104 whitesmoke: 16119285,
2105 yellow: 16776960,
2106 yellowgreen: 10145074
2107 });
2108 d3_rgb_names.forEach(function(key, value) {
2109 d3_rgb_names.set(key, d3_rgbNumber(value));
2110 });
2111 function d3_functor(v) {
2112 return typeof v === "function" ? v : function() {
2113 return v;
2114 };
2115 }
2116 d3.functor = d3_functor;
2117 d3.xhr = d3_xhrType(d3_identity);
2118 function d3_xhrType(response) {
2119 return function(url, mimeType, callback) {
2120 if (arguments.length === 2 && typeof mimeType === "function") callback = mimeType,
2121 mimeType = null;
2122 return d3_xhr(url, mimeType, response, callback);
2123 };
2124 }
2125 function d3_xhr(url, mimeType, response, callback) {
2126 var xhr = {}, dispatch = d3.dispatch("beforesend", "progress", "load", "error"), headers = {}, request = new XMLHttpRequest(), responseType = null;
2127 if (self.XDomainRequest && !("withCredentials" in request) && /^(http(s)?:)?\/\//.test(url)) request = new XDomainRequest();
2128 "onload" in request ? request.onload = request.onerror = respond : request.onreadystatechange = function() {
2129 request.readyState > 3 && respond();
2130 };
2131 function respond() {
2132 var status = request.status, result;
2133 if (!status && d3_xhrHasResponse(request) || status >= 200 && status < 300 || status === 304) {
2134 try {
2135 result = response.call(xhr, request);
2136 } catch (e) {
2137 dispatch.error.call(xhr, e);
2138 return;
2139 }
2140 dispatch.load.call(xhr, result);
2141 } else {
2142 dispatch.error.call(xhr, request);
2143 }
2144 }
2145 request.onprogress = function(event) {
2146 var o = d3.event;
2147 d3.event = event;
2148 try {
2149 dispatch.progress.call(xhr, request);
2150 } finally {
2151 d3.event = o;
2152 }
2153 };
2154 xhr.header = function(name, value) {
2155 name = (name + "").toLowerCase();
2156 if (arguments.length < 2) return headers[name];
2157 if (value == null) delete headers[name]; else headers[name] = value + "";
2158 return xhr;
2159 };
2160 xhr.mimeType = function(value) {
2161 if (!arguments.length) return mimeType;
2162 mimeType = value == null ? null : value + "";
2163 return xhr;
2164 };
2165 xhr.responseType = function(value) {
2166 if (!arguments.length) return responseType;
2167 responseType = value;
2168 return xhr;
2169 };
2170 xhr.response = function(value) {
2171 response = value;
2172 return xhr;
2173 };
2174 [ "get", "post" ].forEach(function(method) {
2175 xhr[method] = function() {
2176 return xhr.send.apply(xhr, [ method ].concat(d3_array(arguments)));
2177 };
2178 });
2179 xhr.send = function(method, data, callback) {
2180 if (arguments.length === 2 && typeof data === "function") callback = data, data = null;
2181 request.open(method, url, true);
2182 if (mimeType != null && !("accept" in headers)) headers["accept"] = mimeType + ",*/*";
2183 if (request.setRequestHeader) for (var name in headers) request.setRequestHeader(name, headers[name]);
2184 if (mimeType != null && request.overrideMimeType) request.overrideMimeType(mimeType);
2185 if (responseType != null) request.responseType = responseType;
2186 if (callback != null) xhr.on("error", callback).on("load", function(request) {
2187 callback(null, request);
2188 });
2189 dispatch.beforesend.call(xhr, request);
2190 request.send(data == null ? null : data);
2191 return xhr;
2192 };
2193 xhr.abort = function() {
2194 request.abort();
2195 return xhr;
2196 };
2197 d3.rebind(xhr, dispatch, "on");
2198 return callback == null ? xhr : xhr.get(d3_xhr_fixCallback(callback));
2199 }
2200 function d3_xhr_fixCallback(callback) {
2201 return callback.length === 1 ? function(error, request) {
2202 callback(error == null ? request : null);
2203 } : callback;
2204 }
2205 function d3_xhrHasResponse(request) {
2206 var type = request.responseType;
2207 return type && type !== "text" ? request.response : request.responseText;
2208 }
2209 d3.dsv = function(delimiter, mimeType) {
2210 var reFormat = new RegExp('["' + delimiter + "\n]"), delimiterCode = delimiter.charCodeAt(0);
2211 function dsv(url, row, callback) {
2212 if (arguments.length < 3) callback = row, row = null;
2213 var xhr = d3_xhr(url, mimeType, row == null ? response : typedResponse(row), callback);
2214 xhr.row = function(_) {
2215 return arguments.length ? xhr.response((row = _) == null ? response : typedResponse(_)) : row;
2216 };
2217 return xhr;
2218 }
2219 function response(request) {
2220 return dsv.parse(request.responseText);
2221 }
2222 function typedResponse(f) {
2223 return function(request) {
2224 return dsv.parse(request.responseText, f);
2225 };
2226 }
2227 dsv.parse = function(text, f) {
2228 var o;
2229 return dsv.parseRows(text, function(row, i) {
2230 if (o) return o(row, i - 1);
2231 var a = function(d) {
2232 var obj = {};
2233 var len = row.length;
2234 for (var k = 0; k < len; ++k) {
2235 obj[row[k]] = d[k];
2236 }
2237 return obj;
2238 };
2239 o = f ? function(row, i) {
2240 return f(a(row), i);
2241 } : a;
2242 });
2243 };
2244 dsv.parseRows = function(text, f) {
2245 var EOL = {}, EOF = {}, rows = [], N = text.length, I = 0, n = 0, t, eol;
2246 function token() {
2247 if (I >= N) return EOF;
2248 if (eol) return eol = false, EOL;
2249 var j = I;
2250 if (text.charCodeAt(j) === 34) {
2251 var i = j;
2252 while (i++ < N) {
2253 if (text.charCodeAt(i) === 34) {
2254 if (text.charCodeAt(i + 1) !== 34) break;
2255 ++i;
2256 }
2257 }
2258 I = i + 2;
2259 var c = text.charCodeAt(i + 1);
2260 if (c === 13) {
2261 eol = true;
2262 if (text.charCodeAt(i + 2) === 10) ++I;
2263 } else if (c === 10) {
2264 eol = true;
2265 }
2266 return text.slice(j + 1, i).replace(/""/g, '"');
2267 }
2268 while (I < N) {
2269 var c = text.charCodeAt(I++), k = 1;
2270 if (c === 10) eol = true; else if (c === 13) {
2271 eol = true;
2272 if (text.charCodeAt(I) === 10) ++I, ++k;
2273 } else if (c !== delimiterCode) continue;
2274 return text.slice(j, I - k);
2275 }
2276 return text.slice(j);
2277 }
2278 while ((t = token()) !== EOF) {
2279 var a = [];
2280 while (t !== EOL && t !== EOF) {
2281 a.push(t);
2282 t = token();
2283 }
2284 if (f && (a = f(a, n++)) == null) continue;
2285 rows.push(a);
2286 }
2287 return rows;
2288 };
2289 dsv.format = function(rows) {
2290 if (Array.isArray(rows[0])) return dsv.formatRows(rows);
2291 var fieldSet = new d3_Set(), fields = [];
2292 rows.forEach(function(row) {
2293 for (var field in row) {
2294 if (!fieldSet.has(field)) {
2295 fields.push(fieldSet.add(field));
2296 }
2297 }
2298 });
2299 return [ fields.map(formatValue).join(delimiter) ].concat(rows.map(function(row) {
2300 return fields.map(function(field) {
2301 return formatValue(row[field]);
2302 }).join(delimiter);
2303 })).join("\n");
2304 };
2305 dsv.formatRows = function(rows) {
2306 return rows.map(formatRow).join("\n");
2307 };
2308 function formatRow(row) {
2309 return row.map(formatValue).join(delimiter);
2310 }
2311 function formatValue(text) {
2312 return reFormat.test(text) ? '"' + text.replace(/\"/g, '""') + '"' : text;
2313 }
2314 return dsv;
2315 };
2316 d3.csv = d3.dsv(",", "text/csv");
2317 d3.tsv = d3.dsv(" ", "text/tab-separated-values");
2318 var d3_timer_queueHead, d3_timer_queueTail, d3_timer_interval, d3_timer_timeout, d3_timer_frame = this[d3_vendorSymbol(this, "requestAnimationFrame")] || function(callback) {
2319 setTimeout(callback, 17);
2320 };
2321 d3.timer = function() {
2322 d3_timer.apply(this, arguments);
2323 };
2324 function d3_timer(callback, delay, then) {
2325 var n = arguments.length;
2326 if (n < 2) delay = 0;
2327 if (n < 3) then = Date.now();
2328 var time = then + delay, timer = {
2329 c: callback,
2330 t: time,
2331 n: null
2332 };
2333 if (d3_timer_queueTail) d3_timer_queueTail.n = timer; else d3_timer_queueHead = timer;
2334 d3_timer_queueTail = timer;
2335 if (!d3_timer_interval) {
2336 d3_timer_timeout = clearTimeout(d3_timer_timeout);
2337 d3_timer_interval = 1;
2338 d3_timer_frame(d3_timer_step);
2339 }
2340 return timer;
2341 }
2342 function d3_timer_step() {
2343 var now = d3_timer_mark(), delay = d3_timer_sweep() - now;
2344 if (delay > 24) {
2345 if (isFinite(delay)) {
2346 clearTimeout(d3_timer_timeout);
2347 d3_timer_timeout = setTimeout(d3_timer_step, delay);
2348 }
2349 d3_timer_interval = 0;
2350 } else {
2351 d3_timer_interval = 1;
2352 d3_timer_frame(d3_timer_step);
2353 }
2354 }
2355 d3.timer.flush = function() {
2356 d3_timer_mark();
2357 d3_timer_sweep();
2358 };
2359 function d3_timer_mark() {
2360 var now = Date.now(), timer = d3_timer_queueHead;
2361 while (timer) {
2362 if (now >= timer.t && timer.c(now - timer.t)) timer.c = null;
2363 timer = timer.n;
2364 }
2365 return now;
2366 }
2367 function d3_timer_sweep() {
2368 var t0, t1 = d3_timer_queueHead, time = Infinity;
2369 while (t1) {
2370 if (t1.c) {
2371 if (t1.t < time) time = t1.t;
2372 t1 = (t0 = t1).n;
2373 } else {
2374 t1 = t0 ? t0.n = t1.n : d3_timer_queueHead = t1.n;
2375 }
2376 }
2377 d3_timer_queueTail = t0;
2378 return time;
2379 }
2380 d3.round = function(x, n) {
2381 return n ? Math.round(x * (n = Math.pow(10, n))) / n : Math.round(x);
2382 };
2383 d3.geom = {};
2384 function d3_geom_pointX(d) {
2385 return d[0];
2386 }
2387 function d3_geom_pointY(d) {
2388 return d[1];
2389 }
2390 d3.geom.hull = function(vertices) {
2391 var x = d3_geom_pointX, y = d3_geom_pointY;
2392 if (arguments.length) return hull(vertices);
2393 function hull(data) {
2394 if (data.length < 3) return [];
2395 var fx = d3_functor(x), fy = d3_functor(y), i, n = data.length, points = [], flippedPoints = [];
2396 for (i = 0; i < n; i++) {
2397 points.push([ +fx.call(this, data[i], i), +fy.call(this, data[i], i), i ]);
2398 }
2399 points.sort(d3_geom_hullOrder);
2400 for (i = 0; i < n; i++) flippedPoints.push([ points[i][0], -points[i][1] ]);
2401 var upper = d3_geom_hullUpper(points), lower = d3_geom_hullUpper(flippedPoints);
2402 var skipLeft = lower[0] === upper[0], skipRight = lower[lower.length - 1] === upper[upper.length - 1], polygon = [];
2403 for (i = upper.length - 1; i >= 0; --i) polygon.push(data[points[upper[i]][2]]);
2404 for (i = +skipLeft; i < lower.length - skipRight; ++i) polygon.push(data[points[lower[i]][2]]);
2405 return polygon;
2406 }
2407 hull.x = function(_) {
2408 return arguments.length ? (x = _, hull) : x;
2409 };
2410 hull.y = function(_) {
2411 return arguments.length ? (y = _, hull) : y;
2412 };
2413 return hull;
2414 };
2415 function d3_geom_hullUpper(points) {
2416 var n = points.length, hull = [ 0, 1 ], hs = 2;
2417 for (var i = 2; i < n; i++) {
2418 while (hs > 1 && d3_cross2d(points[hull[hs - 2]], points[hull[hs - 1]], points[i]) <= 0) --hs;
2419 hull[hs++] = i;
2420 }
2421 return hull.slice(0, hs);
2422 }
2423 function d3_geom_hullOrder(a, b) {
2424 return a[0] - b[0] || a[1] - b[1];
2425 }
2426 d3.geom.polygon = function(coordinates) {
2427 d3_subclass(coordinates, d3_geom_polygonPrototype);
2428 return coordinates;
2429 };
2430 var d3_geom_polygonPrototype = d3.geom.polygon.prototype = [];
2431 d3_geom_polygonPrototype.area = function() {
2432 var i = -1, n = this.length, a, b = this[n - 1], area = 0;
2433 while (++i < n) {
2434 a = b;
2435 b = this[i];
2436 area += a[1] * b[0] - a[0] * b[1];
2437 }
2438 return area * .5;
2439 };
2440 d3_geom_polygonPrototype.centroid = function(k) {
2441 var i = -1, n = this.length, x = 0, y = 0, a, b = this[n - 1], c;
2442 if (!arguments.length) k = -1 / (6 * this.area());
2443 while (++i < n) {
2444 a = b;
2445 b = this[i];
2446 c = a[0] * b[1] - b[0] * a[1];
2447 x += (a[0] + b[0]) * c;
2448 y += (a[1] + b[1]) * c;
2449 }
2450 return [ x * k, y * k ];
2451 };
2452 d3_geom_polygonPrototype.clip = function(subject) {
2453 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;
2454 while (++i < n) {
2455 input = subject.slice();
2456 subject.length = 0;
2457 b = this[i];
2458 c = input[(m = input.length - closed) - 1];
2459 j = -1;
2460 while (++j < m) {
2461 d = input[j];
2462 if (d3_geom_polygonInside(d, a, b)) {
2463 if (!d3_geom_polygonInside(c, a, b)) {
2464 subject.push(d3_geom_polygonIntersect(c, d, a, b));
2465 }
2466 subject.push(d);
2467 } else if (d3_geom_polygonInside(c, a, b)) {
2468 subject.push(d3_geom_polygonIntersect(c, d, a, b));
2469 }
2470 c = d;
2471 }
2472 if (closed) subject.push(subject[0]);
2473 a = b;
2474 }
2475 return subject;
2476 };
2477 function d3_geom_polygonInside(p, a, b) {
2478 return (b[0] - a[0]) * (p[1] - a[1]) < (b[1] - a[1]) * (p[0] - a[0]);
2479 }
2480 function d3_geom_polygonIntersect(c, d, a, b) {
2481 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);
2482 return [ x1 + ua * x21, y1 + ua * y21 ];
2483 }
2484 function d3_geom_polygonClosed(coordinates) {
2485 var a = coordinates[0], b = coordinates[coordinates.length - 1];
2486 return !(a[0] - b[0] || a[1] - b[1]);
2487 }
2488 var d3_geom_voronoiEdges, d3_geom_voronoiCells, d3_geom_voronoiBeaches, d3_geom_voronoiBeachPool = [], d3_geom_voronoiFirstCircle, d3_geom_voronoiCircles, d3_geom_voronoiCirclePool = [];
2489 function d3_geom_voronoiBeach() {
2490 d3_geom_voronoiRedBlackNode(this);
2491 this.edge = this.site = this.circle = null;
2492 }
2493 function d3_geom_voronoiCreateBeach(site) {
2494 var beach = d3_geom_voronoiBeachPool.pop() || new d3_geom_voronoiBeach();
2495 beach.site = site;
2496 return beach;
2497 }
2498 function d3_geom_voronoiDetachBeach(beach) {
2499 d3_geom_voronoiDetachCircle(beach);
2500 d3_geom_voronoiBeaches.remove(beach);
2501 d3_geom_voronoiBeachPool.push(beach);
2502 d3_geom_voronoiRedBlackNode(beach);
2503 }
2504 function d3_geom_voronoiRemoveBeach(beach) {
2505 var circle = beach.circle, x = circle.x, y = circle.cy, vertex = {
2506 x: x,
2507 y: y
2508 }, previous = beach.P, next = beach.N, disappearing = [ beach ];
2509 d3_geom_voronoiDetachBeach(beach);
2510 var lArc = previous;
2511 while (lArc.circle && abs(x - lArc.circle.x) < ε && abs(y - lArc.circle.cy) < ε) {
2512 previous = lArc.P;
2513 disappearing.unshift(lArc);
2514 d3_geom_voronoiDetachBeach(lArc);
2515 lArc = previous;
2516 }
2517 disappearing.unshift(lArc);
2518 d3_geom_voronoiDetachCircle(lArc);
2519 var rArc = next;
2520 while (rArc.circle && abs(x - rArc.circle.x) < ε && abs(y - rArc.circle.cy) < ε) {
2521 next = rArc.N;
2522 disappearing.push(rArc);
2523 d3_geom_voronoiDetachBeach(rArc);
2524 rArc = next;
2525 }
2526 disappearing.push(rArc);
2527 d3_geom_voronoiDetachCircle(rArc);
2528 var nArcs = disappearing.length, iArc;
2529 for (iArc = 1; iArc < nArcs; ++iArc) {
2530 rArc = disappearing[iArc];
2531 lArc = disappearing[iArc - 1];
2532 d3_geom_voronoiSetEdgeEnd(rArc.edge, lArc.site, rArc.site, vertex);
2533 }
2534 lArc = disappearing[0];
2535 rArc = disappearing[nArcs - 1];
2536 rArc.edge = d3_geom_voronoiCreateEdge(lArc.site, rArc.site, null, vertex);
2537 d3_geom_voronoiAttachCircle(lArc);
2538 d3_geom_voronoiAttachCircle(rArc);
2539 }
2540 function d3_geom_voronoiAddBeach(site) {
2541 var x = site.x, directrix = site.y, lArc, rArc, dxl, dxr, node = d3_geom_voronoiBeaches._;
2542 while (node) {
2543 dxl = d3_geom_voronoiLeftBreakPoint(node, directrix) - x;
2544 if (dxl > ε) node = node.L; else {
2545 dxr = x - d3_geom_voronoiRightBreakPoint(node, directrix);
2546 if (dxr > ε) {
2547 if (!node.R) {
2548 lArc = node;
2549 break;
2550 }
2551 node = node.R;
2552 } else {
2553 if (dxl > -ε) {
2554 lArc = node.P;
2555 rArc = node;
2556 } else if (dxr > -ε) {
2557 lArc = node;
2558 rArc = node.N;
2559 } else {
2560 lArc = rArc = node;
2561 }
2562 break;
2563 }
2564 }
2565 }
2566 var newArc = d3_geom_voronoiCreateBeach(site);
2567 d3_geom_voronoiBeaches.insert(lArc, newArc);
2568 if (!lArc && !rArc) return;
2569 if (lArc === rArc) {
2570 d3_geom_voronoiDetachCircle(lArc);
2571 rArc = d3_geom_voronoiCreateBeach(lArc.site);
2572 d3_geom_voronoiBeaches.insert(newArc, rArc);
2573 newArc.edge = rArc.edge = d3_geom_voronoiCreateEdge(lArc.site, newArc.site);
2574 d3_geom_voronoiAttachCircle(lArc);
2575 d3_geom_voronoiAttachCircle(rArc);
2576 return;
2577 }
2578 if (!rArc) {
2579 newArc.edge = d3_geom_voronoiCreateEdge(lArc.site, newArc.site);
2580 return;
2581 }
2582 d3_geom_voronoiDetachCircle(lArc);
2583 d3_geom_voronoiDetachCircle(rArc);
2584 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 = {
2585 x: (cy * hb - by * hc) / d + ax,
2586 y: (bx * hc - cx * hb) / d + ay
2587 };
2588 d3_geom_voronoiSetEdgeEnd(rArc.edge, lSite, rSite, vertex);
2589 newArc.edge = d3_geom_voronoiCreateEdge(lSite, site, null, vertex);
2590 rArc.edge = d3_geom_voronoiCreateEdge(site, rSite, null, vertex);
2591 d3_geom_voronoiAttachCircle(lArc);
2592 d3_geom_voronoiAttachCircle(rArc);
2593 }
2594 function d3_geom_voronoiLeftBreakPoint(arc, directrix) {
2595 var site = arc.site, rfocx = site.x, rfocy = site.y, pby2 = rfocy - directrix;
2596 if (!pby2) return rfocx;
2597 var lArc = arc.P;
2598 if (!lArc) return -Infinity;
2599 site = lArc.site;
2600 var lfocx = site.x, lfocy = site.y, plby2 = lfocy - directrix;
2601 if (!plby2) return lfocx;
2602 var hl = lfocx - rfocx, aby2 = 1 / pby2 - 1 / plby2, b = hl / plby2;
2603 if (aby2) return (-b + Math.sqrt(b * b - 2 * aby2 * (hl * hl / (-2 * plby2) - lfocy + plby2 / 2 + rfocy - pby2 / 2))) / aby2 + rfocx;
2604 return (rfocx + lfocx) / 2;
2605 }
2606 function d3_geom_voronoiRightBreakPoint(arc, directrix) {
2607 var rArc = arc.N;
2608 if (rArc) return d3_geom_voronoiLeftBreakPoint(rArc, directrix);
2609 var site = arc.site;
2610 return site.y === directrix ? site.x : Infinity;
2611 }
2612 function d3_geom_voronoiCell(site) {
2613 this.site = site;
2614 this.edges = [];
2615 }
2616 d3_geom_voronoiCell.prototype.prepare = function() {
2617 var halfEdges = this.edges, iHalfEdge = halfEdges.length, edge;
2618 while (iHalfEdge--) {
2619 edge = halfEdges[iHalfEdge].edge;
2620 if (!edge.b || !edge.a) halfEdges.splice(iHalfEdge, 1);
2621 }
2622 halfEdges.sort(d3_geom_voronoiHalfEdgeOrder);
2623 return halfEdges.length;
2624 };
2625 function d3_geom_voronoiCloseCells(extent) {
2626 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;
2627 while (iCell--) {
2628 cell = cells[iCell];
2629 if (!cell || !cell.prepare()) continue;
2630 halfEdges = cell.edges;
2631 nHalfEdges = halfEdges.length;
2632 iHalfEdge = 0;
2633 while (iHalfEdge < nHalfEdges) {
2634 end = halfEdges[iHalfEdge].end(), x3 = end.x, y3 = end.y;
2635 start = halfEdges[++iHalfEdge % nHalfEdges].start(), x2 = start.x, y2 = start.y;
2636 if (abs(x3 - x2) > ε || abs(y3 - y2) > ε) {
2637 halfEdges.splice(iHalfEdge, 0, new d3_geom_voronoiHalfEdge(d3_geom_voronoiCreateBorderEdge(cell.site, end, abs(x3 - x0) < ε && y1 - y3 > ε ? {
2638 x: x0,
2639 y: abs(x2 - x0) < ε ? y2 : y1
2640 } : abs(y3 - y1) < ε && x1 - x3 > ε ? {
2641 x: abs(y2 - y1) < ε ? x2 : x1,
2642 y: y1
2643 } : abs(x3 - x1) < ε && y3 - y0 > ε ? {
2644 x: x1,
2645 y: abs(x2 - x1) < ε ? y2 : y0
2646 } : abs(y3 - y0) < ε && x3 - x0 > ε ? {
2647 x: abs(y2 - y0) < ε ? x2 : x0,
2648 y: y0
2649 } : null), cell.site, null));
2650 ++nHalfEdges;
2651 }
2652 }
2653 }
2654 }
2655 function d3_geom_voronoiHalfEdgeOrder(a, b) {
2656 return b.angle - a.angle;
2657 }
2658 function d3_geom_voronoiCircle() {
2659 d3_geom_voronoiRedBlackNode(this);
2660 this.x = this.y = this.arc = this.site = this.cy = null;
2661 }
2662 function d3_geom_voronoiAttachCircle(arc) {
2663 var lArc = arc.P, rArc = arc.N;
2664 if (!lArc || !rArc) return;
2665 var lSite = lArc.site, cSite = arc.site, rSite = rArc.site;
2666 if (lSite === rSite) return;
2667 var bx = cSite.x, by = cSite.y, ax = lSite.x - bx, ay = lSite.y - by, cx = rSite.x - bx, cy = rSite.y - by;
2668 var d = 2 * (ax * cy - ay * cx);
2669 if (d >= -ε2) return;
2670 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;
2671 var circle = d3_geom_voronoiCirclePool.pop() || new d3_geom_voronoiCircle();
2672 circle.arc = arc;
2673 circle.site = cSite;
2674 circle.x = x + bx;
2675 circle.y = cy + Math.sqrt(x * x + y * y);
2676 circle.cy = cy;
2677 arc.circle = circle;
2678 var before = null, node = d3_geom_voronoiCircles._;
2679 while (node) {
2680 if (circle.y < node.y || circle.y === node.y && circle.x <= node.x) {
2681 if (node.L) node = node.L; else {
2682 before = node.P;
2683 break;
2684 }
2685 } else {
2686 if (node.R) node = node.R; else {
2687 before = node;
2688 break;
2689 }
2690 }
2691 }
2692 d3_geom_voronoiCircles.insert(before, circle);
2693 if (!before) d3_geom_voronoiFirstCircle = circle;
2694 }
2695 function d3_geom_voronoiDetachCircle(arc) {
2696 var circle = arc.circle;
2697 if (circle) {
2698 if (!circle.P) d3_geom_voronoiFirstCircle = circle.N;
2699 d3_geom_voronoiCircles.remove(circle);
2700 d3_geom_voronoiCirclePool.push(circle);
2701 d3_geom_voronoiRedBlackNode(circle);
2702 arc.circle = null;
2703 }
2704 }
2705 function d3_geom_clipLine(x0, y0, x1, y1) {
2706 return function(line) {
2707 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;
2708 r = x0 - ax;
2709 if (!dx && r > 0) return;
2710 r /= dx;
2711 if (dx < 0) {
2712 if (r < t0) return;
2713 if (r < t1) t1 = r;
2714 } else if (dx > 0) {
2715 if (r > t1) return;
2716 if (r > t0) t0 = r;
2717 }
2718 r = x1 - ax;
2719 if (!dx && r < 0) return;
2720 r /= dx;
2721 if (dx < 0) {
2722 if (r > t1) return;
2723 if (r > t0) t0 = r;
2724 } else if (dx > 0) {
2725 if (r < t0) return;
2726 if (r < t1) t1 = r;
2727 }
2728 r = y0 - ay;
2729 if (!dy && r > 0) return;
2730 r /= dy;
2731 if (dy < 0) {
2732 if (r < t0) return;
2733 if (r < t1) t1 = r;
2734 } else if (dy > 0) {
2735 if (r > t1) return;
2736 if (r > t0) t0 = r;
2737 }
2738 r = y1 - ay;
2739 if (!dy && r < 0) return;
2740 r /= dy;
2741 if (dy < 0) {
2742 if (r > t1) return;
2743 if (r > t0) t0 = r;
2744 } else if (dy > 0) {
2745 if (r < t0) return;
2746 if (r < t1) t1 = r;
2747 }
2748 if (t0 > 0) line.a = {
2749 x: ax + t0 * dx,
2750 y: ay + t0 * dy
2751 };
2752 if (t1 < 1) line.b = {
2753 x: ax + t1 * dx,
2754 y: ay + t1 * dy
2755 };
2756 return line;
2757 };
2758 }
2759 function d3_geom_voronoiClipEdges(extent) {
2760 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;
2761 while (i--) {
2762 e = edges[i];
2763 if (!d3_geom_voronoiConnectEdge(e, extent) || !clip(e) || abs(e.a.x - e.b.x) < ε && abs(e.a.y - e.b.y) < ε) {
2764 e.a = e.b = null;
2765 edges.splice(i, 1);
2766 }
2767 }
2768 }
2769 function d3_geom_voronoiConnectEdge(edge, extent) {
2770 var vb = edge.b;
2771 if (vb) return true;
2772 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;
2773 if (ry === ly) {
2774 if (fx < x0 || fx >= x1) return;
2775 if (lx > rx) {
2776 if (!va) va = {
2777 x: fx,
2778 y: y0
2779 }; else if (va.y >= y1) return;
2780 vb = {
2781 x: fx,
2782 y: y1
2783 };
2784 } else {
2785 if (!va) va = {
2786 x: fx,
2787 y: y1
2788 }; else if (va.y < y0) return;
2789 vb = {
2790 x: fx,
2791 y: y0
2792 };
2793 }
2794 } else {
2795 fm = (lx - rx) / (ry - ly);
2796 fb = fy - fm * fx;
2797 if (fm < -1 || fm > 1) {
2798 if (lx > rx) {
2799 if (!va) va = {
2800 x: (y0 - fb) / fm,
2801 y: y0
2802 }; else if (va.y >= y1) return;
2803 vb = {
2804 x: (y1 - fb) / fm,
2805 y: y1
2806 };
2807 } else {
2808 if (!va) va = {
2809 x: (y1 - fb) / fm,
2810 y: y1
2811 }; else if (va.y < y0) return;
2812 vb = {
2813 x: (y0 - fb) / fm,
2814 y: y0
2815 };
2816 }
2817 } else {
2818 if (ly < ry) {
2819 if (!va) va = {
2820 x: x0,
2821 y: fm * x0 + fb
2822 }; else if (va.x >= x1) return;
2823 vb = {
2824 x: x1,
2825 y: fm * x1 + fb
2826 };
2827 } else {
2828 if (!va) va = {
2829 x: x1,
2830 y: fm * x1 + fb
2831 }; else if (va.x < x0) return;
2832 vb = {
2833 x: x0,
2834 y: fm * x0 + fb
2835 };
2836 }
2837 }
2838 }
2839 edge.a = va;
2840 edge.b = vb;
2841 return true;
2842 }
2843 function d3_geom_voronoiEdge(lSite, rSite) {
2844 this.l = lSite;
2845 this.r = rSite;
2846 this.a = this.b = null;
2847 }
2848 function d3_geom_voronoiCreateEdge(lSite, rSite, va, vb) {
2849 var edge = new d3_geom_voronoiEdge(lSite, rSite);
2850 d3_geom_voronoiEdges.push(edge);
2851 if (va) d3_geom_voronoiSetEdgeEnd(edge, lSite, rSite, va);
2852 if (vb) d3_geom_voronoiSetEdgeEnd(edge, rSite, lSite, vb);
2853 d3_geom_voronoiCells[lSite.i].edges.push(new d3_geom_voronoiHalfEdge(edge, lSite, rSite));
2854 d3_geom_voronoiCells[rSite.i].edges.push(new d3_geom_voronoiHalfEdge(edge, rSite, lSite));
2855 return edge;
2856 }
2857 function d3_geom_voronoiCreateBorderEdge(lSite, va, vb) {
2858 var edge = new d3_geom_voronoiEdge(lSite, null);
2859 edge.a = va;
2860 edge.b = vb;
2861 d3_geom_voronoiEdges.push(edge);
2862 return edge;
2863 }
2864 function d3_geom_voronoiSetEdgeEnd(edge, lSite, rSite, vertex) {
2865 if (!edge.a && !edge.b) {
2866 edge.a = vertex;
2867 edge.l = lSite;
2868 edge.r = rSite;
2869 } else if (edge.l === rSite) {
2870 edge.b = vertex;
2871 } else {
2872 edge.a = vertex;
2873 }
2874 }
2875 function d3_geom_voronoiHalfEdge(edge, lSite, rSite) {
2876 var va = edge.a, vb = edge.b;
2877 this.edge = edge;
2878 this.site = lSite;
2879 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);
2880 }
2881 d3_geom_voronoiHalfEdge.prototype = {
2882 start: function() {
2883 return this.edge.l === this.site ? this.edge.a : this.edge.b;
2884 },
2885 end: function() {
2886 return this.edge.l === this.site ? this.edge.b : this.edge.a;
2887 }
2888 };
2889 function d3_geom_voronoiRedBlackTree() {
2890 this._ = null;
2891 }
2892 function d3_geom_voronoiRedBlackNode(node) {
2893 node.U = node.C = node.L = node.R = node.P = node.N = null;
2894 }
2895 d3_geom_voronoiRedBlackTree.prototype = {
2896 insert: function(after, node) {
2897 var parent, grandpa, uncle;
2898 if (after) {
2899 node.P = after;
2900 node.N = after.N;
2901 if (after.N) after.N.P = node;
2902 after.N = node;
2903 if (after.R) {
2904 after = after.R;
2905 while (after.L) after = after.L;
2906 after.L = node;
2907 } else {
2908 after.R = node;
2909 }
2910 parent = after;
2911 } else if (this._) {
2912 after = d3_geom_voronoiRedBlackFirst(this._);
2913 node.P = null;
2914 node.N = after;
2915 after.P = after.L = node;
2916 parent = after;
2917 } else {
2918 node.P = node.N = null;
2919 this._ = node;
2920 parent = null;
2921 }
2922 node.L = node.R = null;
2923 node.U = parent;
2924 node.C = true;
2925 after = node;
2926 while (parent && parent.C) {
2927 grandpa = parent.U;
2928 if (parent === grandpa.L) {
2929 uncle = grandpa.R;
2930 if (uncle && uncle.C) {
2931 parent.C = uncle.C = false;
2932 grandpa.C = true;
2933 after = grandpa;
2934 } else {
2935 if (after === parent.R) {
2936 d3_geom_voronoiRedBlackRotateLeft(this, parent);
2937 after = parent;
2938 parent = after.U;
2939 }
2940 parent.C = false;
2941 grandpa.C = true;
2942 d3_geom_voronoiRedBlackRotateRight(this, grandpa);
2943 }
2944 } else {
2945 uncle = grandpa.L;
2946 if (uncle && uncle.C) {
2947 parent.C = uncle.C = false;
2948 grandpa.C = true;
2949 after = grandpa;
2950 } else {
2951 if (after === parent.L) {
2952 d3_geom_voronoiRedBlackRotateRight(this, parent);
2953 after = parent;
2954 parent = after.U;
2955 }
2956 parent.C = false;
2957 grandpa.C = true;
2958 d3_geom_voronoiRedBlackRotateLeft(this, grandpa);
2959 }
2960 }
2961 parent = after.U;
2962 }
2963 this._.C = false;
2964 },
2965 remove: function(node) {
2966 if (node.N) node.N.P = node.P;
2967 if (node.P) node.P.N = node.N;
2968 node.N = node.P = null;
2969 var parent = node.U, sibling, left = node.L, right = node.R, next, red;
2970 if (!left) next = right; else if (!right) next = left; else next = d3_geom_voronoiRedBlackFirst(right);
2971 if (parent) {
2972 if (parent.L === node) parent.L = next; else parent.R = next;
2973 } else {
2974 this._ = next;
2975 }
2976 if (left && right) {
2977 red = next.C;
2978 next.C = node.C;
2979 next.L = left;
2980 left.U = next;
2981 if (next !== right) {
2982 parent = next.U;
2983 next.U = node.U;
2984 node = next.R;
2985 parent.L = node;
2986 next.R = right;
2987 right.U = next;
2988 } else {
2989 next.U = parent;
2990 parent = next;
2991 node = next.R;
2992 }
2993 } else {
2994 red = node.C;
2995 node = next;
2996 }
2997 if (node) node.U = parent;
2998 if (red) return;
2999 if (node && node.C) {
3000 node.C = false;
3001 return;
3002 }
3003 do {
3004 if (node === this._) break;
3005 if (node === parent.L) {
3006 sibling = parent.R;
3007 if (sibling.C) {
3008 sibling.C = false;
3009 parent.C = true;
3010 d3_geom_voronoiRedBlackRotateLeft(this, parent);
3011 sibling = parent.R;
3012 }
3013 if (sibling.L && sibling.L.C || sibling.R && sibling.R.C) {
3014 if (!sibling.R || !sibling.R.C) {
3015 sibling.L.C = false;
3016 sibling.C = true;
3017 d3_geom_voronoiRedBlackRotateRight(this, sibling);
3018 sibling = parent.R;
3019 }
3020 sibling.C = parent.C;
3021 parent.C = sibling.R.C = false;
3022 d3_geom_voronoiRedBlackRotateLeft(this, parent);
3023 node = this._;
3024 break;
3025 }
3026 } else {
3027 sibling = parent.L;
3028 if (sibling.C) {
3029 sibling.C = false;
3030 parent.C = true;
3031 d3_geom_voronoiRedBlackRotateRight(this, parent);
3032 sibling = parent.L;
3033 }
3034 if (sibling.L && sibling.L.C || sibling.R && sibling.R.C) {
3035 if (!sibling.L || !sibling.L.C) {
3036 sibling.R.C = false;
3037 sibling.C = true;
3038 d3_geom_voronoiRedBlackRotateLeft(this, sibling);
3039 sibling = parent.L;
3040 }
3041 sibling.C = parent.C;
3042 parent.C = sibling.L.C = false;
3043 d3_geom_voronoiRedBlackRotateRight(this, parent);
3044 node = this._;
3045 break;
3046 }
3047 }
3048 sibling.C = true;
3049 node = parent;
3050 parent = parent.U;
3051 } while (!node.C);
3052 if (node) node.C = false;
3053 }
3054 };
3055 function d3_geom_voronoiRedBlackRotateLeft(tree, node) {
3056 var p = node, q = node.R, parent = p.U;
3057 if (parent) {
3058 if (parent.L === p) parent.L = q; else parent.R = q;
3059 } else {
3060 tree._ = q;
3061 }
3062 q.U = parent;
3063 p.U = q;
3064 p.R = q.L;
3065 if (p.R) p.R.U = p;
3066 q.L = p;
3067 }
3068 function d3_geom_voronoiRedBlackRotateRight(tree, node) {
3069 var p = node, q = node.L, parent = p.U;
3070 if (parent) {
3071 if (parent.L === p) parent.L = q; else parent.R = q;
3072 } else {
3073 tree._ = q;
3074 }
3075 q.U = parent;
3076 p.U = q;
3077 p.L = q.R;
3078 if (p.L) p.L.U = p;
3079 q.R = p;
3080 }
3081 function d3_geom_voronoiRedBlackFirst(node) {
3082 while (node.L) node = node.L;
3083 return node;
3084 }
3085 function d3_geom_voronoi(sites, bbox) {
3086 var site = sites.sort(d3_geom_voronoiVertexOrder).pop(), x0, y0, circle;
3087 d3_geom_voronoiEdges = [];
3088 d3_geom_voronoiCells = new Array(sites.length);
3089 d3_geom_voronoiBeaches = new d3_geom_voronoiRedBlackTree();
3090 d3_geom_voronoiCircles = new d3_geom_voronoiRedBlackTree();
3091 while (true) {
3092 circle = d3_geom_voronoiFirstCircle;
3093 if (site && (!circle || site.y < circle.y || site.y === circle.y && site.x < circle.x)) {
3094 if (site.x !== x0 || site.y !== y0) {
3095 d3_geom_voronoiCells[site.i] = new d3_geom_voronoiCell(site);
3096 d3_geom_voronoiAddBeach(site);
3097 x0 = site.x, y0 = site.y;
3098 }
3099 site = sites.pop();
3100 } else if (circle) {
3101 d3_geom_voronoiRemoveBeach(circle.arc);
3102 } else {
3103 break;
3104 }
3105 }
3106 if (bbox) d3_geom_voronoiClipEdges(bbox), d3_geom_voronoiCloseCells(bbox);
3107 var diagram = {
3108 cells: d3_geom_voronoiCells,
3109 edges: d3_geom_voronoiEdges
3110 };
3111 d3_geom_voronoiBeaches = d3_geom_voronoiCircles = d3_geom_voronoiEdges = d3_geom_voronoiCells = null;
3112 return diagram;
3113 }
3114 function d3_geom_voronoiVertexOrder(a, b) {
3115 return b.y - a.y || b.x - a.x;
3116 }
3117 d3.geom.voronoi = function(points) {
3118 var x = d3_geom_pointX, y = d3_geom_pointY, fx = x, fy = y, clipExtent = d3_geom_voronoiClipExtent;
3119 if (points) return voronoi(points);
3120 function voronoi(data) {
3121 var polygons = new Array(data.length), x0 = clipExtent[0][0], y0 = clipExtent[0][1], x1 = clipExtent[1][0], y1 = clipExtent[1][1];
3122 d3_geom_voronoi(sites(data), clipExtent).cells.forEach(function(cell, i) {
3123 var edges = cell.edges, site = cell.site, polygon = polygons[i] = edges.length ? edges.map(function(e) {
3124 var s = e.start();
3125 return [ s.x, s.y ];
3126 }) : site.x >= x0 && site.x <= x1 && site.y >= y0 && site.y <= y1 ? [ [ x0, y1 ], [ x1, y1 ], [ x1, y0 ], [ x0, y0 ] ] : [];
3127 polygon.point = data[i];
3128 });
3129 return polygons;
3130 }
3131 function sites(data) {
3132 return data.map(function(d, i) {
3133 return {
3134 x: Math.round(fx(d, i) / ε) * ε,
3135 y: Math.round(fy(d, i) / ε) * ε,
3136 i: i
3137 };
3138 });
3139 }
3140 voronoi.links = function(data) {
3141 return d3_geom_voronoi(sites(data)).edges.filter(function(edge) {
3142 return edge.l && edge.r;
3143 }).map(function(edge) {
3144 return {
3145 source: data[edge.l.i],
3146 target: data[edge.r.i]
3147 };
3148 });
3149 };
3150 voronoi.triangles = function(data) {
3151 var triangles = [];
3152 d3_geom_voronoi(sites(data)).cells.forEach(function(cell, i) {
3153 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;
3154 while (++j < m) {
3155 e0 = e1;
3156 s0 = s1;
3157 e1 = edges[j].edge;
3158 s1 = e1.l === site ? e1.r : e1.l;
3159 if (i < s0.i && i < s1.i && d3_geom_voronoiTriangleArea(site, s0, s1) < 0) {
3160 triangles.push([ data[i], data[s0.i], data[s1.i] ]);
3161 }
3162 }
3163 });
3164 return triangles;
3165 };
3166 voronoi.x = function(_) {
3167 return arguments.length ? (fx = d3_functor(x = _), voronoi) : x;
3168 };
3169 voronoi.y = function(_) {
3170 return arguments.length ? (fy = d3_functor(y = _), voronoi) : y;
3171 };
3172 voronoi.clipExtent = function(_) {
3173 if (!arguments.length) return clipExtent === d3_geom_voronoiClipExtent ? null : clipExtent;
3174 clipExtent = _ == null ? d3_geom_voronoiClipExtent : _;
3175 return voronoi;
3176 };
3177 voronoi.size = function(_) {
3178 if (!arguments.length) return clipExtent === d3_geom_voronoiClipExtent ? null : clipExtent && clipExtent[1];
3179 return voronoi.clipExtent(_ && [ [ 0, 0 ], _ ]);
3180 };
3181 return voronoi;
3182 };
3183 var d3_geom_voronoiClipExtent = [ [ -1e6, -1e6 ], [ 1e6, 1e6 ] ];
3184 function d3_geom_voronoiTriangleArea(a, b, c) {
3185 return (a.x - c.x) * (b.y - a.y) - (a.x - b.x) * (c.y - a.y);
3186 }
3187 d3.geom.delaunay = function(vertices) {
3188 return d3.geom.voronoi().triangles(vertices);
3189 };
3190 d3.geom.quadtree = function(points, x1, y1, x2, y2) {
3191 var x = d3_geom_pointX, y = d3_geom_pointY, compat;
3192 if (compat = arguments.length) {
3193 x = d3_geom_quadtreeCompatX;
3194 y = d3_geom_quadtreeCompatY;
3195 if (compat === 3) {
3196 y2 = y1;
3197 x2 = x1;
3198 y1 = x1 = 0;
3199 }
3200 return quadtree(points);
3201 }
3202 function quadtree(data) {
3203 var d, fx = d3_functor(x), fy = d3_functor(y), xs, ys, i, n, x1_, y1_, x2_, y2_;
3204 if (x1 != null) {
3205 x1_ = x1, y1_ = y1, x2_ = x2, y2_ = y2;
3206 } else {
3207 x2_ = y2_ = -(x1_ = y1_ = Infinity);
3208 xs = [], ys = [];
3209 n = data.length;
3210 if (compat) for (i = 0; i < n; ++i) {
3211 d = data[i];
3212 if (d.x < x1_) x1_ = d.x;
3213 if (d.y < y1_) y1_ = d.y;
3214 if (d.x > x2_) x2_ = d.x;
3215 if (d.y > y2_) y2_ = d.y;
3216 xs.push(d.x);
3217 ys.push(d.y);
3218 } else for (i = 0; i < n; ++i) {
3219 var x_ = +fx(d = data[i], i), y_ = +fy(d, i);
3220 if (x_ < x1_) x1_ = x_;
3221 if (y_ < y1_) y1_ = y_;
3222 if (x_ > x2_) x2_ = x_;
3223 if (y_ > y2_) y2_ = y_;
3224 xs.push(x_);
3225 ys.push(y_);
3226 }
3227 }
3228 var dx = x2_ - x1_, dy = y2_ - y1_;
3229 if (dx > dy) y2_ = y1_ + dx; else x2_ = x1_ + dy;
3230 function insert(n, d, x, y, x1, y1, x2, y2) {
3231 if (isNaN(x) || isNaN(y)) return;
3232 if (n.leaf) {
3233 var nx = n.x, ny = n.y;
3234 if (nx != null) {
3235 if (abs(nx - x) + abs(ny - y) < .01) {
3236 insertChild(n, d, x, y, x1, y1, x2, y2);
3237 } else {
3238 var nPoint = n.point;
3239 n.x = n.y = n.point = null;
3240 insertChild(n, nPoint, nx, ny, x1, y1, x2, y2);
3241 insertChild(n, d, x, y, x1, y1, x2, y2);
3242 }
3243 } else {
3244 n.x = x, n.y = y, n.point = d;
3245 }
3246 } else {
3247 insertChild(n, d, x, y, x1, y1, x2, y2);
3248 }
3249 }
3250 function insertChild(n, d, x, y, x1, y1, x2, y2) {
3251 var xm = (x1 + x2) * .5, ym = (y1 + y2) * .5, right = x >= xm, below = y >= ym, i = below << 1 | right;
3252 n.leaf = false;
3253 n = n.nodes[i] || (n.nodes[i] = d3_geom_quadtreeNode());
3254 if (right) x1 = xm; else x2 = xm;
3255 if (below) y1 = ym; else y2 = ym;
3256 insert(n, d, x, y, x1, y1, x2, y2);
3257 }
3258 var root = d3_geom_quadtreeNode();
3259 root.add = function(d) {
3260 insert(root, d, +fx(d, ++i), +fy(d, i), x1_, y1_, x2_, y2_);
3261 };
3262 root.visit = function(f) {
3263 d3_geom_quadtreeVisit(f, root, x1_, y1_, x2_, y2_);
3264 };
3265 root.find = function(point) {
3266 return d3_geom_quadtreeFind(root, point[0], point[1], x1_, y1_, x2_, y2_);
3267 };
3268 i = -1;
3269 if (x1 == null) {
3270 while (++i < n) {
3271 insert(root, data[i], xs[i], ys[i], x1_, y1_, x2_, y2_);
3272 }
3273 --i;
3274 } else data.forEach(root.add);
3275 xs = ys = data = d = null;
3276 return root;
3277 }
3278 quadtree.x = function(_) {
3279 return arguments.length ? (x = _, quadtree) : x;
3280 };
3281 quadtree.y = function(_) {
3282 return arguments.length ? (y = _, quadtree) : y;
3283 };
3284 quadtree.extent = function(_) {
3285 if (!arguments.length) return x1 == null ? null : [ [ x1, y1 ], [ x2, y2 ] ];
3286 if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = +_[0][0], y1 = +_[0][1], x2 = +_[1][0],
3287 y2 = +_[1][1];
3288 return quadtree;
3289 };
3290 quadtree.size = function(_) {
3291 if (!arguments.length) return x1 == null ? null : [ x2 - x1, y2 - y1 ];
3292 if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = y1 = 0, x2 = +_[0], y2 = +_[1];
3293 return quadtree;
3294 };
3295 return quadtree;
3296 };
3297 function d3_geom_quadtreeCompatX(d) {
3298 return d.x;
3299 }
3300 function d3_geom_quadtreeCompatY(d) {
3301 return d.y;
3302 }
3303 function d3_geom_quadtreeNode() {
3304 return {
3305 leaf: true,
3306 nodes: [],
3307 point: null,
3308 x: null,
3309 y: null
3310 };
3311 }
3312 function d3_geom_quadtreeVisit(f, node, x1, y1, x2, y2) {
3313 if (!f(node, x1, y1, x2, y2)) {
3314 var sx = (x1 + x2) * .5, sy = (y1 + y2) * .5, children = node.nodes;
3315 if (children[0]) d3_geom_quadtreeVisit(f, children[0], x1, y1, sx, sy);
3316 if (children[1]) d3_geom_quadtreeVisit(f, children[1], sx, y1, x2, sy);
3317 if (children[2]) d3_geom_quadtreeVisit(f, children[2], x1, sy, sx, y2);
3318 if (children[3]) d3_geom_quadtreeVisit(f, children[3], sx, sy, x2, y2);
3319 }
3320 }
3321 function d3_geom_quadtreeFind(root, x, y, x0, y0, x3, y3) {
3322 var minDistance2 = Infinity, closestPoint;
3323 (function find(node, x1, y1, x2, y2) {
3324 if (x1 > x3 || y1 > y3 || x2 < x0 || y2 < y0) return;
3325 if (point = node.point) {
3326 var point, dx = x - node.x, dy = y - node.y, distance2 = dx * dx + dy * dy;
3327 if (distance2 < minDistance2) {
3328 var distance = Math.sqrt(minDistance2 = distance2);
3329 x0 = x - distance, y0 = y - distance;
3330 x3 = x + distance, y3 = y + distance;
3331 closestPoint = point;
3332 }
3333 }
3334 var children = node.nodes, xm = (x1 + x2) * .5, ym = (y1 + y2) * .5, right = x >= xm, below = y >= ym;
3335 for (var i = below << 1 | right, j = i + 4; i < j; ++i) {
3336 if (node = children[i & 3]) switch (i & 3) {
3337 case 0:
3338 find(node, x1, y1, xm, ym);
3339 break;
3340
3341 case 1:
3342 find(node, xm, y1, x2, ym);
3343 break;
3344
3345 case 2:
3346 find(node, x1, ym, xm, y2);
3347 break;
3348
3349 case 3:
3350 find(node, xm, ym, x2, y2);
3351 break;
3352 }
3353 }
3354 })(root, x0, y0, x3, y3);
3355 return closestPoint;
3356 }
3357 d3.interpolateRgb = d3_interpolateRgb;
3358 function d3_interpolateRgb(a, b) {
3359 a = d3.rgb(a);
3360 b = d3.rgb(b);
3361 var ar = a.r, ag = a.g, ab = a.b, br = b.r - ar, bg = b.g - ag, bb = b.b - ab;
3362 return function(t) {
3363 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));
3364 };
3365 }
3366 d3.interpolateObject = d3_interpolateObject;
3367 function d3_interpolateObject(a, b) {
3368 var i = {}, c = {}, k;
3369 for (k in a) {
3370 if (k in b) {
3371 i[k] = d3_interpolate(a[k], b[k]);
3372 } else {
3373 c[k] = a[k];
3374 }
3375 }
3376 for (k in b) {
3377 if (!(k in a)) {
3378 c[k] = b[k];
3379 }
3380 }
3381 return function(t) {
3382 for (k in i) c[k] = i[k](t);
3383 return c;
3384 };
3385 }
3386 d3.interpolateNumber = d3_interpolateNumber;
3387 function d3_interpolateNumber(a, b) {
3388 a = +a, b = +b;
3389 return function(t) {
3390 return a * (1 - t) + b * t;
3391 };
3392 }
3393 d3.interpolateString = d3_interpolateString;
3394 function d3_interpolateString(a, b) {
3395 var bi = d3_interpolate_numberA.lastIndex = d3_interpolate_numberB.lastIndex = 0, am, bm, bs, i = -1, s = [], q = [];
3396 a = a + "", b = b + "";
3397 while ((am = d3_interpolate_numberA.exec(a)) && (bm = d3_interpolate_numberB.exec(b))) {
3398 if ((bs = bm.index) > bi) {
3399 bs = b.slice(bi, bs);
3400 if (s[i]) s[i] += bs; else s[++i] = bs;
3401 }
3402 if ((am = am[0]) === (bm = bm[0])) {
3403 if (s[i]) s[i] += bm; else s[++i] = bm;
3404 } else {
3405 s[++i] = null;
3406 q.push({
3407 i: i,
3408 x: d3_interpolateNumber(am, bm)
3409 });
3410 }
3411 bi = d3_interpolate_numberB.lastIndex;
3412 }
3413 if (bi < b.length) {
3414 bs = b.slice(bi);
3415 if (s[i]) s[i] += bs; else s[++i] = bs;
3416 }
3417 return s.length < 2 ? q[0] ? (b = q[0].x, function(t) {
3418 return b(t) + "";
3419 }) : function() {
3420 return b;
3421 } : (b = q.length, function(t) {
3422 for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t);
3423 return s.join("");
3424 });
3425 }
3426 var d3_interpolate_numberA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g, d3_interpolate_numberB = new RegExp(d3_interpolate_numberA.source, "g");
3427 d3.interpolate = d3_interpolate;
3428 function d3_interpolate(a, b) {
3429 var i = d3.interpolators.length, f;
3430 while (--i >= 0 && !(f = d3.interpolators[i](a, b))) ;
3431 return f;
3432 }
3433 d3.interpolators = [ function(a, b) {
3434 var t = typeof b;
3435 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);
3436 } ];
3437 d3.interpolateArray = d3_interpolateArray;
3438 function d3_interpolateArray(a, b) {
3439 var x = [], c = [], na = a.length, nb = b.length, n0 = Math.min(a.length, b.length), i;
3440 for (i = 0; i < n0; ++i) x.push(d3_interpolate(a[i], b[i]));
3441 for (;i < na; ++i) c[i] = a[i];
3442 for (;i < nb; ++i) c[i] = b[i];
3443 return function(t) {
3444 for (i = 0; i < n0; ++i) c[i] = x[i](t);
3445 return c;
3446 };
3447 }
3448 var d3_ease_default = function() {
3449 return d3_identity;
3450 };
3451 var d3_ease = d3.map({
3452 linear: d3_ease_default,
3453 poly: d3_ease_poly,
3454 quad: function() {
3455 return d3_ease_quad;
3456 },
3457 cubic: function() {
3458 return d3_ease_cubic;
3459 },
3460 sin: function() {
3461 return d3_ease_sin;
3462 },
3463 exp: function() {
3464 return d3_ease_exp;
3465 },
3466 circle: function() {
3467 return d3_ease_circle;
3468 },
3469 elastic: d3_ease_elastic,
3470 back: d3_ease_back,
3471 bounce: function() {
3472 return d3_ease_bounce;
3473 }
3474 });
3475 var d3_ease_mode = d3.map({
3476 "in": d3_identity,
3477 out: d3_ease_reverse,
3478 "in-out": d3_ease_reflect,
3479 "out-in": function(f) {
3480 return d3_ease_reflect(d3_ease_reverse(f));
3481 }
3482 });
3483 d3.ease = function(name) {
3484 var i = name.indexOf("-"), t = i >= 0 ? name.slice(0, i) : name, m = i >= 0 ? name.slice(i + 1) : "in";
3485 t = d3_ease.get(t) || d3_ease_default;
3486 m = d3_ease_mode.get(m) || d3_identity;
3487 return d3_ease_clamp(m(t.apply(null, d3_arraySlice.call(arguments, 1))));
3488 };
3489 function d3_ease_clamp(f) {
3490 return function(t) {
3491 return t <= 0 ? 0 : t >= 1 ? 1 : f(t);
3492 };
3493 }
3494 function d3_ease_reverse(f) {
3495 return function(t) {
3496 return 1 - f(1 - t);
3497 };
3498 }
3499 function d3_ease_reflect(f) {
3500 return function(t) {
3501 return .5 * (t < .5 ? f(2 * t) : 2 - f(2 - 2 * t));
3502 };
3503 }
3504 function d3_ease_quad(t) {
3505 return t * t;
3506 }
3507 function d3_ease_cubic(t) {
3508 return t * t * t;
3509 }
3510 function d3_ease_cubicInOut(t) {
3511 if (t <= 0) return 0;
3512 if (t >= 1) return 1;
3513 var t2 = t * t, t3 = t2 * t;
3514 return 4 * (t < .5 ? t3 : 3 * (t - t2) + t3 - .75);
3515 }
3516 function d3_ease_poly(e) {
3517 return function(t) {
3518 return Math.pow(t, e);
3519 };
3520 }
3521 function d3_ease_sin(t) {
3522 return 1 - Math.cos(t * halfπ);
3523 }
3524 function d3_ease_exp(t) {
3525 return Math.pow(2, 10 * (t - 1));
3526 }
3527 function d3_ease_circle(t) {
3528 return 1 - Math.sqrt(1 - t * t);
3529 }
3530 function d3_ease_elastic(a, p) {
3531 var s;
3532 if (arguments.length < 2) p = .45;
3533 if (arguments.length) s = p / τ * Math.asin(1 / a); else a = 1, s = p / 4;
3534 return function(t) {
3535 return 1 + a * Math.pow(2, -10 * t) * Math.sin((t - s) * τ / p);
3536 };
3537 }
3538 function d3_ease_back(s) {
3539 if (!s) s = 1.70158;
3540 return function(t) {
3541 return t * t * ((s + 1) * t - s);
3542 };
3543 }
3544 function d3_ease_bounce(t) {
3545 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;
3546 }
3547 d3.interpolateHcl = d3_interpolateHcl;
3548 function d3_interpolateHcl(a, b) {
3549 a = d3.hcl(a);
3550 b = d3.hcl(b);
3551 var ah = a.h, ac = a.c, al = a.l, bh = b.h - ah, bc = b.c - ac, bl = b.l - al;
3552 if (isNaN(bc)) bc = 0, ac = isNaN(ac) ? b.c : ac;
3553 if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360;
3554 return function(t) {
3555 return d3_hcl_lab(ah + bh * t, ac + bc * t, al + bl * t) + "";
3556 };
3557 }
3558 d3.interpolateHsl = d3_interpolateHsl;
3559 function d3_interpolateHsl(a, b) {
3560 a = d3.hsl(a);
3561 b = d3.hsl(b);
3562 var ah = a.h, as = a.s, al = a.l, bh = b.h - ah, bs = b.s - as, bl = b.l - al;
3563 if (isNaN(bs)) bs = 0, as = isNaN(as) ? b.s : as;
3564 if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360;
3565 return function(t) {
3566 return d3_hsl_rgb(ah + bh * t, as + bs * t, al + bl * t) + "";
3567 };
3568 }
3569 d3.interpolateLab = d3_interpolateLab;
3570 function d3_interpolateLab(a, b) {
3571 a = d3.lab(a);
3572 b = d3.lab(b);
3573 var al = a.l, aa = a.a, ab = a.b, bl = b.l - al, ba = b.a - aa, bb = b.b - ab;
3574 return function(t) {
3575 return d3_lab_rgb(al + bl * t, aa + ba * t, ab + bb * t) + "";
3576 };
3577 }
3578 d3.interpolateRound = d3_interpolateRound;
3579 function d3_interpolateRound(a, b) {
3580 b -= a;
3581 return function(t) {
3582 return Math.round(a + b * t);
3583 };
3584 }
3585 d3.transform = function(string) {
3586 var g = d3_document.createElementNS(d3.ns.prefix.svg, "g");
3587 return (d3.transform = function(string) {
3588 if (string != null) {
3589 g.setAttribute("transform", string);
3590 var t = g.transform.baseVal.consolidate();
3591 }
3592 return new d3_transform(t ? t.matrix : d3_transformIdentity);
3593 })(string);
3594 };
3595 function d3_transform(m) {
3596 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;
3597 if (r0[0] * r1[1] < r1[0] * r0[1]) {
3598 r0[0] *= -1;
3599 r0[1] *= -1;
3600 kx *= -1;
3601 kz *= -1;
3602 }
3603 this.rotate = (kx ? Math.atan2(r0[1], r0[0]) : Math.atan2(-r1[0], r1[1])) * d3_degrees;
3604 this.translate = [ m.e, m.f ];
3605 this.scale = [ kx, ky ];
3606 this.skew = ky ? Math.atan2(kz, ky) * d3_degrees : 0;
3607 }
3608 d3_transform.prototype.toString = function() {
3609 return "translate(" + this.translate + ")rotate(" + this.rotate + ")skewX(" + this.skew + ")scale(" + this.scale + ")";
3610 };
3611 function d3_transformDot(a, b) {
3612 return a[0] * b[0] + a[1] * b[1];
3613 }
3614 function d3_transformNormalize(a) {
3615 var k = Math.sqrt(d3_transformDot(a, a));
3616 if (k) {
3617 a[0] /= k;
3618 a[1] /= k;
3619 }
3620 return k;
3621 }
3622 function d3_transformCombine(a, b, k) {
3623 a[0] += k * b[0];
3624 a[1] += k * b[1];
3625 return a;
3626 }
3627 var d3_transformIdentity = {
3628 a: 1,
3629 b: 0,
3630 c: 0,
3631 d: 1,
3632 e: 0,
3633 f: 0
3634 };
3635 d3.interpolateTransform = d3_interpolateTransform;
3636 function d3_interpolateTransformPop(s) {
3637 return s.length ? s.pop() + "," : "";
3638 }
3639 function d3_interpolateTranslate(ta, tb, s, q) {
3640 if (ta[0] !== tb[0] || ta[1] !== tb[1]) {
3641 var i = s.push("translate(", null, ",", null, ")");
3642 q.push({
3643 i: i - 4,
3644 x: d3_interpolateNumber(ta[0], tb[0])
3645 }, {
3646 i: i - 2,
3647 x: d3_interpolateNumber(ta[1], tb[1])
3648 });
3649 } else if (tb[0] || tb[1]) {
3650 s.push("translate(" + tb + ")");
3651 }
3652 }
3653 function d3_interpolateRotate(ra, rb, s, q) {
3654 if (ra !== rb) {
3655 if (ra - rb > 180) rb += 360; else if (rb - ra > 180) ra += 360;
3656 q.push({
3657 i: s.push(d3_interpolateTransformPop(s) + "rotate(", null, ")") - 2,
3658 x: d3_interpolateNumber(ra, rb)
3659 });
3660 } else if (rb) {
3661 s.push(d3_interpolateTransformPop(s) + "rotate(" + rb + ")");
3662 }
3663 }
3664 function d3_interpolateSkew(wa, wb, s, q) {
3665 if (wa !== wb) {
3666 q.push({
3667 i: s.push(d3_interpolateTransformPop(s) + "skewX(", null, ")") - 2,
3668 x: d3_interpolateNumber(wa, wb)
3669 });
3670 } else if (wb) {
3671 s.push(d3_interpolateTransformPop(s) + "skewX(" + wb + ")");
3672 }
3673 }
3674 function d3_interpolateScale(ka, kb, s, q) {
3675 if (ka[0] !== kb[0] || ka[1] !== kb[1]) {
3676 var i = s.push(d3_interpolateTransformPop(s) + "scale(", null, ",", null, ")");
3677 q.push({
3678 i: i - 4,
3679 x: d3_interpolateNumber(ka[0], kb[0])
3680 }, {
3681 i: i - 2,
3682 x: d3_interpolateNumber(ka[1], kb[1])
3683 });
3684 } else if (kb[0] !== 1 || kb[1] !== 1) {
3685 s.push(d3_interpolateTransformPop(s) + "scale(" + kb + ")");
3686 }
3687 }
3688 function d3_interpolateTransform(a, b) {
3689 var s = [], q = [];
3690 a = d3.transform(a), b = d3.transform(b);
3691 d3_interpolateTranslate(a.translate, b.translate, s, q);
3692 d3_interpolateRotate(a.rotate, b.rotate, s, q);
3693 d3_interpolateSkew(a.skew, b.skew, s, q);
3694 d3_interpolateScale(a.scale, b.scale, s, q);
3695 a = b = null;
3696 return function(t) {
3697 var i = -1, n = q.length, o;
3698 while (++i < n) s[(o = q[i]).i] = o.x(t);
3699 return s.join("");
3700 };
3701 }
3702 function d3_uninterpolateNumber(a, b) {
3703 b = (b -= a = +a) || 1 / b;
3704 return function(x) {
3705 return (x - a) / b;
3706 };
3707 }
3708 function d3_uninterpolateClamp(a, b) {
3709 b = (b -= a = +a) || 1 / b;
3710 return function(x) {
3711 return Math.max(0, Math.min(1, (x - a) / b));
3712 };
3713 }
3714 d3.layout = {};
3715 d3.layout.bundle = function() {
3716 return function(links) {
3717 var paths = [], i = -1, n = links.length;
3718 while (++i < n) paths.push(d3_layout_bundlePath(links[i]));
3719 return paths;
3720 };
3721 };
3722 function d3_layout_bundlePath(link) {
3723 var start = link.source, end = link.target, lca = d3_layout_bundleLeastCommonAncestor(start, end), points = [ start ];
3724 while (start !== lca) {
3725 start = start.parent;
3726 points.push(start);
3727 }
3728 var k = points.length;
3729 while (end !== lca) {
3730 points.splice(k, 0, end);
3731 end = end.parent;
3732 }
3733 return points;
3734 }
3735 function d3_layout_bundleAncestors(node) {
3736 var ancestors = [], parent = node.parent;
3737 while (parent != null) {
3738 ancestors.push(node);
3739 node = parent;
3740 parent = parent.parent;
3741 }
3742 ancestors.push(node);
3743 return ancestors;
3744 }
3745 function d3_layout_bundleLeastCommonAncestor(a, b) {
3746 if (a === b) return a;
3747 var aNodes = d3_layout_bundleAncestors(a), bNodes = d3_layout_bundleAncestors(b), aNode = aNodes.pop(), bNode = bNodes.pop(), sharedNode = null;
3748 while (aNode === bNode) {
3749 sharedNode = aNode;
3750 aNode = aNodes.pop();
3751 bNode = bNodes.pop();
3752 }
3753 return sharedNode;
3754 }
3755 d3.layout.chord = function() {
3756 var chord = {}, chords, groups, matrix, n, padding = 0, sortGroups, sortSubgroups, sortChords;
3757 function relayout() {
3758 var subgroups = {}, groupSums = [], groupIndex = d3.range(n), subgroupIndex = [], k, x, x0, i, j;
3759 chords = [];
3760 groups = [];
3761 k = 0, i = -1;
3762 while (++i < n) {
3763 x = 0, j = -1;
3764 while (++j < n) {
3765 x += matrix[i][j];
3766 }
3767 groupSums.push(x);
3768 subgroupIndex.push(d3.range(n));
3769 k += x;
3770 }
3771 if (sortGroups) {
3772 groupIndex.sort(function(a, b) {
3773 return sortGroups(groupSums[a], groupSums[b]);
3774 });
3775 }
3776 if (sortSubgroups) {
3777 subgroupIndex.forEach(function(d, i) {
3778 d.sort(function(a, b) {
3779 return sortSubgroups(matrix[i][a], matrix[i][b]);
3780 });
3781 });
3782 }
3783 k = (τ - padding * n) / k;
3784 x = 0, i = -1;
3785 while (++i < n) {
3786 x0 = x, j = -1;
3787 while (++j < n) {
3788 var di = groupIndex[i], dj = subgroupIndex[di][j], v = matrix[di][dj], a0 = x, a1 = x += v * k;
3789 subgroups[di + "-" + dj] = {
3790 index: di,
3791 subindex: dj,
3792 startAngle: a0,
3793 endAngle: a1,
3794 value: v
3795 };
3796 }
3797 groups[di] = {
3798 index: di,
3799 startAngle: x0,
3800 endAngle: x,
3801 value: groupSums[di]
3802 };
3803 x += padding;
3804 }
3805 i = -1;
3806 while (++i < n) {
3807 j = i - 1;
3808 while (++j < n) {
3809 var source = subgroups[i + "-" + j], target = subgroups[j + "-" + i];
3810 if (source.value || target.value) {
3811 chords.push(source.value < target.value ? {
3812 source: target,
3813 target: source
3814 } : {
3815 source: source,
3816 target: target
3817 });
3818 }
3819 }
3820 }
3821 if (sortChords) resort();
3822 }
3823 function resort() {
3824 chords.sort(function(a, b) {
3825 return sortChords((a.source.value + a.target.value) / 2, (b.source.value + b.target.value) / 2);
3826 });
3827 }
3828 chord.matrix = function(x) {
3829 if (!arguments.length) return matrix;
3830 n = (matrix = x) && matrix.length;
3831 chords = groups = null;
3832 return chord;
3833 };
3834 chord.padding = function(x) {
3835 if (!arguments.length) return padding;
3836 padding = x;
3837 chords = groups = null;
3838 return chord;
3839 };
3840 chord.sortGroups = function(x) {
3841 if (!arguments.length) return sortGroups;
3842 sortGroups = x;
3843 chords = groups = null;
3844 return chord;
3845 };
3846 chord.sortSubgroups = function(x) {
3847 if (!arguments.length) return sortSubgroups;
3848 sortSubgroups = x;
3849 chords = null;
3850 return chord;
3851 };
3852 chord.sortChords = function(x) {
3853 if (!arguments.length) return sortChords;
3854 sortChords = x;
3855 if (chords) resort();
3856 return chord;
3857 };
3858 chord.chords = function() {
3859 if (!chords) relayout();
3860 return chords;
3861 };
3862 chord.groups = function() {
3863 if (!groups) relayout();
3864 return groups;
3865 };
3866 return chord;
3867 };
3868 d3.layout.force = function() {
3869 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;
3870 function repulse(node) {
3871 return function(quad, x1, _, x2) {
3872 if (quad.point !== node) {
3873 var dx = quad.cx - node.x, dy = quad.cy - node.y, dw = x2 - x1, dn = dx * dx + dy * dy;
3874 if (dw * dw / theta2 < dn) {
3875 if (dn < chargeDistance2) {
3876 var k = quad.charge / dn;
3877 node.px -= dx * k;
3878 node.py -= dy * k;
3879 }
3880 return true;
3881 }
3882 if (quad.point && dn && dn < chargeDistance2) {
3883 var k = quad.pointCharge / dn;
3884 node.px -= dx * k;
3885 node.py -= dy * k;
3886 }
3887 }
3888 return !quad.charge;
3889 };
3890 }
3891 force.tick = function() {
3892 if ((alpha *= .99) < .005) {
3893 timer = null;
3894 event.end({
3895 type: "end",
3896 alpha: alpha = 0
3897 });
3898 return true;
3899 }
3900 var n = nodes.length, m = links.length, q, i, o, s, t, l, k, x, y;
3901 for (i = 0; i < m; ++i) {
3902 o = links[i];
3903 s = o.source;
3904 t = o.target;
3905 x = t.x - s.x;
3906 y = t.y - s.y;
3907 if (l = x * x + y * y) {
3908 l = alpha * strengths[i] * ((l = Math.sqrt(l)) - distances[i]) / l;
3909 x *= l;
3910 y *= l;
3911 t.x -= x * (k = s.weight + t.weight ? s.weight / (s.weight + t.weight) : .5);
3912 t.y -= y * k;
3913 s.x += x * (k = 1 - k);
3914 s.y += y * k;
3915 }
3916 }
3917 if (k = alpha * gravity) {
3918 x = size[0] / 2;
3919 y = size[1] / 2;
3920 i = -1;
3921 if (k) while (++i < n) {
3922 o = nodes[i];
3923 o.x += (x - o.x) * k;
3924 o.y += (y - o.y) * k;
3925 }
3926 }
3927 if (charge) {
3928 d3_layout_forceAccumulate(q = d3.geom.quadtree(nodes), alpha, charges);
3929 i = -1;
3930 while (++i < n) {
3931 if (!(o = nodes[i]).fixed) {
3932 q.visit(repulse(o));
3933 }
3934 }
3935 }
3936 i = -1;
3937 while (++i < n) {
3938 o = nodes[i];
3939 if (o.fixed) {
3940 o.x = o.px;
3941 o.y = o.py;
3942 } else {
3943 o.x -= (o.px - (o.px = o.x)) * friction;
3944 o.y -= (o.py - (o.py = o.y)) * friction;
3945 }
3946 }
3947 event.tick({
3948 type: "tick",
3949 alpha: alpha
3950 });
3951 };
3952 force.nodes = function(x) {
3953 if (!arguments.length) return nodes;
3954 nodes = x;
3955 return force;
3956 };
3957 force.links = function(x) {
3958 if (!arguments.length) return links;
3959 links = x;
3960 return force;
3961 };
3962 force.size = function(x) {
3963 if (!arguments.length) return size;
3964 size = x;
3965 return force;
3966 };
3967 force.linkDistance = function(x) {
3968 if (!arguments.length) return linkDistance;
3969 linkDistance = typeof x === "function" ? x : +x;
3970 return force;
3971 };
3972 force.distance = force.linkDistance;
3973 force.linkStrength = function(x) {
3974 if (!arguments.length) return linkStrength;
3975 linkStrength = typeof x === "function" ? x : +x;
3976 return force;
3977 };
3978 force.friction = function(x) {
3979 if (!arguments.length) return friction;
3980 friction = +x;
3981 return force;
3982 };
3983 force.charge = function(x) {
3984 if (!arguments.length) return charge;
3985 charge = typeof x === "function" ? x : +x;
3986 return force;
3987 };
3988 force.chargeDistance = function(x) {
3989 if (!arguments.length) return Math.sqrt(chargeDistance2);
3990 chargeDistance2 = x * x;
3991 return force;
3992 };
3993 force.gravity = function(x) {
3994 if (!arguments.length) return gravity;
3995 gravity = +x;
3996 return force;
3997 };
3998 force.theta = function(x) {
3999 if (!arguments.length) return Math.sqrt(theta2);
4000 theta2 = x * x;
4001 return force;
4002 };
4003 force.alpha = function(x) {
4004 if (!arguments.length) return alpha;
4005 x = +x;
4006 if (alpha) {
4007 if (x > 0) {
4008 alpha = x;
4009 } else {
4010 timer.c = null, timer.t = NaN, timer = null;
4011 event.end({
4012 type: "end",
4013 alpha: alpha = 0
4014 });
4015 }
4016 } else if (x > 0) {
4017 event.start({
4018 type: "start",
4019 alpha: alpha = x
4020 });
4021 timer = d3_timer(force.tick);
4022 }
4023 return force;
4024 };
4025 force.start = function() {
4026 var i, n = nodes.length, m = links.length, w = size[0], h = size[1], neighbors, o;
4027 for (i = 0; i < n; ++i) {
4028 (o = nodes[i]).index = i;
4029 o.weight = 0;
4030 }
4031 for (i = 0; i < m; ++i) {
4032 o = links[i];
4033 if (typeof o.source == "number") o.source = nodes[o.source];
4034 if (typeof o.target == "number") o.target = nodes[o.target];
4035 ++o.source.weight;
4036 ++o.target.weight;
4037 }
4038 for (i = 0; i < n; ++i) {
4039 o = nodes[i];
4040 if (isNaN(o.x)) o.x = position("x", w);
4041 if (isNaN(o.y)) o.y = position("y", h);
4042 if (isNaN(o.px)) o.px = o.x;
4043 if (isNaN(o.py)) o.py = o.y;
4044 }
4045 distances = [];
4046 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;
4047 strengths = [];
4048 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;
4049 charges = [];
4050 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;
4051 function position(dimension, size) {
4052 if (!neighbors) {
4053 neighbors = new Array(n);
4054 for (j = 0; j < n; ++j) {
4055 neighbors[j] = [];
4056 }
4057 for (j = 0; j < m; ++j) {
4058 var o = links[j];
4059 neighbors[o.source.index].push(o.target);
4060 neighbors[o.target.index].push(o.source);
4061 }
4062 }
4063 var candidates = neighbors[i], j = -1, l = candidates.length, x;
4064 while (++j < l) if (!isNaN(x = candidates[j][dimension])) return x;
4065 return Math.random() * size;
4066 }
4067 return force.resume();
4068 };
4069 force.resume = function() {
4070 return force.alpha(.1);
4071 };
4072 force.stop = function() {
4073 return force.alpha(0);
4074 };
4075 force.drag = function() {
4076 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);
4077 if (!arguments.length) return drag;
4078 this.on("mouseover.force", d3_layout_forceMouseover).on("mouseout.force", d3_layout_forceMouseout).call(drag);
4079 };
4080 function dragmove(d) {
4081 d.px = d3.event.x, d.py = d3.event.y;
4082 force.resume();
4083 }
4084 return d3.rebind(force, event, "on");
4085 };
4086 function d3_layout_forceDragstart(d) {
4087 d.fixed |= 2;
4088 }
4089 function d3_layout_forceDragend(d) {
4090 d.fixed &= ~6;
4091 }
4092 function d3_layout_forceMouseover(d) {
4093 d.fixed |= 4;
4094 d.px = d.x, d.py = d.y;
4095 }
4096 function d3_layout_forceMouseout(d) {
4097 d.fixed &= ~4;
4098 }
4099 function d3_layout_forceAccumulate(quad, alpha, charges) {
4100 var cx = 0, cy = 0;
4101 quad.charge = 0;
4102 if (!quad.leaf) {
4103 var nodes = quad.nodes, n = nodes.length, i = -1, c;
4104 while (++i < n) {
4105 c = nodes[i];
4106 if (c == null) continue;
4107 d3_layout_forceAccumulate(c, alpha, charges);
4108 quad.charge += c.charge;
4109 cx += c.charge * c.cx;
4110 cy += c.charge * c.cy;
4111 }
4112 }
4113 if (quad.point) {
4114 if (!quad.leaf) {
4115 quad.point.x += Math.random() - .5;
4116 quad.point.y += Math.random() - .5;
4117 }
4118 var k = alpha * charges[quad.point.index];
4119 quad.charge += quad.pointCharge = k;
4120 cx += k * quad.point.x;
4121 cy += k * quad.point.y;
4122 }
4123 quad.cx = cx / quad.charge;
4124 quad.cy = cy / quad.charge;
4125 }
4126 var d3_layout_forceLinkDistance = 20, d3_layout_forceLinkStrength = 1, d3_layout_forceChargeDistance2 = Infinity;
4127 d3.layout.hierarchy = function() {
4128 var sort = d3_layout_hierarchySort, children = d3_layout_hierarchyChildren, value = d3_layout_hierarchyValue;
4129 function hierarchy(root) {
4130 var stack = [ root ], nodes = [], node;
4131 root.depth = 0;
4132 while ((node = stack.pop()) != null) {
4133 nodes.push(node);
4134 if ((childs = children.call(hierarchy, node, node.depth)) && (n = childs.length)) {
4135 var n, childs, child;
4136 while (--n >= 0) {
4137 stack.push(child = childs[n]);
4138 child.parent = node;
4139 child.depth = node.depth + 1;
4140 }
4141 if (value) node.value = 0;
4142 node.children = childs;
4143 } else {
4144 if (value) node.value = +value.call(hierarchy, node, node.depth) || 0;
4145 delete node.children;
4146 }
4147 }
4148 d3_layout_hierarchyVisitAfter(root, function(node) {
4149 var childs, parent;
4150 if (sort && (childs = node.children)) childs.sort(sort);
4151 if (value && (parent = node.parent)) parent.value += node.value;
4152 });
4153 return nodes;
4154 }
4155 hierarchy.sort = function(x) {
4156 if (!arguments.length) return sort;
4157 sort = x;
4158 return hierarchy;
4159 };
4160 hierarchy.children = function(x) {
4161 if (!arguments.length) return children;
4162 children = x;
4163 return hierarchy;
4164 };
4165 hierarchy.value = function(x) {
4166 if (!arguments.length) return value;
4167 value = x;
4168 return hierarchy;
4169 };
4170 hierarchy.revalue = function(root) {
4171 if (value) {
4172 d3_layout_hierarchyVisitBefore(root, function(node) {
4173 if (node.children) node.value = 0;
4174 });
4175 d3_layout_hierarchyVisitAfter(root, function(node) {
4176 var parent;
4177 if (!node.children) node.value = +value.call(hierarchy, node, node.depth) || 0;
4178 if (parent = node.parent) parent.value += node.value;
4179 });
4180 }
4181 return root;
4182 };
4183 return hierarchy;
4184 };
4185 function d3_layout_hierarchyRebind(object, hierarchy) {
4186 d3.rebind(object, hierarchy, "sort", "children", "value");
4187 object.nodes = object;
4188 object.links = d3_layout_hierarchyLinks;
4189 return object;
4190 }
4191 function d3_layout_hierarchyVisitBefore(node, callback) {
4192 var nodes = [ node ];
4193 while ((node = nodes.pop()) != null) {
4194 callback(node);
4195 if ((children = node.children) && (n = children.length)) {
4196 var n, children;
4197 while (--n >= 0) nodes.push(children[n]);
4198 }
4199 }
4200 }
4201 function d3_layout_hierarchyVisitAfter(node, callback) {
4202 var nodes = [ node ], nodes2 = [];
4203 while ((node = nodes.pop()) != null) {
4204 nodes2.push(node);
4205 if ((children = node.children) && (n = children.length)) {
4206 var i = -1, n, children;
4207 while (++i < n) nodes.push(children[i]);
4208 }
4209 }
4210 while ((node = nodes2.pop()) != null) {
4211 callback(node);
4212 }
4213 }
4214 function d3_layout_hierarchyChildren(d) {
4215 return d.children;
4216 }
4217 function d3_layout_hierarchyValue(d) {
4218 return d.value;
4219 }
4220 function d3_layout_hierarchySort(a, b) {
4221 return b.value - a.value;
4222 }
4223 function d3_layout_hierarchyLinks(nodes) {
4224 return d3.merge(nodes.map(function(parent) {
4225 return (parent.children || []).map(function(child) {
4226 return {
4227 source: parent,
4228 target: child
4229 };
4230 });
4231 }));
4232 }
4233 d3.layout.partition = function() {
4234 var hierarchy = d3.layout.hierarchy(), size = [ 1, 1 ];
4235 function position(node, x, dx, dy) {
4236 var children = node.children;
4237 node.x = x;
4238 node.y = node.depth * dy;
4239 node.dx = dx;
4240 node.dy = dy;
4241 if (children && (n = children.length)) {
4242 var i = -1, n, c, d;
4243 dx = node.value ? dx / node.value : 0;
4244 while (++i < n) {
4245 position(c = children[i], x, d = c.value * dx, dy);
4246 x += d;
4247 }
4248 }
4249 }
4250 function depth(node) {
4251 var children = node.children, d = 0;
4252 if (children && (n = children.length)) {
4253 var i = -1, n;
4254 while (++i < n) d = Math.max(d, depth(children[i]));
4255 }
4256 return 1 + d;
4257 }
4258 function partition(d, i) {
4259 var nodes = hierarchy.call(this, d, i);
4260 position(nodes[0], 0, size[0], size[1] / depth(nodes[0]));
4261 return nodes;
4262 }
4263 partition.size = function(x) {
4264 if (!arguments.length) return size;
4265 size = x;
4266 return partition;
4267 };
4268 return d3_layout_hierarchyRebind(partition, hierarchy);
4269 };
4270 d3.layout.pie = function() {
4271 var value = Number, sort = d3_layout_pieSortByValue, startAngle = 0, endAngle = τ, padAngle = 0;
4272 function pie(data) {
4273 var n = data.length, values = data.map(function(d, i) {
4274 return +value.call(pie, d, i);
4275 }), 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;
4276 if (sort != null) index.sort(sort === d3_layout_pieSortByValue ? function(i, j) {
4277 return values[j] - values[i];
4278 } : function(i, j) {
4279 return sort(data[i], data[j]);
4280 });
4281 index.forEach(function(i) {
4282 arcs[i] = {
4283 data: data[i],
4284 value: v = values[i],
4285 startAngle: a,
4286 endAngle: a += v * k + pa,
4287 padAngle: p
4288 };
4289 });
4290 return arcs;
4291 }
4292 pie.value = function(_) {
4293 if (!arguments.length) return value;
4294 value = _;
4295 return pie;
4296 };
4297 pie.sort = function(_) {
4298 if (!arguments.length) return sort;
4299 sort = _;
4300 return pie;
4301 };
4302 pie.startAngle = function(_) {
4303 if (!arguments.length) return startAngle;
4304 startAngle = _;
4305 return pie;
4306 };
4307 pie.endAngle = function(_) {
4308 if (!arguments.length) return endAngle;
4309 endAngle = _;
4310 return pie;
4311 };
4312 pie.padAngle = function(_) {
4313 if (!arguments.length) return padAngle;
4314 padAngle = _;
4315 return pie;
4316 };
4317 return pie;
4318 };
4319 var d3_layout_pieSortByValue = {};
4320 d3.layout.stack = function() {
4321 var values = d3_identity, order = d3_layout_stackOrderDefault, offset = d3_layout_stackOffsetZero, out = d3_layout_stackOut, x = d3_layout_stackX, y = d3_layout_stackY;
4322 function stack(data, index) {
4323 if (!(n = data.length)) return data;
4324 var series = data.map(function(d, i) {
4325 return values.call(stack, d, i);
4326 });
4327 var points = series.map(function(d) {
4328 return d.map(function(v, i) {
4329 return [ x.call(stack, v, i), y.call(stack, v, i) ];
4330 });
4331 });
4332 var orders = order.call(stack, points, index);
4333 series = d3.permute(series, orders);
4334 points = d3.permute(points, orders);
4335 var offsets = offset.call(stack, points, index);
4336 var m = series[0].length, n, i, j, o;
4337 for (j = 0; j < m; ++j) {
4338 out.call(stack, series[0][j], o = offsets[j], points[0][j][1]);
4339 for (i = 1; i < n; ++i) {
4340 out.call(stack, series[i][j], o += points[i - 1][j][1], points[i][j][1]);
4341 }
4342 }
4343 return data;
4344 }
4345 stack.values = function(x) {
4346 if (!arguments.length) return values;
4347 values = x;
4348 return stack;
4349 };
4350 stack.order = function(x) {
4351 if (!arguments.length) return order;
4352 order = typeof x === "function" ? x : d3_layout_stackOrders.get(x) || d3_layout_stackOrderDefault;
4353 return stack;
4354 };
4355 stack.offset = function(x) {
4356 if (!arguments.length) return offset;
4357 offset = typeof x === "function" ? x : d3_layout_stackOffsets.get(x) || d3_layout_stackOffsetZero;
4358 return stack;
4359 };
4360 stack.x = function(z) {
4361 if (!arguments.length) return x;
4362 x = z;
4363 return stack;
4364 };
4365 stack.y = function(z) {
4366 if (!arguments.length) return y;
4367 y = z;
4368 return stack;
4369 };
4370 stack.out = function(z) {
4371 if (!arguments.length) return out;
4372 out = z;
4373 return stack;
4374 };
4375 return stack;
4376 };
4377 function d3_layout_stackX(d) {
4378 return d.x;
4379 }
4380 function d3_layout_stackY(d) {
4381 return d.y;
4382 }
4383 function d3_layout_stackOut(d, y0, y) {
4384 d.y0 = y0;
4385 d.y = y;
4386 }
4387 var d3_layout_stackOrders = d3.map({
4388 "inside-out": function(data) {
4389 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) {
4390 return max[a] - max[b];
4391 }), top = 0, bottom = 0, tops = [], bottoms = [];
4392 for (i = 0; i < n; ++i) {
4393 j = index[i];
4394 if (top < bottom) {
4395 top += sums[j];
4396 tops.push(j);
4397 } else {
4398 bottom += sums[j];
4399 bottoms.push(j);
4400 }
4401 }
4402 return bottoms.reverse().concat(tops);
4403 },
4404 reverse: function(data) {
4405 return d3.range(data.length).reverse();
4406 },
4407 "default": d3_layout_stackOrderDefault
4408 });
4409 var d3_layout_stackOffsets = d3.map({
4410 silhouette: function(data) {
4411 var n = data.length, m = data[0].length, sums = [], max = 0, i, j, o, y0 = [];
4412 for (j = 0; j < m; ++j) {
4413 for (i = 0, o = 0; i < n; i++) o += data[i][j][1];
4414 if (o > max) max = o;
4415 sums.push(o);
4416 }
4417 for (j = 0; j < m; ++j) {
4418 y0[j] = (max - sums[j]) / 2;
4419 }
4420 return y0;
4421 },
4422 wiggle: function(data) {
4423 var n = data.length, x = data[0], m = x.length, i, j, k, s1, s2, s3, dx, o, o0, y0 = [];
4424 y0[0] = o = o0 = 0;
4425 for (j = 1; j < m; ++j) {
4426 for (i = 0, s1 = 0; i < n; ++i) s1 += data[i][j][1];
4427 for (i = 0, s2 = 0, dx = x[j][0] - x[j - 1][0]; i < n; ++i) {
4428 for (k = 0, s3 = (data[i][j][1] - data[i][j - 1][1]) / (2 * dx); k < i; ++k) {
4429 s3 += (data[k][j][1] - data[k][j - 1][1]) / dx;
4430 }
4431 s2 += s3 * data[i][j][1];
4432 }
4433 y0[j] = o -= s1 ? s2 / s1 * dx : 0;
4434 if (o < o0) o0 = o;
4435 }
4436 for (j = 0; j < m; ++j) y0[j] -= o0;
4437 return y0;
4438 },
4439 expand: function(data) {
4440 var n = data.length, m = data[0].length, k = 1 / n, i, j, o, y0 = [];
4441 for (j = 0; j < m; ++j) {
4442 for (i = 0, o = 0; i < n; i++) o += data[i][j][1];
4443 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;
4444 }
4445 for (j = 0; j < m; ++j) y0[j] = 0;
4446 return y0;
4447 },
4448 zero: d3_layout_stackOffsetZero
4449 });
4450 function d3_layout_stackOrderDefault(data) {
4451 return d3.range(data.length);
4452 }
4453 function d3_layout_stackOffsetZero(data) {
4454 var j = -1, m = data[0].length, y0 = [];
4455 while (++j < m) y0[j] = 0;
4456 return y0;
4457 }
4458 function d3_layout_stackMaxIndex(array) {
4459 var i = 1, j = 0, v = array[0][1], k, n = array.length;
4460 for (;i < n; ++i) {
4461 if ((k = array[i][1]) > v) {
4462 j = i;
4463 v = k;
4464 }
4465 }
4466 return j;
4467 }
4468 function d3_layout_stackReduceSum(d) {
4469 return d.reduce(d3_layout_stackSum, 0);
4470 }
4471 function d3_layout_stackSum(p, d) {
4472 return p + d[1];
4473 }
4474 d3.layout.histogram = function() {
4475 var frequency = true, valuer = Number, ranger = d3_layout_histogramRange, binner = d3_layout_histogramBinSturges;
4476 function histogram(data, i) {
4477 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;
4478 while (++i < m) {
4479 bin = bins[i] = [];
4480 bin.dx = thresholds[i + 1] - (bin.x = thresholds[i]);
4481 bin.y = 0;
4482 }
4483 if (m > 0) {
4484 i = -1;
4485 while (++i < n) {
4486 x = values[i];
4487 if (x >= range[0] && x <= range[1]) {
4488 bin = bins[d3.bisect(thresholds, x, 1, m) - 1];
4489 bin.y += k;
4490 bin.push(data[i]);
4491 }
4492 }
4493 }
4494 return bins;
4495 }
4496 histogram.value = function(x) {
4497 if (!arguments.length) return valuer;
4498 valuer = x;
4499 return histogram;
4500 };
4501 histogram.range = function(x) {
4502 if (!arguments.length) return ranger;
4503 ranger = d3_functor(x);
4504 return histogram;
4505 };
4506 histogram.bins = function(x) {
4507 if (!arguments.length) return binner;
4508 binner = typeof x === "number" ? function(range) {
4509 return d3_layout_histogramBinFixed(range, x);
4510 } : d3_functor(x);
4511 return histogram;
4512 };
4513 histogram.frequency = function(x) {
4514 if (!arguments.length) return frequency;
4515 frequency = !!x;
4516 return histogram;
4517 };
4518 return histogram;
4519 };
4520 function d3_layout_histogramBinSturges(range, values) {
4521 return d3_layout_histogramBinFixed(range, Math.ceil(Math.log(values.length) / Math.LN2 + 1));
4522 }
4523 function d3_layout_histogramBinFixed(range, n) {
4524 var x = -1, b = +range[0], m = (range[1] - b) / n, f = [];
4525 while (++x <= n) f[x] = m * x + b;
4526 return f;
4527 }
4528 function d3_layout_histogramRange(values) {
4529 return [ d3.min(values), d3.max(values) ];
4530 }
4531 d3.layout.pack = function() {
4532 var hierarchy = d3.layout.hierarchy().sort(d3_layout_packSort), padding = 0, size = [ 1, 1 ], radius;
4533 function pack(d, i) {
4534 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() {
4535 return radius;
4536 };
4537 root.x = root.y = 0;
4538 d3_layout_hierarchyVisitAfter(root, function(d) {
4539 d.r = +r(d.value);
4540 });
4541 d3_layout_hierarchyVisitAfter(root, d3_layout_packSiblings);
4542 if (padding) {
4543 var dr = padding * (radius ? 1 : Math.max(2 * root.r / w, 2 * root.r / h)) / 2;
4544 d3_layout_hierarchyVisitAfter(root, function(d) {
4545 d.r += dr;
4546 });
4547 d3_layout_hierarchyVisitAfter(root, d3_layout_packSiblings);
4548 d3_layout_hierarchyVisitAfter(root, function(d) {
4549 d.r -= dr;
4550 });
4551 }
4552 d3_layout_packTransform(root, w / 2, h / 2, radius ? 1 : 1 / Math.max(2 * root.r / w, 2 * root.r / h));
4553 return nodes;
4554 }
4555 pack.size = function(_) {
4556 if (!arguments.length) return size;
4557 size = _;
4558 return pack;
4559 };
4560 pack.radius = function(_) {
4561 if (!arguments.length) return radius;
4562 radius = _ == null || typeof _ === "function" ? _ : +_;
4563 return pack;
4564 };
4565 pack.padding = function(_) {
4566 if (!arguments.length) return padding;
4567 padding = +_;
4568 return pack;
4569 };
4570 return d3_layout_hierarchyRebind(pack, hierarchy);
4571 };
4572 function d3_layout_packSort(a, b) {
4573 return a.value - b.value;
4574 }
4575 function d3_layout_packInsert(a, b) {
4576 var c = a._pack_next;
4577 a._pack_next = b;
4578 b._pack_prev = a;
4579 b._pack_next = c;
4580 c._pack_prev = b;
4581 }
4582 function d3_layout_packSplice(a, b) {
4583 a._pack_next = b;
4584 b._pack_prev = a;
4585 }
4586 function d3_layout_packIntersects(a, b) {
4587 var dx = b.x - a.x, dy = b.y - a.y, dr = a.r + b.r;
4588 return .999 * dr * dr > dx * dx + dy * dy;
4589 }
4590 function d3_layout_packSiblings(node) {
4591 if (!(nodes = node.children) || !(n = nodes.length)) return;
4592 var nodes, xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity, a, b, c, i, j, k, n;
4593 function bound(node) {
4594 xMin = Math.min(node.x - node.r, xMin);
4595 xMax = Math.max(node.x + node.r, xMax);
4596 yMin = Math.min(node.y - node.r, yMin);
4597 yMax = Math.max(node.y + node.r, yMax);
4598 }
4599 nodes.forEach(d3_layout_packLink);
4600 a = nodes[0];
4601 a.x = -a.r;
4602 a.y = 0;
4603 bound(a);
4604 if (n > 1) {
4605 b = nodes[1];
4606 b.x = b.r;
4607 b.y = 0;
4608 bound(b);
4609 if (n > 2) {
4610 c = nodes[2];
4611 d3_layout_packPlace(a, b, c);
4612 bound(c);
4613 d3_layout_packInsert(a, c);
4614 a._pack_prev = c;
4615 d3_layout_packInsert(c, b);
4616 b = a._pack_next;
4617 for (i = 3; i < n; i++) {
4618 d3_layout_packPlace(a, b, c = nodes[i]);
4619 var isect = 0, s1 = 1, s2 = 1;
4620 for (j = b._pack_next; j !== b; j = j._pack_next, s1++) {
4621 if (d3_layout_packIntersects(j, c)) {
4622 isect = 1;
4623 break;
4624 }
4625 }
4626 if (isect == 1) {
4627 for (k = a._pack_prev; k !== j._pack_prev; k = k._pack_prev, s2++) {
4628 if (d3_layout_packIntersects(k, c)) {
4629 break;
4630 }
4631 }
4632 }
4633 if (isect) {
4634 if (s1 < s2 || s1 == s2 && b.r < a.r) d3_layout_packSplice(a, b = j); else d3_layout_packSplice(a = k, b);
4635 i--;
4636 } else {
4637 d3_layout_packInsert(a, c);
4638 b = c;
4639 bound(c);
4640 }
4641 }
4642 }
4643 }
4644 var cx = (xMin + xMax) / 2, cy = (yMin + yMax) / 2, cr = 0;
4645 for (i = 0; i < n; i++) {
4646 c = nodes[i];
4647 c.x -= cx;
4648 c.y -= cy;
4649 cr = Math.max(cr, c.r + Math.sqrt(c.x * c.x + c.y * c.y));
4650 }
4651 node.r = cr;
4652 nodes.forEach(d3_layout_packUnlink);
4653 }
4654 function d3_layout_packLink(node) {
4655 node._pack_next = node._pack_prev = node;
4656 }
4657 function d3_layout_packUnlink(node) {
4658 delete node._pack_next;
4659 delete node._pack_prev;
4660 }
4661 function d3_layout_packTransform(node, x, y, k) {
4662 var children = node.children;
4663 node.x = x += k * node.x;
4664 node.y = y += k * node.y;
4665 node.r *= k;
4666 if (children) {
4667 var i = -1, n = children.length;
4668 while (++i < n) d3_layout_packTransform(children[i], x, y, k);
4669 }
4670 }
4671 function d3_layout_packPlace(a, b, c) {
4672 var db = a.r + c.r, dx = b.x - a.x, dy = b.y - a.y;
4673 if (db && (dx || dy)) {
4674 var da = b.r + c.r, dc = dx * dx + dy * dy;
4675 da *= da;
4676 db *= db;
4677 var x = .5 + (db - da) / (2 * dc), y = Math.sqrt(Math.max(0, 2 * da * (db + dc) - (db -= dc) * db - da * da)) / (2 * dc);
4678 c.x = a.x + x * dx + y * dy;
4679 c.y = a.y + x * dy - y * dx;
4680 } else {
4681 c.x = a.x + db;
4682 c.y = a.y;
4683 }
4684 }
4685 d3.layout.tree = function() {
4686 var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = null;
4687 function tree(d, i) {
4688 var nodes = hierarchy.call(this, d, i), root0 = nodes[0], root1 = wrapTree(root0);
4689 d3_layout_hierarchyVisitAfter(root1, firstWalk), root1.parent.m = -root1.z;
4690 d3_layout_hierarchyVisitBefore(root1, secondWalk);
4691 if (nodeSize) d3_layout_hierarchyVisitBefore(root0, sizeNode); else {
4692 var left = root0, right = root0, bottom = root0;
4693 d3_layout_hierarchyVisitBefore(root0, function(node) {
4694 if (node.x < left.x) left = node;
4695 if (node.x > right.x) right = node;
4696 if (node.depth > bottom.depth) bottom = node;
4697 });
4698 var tx = separation(left, right) / 2 - left.x, kx = size[0] / (right.x + separation(right, left) / 2 + tx), ky = size[1] / (bottom.depth || 1);
4699 d3_layout_hierarchyVisitBefore(root0, function(node) {
4700 node.x = (node.x + tx) * kx;
4701 node.y = node.depth * ky;
4702 });
4703 }
4704 return nodes;
4705 }
4706 function wrapTree(root0) {
4707 var root1 = {
4708 A: null,
4709 children: [ root0 ]
4710 }, queue = [ root1 ], node1;
4711 while ((node1 = queue.pop()) != null) {
4712 for (var children = node1.children, child, i = 0, n = children.length; i < n; ++i) {
4713 queue.push((children[i] = child = {
4714 _: children[i],
4715 parent: node1,
4716 children: (child = children[i].children) && child.slice() || [],
4717 A: null,
4718 a: null,
4719 z: 0,
4720 m: 0,
4721 c: 0,
4722 s: 0,
4723 t: null,
4724 i: i
4725 }).a = child);
4726 }
4727 }
4728 return root1.children[0];
4729 }
4730 function firstWalk(v) {
4731 var children = v.children, siblings = v.parent.children, w = v.i ? siblings[v.i - 1] : null;
4732 if (children.length) {
4733 d3_layout_treeShift(v);
4734 var midpoint = (children[0].z + children[children.length - 1].z) / 2;
4735 if (w) {
4736 v.z = w.z + separation(v._, w._);
4737 v.m = v.z - midpoint;
4738 } else {
4739 v.z = midpoint;
4740 }
4741 } else if (w) {
4742 v.z = w.z + separation(v._, w._);
4743 }
4744 v.parent.A = apportion(v, w, v.parent.A || siblings[0]);
4745 }
4746 function secondWalk(v) {
4747 v._.x = v.z + v.parent.m;
4748 v.m += v.parent.m;
4749 }
4750 function apportion(v, w, ancestor) {
4751 if (w) {
4752 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;
4753 while (vim = d3_layout_treeRight(vim), vip = d3_layout_treeLeft(vip), vim && vip) {
4754 vom = d3_layout_treeLeft(vom);
4755 vop = d3_layout_treeRight(vop);
4756 vop.a = v;
4757 shift = vim.z + sim - vip.z - sip + separation(vim._, vip._);
4758 if (shift > 0) {
4759 d3_layout_treeMove(d3_layout_treeAncestor(vim, v, ancestor), v, shift);
4760 sip += shift;
4761 sop += shift;
4762 }
4763 sim += vim.m;
4764 sip += vip.m;
4765 som += vom.m;
4766 sop += vop.m;
4767 }
4768 if (vim && !d3_layout_treeRight(vop)) {
4769 vop.t = vim;
4770 vop.m += sim - sop;
4771 }
4772 if (vip && !d3_layout_treeLeft(vom)) {
4773 vom.t = vip;
4774 vom.m += sip - som;
4775 ancestor = v;
4776 }
4777 }
4778 return ancestor;
4779 }
4780 function sizeNode(node) {
4781 node.x *= size[0];
4782 node.y = node.depth * size[1];
4783 }
4784 tree.separation = function(x) {
4785 if (!arguments.length) return separation;
4786 separation = x;
4787 return tree;
4788 };
4789 tree.size = function(x) {
4790 if (!arguments.length) return nodeSize ? null : size;
4791 nodeSize = (size = x) == null ? sizeNode : null;
4792 return tree;
4793 };
4794 tree.nodeSize = function(x) {
4795 if (!arguments.length) return nodeSize ? size : null;
4796 nodeSize = (size = x) == null ? null : sizeNode;
4797 return tree;
4798 };
4799 return d3_layout_hierarchyRebind(tree, hierarchy);
4800 };
4801 function d3_layout_treeSeparation(a, b) {
4802 return a.parent == b.parent ? 1 : 2;
4803 }
4804 function d3_layout_treeLeft(v) {
4805 var children = v.children;
4806 return children.length ? children[0] : v.t;
4807 }
4808 function d3_layout_treeRight(v) {
4809 var children = v.children, n;
4810 return (n = children.length) ? children[n - 1] : v.t;
4811 }
4812 function d3_layout_treeMove(wm, wp, shift) {
4813 var change = shift / (wp.i - wm.i);
4814 wp.c -= change;
4815 wp.s += shift;
4816 wm.c += change;
4817 wp.z += shift;
4818 wp.m += shift;
4819 }
4820 function d3_layout_treeShift(v) {
4821 var shift = 0, change = 0, children = v.children, i = children.length, w;
4822 while (--i >= 0) {
4823 w = children[i];
4824 w.z += shift;
4825 w.m += shift;
4826 shift += w.s + (change += w.c);
4827 }
4828 }
4829 function d3_layout_treeAncestor(vim, v, ancestor) {
4830 return vim.a.parent === v.parent ? vim.a : ancestor;
4831 }
4832 d3.layout.cluster = function() {
4833 var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = false;
4834 function cluster(d, i) {
4835 var nodes = hierarchy.call(this, d, i), root = nodes[0], previousNode, x = 0;
4836 d3_layout_hierarchyVisitAfter(root, function(node) {
4837 var children = node.children;
4838 if (children && children.length) {
4839 node.x = d3_layout_clusterX(children);
4840 node.y = d3_layout_clusterY(children);
4841 } else {
4842 node.x = previousNode ? x += separation(node, previousNode) : 0;
4843 node.y = 0;
4844 previousNode = node;
4845 }
4846 });
4847 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;
4848 d3_layout_hierarchyVisitAfter(root, nodeSize ? function(node) {
4849 node.x = (node.x - root.x) * size[0];
4850 node.y = (root.y - node.y) * size[1];
4851 } : function(node) {
4852 node.x = (node.x - x0) / (x1 - x0) * size[0];
4853 node.y = (1 - (root.y ? node.y / root.y : 1)) * size[1];
4854 });
4855 return nodes;
4856 }
4857 cluster.separation = function(x) {
4858 if (!arguments.length) return separation;
4859 separation = x;
4860 return cluster;
4861 };
4862 cluster.size = function(x) {
4863 if (!arguments.length) return nodeSize ? null : size;
4864 nodeSize = (size = x) == null;
4865 return cluster;
4866 };
4867 cluster.nodeSize = function(x) {
4868 if (!arguments.length) return nodeSize ? size : null;
4869 nodeSize = (size = x) != null;
4870 return cluster;
4871 };
4872 return d3_layout_hierarchyRebind(cluster, hierarchy);
4873 };
4874 function d3_layout_clusterY(children) {
4875 return 1 + d3.max(children, function(child) {
4876 return child.y;
4877 });
4878 }
4879 function d3_layout_clusterX(children) {
4880 return children.reduce(function(x, child) {
4881 return x + child.x;
4882 }, 0) / children.length;
4883 }
4884 function d3_layout_clusterLeft(node) {
4885 var children = node.children;
4886 return children && children.length ? d3_layout_clusterLeft(children[0]) : node;
4887 }
4888 function d3_layout_clusterRight(node) {
4889 var children = node.children, n;
4890 return children && (n = children.length) ? d3_layout_clusterRight(children[n - 1]) : node;
4891 }
4892 d3.layout.treemap = function() {
4893 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));
4894 function scale(children, k) {
4895 var i = -1, n = children.length, child, area;
4896 while (++i < n) {
4897 area = (child = children[i]).value * (k < 0 ? 0 : k);
4898 child.area = isNaN(area) || area <= 0 ? 0 : area;
4899 }
4900 }
4901 function squarify(node) {
4902 var children = node.children;
4903 if (children && children.length) {
4904 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;
4905 scale(remaining, rect.dx * rect.dy / node.value);
4906 row.area = 0;
4907 while ((n = remaining.length) > 0) {
4908 row.push(child = remaining[n - 1]);
4909 row.area += child.area;
4910 if (mode !== "squarify" || (score = worst(row, u)) <= best) {
4911 remaining.pop();
4912 best = score;
4913 } else {
4914 row.area -= row.pop().area;
4915 position(row, u, rect, false);
4916 u = Math.min(rect.dx, rect.dy);
4917 row.length = row.area = 0;
4918 best = Infinity;
4919 }
4920 }
4921 if (row.length) {
4922 position(row, u, rect, true);
4923 row.length = row.area = 0;
4924 }
4925 children.forEach(squarify);
4926 }
4927 }
4928 function stickify(node) {
4929 var children = node.children;
4930 if (children && children.length) {
4931 var rect = pad(node), remaining = children.slice(), child, row = [];
4932 scale(remaining, rect.dx * rect.dy / node.value);
4933 row.area = 0;
4934 while (child = remaining.pop()) {
4935 row.push(child);
4936 row.area += child.area;
4937 if (child.z != null) {
4938 position(row, child.z ? rect.dx : rect.dy, rect, !remaining.length);
4939 row.length = row.area = 0;
4940 }
4941 }
4942 children.forEach(stickify);
4943 }
4944 }
4945 function worst(row, u) {
4946 var s = row.area, r, rmax = 0, rmin = Infinity, i = -1, n = row.length;
4947 while (++i < n) {
4948 if (!(r = row[i].area)) continue;
4949 if (r < rmin) rmin = r;
4950 if (r > rmax) rmax = r;
4951 }
4952 s *= s;
4953 u *= u;
4954 return s ? Math.max(u * rmax * ratio / s, s / (u * rmin * ratio)) : Infinity;
4955 }
4956 function position(row, u, rect, flush) {
4957 var i = -1, n = row.length, x = rect.x, y = rect.y, v = u ? round(row.area / u) : 0, o;
4958 if (u == rect.dx) {
4959 if (flush || v > rect.dy) v = rect.dy;
4960 while (++i < n) {
4961 o = row[i];
4962 o.x = x;
4963 o.y = y;
4964 o.dy = v;
4965 x += o.dx = Math.min(rect.x + rect.dx - x, v ? round(o.area / v) : 0);
4966 }
4967 o.z = true;
4968 o.dx += rect.x + rect.dx - x;
4969 rect.y += v;
4970 rect.dy -= v;
4971 } else {
4972 if (flush || v > rect.dx) v = rect.dx;
4973 while (++i < n) {
4974 o = row[i];
4975 o.x = x;
4976 o.y = y;
4977 o.dx = v;
4978 y += o.dy = Math.min(rect.y + rect.dy - y, v ? round(o.area / v) : 0);
4979 }
4980 o.z = false;
4981 o.dy += rect.y + rect.dy - y;
4982 rect.x += v;
4983 rect.dx -= v;
4984 }
4985 }
4986 function treemap(d) {
4987 var nodes = stickies || hierarchy(d), root = nodes[0];
4988 root.x = root.y = 0;
4989 if (root.value) root.dx = size[0], root.dy = size[1]; else root.dx = root.dy = 0;
4990 if (stickies) hierarchy.revalue(root);
4991 scale([ root ], root.dx * root.dy / root.value);
4992 (stickies ? stickify : squarify)(root);
4993 if (sticky) stickies = nodes;
4994 return nodes;
4995 }
4996 treemap.size = function(x) {
4997 if (!arguments.length) return size;
4998 size = x;
4999 return treemap;
5000 };
5001 treemap.padding = function(x) {
5002 if (!arguments.length) return padding;
5003 function padFunction(node) {
5004 var p = x.call(treemap, node, node.depth);
5005 return p == null ? d3_layout_treemapPadNull(node) : d3_layout_treemapPad(node, typeof p === "number" ? [ p, p, p, p ] : p);
5006 }
5007 function padConstant(node) {
5008 return d3_layout_treemapPad(node, x);
5009 }
5010 var type;
5011 pad = (padding = x) == null ? d3_layout_treemapPadNull : (type = typeof x) === "function" ? padFunction : type === "number" ? (x = [ x, x, x, x ],
5012 padConstant) : padConstant;
5013 return treemap;
5014 };
5015 treemap.round = function(x) {
5016 if (!arguments.length) return round != Number;
5017 round = x ? Math.round : Number;
5018 return treemap;
5019 };
5020 treemap.sticky = function(x) {
5021 if (!arguments.length) return sticky;
5022 sticky = x;
5023 stickies = null;
5024 return treemap;
5025 };
5026 treemap.ratio = function(x) {
5027 if (!arguments.length) return ratio;
5028 ratio = x;
5029 return treemap;
5030 };
5031 treemap.mode = function(x) {
5032 if (!arguments.length) return mode;
5033 mode = x + "";
5034 return treemap;
5035 };
5036 return d3_layout_hierarchyRebind(treemap, hierarchy);
5037 };
5038 function d3_layout_treemapPadNull(node) {
5039 return {
5040 x: node.x,
5041 y: node.y,
5042 dx: node.dx,
5043 dy: node.dy
5044 };
5045 }
5046 function d3_layout_treemapPad(node, padding) {
5047 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];
5048 if (dx < 0) {
5049 x += dx / 2;
5050 dx = 0;
5051 }
5052 if (dy < 0) {
5053 y += dy / 2;
5054 dy = 0;
5055 }
5056 return {
5057 x: x,
5058 y: y,
5059 dx: dx,
5060 dy: dy
5061 };
5062 }
5063 d3.random = {
5064 normal: function(µ, σ) {
5065 var n = arguments.length;
5066 if (n < 2) σ = 1;
5067 if (n < 1) µ = 0;
5068 return function() {
5069 var x, y, r;
5070 do {
5071 x = Math.random() * 2 - 1;
5072 y = Math.random() * 2 - 1;
5073 r = x * x + y * y;
5074 } while (!r || r > 1);
5075 return µ + σ * x * Math.sqrt(-2 * Math.log(r) / r);
5076 };
5077 },
5078 logNormal: function() {
5079 var random = d3.random.normal.apply(d3, arguments);
5080 return function() {
5081 return Math.exp(random());
5082 };
5083 },
5084 bates: function(m) {
5085 var random = d3.random.irwinHall(m);
5086 return function() {
5087 return random() / m;
5088 };
5089 },
5090 irwinHall: function(m) {
5091 return function() {
5092 for (var s = 0, j = 0; j < m; j++) s += Math.random();
5093 return s;
5094 };
5095 }
5096 };
5097 d3.scale = {};
5098 function d3_scaleExtent(domain) {
5099 var start = domain[0], stop = domain[domain.length - 1];
5100 return start < stop ? [ start, stop ] : [ stop, start ];
5101 }
5102 function d3_scaleRange(scale) {
5103 return scale.rangeExtent ? scale.rangeExtent() : d3_scaleExtent(scale.range());
5104 }
5105 function d3_scale_bilinear(domain, range, uninterpolate, interpolate) {
5106 var u = uninterpolate(domain[0], domain[1]), i = interpolate(range[0], range[1]);
5107 return function(x) {
5108 return i(u(x));
5109 };
5110 }
5111 function d3_scale_nice(domain, nice) {
5112 var i0 = 0, i1 = domain.length - 1, x0 = domain[i0], x1 = domain[i1], dx;
5113 if (x1 < x0) {
5114 dx = i0, i0 = i1, i1 = dx;
5115 dx = x0, x0 = x1, x1 = dx;
5116 }
5117 domain[i0] = nice.floor(x0);
5118 domain[i1] = nice.ceil(x1);
5119 return domain;
5120 }
5121 function d3_scale_niceStep(step) {
5122 return step ? {
5123 floor: function(x) {
5124 return Math.floor(x / step) * step;
5125 },
5126 ceil: function(x) {
5127 return Math.ceil(x / step) * step;
5128 }
5129 } : d3_scale_niceIdentity;
5130 }
5131 var d3_scale_niceIdentity = {
5132 floor: d3_identity,
5133 ceil: d3_identity
5134 };
5135 function d3_scale_polylinear(domain, range, uninterpolate, interpolate) {
5136 var u = [], i = [], j = 0, k = Math.min(domain.length, range.length) - 1;
5137 if (domain[k] < domain[0]) {
5138 domain = domain.slice().reverse();
5139 range = range.slice().reverse();
5140 }
5141 while (++j <= k) {
5142 u.push(uninterpolate(domain[j - 1], domain[j]));
5143 i.push(interpolate(range[j - 1], range[j]));
5144 }
5145 return function(x) {
5146 var j = d3.bisect(domain, x, 1, k) - 1;
5147 return i[j](u[j](x));
5148 };
5149 }
5150 d3.scale.linear = function() {
5151 return d3_scale_linear([ 0, 1 ], [ 0, 1 ], d3_interpolate, false);
5152 };
5153 function d3_scale_linear(domain, range, interpolate, clamp) {
5154 var output, input;
5155 function rescale() {
5156 var linear = Math.min(domain.length, range.length) > 2 ? d3_scale_polylinear : d3_scale_bilinear, uninterpolate = clamp ? d3_uninterpolateClamp : d3_uninterpolateNumber;
5157 output = linear(domain, range, uninterpolate, interpolate);
5158 input = linear(range, domain, uninterpolate, d3_interpolate);
5159 return scale;
5160 }
5161 function scale(x) {
5162 return output(x);
5163 }
5164 scale.invert = function(y) {
5165 return input(y);
5166 };
5167 scale.domain = function(x) {
5168 if (!arguments.length) return domain;
5169 domain = x.map(Number);
5170 return rescale();
5171 };
5172 scale.range = function(x) {
5173 if (!arguments.length) return range;
5174 range = x;
5175 return rescale();
5176 };
5177 scale.rangeRound = function(x) {
5178 return scale.range(x).interpolate(d3_interpolateRound);
5179 };
5180 scale.clamp = function(x) {
5181 if (!arguments.length) return clamp;
5182 clamp = x;
5183 return rescale();
5184 };
5185 scale.interpolate = function(x) {
5186 if (!arguments.length) return interpolate;
5187 interpolate = x;
5188 return rescale();
5189 };
5190 scale.ticks = function(m) {
5191 return d3_scale_linearTicks(domain, m);
5192 };
5193 scale.tickFormat = function(m, format) {
5194 return d3_scale_linearTickFormat(domain, m, format);
5195 };
5196 scale.nice = function(m) {
5197 d3_scale_linearNice(domain, m);
5198 return rescale();
5199 };
5200 scale.copy = function() {
5201 return d3_scale_linear(domain, range, interpolate, clamp);
5202 };
5203 return rescale();
5204 }
5205 function d3_scale_linearRebind(scale, linear) {
5206 return d3.rebind(scale, linear, "range", "rangeRound", "interpolate", "clamp");
5207 }
5208 function d3_scale_linearNice(domain, m) {
5209 d3_scale_nice(domain, d3_scale_niceStep(d3_scale_linearTickRange(domain, m)[2]));
5210 d3_scale_nice(domain, d3_scale_niceStep(d3_scale_linearTickRange(domain, m)[2]));
5211 return domain;
5212 }
5213 function d3_scale_linearTickRange(domain, m) {
5214 if (m == null) m = 10;
5215 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;
5216 if (err <= .15) step *= 10; else if (err <= .35) step *= 5; else if (err <= .75) step *= 2;
5217 extent[0] = Math.ceil(extent[0] / step) * step;
5218 extent[1] = Math.floor(extent[1] / step) * step + step * .5;
5219 extent[2] = step;
5220 return extent;
5221 }
5222 function d3_scale_linearTicks(domain, m) {
5223 return d3.range.apply(d3, d3_scale_linearTickRange(domain, m));
5224 }
5225 var d3_scale_linearFormatSignificant = {
5226 s: 1,
5227 g: 1,
5228 p: 1,
5229 r: 1,
5230 e: 1
5231 };
5232 function d3_scale_linearPrecision(value) {
5233 return -Math.floor(Math.log(value) / Math.LN10 + .01);
5234 }
5235 function d3_scale_linearFormatPrecision(type, range) {
5236 var p = d3_scale_linearPrecision(range[2]);
5237 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;
5238 }
5239 d3.scale.log = function() {
5240 return d3_scale_log(d3.scale.linear().domain([ 0, 1 ]), 10, true, [ 1, 10 ]);
5241 };
5242 function d3_scale_log(linear, base, positive, domain) {
5243 function log(x) {
5244 return (positive ? Math.log(x < 0 ? 0 : x) : -Math.log(x > 0 ? 0 : -x)) / Math.log(base);
5245 }
5246 function pow(x) {
5247 return positive ? Math.pow(base, x) : -Math.pow(base, -x);
5248 }
5249 function scale(x) {
5250 return linear(log(x));
5251 }
5252 scale.invert = function(x) {
5253 return pow(linear.invert(x));
5254 };
5255 scale.domain = function(x) {
5256 if (!arguments.length) return domain;
5257 positive = x[0] >= 0;
5258 linear.domain((domain = x.map(Number)).map(log));
5259 return scale;
5260 };
5261 scale.base = function(_) {
5262 if (!arguments.length) return base;
5263 base = +_;
5264 linear.domain(domain.map(log));
5265 return scale;
5266 };
5267 scale.nice = function() {
5268 var niced = d3_scale_nice(domain.map(log), positive ? Math : d3_scale_logNiceNegative);
5269 linear.domain(niced);
5270 domain = niced.map(pow);
5271 return scale;
5272 };
5273 scale.ticks = function() {
5274 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;
5275 if (isFinite(j - i)) {
5276 if (positive) {
5277 for (;i < j; i++) for (var k = 1; k < n; k++) ticks.push(pow(i) * k);
5278 ticks.push(pow(i));
5279 } else {
5280 ticks.push(pow(i));
5281 for (;i++ < j; ) for (var k = n - 1; k > 0; k--) ticks.push(pow(i) * k);
5282 }
5283 for (i = 0; ticks[i] < u; i++) {}
5284 for (j = ticks.length; ticks[j - 1] > v; j--) {}
5285 ticks = ticks.slice(i, j);
5286 }
5287 return ticks;
5288 };
5289 scale.copy = function() {
5290 return d3_scale_log(linear.copy(), base, positive, domain);
5291 };
5292 return d3_scale_linearRebind(scale, linear);
5293 }
5294 var d3_scale_logNiceNegative = {
5295 floor: function(x) {
5296 return -Math.ceil(-x);
5297 },
5298 ceil: function(x) {
5299 return -Math.floor(-x);
5300 }
5301 };
5302 d3.scale.pow = function() {
5303 return d3_scale_pow(d3.scale.linear(), 1, [ 0, 1 ]);
5304 };
5305 function d3_scale_pow(linear, exponent, domain) {
5306 var powp = d3_scale_powPow(exponent), powb = d3_scale_powPow(1 / exponent);
5307 function scale(x) {
5308 return linear(powp(x));
5309 }
5310 scale.invert = function(x) {
5311 return powb(linear.invert(x));
5312 };
5313 scale.domain = function(x) {
5314 if (!arguments.length) return domain;
5315 linear.domain((domain = x.map(Number)).map(powp));
5316 return scale;
5317 };
5318 scale.ticks = function(m) {
5319 return d3_scale_linearTicks(domain, m);
5320 };
5321 scale.tickFormat = function(m, format) {
5322 return d3_scale_linearTickFormat(domain, m, format);
5323 };
5324 scale.nice = function(m) {
5325 return scale.domain(d3_scale_linearNice(domain, m));
5326 };
5327 scale.exponent = function(x) {
5328 if (!arguments.length) return exponent;
5329 powp = d3_scale_powPow(exponent = x);
5330 powb = d3_scale_powPow(1 / exponent);
5331 linear.domain(domain.map(powp));
5332 return scale;
5333 };
5334 scale.copy = function() {
5335 return d3_scale_pow(linear.copy(), exponent, domain);
5336 };
5337 return d3_scale_linearRebind(scale, linear);
5338 }
5339 function d3_scale_powPow(e) {
5340 return function(x) {
5341 return x < 0 ? -Math.pow(-x, e) : Math.pow(x, e);
5342 };
5343 }
5344 d3.scale.sqrt = function() {
5345 return d3.scale.pow().exponent(.5);
5346 };
5347 d3.scale.ordinal = function() {
5348 return d3_scale_ordinal([], {
5349 t: "range",
5350 a: [ [] ]
5351 });
5352 };
5353 function d3_scale_ordinal(domain, ranger) {
5354 var index, range, rangeBand;
5355 function scale(x) {
5356 return range[((index.get(x) || (ranger.t === "range" ? index.set(x, domain.push(x)) : NaN)) - 1) % range.length];
5357 }
5358 function steps(start, step) {
5359 return d3.range(domain.length).map(function(i) {
5360 return start + step * i;
5361 });
5362 }
5363 scale.domain = function(x) {
5364 if (!arguments.length) return domain;
5365 domain = [];
5366 index = new d3_Map();
5367 var i = -1, n = x.length, xi;
5368 while (++i < n) if (!index.has(xi = x[i])) index.set(xi, domain.push(xi));
5369 return scale[ranger.t].apply(scale, ranger.a);
5370 };
5371 scale.range = function(x) {
5372 if (!arguments.length) return range;
5373 range = x;
5374 rangeBand = 0;
5375 ranger = {
5376 t: "range",
5377 a: arguments
5378 };
5379 return scale;
5380 };
5381 scale.rangePoints = function(x, padding) {
5382 if (arguments.length < 2) padding = 0;
5383 var start = x[0], stop = x[1], step = domain.length < 2 ? (start = (start + stop) / 2,
5384 0) : (stop - start) / (domain.length - 1 + padding);
5385 range = steps(start + step * padding / 2, step);
5386 rangeBand = 0;
5387 ranger = {
5388 t: "rangePoints",
5389 a: arguments
5390 };
5391 return scale;
5392 };
5393 scale.rangeRoundPoints = function(x, padding) {
5394 if (arguments.length < 2) padding = 0;
5395 var start = x[0], stop = x[1], step = domain.length < 2 ? (start = stop = Math.round((start + stop) / 2),
5396 0) : (stop - start) / (domain.length - 1 + padding) | 0;
5397 range = steps(start + Math.round(step * padding / 2 + (stop - start - (domain.length - 1 + padding) * step) / 2), step);
5398 rangeBand = 0;
5399 ranger = {
5400 t: "rangeRoundPoints",
5401 a: arguments
5402 };
5403 return scale;
5404 };
5405 scale.rangeBands = function(x, padding, outerPadding) {
5406 if (arguments.length < 2) padding = 0;
5407 if (arguments.length < 3) outerPadding = padding;
5408 var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = (stop - start) / (domain.length - padding + 2 * outerPadding);
5409 range = steps(start + step * outerPadding, step);
5410 if (reverse) range.reverse();
5411 rangeBand = step * (1 - padding);
5412 ranger = {
5413 t: "rangeBands",
5414 a: arguments
5415 };
5416 return scale;
5417 };
5418 scale.rangeRoundBands = function(x, padding, outerPadding) {
5419 if (arguments.length < 2) padding = 0;
5420 if (arguments.length < 3) outerPadding = padding;
5421 var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = Math.floor((stop - start) / (domain.length - padding + 2 * outerPadding));
5422 range = steps(start + Math.round((stop - start - (domain.length - padding) * step) / 2), step);
5423 if (reverse) range.reverse();
5424 rangeBand = Math.round(step * (1 - padding));
5425 ranger = {
5426 t: "rangeRoundBands",
5427 a: arguments
5428 };
5429 return scale;
5430 };
5431 scale.rangeBand = function() {
5432 return rangeBand;
5433 };
5434 scale.rangeExtent = function() {
5435 return d3_scaleExtent(ranger.a[0]);
5436 };
5437 scale.copy = function() {
5438 return d3_scale_ordinal(domain, ranger);
5439 };
5440 return scale.domain(domain);
5441 }
5442 d3.scale.category10 = function() {
5443 return d3.scale.ordinal().range(d3_category10);
5444 };
5445 d3.scale.category20 = function() {
5446 return d3.scale.ordinal().range(d3_category20);
5447 };
5448 d3.scale.category20b = function() {
5449 return d3.scale.ordinal().range(d3_category20b);
5450 };
5451 d3.scale.category20c = function() {
5452 return d3.scale.ordinal().range(d3_category20c);
5453 };
5454 var d3_category10 = [ 2062260, 16744206, 2924588, 14034728, 9725885, 9197131, 14907330, 8355711, 12369186, 1556175 ].map(d3_rgbString);
5455 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);
5456 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);
5457 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);
5458 d3.scale.quantile = function() {
5459 return d3_scale_quantile([], []);
5460 };
5461 function d3_scale_quantile(domain, range) {
5462 var thresholds;
5463 function rescale() {
5464 var k = 0, q = range.length;
5465 thresholds = [];
5466 while (++k < q) thresholds[k - 1] = d3.quantile(domain, k / q);
5467 return scale;
5468 }
5469 function scale(x) {
5470 if (!isNaN(x = +x)) return range[d3.bisect(thresholds, x)];
5471 }
5472 scale.domain = function(x) {
5473 if (!arguments.length) return domain;
5474 domain = x.map(d3_number).filter(d3_numeric).sort(d3_ascending);
5475 return rescale();
5476 };
5477 scale.range = function(x) {
5478 if (!arguments.length) return range;
5479 range = x;
5480 return rescale();
5481 };
5482 scale.quantiles = function() {
5483 return thresholds;
5484 };
5485 scale.invertExtent = function(y) {
5486 y = range.indexOf(y);
5487 return y < 0 ? [ NaN, NaN ] : [ y > 0 ? thresholds[y - 1] : domain[0], y < thresholds.length ? thresholds[y] : domain[domain.length - 1] ];
5488 };
5489 scale.copy = function() {
5490 return d3_scale_quantile(domain, range);
5491 };
5492 return rescale();
5493 }
5494 d3.scale.quantize = function() {
5495 return d3_scale_quantize(0, 1, [ 0, 1 ]);
5496 };
5497 function d3_scale_quantize(x0, x1, range) {
5498 var kx, i;
5499 function scale(x) {
5500 return range[Math.max(0, Math.min(i, Math.floor(kx * (x - x0))))];
5501 }
5502 function rescale() {
5503 kx = range.length / (x1 - x0);
5504 i = range.length - 1;
5505 return scale;
5506 }
5507 scale.domain = function(x) {
5508 if (!arguments.length) return [ x0, x1 ];
5509 x0 = +x[0];
5510 x1 = +x[x.length - 1];
5511 return rescale();
5512 };
5513 scale.range = function(x) {
5514 if (!arguments.length) return range;
5515 range = x;
5516 return rescale();
5517 };
5518 scale.invertExtent = function(y) {
5519 y = range.indexOf(y);
5520 y = y < 0 ? NaN : y / kx + x0;
5521 return [ y, y + 1 / kx ];
5522 };
5523 scale.copy = function() {
5524 return d3_scale_quantize(x0, x1, range);
5525 };
5526 return rescale();
5527 }
5528 d3.scale.threshold = function() {
5529 return d3_scale_threshold([ .5 ], [ 0, 1 ]);
5530 };
5531 function d3_scale_threshold(domain, range) {
5532 function scale(x) {
5533 if (x <= x) return range[d3.bisect(domain, x)];
5534 }
5535 scale.domain = function(_) {
5536 if (!arguments.length) return domain;
5537 domain = _;
5538 return scale;
5539 };
5540 scale.range = function(_) {
5541 if (!arguments.length) return range;
5542 range = _;
5543 return scale;
5544 };
5545 scale.invertExtent = function(y) {
5546 y = range.indexOf(y);
5547 return [ domain[y - 1], domain[y] ];
5548 };
5549 scale.copy = function() {
5550 return d3_scale_threshold(domain, range);
5551 };
5552 return scale;
5553 }
5554 d3.scale.identity = function() {
5555 return d3_scale_identity([ 0, 1 ]);
5556 };
5557 function d3_scale_identity(domain) {
5558 function identity(x) {
5559 return +x;
5560 }
5561 identity.invert = identity;
5562 identity.domain = identity.range = function(x) {
5563 if (!arguments.length) return domain;
5564 domain = x.map(identity);
5565 return identity;
5566 };
5567 identity.ticks = function(m) {
5568 return d3_scale_linearTicks(domain, m);
5569 };
5570 identity.tickFormat = function(m, format) {
5571 return d3_scale_linearTickFormat(domain, m, format);
5572 };
5573 identity.copy = function() {
5574 return d3_scale_identity(domain);
5575 };
5576 return identity;
5577 }
5578 d3.svg = {};
5579 function d3_zero() {
5580 return 0;
5581 }
5582 d3.svg.arc = function() {
5583 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;
5584 function arc() {
5585 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;
5586 if (r1 < r0) rc = r1, r1 = r0, r0 = rc;
5587 if (da >= τε) return circleSegment(r1, cw) + (r0 ? circleSegment(r0, 1 - cw) : "") + "Z";
5588 var rc, cr, rp, ap, p0 = 0, p1 = 0, x0, y0, x1, y1, x2, y2, x3, y3, path = [];
5589 if (ap = (+padAngle.apply(this, arguments) || 0) / 2) {
5590 rp = padRadius === d3_svg_arcAuto ? Math.sqrt(r0 * r0 + r1 * r1) : +padRadius.apply(this, arguments);
5591 if (!cw) p1 *= -1;
5592 if (r1) p1 = d3_asin(rp / r1 * Math.sin(ap));
5593 if (r0) p0 = d3_asin(rp / r0 * Math.sin(ap));
5594 }
5595 if (r1) {
5596 x0 = r1 * Math.cos(a0 + p1);
5597 y0 = r1 * Math.sin(a0 + p1);
5598 x1 = r1 * Math.cos(a1 - p1);
5599 y1 = r1 * Math.sin(a1 - p1);
5600 var l1 = Math.abs(a1 - a0 - 2 * p1) <= π ? 0 : 1;
5601 if (p1 && d3_svg_arcSweep(x0, y0, x1, y1) === cw ^ l1) {
5602 var h1 = (a0 + a1) / 2;
5603 x0 = r1 * Math.cos(h1);
5604 y0 = r1 * Math.sin(h1);
5605 x1 = y1 = null;
5606 }
5607 } else {
5608 x0 = y0 = 0;
5609 }
5610 if (r0) {
5611 x2 = r0 * Math.cos(a1 - p0);
5612 y2 = r0 * Math.sin(a1 - p0);
5613 x3 = r0 * Math.cos(a0 + p0);
5614 y3 = r0 * Math.sin(a0 + p0);
5615 var l0 = Math.abs(a0 - a1 + 2 * p0) <= π ? 0 : 1;
5616 if (p0 && d3_svg_arcSweep(x2, y2, x3, y3) === 1 - cw ^ l0) {
5617 var h0 = (a0 + a1) / 2;
5618 x2 = r0 * Math.cos(h0);
5619 y2 = r0 * Math.sin(h0);
5620 x3 = y3 = null;
5621 }
5622 } else {
5623 x2 = y2 = 0;
5624 }
5625 if (da > ε && (rc = Math.min(Math.abs(r1 - r0) / 2, +cornerRadius.apply(this, arguments))) > .001) {
5626 cr = r0 < r1 ^ cw ? 0 : 1;
5627 var rc1 = rc, rc0 = rc;
5628 if (da < π) {
5629 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]);
5630 rc0 = Math.min(rc, (r0 - lc) / (kc - 1));
5631 rc1 = Math.min(rc, (r1 - lc) / (kc + 1));
5632 }
5633 if (x1 != null) {
5634 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);
5635 if (rc === rc1) {
5636 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]);
5637 } else {
5638 path.push("M", t30[0], "A", rc1, ",", rc1, " 0 1,", cr, " ", t12[0]);
5639 }
5640 } else {
5641 path.push("M", x0, ",", y0);
5642 }
5643 if (x3 != null) {
5644 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);
5645 if (rc === rc0) {
5646 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]);
5647 } else {
5648 path.push("L", t21[0], "A", rc0, ",", rc0, " 0 0,", cr, " ", t03[0]);
5649 }
5650 } else {
5651 path.push("L", x2, ",", y2);
5652 }
5653 } else {
5654 path.push("M", x0, ",", y0);
5655 if (x1 != null) path.push("A", r1, ",", r1, " 0 ", l1, ",", cw, " ", x1, ",", y1);
5656 path.push("L", x2, ",", y2);
5657 if (x3 != null) path.push("A", r0, ",", r0, " 0 ", l0, ",", 1 - cw, " ", x3, ",", y3);
5658 }
5659 path.push("Z");
5660 return path.join("");
5661 }
5662 function circleSegment(r1, cw) {
5663 return "M0," + r1 + "A" + r1 + "," + r1 + " 0 1," + cw + " 0," + -r1 + "A" + r1 + "," + r1 + " 0 1," + cw + " 0," + r1;
5664 }
5665 arc.innerRadius = function(v) {
5666 if (!arguments.length) return innerRadius;
5667 innerRadius = d3_functor(v);
5668 return arc;
5669 };
5670 arc.outerRadius = function(v) {
5671 if (!arguments.length) return outerRadius;
5672 outerRadius = d3_functor(v);
5673 return arc;
5674 };
5675 arc.cornerRadius = function(v) {
5676 if (!arguments.length) return cornerRadius;
5677 cornerRadius = d3_functor(v);
5678 return arc;
5679 };
5680 arc.padRadius = function(v) {
5681 if (!arguments.length) return padRadius;
5682 padRadius = v == d3_svg_arcAuto ? d3_svg_arcAuto : d3_functor(v);
5683 return arc;
5684 };
5685 arc.startAngle = function(v) {
5686 if (!arguments.length) return startAngle;
5687 startAngle = d3_functor(v);
5688 return arc;
5689 };
5690 arc.endAngle = function(v) {
5691 if (!arguments.length) return endAngle;
5692 endAngle = d3_functor(v);
5693 return arc;
5694 };
5695 arc.padAngle = function(v) {
5696 if (!arguments.length) return padAngle;
5697 padAngle = d3_functor(v);
5698 return arc;
5699 };
5700 arc.centroid = function() {
5701 var r = (+innerRadius.apply(this, arguments) + +outerRadius.apply(this, arguments)) / 2, a = (+startAngle.apply(this, arguments) + +endAngle.apply(this, arguments)) / 2 - halfπ;
5702 return [ Math.cos(a) * r, Math.sin(a) * r ];
5703 };
5704 return arc;
5705 };
5706 var d3_svg_arcAuto = "auto";
5707 function d3_svg_arcInnerRadius(d) {
5708 return d.innerRadius;
5709 }
5710 function d3_svg_arcOuterRadius(d) {
5711 return d.outerRadius;
5712 }
5713 function d3_svg_arcStartAngle(d) {
5714 return d.startAngle;
5715 }
5716 function d3_svg_arcEndAngle(d) {
5717 return d.endAngle;
5718 }
5719 function d3_svg_arcPadAngle(d) {
5720 return d && d.padAngle;
5721 }
5722 function d3_svg_arcSweep(x0, y0, x1, y1) {
5723 return (x0 - x1) * y0 - (y0 - y1) * x0 > 0 ? 0 : 1;
5724 }
5725 function d3_svg_arcCornerTangents(p0, p1, r1, rc, cw) {
5726 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;
5727 if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) cx0 = cx1, cy0 = cy1;
5728 return [ [ cx0 - ox, cy0 - oy ], [ cx0 * r1 / r, cy0 * r1 / r ] ];
5729 }
5730 function d3_true() {
5731 return true;
5732 }
5733 function d3_svg_line(projection) {
5734 var x = d3_geom_pointX, y = d3_geom_pointY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, tension = .7;
5735 function line(data) {
5736 var segments = [], points = [], i = -1, n = data.length, d, fx = d3_functor(x), fy = d3_functor(y);
5737 function segment() {
5738 segments.push("M", interpolate(projection(points), tension));
5739 }
5740 while (++i < n) {
5741 if (defined.call(this, d = data[i], i)) {
5742 points.push([ +fx.call(this, d, i), +fy.call(this, d, i) ]);
5743 } else if (points.length) {
5744 segment();
5745 points = [];
5746 }
5747 }
5748 if (points.length) segment();
5749 return segments.length ? segments.join("") : null;
5750 }
5751 line.x = function(_) {
5752 if (!arguments.length) return x;
5753 x = _;
5754 return line;
5755 };
5756 line.y = function(_) {
5757 if (!arguments.length) return y;
5758 y = _;
5759 return line;
5760 };
5761 line.defined = function(_) {
5762 if (!arguments.length) return defined;
5763 defined = _;
5764 return line;
5765 };
5766 line.interpolate = function(_) {
5767 if (!arguments.length) return interpolateKey;
5768 if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key;
5769 return line;
5770 };
5771 line.tension = function(_) {
5772 if (!arguments.length) return tension;
5773 tension = _;
5774 return line;
5775 };
5776 return line;
5777 }
5778 d3.svg.line = function() {
5779 return d3_svg_line(d3_identity);
5780 };
5781 var d3_svg_lineInterpolators = d3.map({
5782 linear: d3_svg_lineLinear,
5783 "linear-closed": d3_svg_lineLinearClosed,
5784 step: d3_svg_lineStep,
5785 "step-before": d3_svg_lineStepBefore,
5786 "step-after": d3_svg_lineStepAfter,
5787 basis: d3_svg_lineBasis,
5788 "basis-open": d3_svg_lineBasisOpen,
5789 "basis-closed": d3_svg_lineBasisClosed,
5790 bundle: d3_svg_lineBundle,
5791 cardinal: d3_svg_lineCardinal,
5792 "cardinal-open": d3_svg_lineCardinalOpen,
5793 "cardinal-closed": d3_svg_lineCardinalClosed,
5794 monotone: d3_svg_lineMonotone
5795 });
5796 d3_svg_lineInterpolators.forEach(function(key, value) {
5797 value.key = key;
5798 value.closed = /-closed$/.test(key);
5799 });
5800 function d3_svg_lineLinear(points) {
5801 return points.length > 1 ? points.join("L") : points + "Z";
5802 }
5803 function d3_svg_lineLinearClosed(points) {
5804 return points.join("L") + "Z";
5805 }
5806 function d3_svg_lineStep(points) {
5807 var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ];
5808 while (++i < n) path.push("H", (p[0] + (p = points[i])[0]) / 2, "V", p[1]);
5809 if (n > 1) path.push("H", p[0]);
5810 return path.join("");
5811 }
5812 function d3_svg_lineStepBefore(points) {
5813 var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ];
5814 while (++i < n) path.push("V", (p = points[i])[1], "H", p[0]);
5815 return path.join("");
5816 }
5817 function d3_svg_lineStepAfter(points) {
5818 var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ];
5819 while (++i < n) path.push("H", (p = points[i])[0], "V", p[1]);
5820 return path.join("");
5821 }
5822 function d3_svg_lineCardinalOpen(points, tension) {
5823 return points.length < 4 ? d3_svg_lineLinear(points) : points[1] + d3_svg_lineHermite(points.slice(1, -1), d3_svg_lineCardinalTangents(points, tension));
5824 }
5825 function d3_svg_lineCardinalClosed(points, tension) {
5826 return points.length < 3 ? d3_svg_lineLinearClosed(points) : points[0] + d3_svg_lineHermite((points.push(points[0]),
5827 points), d3_svg_lineCardinalTangents([ points[points.length - 2] ].concat(points, [ points[1] ]), tension));
5828 }
5829 function d3_svg_lineCardinal(points, tension) {
5830 return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineCardinalTangents(points, tension));
5831 }
5832 function d3_svg_lineHermite(points, tangents) {
5833 if (tangents.length < 1 || points.length != tangents.length && points.length != tangents.length + 2) {
5834 return d3_svg_lineLinear(points);
5835 }
5836 var quad = points.length != tangents.length, path = "", p0 = points[0], p = points[1], t0 = tangents[0], t = t0, pi = 1;
5837 if (quad) {
5838 path += "Q" + (p[0] - t0[0] * 2 / 3) + "," + (p[1] - t0[1] * 2 / 3) + "," + p[0] + "," + p[1];
5839 p0 = points[1];
5840 pi = 2;
5841 }
5842 if (tangents.length > 1) {
5843 t = tangents[1];
5844 p = points[pi];
5845 pi++;
5846 path += "C" + (p0[0] + t0[0]) + "," + (p0[1] + t0[1]) + "," + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1];
5847 for (var i = 2; i < tangents.length; i++, pi++) {
5848 p = points[pi];
5849 t = tangents[i];
5850 path += "S" + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1];
5851 }
5852 }
5853 if (quad) {
5854 var lp = points[pi];
5855 path += "Q" + (p[0] + t[0] * 2 / 3) + "," + (p[1] + t[1] * 2 / 3) + "," + lp[0] + "," + lp[1];
5856 }
5857 return path;
5858 }
5859 function d3_svg_lineCardinalTangents(points, tension) {
5860 var tangents = [], a = (1 - tension) / 2, p0, p1 = points[0], p2 = points[1], i = 1, n = points.length;
5861 while (++i < n) {
5862 p0 = p1;
5863 p1 = p2;
5864 p2 = points[i];
5865 tangents.push([ a * (p2[0] - p0[0]), a * (p2[1] - p0[1]) ]);
5866 }
5867 return tangents;
5868 }
5869 function d3_svg_lineBasis(points) {
5870 if (points.length < 3) return d3_svg_lineLinear(points);
5871 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) ];
5872 points.push(points[n - 1]);
5873 while (++i <= n) {
5874 pi = points[i];
5875 px.shift();
5876 px.push(pi[0]);
5877 py.shift();
5878 py.push(pi[1]);
5879 d3_svg_lineBasisBezier(path, px, py);
5880 }
5881 points.pop();
5882 path.push("L", pi);
5883 return path.join("");
5884 }
5885 function d3_svg_lineBasisOpen(points) {
5886 if (points.length < 4) return d3_svg_lineLinear(points);
5887 var path = [], i = -1, n = points.length, pi, px = [ 0 ], py = [ 0 ];
5888 while (++i < 3) {
5889 pi = points[i];
5890 px.push(pi[0]);
5891 py.push(pi[1]);
5892 }
5893 path.push(d3_svg_lineDot4(d3_svg_lineBasisBezier3, px) + "," + d3_svg_lineDot4(d3_svg_lineBasisBezier3, py));
5894 --i;
5895 while (++i < n) {
5896 pi = points[i];
5897 px.shift();
5898 px.push(pi[0]);
5899 py.shift();
5900 py.push(pi[1]);
5901 d3_svg_lineBasisBezier(path, px, py);
5902 }
5903 return path.join("");
5904 }
5905 function d3_svg_lineBasisClosed(points) {
5906 var path, i = -1, n = points.length, m = n + 4, pi, px = [], py = [];
5907 while (++i < 4) {
5908 pi = points[i % n];
5909 px.push(pi[0]);
5910 py.push(pi[1]);
5911 }
5912 path = [ d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ];
5913 --i;
5914 while (++i < m) {
5915 pi = points[i % n];
5916 px.shift();
5917 px.push(pi[0]);
5918 py.shift();
5919 py.push(pi[1]);
5920 d3_svg_lineBasisBezier(path, px, py);
5921 }
5922 return path.join("");
5923 }
5924 function d3_svg_lineBundle(points, tension) {
5925 var n = points.length - 1;
5926 if (n) {
5927 var x0 = points[0][0], y0 = points[0][1], dx = points[n][0] - x0, dy = points[n][1] - y0, i = -1, p, t;
5928 while (++i <= n) {
5929 p = points[i];
5930 t = i / n;
5931 p[0] = tension * p[0] + (1 - tension) * (x0 + t * dx);
5932 p[1] = tension * p[1] + (1 - tension) * (y0 + t * dy);
5933 }
5934 }
5935 return d3_svg_lineBasis(points);
5936 }
5937 function d3_svg_lineDot4(a, b) {
5938 return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
5939 }
5940 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 ];
5941 function d3_svg_lineBasisBezier(path, x, y) {
5942 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));
5943 }
5944 function d3_svg_lineSlope(p0, p1) {
5945 return (p1[1] - p0[1]) / (p1[0] - p0[0]);
5946 }
5947 function d3_svg_lineFiniteDifferences(points) {
5948 var i = 0, j = points.length - 1, m = [], p0 = points[0], p1 = points[1], d = m[0] = d3_svg_lineSlope(p0, p1);
5949 while (++i < j) {
5950 m[i] = (d + (d = d3_svg_lineSlope(p0 = p1, p1 = points[i + 1]))) / 2;
5951 }
5952 m[i] = d;
5953 return m;
5954 }
5955 function d3_svg_lineMonotoneTangents(points) {
5956 var tangents = [], d, a, b, s, m = d3_svg_lineFiniteDifferences(points), i = -1, j = points.length - 1;
5957 while (++i < j) {
5958 d = d3_svg_lineSlope(points[i], points[i + 1]);
5959 if (abs(d) < ε) {
5960 m[i] = m[i + 1] = 0;
5961 } else {
5962 a = m[i] / d;
5963 b = m[i + 1] / d;
5964 s = a * a + b * b;
5965 if (s > 9) {
5966 s = d * 3 / Math.sqrt(s);
5967 m[i] = s * a;
5968 m[i + 1] = s * b;
5969 }
5970 }
5971 }
5972 i = -1;
5973 while (++i <= j) {
5974 s = (points[Math.min(j, i + 1)][0] - points[Math.max(0, i - 1)][0]) / (6 * (1 + m[i] * m[i]));
5975 tangents.push([ s || 0, m[i] * s || 0 ]);
5976 }
5977 return tangents;
5978 }
5979 function d3_svg_lineMonotone(points) {
5980 return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineMonotoneTangents(points));
5981 }
5982 d3.svg.line.radial = function() {
5983 var line = d3_svg_line(d3_svg_lineRadial);
5984 line.radius = line.x, delete line.x;
5985 line.angle = line.y, delete line.y;
5986 return line;
5987 };
5988 function d3_svg_lineRadial(points) {
5989 var point, i = -1, n = points.length, r, a;
5990 while (++i < n) {
5991 point = points[i];
5992 r = point[0];
5993 a = point[1] - halfπ;
5994 point[0] = r * Math.cos(a);
5995 point[1] = r * Math.sin(a);
5996 }
5997 return points;
5998 }
5999 function d3_svg_area(projection) {
6000 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;
6001 function area(data) {
6002 var segments = [], points0 = [], points1 = [], i = -1, n = data.length, d, fx0 = d3_functor(x0), fy0 = d3_functor(y0), fx1 = x0 === x1 ? function() {
6003 return x;
6004 } : d3_functor(x1), fy1 = y0 === y1 ? function() {
6005 return y;
6006 } : d3_functor(y1), x, y;
6007 function segment() {
6008 segments.push("M", interpolate(projection(points1), tension), L, interpolateReverse(projection(points0.reverse()), tension), "Z");
6009 }
6010 while (++i < n) {
6011 if (defined.call(this, d = data[i], i)) {
6012 points0.push([ x = +fx0.call(this, d, i), y = +fy0.call(this, d, i) ]);
6013 points1.push([ +fx1.call(this, d, i), +fy1.call(this, d, i) ]);
6014 } else if (points0.length) {
6015 segment();
6016 points0 = [];
6017 points1 = [];
6018 }
6019 }
6020 if (points0.length) segment();
6021 return segments.length ? segments.join("") : null;
6022 }
6023 area.x = function(_) {
6024 if (!arguments.length) return x1;
6025 x0 = x1 = _;
6026 return area;
6027 };
6028 area.x0 = function(_) {
6029 if (!arguments.length) return x0;
6030 x0 = _;
6031 return area;
6032 };
6033 area.x1 = function(_) {
6034 if (!arguments.length) return x1;
6035 x1 = _;
6036 return area;
6037 };
6038 area.y = function(_) {
6039 if (!arguments.length) return y1;
6040 y0 = y1 = _;
6041 return area;
6042 };
6043 area.y0 = function(_) {
6044 if (!arguments.length) return y0;
6045 y0 = _;
6046 return area;
6047 };
6048 area.y1 = function(_) {
6049 if (!arguments.length) return y1;
6050 y1 = _;
6051 return area;
6052 };
6053 area.defined = function(_) {
6054 if (!arguments.length) return defined;
6055 defined = _;
6056 return area;
6057 };
6058 area.interpolate = function(_) {
6059 if (!arguments.length) return interpolateKey;
6060 if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key;
6061 interpolateReverse = interpolate.reverse || interpolate;
6062 L = interpolate.closed ? "M" : "L";
6063 return area;
6064 };
6065 area.tension = function(_) {
6066 if (!arguments.length) return tension;
6067 tension = _;
6068 return area;
6069 };
6070 return area;
6071 }
6072 d3_svg_lineStepBefore.reverse = d3_svg_lineStepAfter;
6073 d3_svg_lineStepAfter.reverse = d3_svg_lineStepBefore;
6074 d3.svg.area = function() {
6075 return d3_svg_area(d3_identity);
6076 };
6077 d3.svg.area.radial = function() {
6078 var area = d3_svg_area(d3_svg_lineRadial);
6079 area.radius = area.x, delete area.x;
6080 area.innerRadius = area.x0, delete area.x0;
6081 area.outerRadius = area.x1, delete area.x1;
6082 area.angle = area.y, delete area.y;
6083 area.startAngle = area.y0, delete area.y0;
6084 area.endAngle = area.y1, delete area.y1;
6085 return area;
6086 };
6087 function d3_source(d) {
6088 return d.source;
6089 }
6090 function d3_target(d) {
6091 return d.target;
6092 }
6093 d3.svg.chord = function() {
6094 var source = d3_source, target = d3_target, radius = d3_svg_chordRadius, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle;
6095 function chord(d, i) {
6096 var s = subgroup(this, source, d, i), t = subgroup(this, target, d, i);
6097 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";
6098 }
6099 function subgroup(self, f, d, i) {
6100 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π;
6101 return {
6102 r: r,
6103 a0: a0,
6104 a1: a1,
6105 p0: [ r * Math.cos(a0), r * Math.sin(a0) ],
6106 p1: [ r * Math.cos(a1), r * Math.sin(a1) ]
6107 };
6108 }
6109 function equals(a, b) {
6110 return a.a0 == b.a0 && a.a1 == b.a1;
6111 }
6112 function arc(r, p, a) {
6113 return "A" + r + "," + r + " 0 " + +(a > π) + ",1 " + p;
6114 }
6115 function curve(r0, p0, r1, p1) {
6116 return "Q 0,0 " + p1;
6117 }
6118 chord.radius = function(v) {
6119 if (!arguments.length) return radius;
6120 radius = d3_functor(v);
6121 return chord;
6122 };
6123 chord.source = function(v) {
6124 if (!arguments.length) return source;
6125 source = d3_functor(v);
6126 return chord;
6127 };
6128 chord.target = function(v) {
6129 if (!arguments.length) return target;
6130 target = d3_functor(v);
6131 return chord;
6132 };
6133 chord.startAngle = function(v) {
6134 if (!arguments.length) return startAngle;
6135 startAngle = d3_functor(v);
6136 return chord;
6137 };
6138 chord.endAngle = function(v) {
6139 if (!arguments.length) return endAngle;
6140 endAngle = d3_functor(v);
6141 return chord;
6142 };
6143 return chord;
6144 };
6145 function d3_svg_chordRadius(d) {
6146 return d.radius;
6147 }
6148 d3.svg.diagonal = function() {
6149 var source = d3_source, target = d3_target, projection = d3_svg_diagonalProjection;
6150 function diagonal(d, i) {
6151 var p0 = source.call(this, d, i), p3 = target.call(this, d, i), m = (p0.y + p3.y) / 2, p = [ p0, {
6152 x: p0.x,
6153 y: m
6154 }, {
6155 x: p3.x,
6156 y: m
6157 }, p3 ];
6158 p = p.map(projection);
6159 return "M" + p[0] + "C" + p[1] + " " + p[2] + " " + p[3];
6160 }
6161 diagonal.source = function(x) {
6162 if (!arguments.length) return source;
6163 source = d3_functor(x);
6164 return diagonal;
6165 };
6166 diagonal.target = function(x) {
6167 if (!arguments.length) return target;
6168 target = d3_functor(x);
6169 return diagonal;
6170 };
6171 diagonal.projection = function(x) {
6172 if (!arguments.length) return projection;
6173 projection = x;
6174 return diagonal;
6175 };
6176 return diagonal;
6177 };
6178 function d3_svg_diagonalProjection(d) {
6179 return [ d.x, d.y ];
6180 }
6181 d3.svg.diagonal.radial = function() {
6182 var diagonal = d3.svg.diagonal(), projection = d3_svg_diagonalProjection, projection_ = diagonal.projection;
6183 diagonal.projection = function(x) {
6184 return arguments.length ? projection_(d3_svg_diagonalRadialProjection(projection = x)) : projection;
6185 };
6186 return diagonal;
6187 };
6188 function d3_svg_diagonalRadialProjection(projection) {
6189 return function() {
6190 var d = projection.apply(this, arguments), r = d[0], a = d[1] - halfπ;
6191 return [ r * Math.cos(a), r * Math.sin(a) ];
6192 };
6193 }
6194 d3.svg.symbol = function() {
6195 var type = d3_svg_symbolType, size = d3_svg_symbolSize;
6196 function symbol(d, i) {
6197 return (d3_svg_symbols.get(type.call(this, d, i)) || d3_svg_symbolCircle)(size.call(this, d, i));
6198 }
6199 symbol.type = function(x) {
6200 if (!arguments.length) return type;
6201 type = d3_functor(x);
6202 return symbol;
6203 };
6204 symbol.size = function(x) {
6205 if (!arguments.length) return size;
6206 size = d3_functor(x);
6207 return symbol;
6208 };
6209 return symbol;
6210 };
6211 function d3_svg_symbolSize() {
6212 return 64;
6213 }
6214 function d3_svg_symbolType() {
6215 return "circle";
6216 }
6217 function d3_svg_symbolCircle(size) {
6218 var r = Math.sqrt(size / π);
6219 return "M0," + r + "A" + r + "," + r + " 0 1,1 0," + -r + "A" + r + "," + r + " 0 1,1 0," + r + "Z";
6220 }
6221 var d3_svg_symbols = d3.map({
6222 circle: d3_svg_symbolCircle,
6223 cross: function(size) {
6224 var r = Math.sqrt(size / 5) / 2;
6225 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";
6226 },
6227 diamond: function(size) {
6228 var ry = Math.sqrt(size / (2 * d3_svg_symbolTan30)), rx = ry * d3_svg_symbolTan30;
6229 return "M0," + -ry + "L" + rx + ",0" + " 0," + ry + " " + -rx + ",0" + "Z";
6230 },
6231 square: function(size) {
6232 var r = Math.sqrt(size) / 2;
6233 return "M" + -r + "," + -r + "L" + r + "," + -r + " " + r + "," + r + " " + -r + "," + r + "Z";
6234 },
6235 "triangle-down": function(size) {
6236 var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2;
6237 return "M0," + ry + "L" + rx + "," + -ry + " " + -rx + "," + -ry + "Z";
6238 },
6239 "triangle-up": function(size) {
6240 var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2;
6241 return "M0," + -ry + "L" + rx + "," + ry + " " + -rx + "," + ry + "Z";
6242 }
6243 });
6244 d3.svg.symbolTypes = d3_svg_symbols.keys();
6245 var d3_svg_symbolSqrt3 = Math.sqrt(3), d3_svg_symbolTan30 = Math.tan(30 * d3_radians);
6246 d3_selectionPrototype.transition = function(name) {
6247 var id = d3_transitionInheritId || ++d3_transitionId, ns = d3_transitionNamespace(name), subgroups = [], subgroup, node, transition = d3_transitionInherit || {
6248 time: Date.now(),
6249 ease: d3_ease_cubicInOut,
6250 delay: 0,
6251 duration: 250
6252 };
6253 for (var j = -1, m = this.length; ++j < m; ) {
6254 subgroups.push(subgroup = []);
6255 for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
6256 if (node = group[i]) d3_transitionNode(node, i, ns, id, transition);
6257 subgroup.push(node);
6258 }
6259 }
6260 return d3_transition(subgroups, ns, id);
6261 };
6262 d3_selectionPrototype.interrupt = function(name) {
6263 return this.each(name == null ? d3_selection_interrupt : d3_selection_interruptNS(d3_transitionNamespace(name)));
6264 };
6265 var d3_selection_interrupt = d3_selection_interruptNS(d3_transitionNamespace());
6266 function d3_selection_interruptNS(ns) {
6267 return function() {
6268 var lock, activeId, active;
6269 if ((lock = this[ns]) && (active = lock[activeId = lock.active])) {
6270 active.timer.c = null;
6271 active.timer.t = NaN;
6272 if (--lock.count) delete lock[activeId]; else delete this[ns];
6273 lock.active += .5;
6274 active.event && active.event.interrupt.call(this, this.__data__, active.index);
6275 }
6276 };
6277 }
6278 function d3_transition(groups, ns, id) {
6279 d3_subclass(groups, d3_transitionPrototype);
6280 groups.namespace = ns;
6281 groups.id = id;
6282 return groups;
6283 }
6284 var d3_transitionPrototype = [], d3_transitionId = 0, d3_transitionInheritId, d3_transitionInherit;
6285 d3_transitionPrototype.call = d3_selectionPrototype.call;
6286 d3_transitionPrototype.empty = d3_selectionPrototype.empty;
6287 d3_transitionPrototype.node = d3_selectionPrototype.node;
6288 d3_transitionPrototype.size = d3_selectionPrototype.size;
6289 d3.transition = function(selection, name) {
6290 return selection && selection.transition ? d3_transitionInheritId ? selection.transition(name) : selection : d3.selection().transition(selection);
6291 };
6292 d3.transition.prototype = d3_transitionPrototype;
6293 d3_transitionPrototype.select = function(selector) {
6294 var id = this.id, ns = this.namespace, subgroups = [], subgroup, subnode, node;
6295 selector = d3_selection_selector(selector);
6296 for (var j = -1, m = this.length; ++j < m; ) {
6297 subgroups.push(subgroup = []);
6298 for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
6299 if ((node = group[i]) && (subnode = selector.call(node, node.__data__, i, j))) {
6300 if ("__data__" in node) subnode.__data__ = node.__data__;
6301 d3_transitionNode(subnode, i, ns, id, node[ns][id]);
6302 subgroup.push(subnode);
6303 } else {
6304 subgroup.push(null);
6305 }
6306 }
6307 }
6308 return d3_transition(subgroups, ns, id);
6309 };
6310 d3_transitionPrototype.selectAll = function(selector) {
6311 var id = this.id, ns = this.namespace, subgroups = [], subgroup, subnodes, node, subnode, transition;
6312 selector = d3_selection_selectorAll(selector);
6313 for (var j = -1, m = this.length; ++j < m; ) {
6314 for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
6315 if (node = group[i]) {
6316 transition = node[ns][id];
6317 subnodes = selector.call(node, node.__data__, i, j);
6318 subgroups.push(subgroup = []);
6319 for (var k = -1, o = subnodes.length; ++k < o; ) {
6320 if (subnode = subnodes[k]) d3_transitionNode(subnode, k, ns, id, transition);
6321 subgroup.push(subnode);
6322 }
6323 }
6324 }
6325 }
6326 return d3_transition(subgroups, ns, id);
6327 };
6328 d3_transitionPrototype.filter = function(filter) {
6329 var subgroups = [], subgroup, group, node;
6330 if (typeof filter !== "function") filter = d3_selection_filter(filter);
6331 for (var j = 0, m = this.length; j < m; j++) {
6332 subgroups.push(subgroup = []);
6333 for (var group = this[j], i = 0, n = group.length; i < n; i++) {
6334 if ((node = group[i]) && filter.call(node, node.__data__, i, j)) {
6335 subgroup.push(node);
6336 }
6337 }
6338 }
6339 return d3_transition(subgroups, this.namespace, this.id);
6340 };
6341 d3_transitionPrototype.tween = function(name, tween) {
6342 var id = this.id, ns = this.namespace;
6343 if (arguments.length < 2) return this.node()[ns][id].tween.get(name);
6344 return d3_selection_each(this, tween == null ? function(node) {
6345 node[ns][id].tween.remove(name);
6346 } : function(node) {
6347 node[ns][id].tween.set(name, tween);
6348 });
6349 };
6350 function d3_transition_tween(groups, name, value, tween) {
6351 var id = groups.id, ns = groups.namespace;
6352 return d3_selection_each(groups, typeof value === "function" ? function(node, i, j) {
6353 node[ns][id].tween.set(name, tween(value.call(node, node.__data__, i, j)));
6354 } : (value = tween(value), function(node) {
6355 node[ns][id].tween.set(name, value);
6356 }));
6357 }
6358 d3_transitionPrototype.attr = function(nameNS, value) {
6359 if (arguments.length < 2) {
6360 for (value in nameNS) this.attr(value, nameNS[value]);
6361 return this;
6362 }
6363 var interpolate = nameNS == "transform" ? d3_interpolateTransform : d3_interpolate, name = d3.ns.qualify(nameNS);
6364 function attrNull() {
6365 this.removeAttribute(name);
6366 }
6367 function attrNullNS() {
6368 this.removeAttributeNS(name.space, name.local);
6369 }
6370 function attrTween(b) {
6371 return b == null ? attrNull : (b += "", function() {
6372 var a = this.getAttribute(name), i;
6373 return a !== b && (i = interpolate(a, b), function(t) {
6374 this.setAttribute(name, i(t));
6375 });
6376 });
6377 }
6378 function attrTweenNS(b) {
6379 return b == null ? attrNullNS : (b += "", function() {
6380 var a = this.getAttributeNS(name.space, name.local), i;
6381 return a !== b && (i = interpolate(a, b), function(t) {
6382 this.setAttributeNS(name.space, name.local, i(t));
6383 });
6384 });
6385 }
6386 return d3_transition_tween(this, "attr." + nameNS, value, name.local ? attrTweenNS : attrTween);
6387 };
6388 d3_transitionPrototype.attrTween = function(nameNS, tween) {
6389 var name = d3.ns.qualify(nameNS);
6390 function attrTween(d, i) {
6391 var f = tween.call(this, d, i, this.getAttribute(name));
6392 return f && function(t) {
6393 this.setAttribute(name, f(t));
6394 };
6395 }
6396 function attrTweenNS(d, i) {
6397 var f = tween.call(this, d, i, this.getAttributeNS(name.space, name.local));
6398 return f && function(t) {
6399 this.setAttributeNS(name.space, name.local, f(t));
6400 };
6401 }
6402 return this.tween("attr." + nameNS, name.local ? attrTweenNS : attrTween);
6403 };
6404 d3_transitionPrototype.style = function(name, value, priority) {
6405 var n = arguments.length;
6406 if (n < 3) {
6407 if (typeof name !== "string") {
6408 if (n < 2) value = "";
6409 for (priority in name) this.style(priority, name[priority], value);
6410 return this;
6411 }
6412 priority = "";
6413 }
6414 function styleNull() {
6415 this.style.removeProperty(name);
6416 }
6417 function styleString(b) {
6418 return b == null ? styleNull : (b += "", function() {
6419 var a = d3_window(this).getComputedStyle(this, null).getPropertyValue(name), i;
6420 return a !== b && (i = d3_interpolate(a, b), function(t) {
6421 this.style.setProperty(name, i(t), priority);
6422 });
6423 });
6424 }
6425 return d3_transition_tween(this, "style." + name, value, styleString);
6426 };
6427 d3_transitionPrototype.styleTween = function(name, tween, priority) {
6428 if (arguments.length < 3) priority = "";
6429 function styleTween(d, i) {
6430 var f = tween.call(this, d, i, d3_window(this).getComputedStyle(this, null).getPropertyValue(name));
6431 return f && function(t) {
6432 this.style.setProperty(name, f(t), priority);
6433 };
6434 }
6435 return this.tween("style." + name, styleTween);
6436 };
6437 d3_transitionPrototype.text = function(value) {
6438 return d3_transition_tween(this, "text", value, d3_transition_text);
6439 };
6440 function d3_transition_text(b) {
6441 if (b == null) b = "";
6442 return function() {
6443 this.textContent = b;
6444 };
6445 }
6446 d3_transitionPrototype.remove = function() {
6447 var ns = this.namespace;
6448 return this.each("end.transition", function() {
6449 var p;
6450 if (this[ns].count < 2 && (p = this.parentNode)) p.removeChild(this);
6451 });
6452 };
6453 d3_transitionPrototype.ease = function(value) {
6454 var id = this.id, ns = this.namespace;
6455 if (arguments.length < 1) return this.node()[ns][id].ease;
6456 if (typeof value !== "function") value = d3.ease.apply(d3, arguments);
6457 return d3_selection_each(this, function(node) {
6458 node[ns][id].ease = value;
6459 });
6460 };
6461 d3_transitionPrototype.delay = function(value) {
6462 var id = this.id, ns = this.namespace;
6463 if (arguments.length < 1) return this.node()[ns][id].delay;
6464 return d3_selection_each(this, typeof value === "function" ? function(node, i, j) {
6465 node[ns][id].delay = +value.call(node, node.__data__, i, j);
6466 } : (value = +value, function(node) {
6467 node[ns][id].delay = value;
6468 }));
6469 };
6470 d3_transitionPrototype.duration = function(value) {
6471 var id = this.id, ns = this.namespace;
6472 if (arguments.length < 1) return this.node()[ns][id].duration;
6473 return d3_selection_each(this, typeof value === "function" ? function(node, i, j) {
6474 node[ns][id].duration = Math.max(1, value.call(node, node.__data__, i, j));
6475 } : (value = Math.max(1, value), function(node) {
6476 node[ns][id].duration = value;
6477 }));
6478 };
6479 d3_transitionPrototype.each = function(type, listener) {
6480 var id = this.id, ns = this.namespace;
6481 if (arguments.length < 2) {
6482 var inherit = d3_transitionInherit, inheritId = d3_transitionInheritId;
6483 try {
6484 d3_transitionInheritId = id;
6485 d3_selection_each(this, function(node, i, j) {
6486 d3_transitionInherit = node[ns][id];
6487 type.call(node, node.__data__, i, j);
6488 });
6489 } finally {
6490 d3_transitionInherit = inherit;
6491 d3_transitionInheritId = inheritId;
6492 }
6493 } else {
6494 d3_selection_each(this, function(node) {
6495 var transition = node[ns][id];
6496 (transition.event || (transition.event = d3.dispatch("start", "end", "interrupt"))).on(type, listener);
6497 });
6498 }
6499 return this;
6500 };
6501 d3_transitionPrototype.transition = function() {
6502 var id0 = this.id, id1 = ++d3_transitionId, ns = this.namespace, subgroups = [], subgroup, group, node, transition;
6503 for (var j = 0, m = this.length; j < m; j++) {
6504 subgroups.push(subgroup = []);
6505 for (var group = this[j], i = 0, n = group.length; i < n; i++) {
6506 if (node = group[i]) {
6507 transition = node[ns][id0];
6508 d3_transitionNode(node, i, ns, id1, {
6509 time: transition.time,
6510 ease: transition.ease,
6511 delay: transition.delay + transition.duration,
6512 duration: transition.duration
6513 });
6514 }
6515 subgroup.push(node);
6516 }
6517 }
6518 return d3_transition(subgroups, ns, id1);
6519 };
6520 function d3_transitionNamespace(name) {
6521 return name == null ? "__transition__" : "__transition_" + name + "__";
6522 }
6523 function d3_transitionNode(node, i, ns, id, inherit) {
6524 var lock = node[ns] || (node[ns] = {
6525 active: 0,
6526 count: 0
6527 }), transition = lock[id], time, timer, duration, ease, tweens;
6528 function schedule(elapsed) {
6529 var delay = transition.delay;
6530 timer.t = delay + time;
6531 if (delay <= elapsed) return start(elapsed - delay);
6532 timer.c = start;
6533 }
6534 function start(elapsed) {
6535 var activeId = lock.active, active = lock[activeId];
6536 if (active) {
6537 active.timer.c = null;
6538 active.timer.t = NaN;
6539 --lock.count;
6540 delete lock[activeId];
6541 active.event && active.event.interrupt.call(node, node.__data__, active.index);
6542 }
6543 for (var cancelId in lock) {
6544 if (+cancelId < id) {
6545 var cancel = lock[cancelId];
6546 cancel.timer.c = null;
6547 cancel.timer.t = NaN;
6548 --lock.count;
6549 delete lock[cancelId];
6550 }
6551 }
6552 timer.c = tick;
6553 d3_timer(function() {
6554 if (timer.c && tick(elapsed || 1)) {
6555 timer.c = null;
6556 timer.t = NaN;
6557 }
6558 return 1;
6559 }, 0, time);
6560 lock.active = id;
6561 transition.event && transition.event.start.call(node, node.__data__, i);
6562 tweens = [];
6563 transition.tween.forEach(function(key, value) {
6564 if (value = value.call(node, node.__data__, i)) {
6565 tweens.push(value);
6566 }
6567 });
6568 ease = transition.ease;
6569 duration = transition.duration;
6570 }
6571 function tick(elapsed) {
6572 var t = elapsed / duration, e = ease(t), n = tweens.length;
6573 while (n > 0) {
6574 tweens[--n].call(node, e);
6575 }
6576 if (t >= 1) {
6577 transition.event && transition.event.end.call(node, node.__data__, i);
6578 if (--lock.count) delete lock[id]; else delete node[ns];
6579 return 1;
6580 }
6581 }
6582 if (!transition) {
6583 time = inherit.time;
6584 timer = d3_timer(schedule, 0, time);
6585 transition = lock[id] = {
6586 tween: new d3_Map(),
6587 time: time,
6588 timer: timer,
6589 delay: inherit.delay,
6590 duration: inherit.duration,
6591 ease: inherit.ease,
6592 index: i
6593 };
6594 inherit = null;
6595 ++lock.count;
6596 }
6597 }
6598 d3.svg.axis = function() {
6599 var scale = d3.scale.linear(), orient = d3_svg_axisDefaultOrient, innerTickSize = 6, outerTickSize = 6, tickPadding = 3, tickArguments_ = [ 10 ], tickValues = null, tickFormat_;
6600 function axis(g) {
6601 g.each(function() {
6602 var g = d3.select(this);
6603 var scale0 = this.__chart__ || scale, scale1 = this.__chart__ = scale.copy();
6604 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;
6605 var range = d3_scaleRange(scale1), path = g.selectAll(".domain").data([ 0 ]), pathUpdate = (path.enter().append("path").attr("class", "domain"),
6606 d3.transition(path));
6607 tickEnter.append("line");
6608 tickEnter.append("text");
6609 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;
6610 if (orient === "bottom" || orient === "top") {
6611 tickTransform = d3_svg_axisX, x1 = "x", y1 = "y", x2 = "x2", y2 = "y2";
6612 text.attr("dy", sign < 0 ? "0em" : ".71em").style("text-anchor", "middle");
6613 pathUpdate.attr("d", "M" + range[0] + "," + sign * outerTickSize + "V0H" + range[1] + "V" + sign * outerTickSize);
6614 } else {
6615 tickTransform = d3_svg_axisY, x1 = "y", y1 = "x", x2 = "y2", y2 = "x2";
6616 text.attr("dy", ".32em").style("text-anchor", sign < 0 ? "end" : "start");
6617 pathUpdate.attr("d", "M" + sign * outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + sign * outerTickSize);
6618 }
6619 lineEnter.attr(y2, sign * innerTickSize);
6620 textEnter.attr(y1, sign * tickSpacing);
6621 lineUpdate.attr(x2, 0).attr(y2, sign * innerTickSize);
6622 textUpdate.attr(x1, 0).attr(y1, sign * tickSpacing);
6623 if (scale1.rangeBand) {
6624 var x = scale1, dx = x.rangeBand() / 2;
6625 scale0 = scale1 = function(d) {
6626 return x(d) + dx;
6627 };
6628 } else if (scale0.rangeBand) {
6629 scale0 = scale1;
6630 } else {
6631 tickExit.call(tickTransform, scale1, scale0);
6632 }
6633 tickEnter.call(tickTransform, scale0, scale1);
6634 tickUpdate.call(tickTransform, scale1, scale1);
6635 });
6636 }
6637 axis.scale = function(x) {
6638 if (!arguments.length) return scale;
6639 scale = x;
6640 return axis;
6641 };
6642 axis.orient = function(x) {
6643 if (!arguments.length) return orient;
6644 orient = x in d3_svg_axisOrients ? x + "" : d3_svg_axisDefaultOrient;
6645 return axis;
6646 };
6647 axis.ticks = function() {
6648 if (!arguments.length) return tickArguments_;
6649 tickArguments_ = d3_array(arguments);
6650 return axis;
6651 };
6652 axis.tickValues = function(x) {
6653 if (!arguments.length) return tickValues;
6654 tickValues = x;
6655 return axis;
6656 };
6657 axis.tickFormat = function(x) {
6658 if (!arguments.length) return tickFormat_;
6659 tickFormat_ = x;
6660 return axis;
6661 };
6662 axis.tickSize = function(x) {
6663 var n = arguments.length;
6664 if (!n) return innerTickSize;
6665 innerTickSize = +x;
6666 outerTickSize = +arguments[n - 1];
6667 return axis;
6668 };
6669 axis.innerTickSize = function(x) {
6670 if (!arguments.length) return innerTickSize;
6671 innerTickSize = +x;
6672 return axis;
6673 };
6674 axis.outerTickSize = function(x) {
6675 if (!arguments.length) return outerTickSize;
6676 outerTickSize = +x;
6677 return axis;
6678 };
6679 axis.tickPadding = function(x) {
6680 if (!arguments.length) return tickPadding;
6681 tickPadding = +x;
6682 return axis;
6683 };
6684 axis.tickSubdivide = function() {
6685 return arguments.length && axis;
6686 };
6687 return axis;
6688 };
6689 var d3_svg_axisDefaultOrient = "bottom", d3_svg_axisOrients = {
6690 top: 1,
6691 right: 1,
6692 bottom: 1,
6693 left: 1
6694 };
6695 function d3_svg_axisX(selection, x0, x1) {
6696 selection.attr("transform", function(d) {
6697 var v0 = x0(d);
6698 return "translate(" + (isFinite(v0) ? v0 : x1(d)) + ",0)";
6699 });
6700 }
6701 function d3_svg_axisY(selection, y0, y1) {
6702 selection.attr("transform", function(d) {
6703 var v0 = y0(d);
6704 return "translate(0," + (isFinite(v0) ? v0 : y1(d)) + ")";
6705 });
6706 }
6707 d3.svg.brush = function() {
6708 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];
6709 function brush(g) {
6710 g.each(function() {
6711 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);
6712 var background = g.selectAll(".background").data([ 0 ]);
6713 background.enter().append("rect").attr("class", "background").style("visibility", "hidden").style("cursor", "crosshair");
6714 g.selectAll(".extent").data([ 0 ]).enter().append("rect").attr("class", "extent").style("cursor", "move");
6715 var resize = g.selectAll(".resize").data(resizes, d3_identity);
6716 resize.exit().remove();
6717 resize.enter().append("g").attr("class", function(d) {
6718 return "resize " + d;
6719 }).style("cursor", function(d) {
6720 return d3_svg_brushCursor[d];
6721 }).append("rect").attr("x", function(d) {
6722 return /[ew]$/.test(d) ? -3 : null;
6723 }).attr("y", function(d) {
6724 return /^[ns]/.test(d) ? -3 : null;
6725 }).attr("width", 6).attr("height", 6).style("visibility", "hidden");
6726 resize.style("display", brush.empty() ? "none" : null);
6727 var gUpdate = d3.transition(g), backgroundUpdate = d3.transition(background), range;
6728 if (x) {
6729 range = d3_scaleRange(x);
6730 backgroundUpdate.attr("x", range[0]).attr("width", range[1] - range[0]);
6731 redrawX(gUpdate);
6732 }
6733 if (y) {
6734 range = d3_scaleRange(y);
6735 backgroundUpdate.attr("y", range[0]).attr("height", range[1] - range[0]);
6736 redrawY(gUpdate);
6737 }
6738 redraw(gUpdate);
6739 });
6740 }
6741 brush.event = function(g) {
6742 g.each(function() {
6743 var event_ = event.of(this, arguments), extent1 = {
6744 x: xExtent,
6745 y: yExtent,
6746 i: xExtentDomain,
6747 j: yExtentDomain
6748 }, extent0 = this.__chart__ || extent1;
6749 this.__chart__ = extent1;
6750 if (d3_transitionInheritId) {
6751 d3.select(this).transition().each("start.brush", function() {
6752 xExtentDomain = extent0.i;
6753 yExtentDomain = extent0.j;
6754 xExtent = extent0.x;
6755 yExtent = extent0.y;
6756 event_({
6757 type: "brushstart"
6758 });
6759 }).tween("brush:brush", function() {
6760 var xi = d3_interpolateArray(xExtent, extent1.x), yi = d3_interpolateArray(yExtent, extent1.y);
6761 xExtentDomain = yExtentDomain = null;
6762 return function(t) {
6763 xExtent = extent1.x = xi(t);
6764 yExtent = extent1.y = yi(t);
6765 event_({
6766 type: "brush",
6767 mode: "resize"
6768 });
6769 };
6770 }).each("end.brush", function() {
6771 xExtentDomain = extent1.i;
6772 yExtentDomain = extent1.j;
6773 event_({
6774 type: "brush",
6775 mode: "resize"
6776 });
6777 event_({
6778 type: "brushend"
6779 });
6780 });
6781 } else {
6782 event_({
6783 type: "brushstart"
6784 });
6785 event_({
6786 type: "brush",
6787 mode: "resize"
6788 });
6789 event_({
6790 type: "brushend"
6791 });
6792 }
6793 });
6794 };
6795 function redraw(g) {
6796 g.selectAll(".resize").attr("transform", function(d) {
6797 return "translate(" + xExtent[+/e$/.test(d)] + "," + yExtent[+/^s/.test(d)] + ")";
6798 });
6799 }
6800 function redrawX(g) {
6801 g.select(".extent").attr("x", xExtent[0]);
6802 g.selectAll(".extent,.n>rect,.s>rect").attr("width", xExtent[1] - xExtent[0]);
6803 }
6804 function redrawY(g) {
6805 g.select(".extent").attr("y", yExtent[0]);
6806 g.selectAll(".extent,.e>rect,.w>rect").attr("height", yExtent[1] - yExtent[0]);
6807 }
6808 function brushstart() {
6809 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;
6810 var w = d3.select(d3_window(target)).on("keydown.brush", keydown).on("keyup.brush", keyup);
6811 if (d3.event.changedTouches) {
6812 w.on("touchmove.brush", brushmove).on("touchend.brush", brushend);
6813 } else {
6814 w.on("mousemove.brush", brushmove).on("mouseup.brush", brushend);
6815 }
6816 g.interrupt().selectAll("*").interrupt();
6817 if (dragging) {
6818 origin[0] = xExtent[0] - origin[0];
6819 origin[1] = yExtent[0] - origin[1];
6820 } else if (resizing) {
6821 var ex = +/w$/.test(resizing), ey = +/^n/.test(resizing);
6822 offset = [ xExtent[1 - ex] - origin[0], yExtent[1 - ey] - origin[1] ];
6823 origin[0] = xExtent[ex];
6824 origin[1] = yExtent[ey];
6825 } else if (d3.event.altKey) center = origin.slice();
6826 g.style("pointer-events", "none").selectAll(".resize").style("display", null);
6827 d3.select("body").style("cursor", eventTarget.style("cursor"));
6828 event_({
6829 type: "brushstart"
6830 });
6831 brushmove();
6832 function keydown() {
6833 if (d3.event.keyCode == 32) {
6834 if (!dragging) {
6835 center = null;
6836 origin[0] -= xExtent[1];
6837 origin[1] -= yExtent[1];
6838 dragging = 2;
6839 }
6840 d3_eventPreventDefault();
6841 }
6842 }
6843 function keyup() {
6844 if (d3.event.keyCode == 32 && dragging == 2) {
6845 origin[0] += xExtent[1];
6846 origin[1] += yExtent[1];
6847 dragging = 0;
6848 d3_eventPreventDefault();
6849 }
6850 }
6851 function brushmove() {
6852 var point = d3.mouse(target), moved = false;
6853 if (offset) {
6854 point[0] += offset[0];
6855 point[1] += offset[1];
6856 }
6857 if (!dragging) {
6858 if (d3.event.altKey) {
6859 if (!center) center = [ (xExtent[0] + xExtent[1]) / 2, (yExtent[0] + yExtent[1]) / 2 ];
6860 origin[0] = xExtent[+(point[0] < center[0])];
6861 origin[1] = yExtent[+(point[1] < center[1])];
6862 } else center = null;
6863 }
6864 if (resizingX && move1(point, x, 0)) {
6865 redrawX(g);
6866 moved = true;
6867 }
6868 if (resizingY && move1(point, y, 1)) {
6869 redrawY(g);
6870 moved = true;
6871 }
6872 if (moved) {
6873 redraw(g);
6874 event_({
6875 type: "brush",
6876 mode: dragging ? "move" : "resize"
6877 });
6878 }
6879 }
6880 function move1(point, scale, i) {
6881 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;
6882 if (dragging) {
6883 r0 -= position;
6884 r1 -= size + position;
6885 }
6886 min = (i ? yClamp : xClamp) ? Math.max(r0, Math.min(r1, point[i])) : point[i];
6887 if (dragging) {
6888 max = (min += position) + size;
6889 } else {
6890 if (center) position = Math.max(r0, Math.min(r1, 2 * center[i] - min));
6891 if (position < min) {
6892 max = min;
6893 min = position;
6894 } else {
6895 max = position;
6896 }
6897 }
6898 if (extent[0] != min || extent[1] != max) {
6899 if (i) yExtentDomain = null; else xExtentDomain = null;
6900 extent[0] = min;
6901 extent[1] = max;
6902 return true;
6903 }
6904 }
6905 function brushend() {
6906 brushmove();
6907 g.style("pointer-events", "all").selectAll(".resize").style("display", brush.empty() ? "none" : null);
6908 d3.select("body").style("cursor", null);
6909 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);
6910 dragRestore();
6911 event_({
6912 type: "brushend"
6913 });
6914 }
6915 }
6916 brush.x = function(z) {
6917 if (!arguments.length) return x;
6918 x = z;
6919 resizes = d3_svg_brushResizes[!x << 1 | !y];
6920 return brush;
6921 };
6922 brush.y = function(z) {
6923 if (!arguments.length) return y;
6924 y = z;
6925 resizes = d3_svg_brushResizes[!x << 1 | !y];
6926 return brush;
6927 };
6928 brush.clamp = function(z) {
6929 if (!arguments.length) return x && y ? [ xClamp, yClamp ] : x ? xClamp : y ? yClamp : null;
6930 if (x && y) xClamp = !!z[0], yClamp = !!z[1]; else if (x) xClamp = !!z; else if (y) yClamp = !!z;
6931 return brush;
6932 };
6933 brush.extent = function(z) {
6934 var x0, x1, y0, y1, t;
6935 if (!arguments.length) {
6936 if (x) {
6937 if (xExtentDomain) {
6938 x0 = xExtentDomain[0], x1 = xExtentDomain[1];
6939 } else {
6940 x0 = xExtent[0], x1 = xExtent[1];
6941 if (x.invert) x0 = x.invert(x0), x1 = x.invert(x1);
6942 if (x1 < x0) t = x0, x0 = x1, x1 = t;
6943 }
6944 }
6945 if (y) {
6946 if (yExtentDomain) {
6947 y0 = yExtentDomain[0], y1 = yExtentDomain[1];
6948 } else {
6949 y0 = yExtent[0], y1 = yExtent[1];
6950 if (y.invert) y0 = y.invert(y0), y1 = y.invert(y1);
6951 if (y1 < y0) t = y0, y0 = y1, y1 = t;
6952 }
6953 }
6954 return x && y ? [ [ x0, y0 ], [ x1, y1 ] ] : x ? [ x0, x1 ] : y && [ y0, y1 ];
6955 }
6956 if (x) {
6957 x0 = z[0], x1 = z[1];
6958 if (y) x0 = x0[0], x1 = x1[0];
6959 xExtentDomain = [ x0, x1 ];
6960 if (x.invert) x0 = x(x0), x1 = x(x1);
6961 if (x1 < x0) t = x0, x0 = x1, x1 = t;
6962 if (x0 != xExtent[0] || x1 != xExtent[1]) xExtent = [ x0, x1 ];
6963 }
6964 if (y) {
6965 y0 = z[0], y1 = z[1];
6966 if (x) y0 = y0[1], y1 = y1[1];
6967 yExtentDomain = [ y0, y1 ];
6968 if (y.invert) y0 = y(y0), y1 = y(y1);
6969 if (y1 < y0) t = y0, y0 = y1, y1 = t;
6970 if (y0 != yExtent[0] || y1 != yExtent[1]) yExtent = [ y0, y1 ];
6971 }
6972 return brush;
6973 };
6974 brush.clear = function() {
6975 if (!brush.empty()) {
6976 xExtent = [ 0, 0 ], yExtent = [ 0, 0 ];
6977 xExtentDomain = yExtentDomain = null;
6978 }
6979 return brush;
6980 };
6981 brush.empty = function() {
6982 return !!x && xExtent[0] == xExtent[1] || !!y && yExtent[0] == yExtent[1];
6983 };
6984 return d3.rebind(brush, event, "on");
6985 };
6986 var d3_svg_brushCursor = {
6987 n: "ns-resize",
6988 e: "ew-resize",
6989 s: "ns-resize",
6990 w: "ew-resize",
6991 nw: "nwse-resize",
6992 ne: "nesw-resize",
6993 se: "nwse-resize",
6994 sw: "nesw-resize"
6995 };
6996 var d3_svg_brushResizes = [ [ "n", "e", "s", "w", "nw", "ne", "se", "sw" ], [ "e", "w" ], [ "n", "s" ], [] ];
6997 d3.text = d3_xhrType(function(request) {
6998 return request.responseText;
6999 });
7000 d3.json = function(url, callback) {
7001 return d3_xhr(url, "application/json", d3_json, callback);
7002 };
7003 function d3_json(request) {
7004 return JSON.parse(request.responseText);
7005 }
7006 d3.html = function(url, callback) {
7007 return d3_xhr(url, "text/html", d3_html, callback);
7008 };
7009 function d3_html(request) {
7010 var range = d3_document.createRange();
7011 range.selectNode(d3_document.body);
7012 return range.createContextualFragment(request.responseText);
7013 }
7014 d3.xml = d3_xhrType(function(request) {
7015 return request.responseXML;
7016 });
7017 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;
7018}.apply(self);
7019},{}],21:[function(_dereq_,module,exports){
7020(function (global){(function (){
7021'use strict';
7022
7023var objectAssign = _dereq_('object-assign');
7024
7025// compare and isBuffer taken from https://github.com/feross/buffer/blob/680e9e5e488f22aac27599a57dc844a6315928dd/index.js
7026// original notice:
7027
7028/*!
7029 * The buffer module from node.js, for the browser.
7030 *
7031 * @author Feross Aboukhadijeh <feross@feross.org> <http://feross.org>
7032 * @license MIT
7033 */
7034function compare(a, b) {
7035 if (a === b) {
7036 return 0;
7037 }
7038
7039 var x = a.length;
7040 var y = b.length;
7041
7042 for (var i = 0, len = Math.min(x, y); i < len; ++i) {
7043 if (a[i] !== b[i]) {
7044 x = a[i];
7045 y = b[i];
7046 break;
7047 }
7048 }
7049
7050 if (x < y) {
7051 return -1;
7052 }
7053 if (y < x) {
7054 return 1;
7055 }
7056 return 0;
7057}
7058function isBuffer(b) {
7059 if (global.Buffer && typeof global.Buffer.isBuffer === 'function') {
7060 return global.Buffer.isBuffer(b);
7061 }
7062 return !!(b != null && b._isBuffer);
7063}
7064
7065// based on node assert, original notice:
7066// NB: The URL to the CommonJS spec is kept just for tradition.
7067// node-assert has evolved a lot since then, both in API and behavior.
7068
7069// http://wiki.commonjs.org/wiki/Unit_Testing/1.0
7070//
7071// THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8!
7072//
7073// Originally from narwhal.js (http://narwhaljs.org)
7074// Copyright (c) 2009 Thomas Robinson <280north.com>
7075//
7076// Permission is hereby granted, free of charge, to any person obtaining a copy
7077// of this software and associated documentation files (the 'Software'), to
7078// deal in the Software without restriction, including without limitation the
7079// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7080// sell copies of the Software, and to permit persons to whom the Software is
7081// furnished to do so, subject to the following conditions:
7082//
7083// The above copyright notice and this permission notice shall be included in
7084// all copies or substantial portions of the Software.
7085//
7086// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
7087// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
7088// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
7089// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
7090// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
7091// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
7092
7093var util = _dereq_('util/');
7094var hasOwn = Object.prototype.hasOwnProperty;
7095var pSlice = Array.prototype.slice;
7096var functionsHaveNames = (function () {
7097 return function foo() {}.name === 'foo';
7098}());
7099function pToString (obj) {
7100 return Object.prototype.toString.call(obj);
7101}
7102function isView(arrbuf) {
7103 if (isBuffer(arrbuf)) {
7104 return false;
7105 }
7106 if (typeof global.ArrayBuffer !== 'function') {
7107 return false;
7108 }
7109 if (typeof ArrayBuffer.isView === 'function') {
7110 return ArrayBuffer.isView(arrbuf);
7111 }
7112 if (!arrbuf) {
7113 return false;
7114 }
7115 if (arrbuf instanceof DataView) {
7116 return true;
7117 }
7118 if (arrbuf.buffer && arrbuf.buffer instanceof ArrayBuffer) {
7119 return true;
7120 }
7121 return false;
7122}
7123// 1. The assert module provides functions that throw
7124// AssertionError's when particular conditions are not met. The
7125// assert module must conform to the following interface.
7126
7127var assert = module.exports = ok;
7128
7129// 2. The AssertionError is defined in assert.
7130// new assert.AssertionError({ message: message,
7131// actual: actual,
7132// expected: expected })
7133
7134var regex = /\s*function\s+([^\(\s]*)\s*/;
7135// based on https://github.com/ljharb/function.prototype.name/blob/adeeeec8bfcc6068b187d7d9fb3d5bb1d3a30899/implementation.js
7136function getName(func) {
7137 if (!util.isFunction(func)) {
7138 return;
7139 }
7140 if (functionsHaveNames) {
7141 return func.name;
7142 }
7143 var str = func.toString();
7144 var match = str.match(regex);
7145 return match && match[1];
7146}
7147assert.AssertionError = function AssertionError(options) {
7148 this.name = 'AssertionError';
7149 this.actual = options.actual;
7150 this.expected = options.expected;
7151 this.operator = options.operator;
7152 if (options.message) {
7153 this.message = options.message;
7154 this.generatedMessage = false;
7155 } else {
7156 this.message = getMessage(this);
7157 this.generatedMessage = true;
7158 }
7159 var stackStartFunction = options.stackStartFunction || fail;
7160 if (Error.captureStackTrace) {
7161 Error.captureStackTrace(this, stackStartFunction);
7162 } else {
7163 // non v8 browsers so we can have a stacktrace
7164 var err = new Error();
7165 if (err.stack) {
7166 var out = err.stack;
7167
7168 // try to strip useless frames
7169 var fn_name = getName(stackStartFunction);
7170 var idx = out.indexOf('\n' + fn_name);
7171 if (idx >= 0) {
7172 // once we have located the function frame
7173 // we need to strip out everything before it (and its line)
7174 var next_line = out.indexOf('\n', idx + 1);
7175 out = out.substring(next_line + 1);
7176 }
7177
7178 this.stack = out;
7179 }
7180 }
7181};
7182
7183// assert.AssertionError instanceof Error
7184util.inherits(assert.AssertionError, Error);
7185
7186function truncate(s, n) {
7187 if (typeof s === 'string') {
7188 return s.length < n ? s : s.slice(0, n);
7189 } else {
7190 return s;
7191 }
7192}
7193function inspect(something) {
7194 if (functionsHaveNames || !util.isFunction(something)) {
7195 return util.inspect(something);
7196 }
7197 var rawname = getName(something);
7198 var name = rawname ? ': ' + rawname : '';
7199 return '[Function' + name + ']';
7200}
7201function getMessage(self) {
7202 return truncate(inspect(self.actual), 128) + ' ' +
7203 self.operator + ' ' +
7204 truncate(inspect(self.expected), 128);
7205}
7206
7207// At present only the three keys mentioned above are used and
7208// understood by the spec. Implementations or sub modules can pass
7209// other keys to the AssertionError's constructor - they will be
7210// ignored.
7211
7212// 3. All of the following functions must throw an AssertionError
7213// when a corresponding condition is not met, with a message that
7214// may be undefined if not provided. All assertion methods provide
7215// both the actual and expected values to the assertion error for
7216// display purposes.
7217
7218function fail(actual, expected, message, operator, stackStartFunction) {
7219 throw new assert.AssertionError({
7220 message: message,
7221 actual: actual,
7222 expected: expected,
7223 operator: operator,
7224 stackStartFunction: stackStartFunction
7225 });
7226}
7227
7228// EXTENSION! allows for well behaved errors defined elsewhere.
7229assert.fail = fail;
7230
7231// 4. Pure assertion tests whether a value is truthy, as determined
7232// by !!guard.
7233// assert.ok(guard, message_opt);
7234// This statement is equivalent to assert.equal(true, !!guard,
7235// message_opt);. To test strictly for the value true, use
7236// assert.strictEqual(true, guard, message_opt);.
7237
7238function ok(value, message) {
7239 if (!value) fail(value, true, message, '==', assert.ok);
7240}
7241assert.ok = ok;
7242
7243// 5. The equality assertion tests shallow, coercive equality with
7244// ==.
7245// assert.equal(actual, expected, message_opt);
7246
7247assert.equal = function equal(actual, expected, message) {
7248 if (actual != expected) fail(actual, expected, message, '==', assert.equal);
7249};
7250
7251// 6. The non-equality assertion tests for whether two objects are not equal
7252// with != assert.notEqual(actual, expected, message_opt);
7253
7254assert.notEqual = function notEqual(actual, expected, message) {
7255 if (actual == expected) {
7256 fail(actual, expected, message, '!=', assert.notEqual);
7257 }
7258};
7259
7260// 7. The equivalence assertion tests a deep equality relation.
7261// assert.deepEqual(actual, expected, message_opt);
7262
7263assert.deepEqual = function deepEqual(actual, expected, message) {
7264 if (!_deepEqual(actual, expected, false)) {
7265 fail(actual, expected, message, 'deepEqual', assert.deepEqual);
7266 }
7267};
7268
7269assert.deepStrictEqual = function deepStrictEqual(actual, expected, message) {
7270 if (!_deepEqual(actual, expected, true)) {
7271 fail(actual, expected, message, 'deepStrictEqual', assert.deepStrictEqual);
7272 }
7273};
7274
7275function _deepEqual(actual, expected, strict, memos) {
7276 // 7.1. All identical values are equivalent, as determined by ===.
7277 if (actual === expected) {
7278 return true;
7279 } else if (isBuffer(actual) && isBuffer(expected)) {
7280 return compare(actual, expected) === 0;
7281
7282 // 7.2. If the expected value is a Date object, the actual value is
7283 // equivalent if it is also a Date object that refers to the same time.
7284 } else if (util.isDate(actual) && util.isDate(expected)) {
7285 return actual.getTime() === expected.getTime();
7286
7287 // 7.3 If the expected value is a RegExp object, the actual value is
7288 // equivalent if it is also a RegExp object with the same source and
7289 // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`).
7290 } else if (util.isRegExp(actual) && util.isRegExp(expected)) {
7291 return actual.source === expected.source &&
7292 actual.global === expected.global &&
7293 actual.multiline === expected.multiline &&
7294 actual.lastIndex === expected.lastIndex &&
7295 actual.ignoreCase === expected.ignoreCase;
7296
7297 // 7.4. Other pairs that do not both pass typeof value == 'object',
7298 // equivalence is determined by ==.
7299 } else if ((actual === null || typeof actual !== 'object') &&
7300 (expected === null || typeof expected !== 'object')) {
7301 return strict ? actual === expected : actual == expected;
7302
7303 // If both values are instances of typed arrays, wrap their underlying
7304 // ArrayBuffers in a Buffer each to increase performance
7305 // This optimization requires the arrays to have the same type as checked by
7306 // Object.prototype.toString (aka pToString). Never perform binary
7307 // comparisons for Float*Arrays, though, since e.g. +0 === -0 but their
7308 // bit patterns are not identical.
7309 } else if (isView(actual) && isView(expected) &&
7310 pToString(actual) === pToString(expected) &&
7311 !(actual instanceof Float32Array ||
7312 actual instanceof Float64Array)) {
7313 return compare(new Uint8Array(actual.buffer),
7314 new Uint8Array(expected.buffer)) === 0;
7315
7316 // 7.5 For all other Object pairs, including Array objects, equivalence is
7317 // determined by having the same number of owned properties (as verified
7318 // with Object.prototype.hasOwnProperty.call), the same set of keys
7319 // (although not necessarily the same order), equivalent values for every
7320 // corresponding key, and an identical 'prototype' property. Note: this
7321 // accounts for both named and indexed properties on Arrays.
7322 } else if (isBuffer(actual) !== isBuffer(expected)) {
7323 return false;
7324 } else {
7325 memos = memos || {actual: [], expected: []};
7326
7327 var actualIndex = memos.actual.indexOf(actual);
7328 if (actualIndex !== -1) {
7329 if (actualIndex === memos.expected.indexOf(expected)) {
7330 return true;
7331 }
7332 }
7333
7334 memos.actual.push(actual);
7335 memos.expected.push(expected);
7336
7337 return objEquiv(actual, expected, strict, memos);
7338 }
7339}
7340
7341function isArguments(object) {
7342 return Object.prototype.toString.call(object) == '[object Arguments]';
7343}
7344
7345function objEquiv(a, b, strict, actualVisitedObjects) {
7346 if (a === null || a === undefined || b === null || b === undefined)
7347 return false;
7348 // if one is a primitive, the other must be same
7349 if (util.isPrimitive(a) || util.isPrimitive(b))
7350 return a === b;
7351 if (strict && Object.getPrototypeOf(a) !== Object.getPrototypeOf(b))
7352 return false;
7353 var aIsArgs = isArguments(a);
7354 var bIsArgs = isArguments(b);
7355 if ((aIsArgs && !bIsArgs) || (!aIsArgs && bIsArgs))
7356 return false;
7357 if (aIsArgs) {
7358 a = pSlice.call(a);
7359 b = pSlice.call(b);
7360 return _deepEqual(a, b, strict);
7361 }
7362 var ka = objectKeys(a);
7363 var kb = objectKeys(b);
7364 var key, i;
7365 // having the same number of owned properties (keys incorporates
7366 // hasOwnProperty)
7367 if (ka.length !== kb.length)
7368 return false;
7369 //the same set of keys (although not necessarily the same order),
7370 ka.sort();
7371 kb.sort();
7372 //~~~cheap key test
7373 for (i = ka.length - 1; i >= 0; i--) {
7374 if (ka[i] !== kb[i])
7375 return false;
7376 }
7377 //equivalent values for every corresponding key, and
7378 //~~~possibly expensive deep test
7379 for (i = ka.length - 1; i >= 0; i--) {
7380 key = ka[i];
7381 if (!_deepEqual(a[key], b[key], strict, actualVisitedObjects))
7382 return false;
7383 }
7384 return true;
7385}
7386
7387// 8. The non-equivalence assertion tests for any deep inequality.
7388// assert.notDeepEqual(actual, expected, message_opt);
7389
7390assert.notDeepEqual = function notDeepEqual(actual, expected, message) {
7391 if (_deepEqual(actual, expected, false)) {
7392 fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual);
7393 }
7394};
7395
7396assert.notDeepStrictEqual = notDeepStrictEqual;
7397function notDeepStrictEqual(actual, expected, message) {
7398 if (_deepEqual(actual, expected, true)) {
7399 fail(actual, expected, message, 'notDeepStrictEqual', notDeepStrictEqual);
7400 }
7401}
7402
7403
7404// 9. The strict equality assertion tests strict equality, as determined by ===.
7405// assert.strictEqual(actual, expected, message_opt);
7406
7407assert.strictEqual = function strictEqual(actual, expected, message) {
7408 if (actual !== expected) {
7409 fail(actual, expected, message, '===', assert.strictEqual);
7410 }
7411};
7412
7413// 10. The strict non-equality assertion tests for strict inequality, as
7414// determined by !==. assert.notStrictEqual(actual, expected, message_opt);
7415
7416assert.notStrictEqual = function notStrictEqual(actual, expected, message) {
7417 if (actual === expected) {
7418 fail(actual, expected, message, '!==', assert.notStrictEqual);
7419 }
7420};
7421
7422function expectedException(actual, expected) {
7423 if (!actual || !expected) {
7424 return false;
7425 }
7426
7427 if (Object.prototype.toString.call(expected) == '[object RegExp]') {
7428 return expected.test(actual);
7429 }
7430
7431 try {
7432 if (actual instanceof expected) {
7433 return true;
7434 }
7435 } catch (e) {
7436 // Ignore. The instanceof check doesn't work for arrow functions.
7437 }
7438
7439 if (Error.isPrototypeOf(expected)) {
7440 return false;
7441 }
7442
7443 return expected.call({}, actual) === true;
7444}
7445
7446function _tryBlock(block) {
7447 var error;
7448 try {
7449 block();
7450 } catch (e) {
7451 error = e;
7452 }
7453 return error;
7454}
7455
7456function _throws(shouldThrow, block, expected, message) {
7457 var actual;
7458
7459 if (typeof block !== 'function') {
7460 throw new TypeError('"block" argument must be a function');
7461 }
7462
7463 if (typeof expected === 'string') {
7464 message = expected;
7465 expected = null;
7466 }
7467
7468 actual = _tryBlock(block);
7469
7470 message = (expected && expected.name ? ' (' + expected.name + ').' : '.') +
7471 (message ? ' ' + message : '.');
7472
7473 if (shouldThrow && !actual) {
7474 fail(actual, expected, 'Missing expected exception' + message);
7475 }
7476
7477 var userProvidedMessage = typeof message === 'string';
7478 var isUnwantedException = !shouldThrow && util.isError(actual);
7479 var isUnexpectedException = !shouldThrow && actual && !expected;
7480
7481 if ((isUnwantedException &&
7482 userProvidedMessage &&
7483 expectedException(actual, expected)) ||
7484 isUnexpectedException) {
7485 fail(actual, expected, 'Got unwanted exception' + message);
7486 }
7487
7488 if ((shouldThrow && actual && expected &&
7489 !expectedException(actual, expected)) || (!shouldThrow && actual)) {
7490 throw actual;
7491 }
7492}
7493
7494// 11. Expected to throw an error:
7495// assert.throws(block, Error_opt, message_opt);
7496
7497assert.throws = function(block, /*optional*/error, /*optional*/message) {
7498 _throws(true, block, error, message);
7499};
7500
7501// EXTENSION! This is annoying to write outside this module.
7502assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) {
7503 _throws(false, block, error, message);
7504};
7505
7506assert.ifError = function(err) { if (err) throw err; };
7507
7508// Expose a strict only variant of assert
7509function strict(value, message) {
7510 if (!value) fail(value, true, message, '==', strict);
7511}
7512assert.strict = objectAssign(strict, assert, {
7513 equal: assert.strictEqual,
7514 deepEqual: assert.deepStrictEqual,
7515 notEqual: assert.notStrictEqual,
7516 notDeepEqual: assert.notDeepStrictEqual
7517});
7518assert.strict.strict = assert.strict;
7519
7520var objectKeys = Object.keys || function (obj) {
7521 var keys = [];
7522 for (var key in obj) {
7523 if (hasOwn.call(obj, key)) keys.push(key);
7524 }
7525 return keys;
7526};
7527
7528}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
7529},{"object-assign":73,"util/":24}],22:[function(_dereq_,module,exports){
7530if (typeof Object.create === 'function') {
7531 // implementation from standard node.js 'util' module
7532 module.exports = function inherits(ctor, superCtor) {
7533 ctor.super_ = superCtor
7534 ctor.prototype = Object.create(superCtor.prototype, {
7535 constructor: {
7536 value: ctor,
7537 enumerable: false,
7538 writable: true,
7539 configurable: true
7540 }
7541 });
7542 };
7543} else {
7544 // old school shim for old browsers
7545 module.exports = function inherits(ctor, superCtor) {
7546 ctor.super_ = superCtor
7547 var TempCtor = function () {}
7548 TempCtor.prototype = superCtor.prototype
7549 ctor.prototype = new TempCtor()
7550 ctor.prototype.constructor = ctor
7551 }
7552}
7553
7554},{}],23:[function(_dereq_,module,exports){
7555module.exports = function isBuffer(arg) {
7556 return arg && typeof arg === 'object'
7557 && typeof arg.copy === 'function'
7558 && typeof arg.fill === 'function'
7559 && typeof arg.readUInt8 === 'function';
7560}
7561},{}],24:[function(_dereq_,module,exports){
7562(function (process,global){(function (){
7563// Copyright Joyent, Inc. and other Node contributors.
7564//
7565// Permission is hereby granted, free of charge, to any person obtaining a
7566// copy of this software and associated documentation files (the
7567// "Software"), to deal in the Software without restriction, including
7568// without limitation the rights to use, copy, modify, merge, publish,
7569// distribute, sublicense, and/or sell copies of the Software, and to permit
7570// persons to whom the Software is furnished to do so, subject to the
7571// following conditions:
7572//
7573// The above copyright notice and this permission notice shall be included
7574// in all copies or substantial portions of the Software.
7575//
7576// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
7577// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
7578// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
7579// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
7580// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
7581// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
7582// USE OR OTHER DEALINGS IN THE SOFTWARE.
7583
7584var formatRegExp = /%[sdj%]/g;
7585exports.format = function(f) {
7586 if (!isString(f)) {
7587 var objects = [];
7588 for (var i = 0; i < arguments.length; i++) {
7589 objects.push(inspect(arguments[i]));
7590 }
7591 return objects.join(' ');
7592 }
7593
7594 var i = 1;
7595 var args = arguments;
7596 var len = args.length;
7597 var str = String(f).replace(formatRegExp, function(x) {
7598 if (x === '%%') return '%';
7599 if (i >= len) return x;
7600 switch (x) {
7601 case '%s': return String(args[i++]);
7602 case '%d': return Number(args[i++]);
7603 case '%j':
7604 try {
7605 return JSON.stringify(args[i++]);
7606 } catch (_) {
7607 return '[Circular]';
7608 }
7609 default:
7610 return x;
7611 }
7612 });
7613 for (var x = args[i]; i < len; x = args[++i]) {
7614 if (isNull(x) || !isObject(x)) {
7615 str += ' ' + x;
7616 } else {
7617 str += ' ' + inspect(x);
7618 }
7619 }
7620 return str;
7621};
7622
7623
7624// Mark that a method should not be used.
7625// Returns a modified function which warns once by default.
7626// If --no-deprecation is set, then it is a no-op.
7627exports.deprecate = function(fn, msg) {
7628 // Allow for deprecating things in the process of starting up.
7629 if (isUndefined(global.process)) {
7630 return function() {
7631 return exports.deprecate(fn, msg).apply(this, arguments);
7632 };
7633 }
7634
7635 if (process.noDeprecation === true) {
7636 return fn;
7637 }
7638
7639 var warned = false;
7640 function deprecated() {
7641 if (!warned) {
7642 if (process.throwDeprecation) {
7643 throw new Error(msg);
7644 } else if (process.traceDeprecation) {
7645 console.trace(msg);
7646 } else {
7647 console.error(msg);
7648 }
7649 warned = true;
7650 }
7651 return fn.apply(this, arguments);
7652 }
7653
7654 return deprecated;
7655};
7656
7657
7658var debugs = {};
7659var debugEnviron;
7660exports.debuglog = function(set) {
7661 if (isUndefined(debugEnviron))
7662 debugEnviron = process.env.NODE_DEBUG || '';
7663 set = set.toUpperCase();
7664 if (!debugs[set]) {
7665 if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) {
7666 var pid = process.pid;
7667 debugs[set] = function() {
7668 var msg = exports.format.apply(exports, arguments);
7669 console.error('%s %d: %s', set, pid, msg);
7670 };
7671 } else {
7672 debugs[set] = function() {};
7673 }
7674 }
7675 return debugs[set];
7676};
7677
7678
7679/**
7680 * Echos the value of a value. Trys to print the value out
7681 * in the best way possible given the different types.
7682 *
7683 * @param {Object} obj The object to print out.
7684 * @param {Object} opts Optional options object that alters the output.
7685 */
7686/* legacy: obj, showHidden, depth, colors*/
7687function inspect(obj, opts) {
7688 // default options
7689 var ctx = {
7690 seen: [],
7691 stylize: stylizeNoColor
7692 };
7693 // legacy...
7694 if (arguments.length >= 3) ctx.depth = arguments[2];
7695 if (arguments.length >= 4) ctx.colors = arguments[3];
7696 if (isBoolean(opts)) {
7697 // legacy...
7698 ctx.showHidden = opts;
7699 } else if (opts) {
7700 // got an "options" object
7701 exports._extend(ctx, opts);
7702 }
7703 // set default options
7704 if (isUndefined(ctx.showHidden)) ctx.showHidden = false;
7705 if (isUndefined(ctx.depth)) ctx.depth = 2;
7706 if (isUndefined(ctx.colors)) ctx.colors = false;
7707 if (isUndefined(ctx.customInspect)) ctx.customInspect = true;
7708 if (ctx.colors) ctx.stylize = stylizeWithColor;
7709 return formatValue(ctx, obj, ctx.depth);
7710}
7711exports.inspect = inspect;
7712
7713
7714// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
7715inspect.colors = {
7716 'bold' : [1, 22],
7717 'italic' : [3, 23],
7718 'underline' : [4, 24],
7719 'inverse' : [7, 27],
7720 'white' : [37, 39],
7721 'grey' : [90, 39],
7722 'black' : [30, 39],
7723 'blue' : [34, 39],
7724 'cyan' : [36, 39],
7725 'green' : [32, 39],
7726 'magenta' : [35, 39],
7727 'red' : [31, 39],
7728 'yellow' : [33, 39]
7729};
7730
7731// Don't use 'blue' not visible on cmd.exe
7732inspect.styles = {
7733 'special': 'cyan',
7734 'number': 'yellow',
7735 'boolean': 'yellow',
7736 'undefined': 'grey',
7737 'null': 'bold',
7738 'string': 'green',
7739 'date': 'magenta',
7740 // "name": intentionally not styling
7741 'regexp': 'red'
7742};
7743
7744
7745function stylizeWithColor(str, styleType) {
7746 var style = inspect.styles[styleType];
7747
7748 if (style) {
7749 return '\u001b[' + inspect.colors[style][0] + 'm' + str +
7750 '\u001b[' + inspect.colors[style][1] + 'm';
7751 } else {
7752 return str;
7753 }
7754}
7755
7756
7757function stylizeNoColor(str, styleType) {
7758 return str;
7759}
7760
7761
7762function arrayToHash(array) {
7763 var hash = {};
7764
7765 array.forEach(function(val, idx) {
7766 hash[val] = true;
7767 });
7768
7769 return hash;
7770}
7771
7772
7773function formatValue(ctx, value, recurseTimes) {
7774 // Provide a hook for user-specified inspect functions.
7775 // Check that value is an object with an inspect function on it
7776 if (ctx.customInspect &&
7777 value &&
7778 isFunction(value.inspect) &&
7779 // Filter out the util module, it's inspect function is special
7780 value.inspect !== exports.inspect &&
7781 // Also filter out any prototype objects using the circular check.
7782 !(value.constructor && value.constructor.prototype === value)) {
7783 var ret = value.inspect(recurseTimes, ctx);
7784 if (!isString(ret)) {
7785 ret = formatValue(ctx, ret, recurseTimes);
7786 }
7787 return ret;
7788 }
7789
7790 // Primitive types cannot have properties
7791 var primitive = formatPrimitive(ctx, value);
7792 if (primitive) {
7793 return primitive;
7794 }
7795
7796 // Look up the keys of the object.
7797 var keys = Object.keys(value);
7798 var visibleKeys = arrayToHash(keys);
7799
7800 if (ctx.showHidden) {
7801 keys = Object.getOwnPropertyNames(value);
7802 }
7803
7804 // IE doesn't make error fields non-enumerable
7805 // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx
7806 if (isError(value)
7807 && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) {
7808 return formatError(value);
7809 }
7810
7811 // Some type of object without properties can be shortcutted.
7812 if (keys.length === 0) {
7813 if (isFunction(value)) {
7814 var name = value.name ? ': ' + value.name : '';
7815 return ctx.stylize('[Function' + name + ']', 'special');
7816 }
7817 if (isRegExp(value)) {
7818 return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
7819 }
7820 if (isDate(value)) {
7821 return ctx.stylize(Date.prototype.toString.call(value), 'date');
7822 }
7823 if (isError(value)) {
7824 return formatError(value);
7825 }
7826 }
7827
7828 var base = '', array = false, braces = ['{', '}'];
7829
7830 // Make Array say that they are Array
7831 if (isArray(value)) {
7832 array = true;
7833 braces = ['[', ']'];
7834 }
7835
7836 // Make functions say that they are functions
7837 if (isFunction(value)) {
7838 var n = value.name ? ': ' + value.name : '';
7839 base = ' [Function' + n + ']';
7840 }
7841
7842 // Make RegExps say that they are RegExps
7843 if (isRegExp(value)) {
7844 base = ' ' + RegExp.prototype.toString.call(value);
7845 }
7846
7847 // Make dates with properties first say the date
7848 if (isDate(value)) {
7849 base = ' ' + Date.prototype.toUTCString.call(value);
7850 }
7851
7852 // Make error with message first say the error
7853 if (isError(value)) {
7854 base = ' ' + formatError(value);
7855 }
7856
7857 if (keys.length === 0 && (!array || value.length == 0)) {
7858 return braces[0] + base + braces[1];
7859 }
7860
7861 if (recurseTimes < 0) {
7862 if (isRegExp(value)) {
7863 return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
7864 } else {
7865 return ctx.stylize('[Object]', 'special');
7866 }
7867 }
7868
7869 ctx.seen.push(value);
7870
7871 var output;
7872 if (array) {
7873 output = formatArray(ctx, value, recurseTimes, visibleKeys, keys);
7874 } else {
7875 output = keys.map(function(key) {
7876 return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array);
7877 });
7878 }
7879
7880 ctx.seen.pop();
7881
7882 return reduceToSingleString(output, base, braces);
7883}
7884
7885
7886function formatPrimitive(ctx, value) {
7887 if (isUndefined(value))
7888 return ctx.stylize('undefined', 'undefined');
7889 if (isString(value)) {
7890 var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '')
7891 .replace(/'/g, "\\'")
7892 .replace(/\\"/g, '"') + '\'';
7893 return ctx.stylize(simple, 'string');
7894 }
7895 if (isNumber(value))
7896 return ctx.stylize('' + value, 'number');
7897 if (isBoolean(value))
7898 return ctx.stylize('' + value, 'boolean');
7899 // For some reason typeof null is "object", so special case here.
7900 if (isNull(value))
7901 return ctx.stylize('null', 'null');
7902}
7903
7904
7905function formatError(value) {
7906 return '[' + Error.prototype.toString.call(value) + ']';
7907}
7908
7909
7910function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
7911 var output = [];
7912 for (var i = 0, l = value.length; i < l; ++i) {
7913 if (hasOwnProperty(value, String(i))) {
7914 output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
7915 String(i), true));
7916 } else {
7917 output.push('');
7918 }
7919 }
7920 keys.forEach(function(key) {
7921 if (!key.match(/^\d+$/)) {
7922 output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
7923 key, true));
7924 }
7925 });
7926 return output;
7927}
7928
7929
7930function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) {
7931 var name, str, desc;
7932 desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] };
7933 if (desc.get) {
7934 if (desc.set) {
7935 str = ctx.stylize('[Getter/Setter]', 'special');
7936 } else {
7937 str = ctx.stylize('[Getter]', 'special');
7938 }
7939 } else {
7940 if (desc.set) {
7941 str = ctx.stylize('[Setter]', 'special');
7942 }
7943 }
7944 if (!hasOwnProperty(visibleKeys, key)) {
7945 name = '[' + key + ']';
7946 }
7947 if (!str) {
7948 if (ctx.seen.indexOf(desc.value) < 0) {
7949 if (isNull(recurseTimes)) {
7950 str = formatValue(ctx, desc.value, null);
7951 } else {
7952 str = formatValue(ctx, desc.value, recurseTimes - 1);
7953 }
7954 if (str.indexOf('\n') > -1) {
7955 if (array) {
7956 str = str.split('\n').map(function(line) {
7957 return ' ' + line;
7958 }).join('\n').substr(2);
7959 } else {
7960 str = '\n' + str.split('\n').map(function(line) {
7961 return ' ' + line;
7962 }).join('\n');
7963 }
7964 }
7965 } else {
7966 str = ctx.stylize('[Circular]', 'special');
7967 }
7968 }
7969 if (isUndefined(name)) {
7970 if (array && key.match(/^\d+$/)) {
7971 return str;
7972 }
7973 name = JSON.stringify('' + key);
7974 if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) {
7975 name = name.substr(1, name.length - 2);
7976 name = ctx.stylize(name, 'name');
7977 } else {
7978 name = name.replace(/'/g, "\\'")
7979 .replace(/\\"/g, '"')
7980 .replace(/(^"|"$)/g, "'");
7981 name = ctx.stylize(name, 'string');
7982 }
7983 }
7984
7985 return name + ': ' + str;
7986}
7987
7988
7989function reduceToSingleString(output, base, braces) {
7990 var numLinesEst = 0;
7991 var length = output.reduce(function(prev, cur) {
7992 numLinesEst++;
7993 if (cur.indexOf('\n') >= 0) numLinesEst++;
7994 return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1;
7995 }, 0);
7996
7997 if (length > 60) {
7998 return braces[0] +
7999 (base === '' ? '' : base + '\n ') +
8000 ' ' +
8001 output.join(',\n ') +
8002 ' ' +
8003 braces[1];
8004 }
8005
8006 return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1];
8007}
8008
8009
8010// NOTE: These type checking functions intentionally don't use `instanceof`
8011// because it is fragile and can be easily faked with `Object.create()`.
8012function isArray(ar) {
8013 return Array.isArray(ar);
8014}
8015exports.isArray = isArray;
8016
8017function isBoolean(arg) {
8018 return typeof arg === 'boolean';
8019}
8020exports.isBoolean = isBoolean;
8021
8022function isNull(arg) {
8023 return arg === null;
8024}
8025exports.isNull = isNull;
8026
8027function isNullOrUndefined(arg) {
8028 return arg == null;
8029}
8030exports.isNullOrUndefined = isNullOrUndefined;
8031
8032function isNumber(arg) {
8033 return typeof arg === 'number';
8034}
8035exports.isNumber = isNumber;
8036
8037function isString(arg) {
8038 return typeof arg === 'string';
8039}
8040exports.isString = isString;
8041
8042function isSymbol(arg) {
8043 return typeof arg === 'symbol';
8044}
8045exports.isSymbol = isSymbol;
8046
8047function isUndefined(arg) {
8048 return arg === void 0;
8049}
8050exports.isUndefined = isUndefined;
8051
8052function isRegExp(re) {
8053 return isObject(re) && objectToString(re) === '[object RegExp]';
8054}
8055exports.isRegExp = isRegExp;
8056
8057function isObject(arg) {
8058 return typeof arg === 'object' && arg !== null;
8059}
8060exports.isObject = isObject;
8061
8062function isDate(d) {
8063 return isObject(d) && objectToString(d) === '[object Date]';
8064}
8065exports.isDate = isDate;
8066
8067function isError(e) {
8068 return isObject(e) &&
8069 (objectToString(e) === '[object Error]' || e instanceof Error);
8070}
8071exports.isError = isError;
8072
8073function isFunction(arg) {
8074 return typeof arg === 'function';
8075}
8076exports.isFunction = isFunction;
8077
8078function isPrimitive(arg) {
8079 return arg === null ||
8080 typeof arg === 'boolean' ||
8081 typeof arg === 'number' ||
8082 typeof arg === 'string' ||
8083 typeof arg === 'symbol' || // ES6 symbol
8084 typeof arg === 'undefined';
8085}
8086exports.isPrimitive = isPrimitive;
8087
8088exports.isBuffer = _dereq_('./support/isBuffer');
8089
8090function objectToString(o) {
8091 return Object.prototype.toString.call(o);
8092}
8093
8094
8095function pad(n) {
8096 return n < 10 ? '0' + n.toString(10) : n.toString(10);
8097}
8098
8099
8100var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
8101 'Oct', 'Nov', 'Dec'];
8102
8103// 26 Feb 16:19:34
8104function timestamp() {
8105 var d = new Date();
8106 var time = [pad(d.getHours()),
8107 pad(d.getMinutes()),
8108 pad(d.getSeconds())].join(':');
8109 return [d.getDate(), months[d.getMonth()], time].join(' ');
8110}
8111
8112
8113// log is just a thin wrapper to console.log that prepends a timestamp
8114exports.log = function() {
8115 console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments));
8116};
8117
8118
8119/**
8120 * Inherit the prototype methods from one constructor into another.
8121 *
8122 * The Function.prototype.inherits from lang.js rewritten as a standalone
8123 * function (not on Function.prototype). NOTE: If this file is to be loaded
8124 * during bootstrapping this function needs to be rewritten using some native
8125 * functions as prototype setup using normal JavaScript does not work as
8126 * expected during bootstrapping (see mirror.js in r114903).
8127 *
8128 * @param {function} ctor Constructor function which needs to inherit the
8129 * prototype.
8130 * @param {function} superCtor Constructor function to inherit prototype from.
8131 */
8132exports.inherits = _dereq_('inherits');
8133
8134exports._extend = function(origin, add) {
8135 // Don't do anything if add isn't an object
8136 if (!add || !isObject(add)) return origin;
8137
8138 var keys = Object.keys(add);
8139 var i = keys.length;
8140 while (i--) {
8141 origin[keys[i]] = add[keys[i]];
8142 }
8143 return origin;
8144};
8145
8146function hasOwnProperty(obj, prop) {
8147 return Object.prototype.hasOwnProperty.call(obj, prop);
8148}
8149
8150}).call(this)}).call(this,_dereq_('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
8151},{"./support/isBuffer":23,"_process":98,"inherits":22}],25:[function(_dereq_,module,exports){
8152'use strict'
8153
8154exports.byteLength = byteLength
8155exports.toByteArray = toByteArray
8156exports.fromByteArray = fromByteArray
8157
8158var lookup = []
8159var revLookup = []
8160var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array
8161
8162var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
8163for (var i = 0, len = code.length; i < len; ++i) {
8164 lookup[i] = code[i]
8165 revLookup[code.charCodeAt(i)] = i
8166}
8167
8168// Support decoding URL-safe base64 strings, as Node.js does.
8169// See: https://en.wikipedia.org/wiki/Base64#URL_applications
8170revLookup['-'.charCodeAt(0)] = 62
8171revLookup['_'.charCodeAt(0)] = 63
8172
8173function getLens (b64) {
8174 var len = b64.length
8175
8176 if (len % 4 > 0) {
8177 throw new Error('Invalid string. Length must be a multiple of 4')
8178 }
8179
8180 // Trim off extra bytes after placeholder bytes are found
8181 // See: https://github.com/beatgammit/base64-js/issues/42
8182 var validLen = b64.indexOf('=')
8183 if (validLen === -1) validLen = len
8184
8185 var placeHoldersLen = validLen === len
8186 ? 0
8187 : 4 - (validLen % 4)
8188
8189 return [validLen, placeHoldersLen]
8190}
8191
8192// base64 is 4/3 + up to two characters of the original data
8193function byteLength (b64) {
8194 var lens = getLens(b64)
8195 var validLen = lens[0]
8196 var placeHoldersLen = lens[1]
8197 return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen
8198}
8199
8200function _byteLength (b64, validLen, placeHoldersLen) {
8201 return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen
8202}
8203
8204function toByteArray (b64) {
8205 var tmp
8206 var lens = getLens(b64)
8207 var validLen = lens[0]
8208 var placeHoldersLen = lens[1]
8209
8210 var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen))
8211
8212 var curByte = 0
8213
8214 // if there are placeholders, only get up to the last complete 4 chars
8215 var len = placeHoldersLen > 0
8216 ? validLen - 4
8217 : validLen
8218
8219 var i
8220 for (i = 0; i < len; i += 4) {
8221 tmp =
8222 (revLookup[b64.charCodeAt(i)] << 18) |
8223 (revLookup[b64.charCodeAt(i + 1)] << 12) |
8224 (revLookup[b64.charCodeAt(i + 2)] << 6) |
8225 revLookup[b64.charCodeAt(i + 3)]
8226 arr[curByte++] = (tmp >> 16) & 0xFF
8227 arr[curByte++] = (tmp >> 8) & 0xFF
8228 arr[curByte++] = tmp & 0xFF
8229 }
8230
8231 if (placeHoldersLen === 2) {
8232 tmp =
8233 (revLookup[b64.charCodeAt(i)] << 2) |
8234 (revLookup[b64.charCodeAt(i + 1)] >> 4)
8235 arr[curByte++] = tmp & 0xFF
8236 }
8237
8238 if (placeHoldersLen === 1) {
8239 tmp =
8240 (revLookup[b64.charCodeAt(i)] << 10) |
8241 (revLookup[b64.charCodeAt(i + 1)] << 4) |
8242 (revLookup[b64.charCodeAt(i + 2)] >> 2)
8243 arr[curByte++] = (tmp >> 8) & 0xFF
8244 arr[curByte++] = tmp & 0xFF
8245 }
8246
8247 return arr
8248}
8249
8250function tripletToBase64 (num) {
8251 return lookup[num >> 18 & 0x3F] +
8252 lookup[num >> 12 & 0x3F] +
8253 lookup[num >> 6 & 0x3F] +
8254 lookup[num & 0x3F]
8255}
8256
8257function encodeChunk (uint8, start, end) {
8258 var tmp
8259 var output = []
8260 for (var i = start; i < end; i += 3) {
8261 tmp =
8262 ((uint8[i] << 16) & 0xFF0000) +
8263 ((uint8[i + 1] << 8) & 0xFF00) +
8264 (uint8[i + 2] & 0xFF)
8265 output.push(tripletToBase64(tmp))
8266 }
8267 return output.join('')
8268}
8269
8270function fromByteArray (uint8) {
8271 var tmp
8272 var len = uint8.length
8273 var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes
8274 var parts = []
8275 var maxChunkLength = 16383 // must be multiple of 3
8276
8277 // go through the array every three bytes, we'll deal with trailing stuff later
8278 for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
8279 parts.push(encodeChunk(
8280 uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength)
8281 ))
8282 }
8283
8284 // pad the end with zeros, but make sure to not forget the extra bytes
8285 if (extraBytes === 1) {
8286 tmp = uint8[len - 1]
8287 parts.push(
8288 lookup[tmp >> 2] +
8289 lookup[(tmp << 4) & 0x3F] +
8290 '=='
8291 )
8292 } else if (extraBytes === 2) {
8293 tmp = (uint8[len - 2] << 8) + uint8[len - 1]
8294 parts.push(
8295 lookup[tmp >> 10] +
8296 lookup[(tmp >> 4) & 0x3F] +
8297 lookup[(tmp << 2) & 0x3F] +
8298 '='
8299 )
8300 }
8301
8302 return parts.join('')
8303}
8304
8305},{}],26:[function(_dereq_,module,exports){
8306
8307},{}],27:[function(_dereq_,module,exports){
8308// Copyright Joyent, Inc. and other Node contributors.
8309//
8310// Permission is hereby granted, free of charge, to any person obtaining a
8311// copy of this software and associated documentation files (the
8312// "Software"), to deal in the Software without restriction, including
8313// without limitation the rights to use, copy, modify, merge, publish,
8314// distribute, sublicense, and/or sell copies of the Software, and to permit
8315// persons to whom the Software is furnished to do so, subject to the
8316// following conditions:
8317//
8318// The above copyright notice and this permission notice shall be included
8319// in all copies or substantial portions of the Software.
8320//
8321// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
8322// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
8323// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
8324// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
8325// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
8326// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
8327// USE OR OTHER DEALINGS IN THE SOFTWARE.
8328
8329'use strict';
8330
8331var R = typeof Reflect === 'object' ? Reflect : null
8332var ReflectApply = R && typeof R.apply === 'function'
8333 ? R.apply
8334 : function ReflectApply(target, receiver, args) {
8335 return Function.prototype.apply.call(target, receiver, args);
8336 }
8337
8338var ReflectOwnKeys
8339if (R && typeof R.ownKeys === 'function') {
8340 ReflectOwnKeys = R.ownKeys
8341} else if (Object.getOwnPropertySymbols) {
8342 ReflectOwnKeys = function ReflectOwnKeys(target) {
8343 return Object.getOwnPropertyNames(target)
8344 .concat(Object.getOwnPropertySymbols(target));
8345 };
8346} else {
8347 ReflectOwnKeys = function ReflectOwnKeys(target) {
8348 return Object.getOwnPropertyNames(target);
8349 };
8350}
8351
8352function ProcessEmitWarning(warning) {
8353 if (console && console.warn) console.warn(warning);
8354}
8355
8356var NumberIsNaN = Number.isNaN || function NumberIsNaN(value) {
8357 return value !== value;
8358}
8359
8360function EventEmitter() {
8361 EventEmitter.init.call(this);
8362}
8363module.exports = EventEmitter;
8364module.exports.once = once;
8365
8366// Backwards-compat with node 0.10.x
8367EventEmitter.EventEmitter = EventEmitter;
8368
8369EventEmitter.prototype._events = undefined;
8370EventEmitter.prototype._eventsCount = 0;
8371EventEmitter.prototype._maxListeners = undefined;
8372
8373// By default EventEmitters will print a warning if more than 10 listeners are
8374// added to it. This is a useful default which helps finding memory leaks.
8375var defaultMaxListeners = 10;
8376
8377function checkListener(listener) {
8378 if (typeof listener !== 'function') {
8379 throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener);
8380 }
8381}
8382
8383Object.defineProperty(EventEmitter, 'defaultMaxListeners', {
8384 enumerable: true,
8385 get: function() {
8386 return defaultMaxListeners;
8387 },
8388 set: function(arg) {
8389 if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) {
8390 throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + arg + '.');
8391 }
8392 defaultMaxListeners = arg;
8393 }
8394});
8395
8396EventEmitter.init = function() {
8397
8398 if (this._events === undefined ||
8399 this._events === Object.getPrototypeOf(this)._events) {
8400 this._events = Object.create(null);
8401 this._eventsCount = 0;
8402 }
8403
8404 this._maxListeners = this._maxListeners || undefined;
8405};
8406
8407// Obviously not all Emitters should be limited to 10. This function allows
8408// that to be increased. Set to zero for unlimited.
8409EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
8410 if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) {
8411 throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + '.');
8412 }
8413 this._maxListeners = n;
8414 return this;
8415};
8416
8417function _getMaxListeners(that) {
8418 if (that._maxListeners === undefined)
8419 return EventEmitter.defaultMaxListeners;
8420 return that._maxListeners;
8421}
8422
8423EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
8424 return _getMaxListeners(this);
8425};
8426
8427EventEmitter.prototype.emit = function emit(type) {
8428 var args = [];
8429 for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);
8430 var doError = (type === 'error');
8431
8432 var events = this._events;
8433 if (events !== undefined)
8434 doError = (doError && events.error === undefined);
8435 else if (!doError)
8436 return false;
8437
8438 // If there is no 'error' event listener then throw.
8439 if (doError) {
8440 var er;
8441 if (args.length > 0)
8442 er = args[0];
8443 if (er instanceof Error) {
8444 // Note: The comments on the `throw` lines are intentional, they show
8445 // up in Node's output if this results in an unhandled exception.
8446 throw er; // Unhandled 'error' event
8447 }
8448 // At least give some kind of context to the user
8449 var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : ''));
8450 err.context = er;
8451 throw err; // Unhandled 'error' event
8452 }
8453
8454 var handler = events[type];
8455
8456 if (handler === undefined)
8457 return false;
8458
8459 if (typeof handler === 'function') {
8460 ReflectApply(handler, this, args);
8461 } else {
8462 var len = handler.length;
8463 var listeners = arrayClone(handler, len);
8464 for (var i = 0; i < len; ++i)
8465 ReflectApply(listeners[i], this, args);
8466 }
8467
8468 return true;
8469};
8470
8471function _addListener(target, type, listener, prepend) {
8472 var m;
8473 var events;
8474 var existing;
8475
8476 checkListener(listener);
8477
8478 events = target._events;
8479 if (events === undefined) {
8480 events = target._events = Object.create(null);
8481 target._eventsCount = 0;
8482 } else {
8483 // To avoid recursion in the case that type === "newListener"! Before
8484 // adding it to the listeners, first emit "newListener".
8485 if (events.newListener !== undefined) {
8486 target.emit('newListener', type,
8487 listener.listener ? listener.listener : listener);
8488
8489 // Re-assign `events` because a newListener handler could have caused the
8490 // this._events to be assigned to a new object
8491 events = target._events;
8492 }
8493 existing = events[type];
8494 }
8495
8496 if (existing === undefined) {
8497 // Optimize the case of one listener. Don't need the extra array object.
8498 existing = events[type] = listener;
8499 ++target._eventsCount;
8500 } else {
8501 if (typeof existing === 'function') {
8502 // Adding the second element, need to change to array.
8503 existing = events[type] =
8504 prepend ? [listener, existing] : [existing, listener];
8505 // If we've already got an array, just append.
8506 } else if (prepend) {
8507 existing.unshift(listener);
8508 } else {
8509 existing.push(listener);
8510 }
8511
8512 // Check for listener leak
8513 m = _getMaxListeners(target);
8514 if (m > 0 && existing.length > m && !existing.warned) {
8515 existing.warned = true;
8516 // No error code for this since it is a Warning
8517 // eslint-disable-next-line no-restricted-syntax
8518 var w = new Error('Possible EventEmitter memory leak detected. ' +
8519 existing.length + ' ' + String(type) + ' listeners ' +
8520 'added. Use emitter.setMaxListeners() to ' +
8521 'increase limit');
8522 w.name = 'MaxListenersExceededWarning';
8523 w.emitter = target;
8524 w.type = type;
8525 w.count = existing.length;
8526 ProcessEmitWarning(w);
8527 }
8528 }
8529
8530 return target;
8531}
8532
8533EventEmitter.prototype.addListener = function addListener(type, listener) {
8534 return _addListener(this, type, listener, false);
8535};
8536
8537EventEmitter.prototype.on = EventEmitter.prototype.addListener;
8538
8539EventEmitter.prototype.prependListener =
8540 function prependListener(type, listener) {
8541 return _addListener(this, type, listener, true);
8542 };
8543
8544function onceWrapper() {
8545 if (!this.fired) {
8546 this.target.removeListener(this.type, this.wrapFn);
8547 this.fired = true;
8548 if (arguments.length === 0)
8549 return this.listener.call(this.target);
8550 return this.listener.apply(this.target, arguments);
8551 }
8552}
8553
8554function _onceWrap(target, type, listener) {
8555 var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };
8556 var wrapped = onceWrapper.bind(state);
8557 wrapped.listener = listener;
8558 state.wrapFn = wrapped;
8559 return wrapped;
8560}
8561
8562EventEmitter.prototype.once = function once(type, listener) {
8563 checkListener(listener);
8564 this.on(type, _onceWrap(this, type, listener));
8565 return this;
8566};
8567
8568EventEmitter.prototype.prependOnceListener =
8569 function prependOnceListener(type, listener) {
8570 checkListener(listener);
8571 this.prependListener(type, _onceWrap(this, type, listener));
8572 return this;
8573 };
8574
8575// Emits a 'removeListener' event if and only if the listener was removed.
8576EventEmitter.prototype.removeListener =
8577 function removeListener(type, listener) {
8578 var list, events, position, i, originalListener;
8579
8580 checkListener(listener);
8581
8582 events = this._events;
8583 if (events === undefined)
8584 return this;
8585
8586 list = events[type];
8587 if (list === undefined)
8588 return this;
8589
8590 if (list === listener || list.listener === listener) {
8591 if (--this._eventsCount === 0)
8592 this._events = Object.create(null);
8593 else {
8594 delete events[type];
8595 if (events.removeListener)
8596 this.emit('removeListener', type, list.listener || listener);
8597 }
8598 } else if (typeof list !== 'function') {
8599 position = -1;
8600
8601 for (i = list.length - 1; i >= 0; i--) {
8602 if (list[i] === listener || list[i].listener === listener) {
8603 originalListener = list[i].listener;
8604 position = i;
8605 break;
8606 }
8607 }
8608
8609 if (position < 0)
8610 return this;
8611
8612 if (position === 0)
8613 list.shift();
8614 else {
8615 spliceOne(list, position);
8616 }
8617
8618 if (list.length === 1)
8619 events[type] = list[0];
8620
8621 if (events.removeListener !== undefined)
8622 this.emit('removeListener', type, originalListener || listener);
8623 }
8624
8625 return this;
8626 };
8627
8628EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
8629
8630EventEmitter.prototype.removeAllListeners =
8631 function removeAllListeners(type) {
8632 var listeners, events, i;
8633
8634 events = this._events;
8635 if (events === undefined)
8636 return this;
8637
8638 // not listening for removeListener, no need to emit
8639 if (events.removeListener === undefined) {
8640 if (arguments.length === 0) {
8641 this._events = Object.create(null);
8642 this._eventsCount = 0;
8643 } else if (events[type] !== undefined) {
8644 if (--this._eventsCount === 0)
8645 this._events = Object.create(null);
8646 else
8647 delete events[type];
8648 }
8649 return this;
8650 }
8651
8652 // emit removeListener for all listeners on all events
8653 if (arguments.length === 0) {
8654 var keys = Object.keys(events);
8655 var key;
8656 for (i = 0; i < keys.length; ++i) {
8657 key = keys[i];
8658 if (key === 'removeListener') continue;
8659 this.removeAllListeners(key);
8660 }
8661 this.removeAllListeners('removeListener');
8662 this._events = Object.create(null);
8663 this._eventsCount = 0;
8664 return this;
8665 }
8666
8667 listeners = events[type];
8668
8669 if (typeof listeners === 'function') {
8670 this.removeListener(type, listeners);
8671 } else if (listeners !== undefined) {
8672 // LIFO order
8673 for (i = listeners.length - 1; i >= 0; i--) {
8674 this.removeListener(type, listeners[i]);
8675 }
8676 }
8677
8678 return this;
8679 };
8680
8681function _listeners(target, type, unwrap) {
8682 var events = target._events;
8683
8684 if (events === undefined)
8685 return [];
8686
8687 var evlistener = events[type];
8688 if (evlistener === undefined)
8689 return [];
8690
8691 if (typeof evlistener === 'function')
8692 return unwrap ? [evlistener.listener || evlistener] : [evlistener];
8693
8694 return unwrap ?
8695 unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);
8696}
8697
8698EventEmitter.prototype.listeners = function listeners(type) {
8699 return _listeners(this, type, true);
8700};
8701
8702EventEmitter.prototype.rawListeners = function rawListeners(type) {
8703 return _listeners(this, type, false);
8704};
8705
8706EventEmitter.listenerCount = function(emitter, type) {
8707 if (typeof emitter.listenerCount === 'function') {
8708 return emitter.listenerCount(type);
8709 } else {
8710 return listenerCount.call(emitter, type);
8711 }
8712};
8713
8714EventEmitter.prototype.listenerCount = listenerCount;
8715function listenerCount(type) {
8716 var events = this._events;
8717
8718 if (events !== undefined) {
8719 var evlistener = events[type];
8720
8721 if (typeof evlistener === 'function') {
8722 return 1;
8723 } else if (evlistener !== undefined) {
8724 return evlistener.length;
8725 }
8726 }
8727
8728 return 0;
8729}
8730
8731EventEmitter.prototype.eventNames = function eventNames() {
8732 return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : [];
8733};
8734
8735function arrayClone(arr, n) {
8736 var copy = new Array(n);
8737 for (var i = 0; i < n; ++i)
8738 copy[i] = arr[i];
8739 return copy;
8740}
8741
8742function spliceOne(list, index) {
8743 for (; index + 1 < list.length; index++)
8744 list[index] = list[index + 1];
8745 list.pop();
8746}
8747
8748function unwrapListeners(arr) {
8749 var ret = new Array(arr.length);
8750 for (var i = 0; i < ret.length; ++i) {
8751 ret[i] = arr[i].listener || arr[i];
8752 }
8753 return ret;
8754}
8755
8756function once(emitter, name) {
8757 return new Promise(function (resolve, reject) {
8758 function eventListener() {
8759 if (errorListener !== undefined) {
8760 emitter.removeListener('error', errorListener);
8761 }
8762 resolve([].slice.call(arguments));
8763 };
8764 var errorListener;
8765
8766 // Adding an error listener is not optional because
8767 // if an error is thrown on an event emitter we cannot
8768 // guarantee that the actual event we are waiting will
8769 // be fired. The result could be a silent way to create
8770 // memory or file descriptor leaks, which is something
8771 // we should avoid.
8772 if (name !== 'error') {
8773 errorListener = function errorListener(err) {
8774 emitter.removeListener(name, eventListener);
8775 reject(err);
8776 };
8777
8778 emitter.once('error', errorListener);
8779 }
8780
8781 emitter.once(name, eventListener);
8782 });
8783}
8784
8785},{}],28:[function(_dereq_,module,exports){
8786(function (Buffer){(function (){
8787/*!
8788 * The buffer module from node.js, for the browser.
8789 *
8790 * @author Feross Aboukhadijeh <https://feross.org>
8791 * @license MIT
8792 */
8793/* eslint-disable no-proto */
8794
8795'use strict'
8796
8797var base64 = _dereq_('base64-js')
8798var ieee754 = _dereq_('ieee754')
8799
8800exports.Buffer = Buffer
8801exports.SlowBuffer = SlowBuffer
8802exports.INSPECT_MAX_BYTES = 50
8803
8804var K_MAX_LENGTH = 0x7fffffff
8805exports.kMaxLength = K_MAX_LENGTH
8806
8807/**
8808 * If `Buffer.TYPED_ARRAY_SUPPORT`:
8809 * === true Use Uint8Array implementation (fastest)
8810 * === false Print warning and recommend using `buffer` v4.x which has an Object
8811 * implementation (most compatible, even IE6)
8812 *
8813 * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+,
8814 * Opera 11.6+, iOS 4.2+.
8815 *
8816 * We report that the browser does not support typed arrays if the are not subclassable
8817 * using __proto__. Firefox 4-29 lacks support for adding new properties to `Uint8Array`
8818 * (See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438). IE 10 lacks support
8819 * for __proto__ and has a buggy typed array implementation.
8820 */
8821Buffer.TYPED_ARRAY_SUPPORT = typedArraySupport()
8822
8823if (!Buffer.TYPED_ARRAY_SUPPORT && typeof console !== 'undefined' &&
8824 typeof console.error === 'function') {
8825 console.error(
8826 'This browser lacks typed array (Uint8Array) support which is required by ' +
8827 '`buffer` v5.x. Use `buffer` v4.x if you require old browser support.'
8828 )
8829}
8830
8831function typedArraySupport () {
8832 // Can typed array instances can be augmented?
8833 try {
8834 var arr = new Uint8Array(1)
8835 arr.__proto__ = { __proto__: Uint8Array.prototype, foo: function () { return 42 } }
8836 return arr.foo() === 42
8837 } catch (e) {
8838 return false
8839 }
8840}
8841
8842Object.defineProperty(Buffer.prototype, 'parent', {
8843 enumerable: true,
8844 get: function () {
8845 if (!Buffer.isBuffer(this)) return undefined
8846 return this.buffer
8847 }
8848})
8849
8850Object.defineProperty(Buffer.prototype, 'offset', {
8851 enumerable: true,
8852 get: function () {
8853 if (!Buffer.isBuffer(this)) return undefined
8854 return this.byteOffset
8855 }
8856})
8857
8858function createBuffer (length) {
8859 if (length > K_MAX_LENGTH) {
8860 throw new RangeError('The value "' + length + '" is invalid for option "size"')
8861 }
8862 // Return an augmented `Uint8Array` instance
8863 var buf = new Uint8Array(length)
8864 buf.__proto__ = Buffer.prototype
8865 return buf
8866}
8867
8868/**
8869 * The Buffer constructor returns instances of `Uint8Array` that have their
8870 * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of
8871 * `Uint8Array`, so the returned instances will have all the node `Buffer` methods
8872 * and the `Uint8Array` methods. Square bracket notation works as expected -- it
8873 * returns a single octet.
8874 *
8875 * The `Uint8Array` prototype remains unmodified.
8876 */
8877
8878function Buffer (arg, encodingOrOffset, length) {
8879 // Common case.
8880 if (typeof arg === 'number') {
8881 if (typeof encodingOrOffset === 'string') {
8882 throw new TypeError(
8883 'The "string" argument must be of type string. Received type number'
8884 )
8885 }
8886 return allocUnsafe(arg)
8887 }
8888 return from(arg, encodingOrOffset, length)
8889}
8890
8891// Fix subarray() in ES2016. See: https://github.com/feross/buffer/pull/97
8892if (typeof Symbol !== 'undefined' && Symbol.species != null &&
8893 Buffer[Symbol.species] === Buffer) {
8894 Object.defineProperty(Buffer, Symbol.species, {
8895 value: null,
8896 configurable: true,
8897 enumerable: false,
8898 writable: false
8899 })
8900}
8901
8902Buffer.poolSize = 8192 // not used by this implementation
8903
8904function from (value, encodingOrOffset, length) {
8905 if (typeof value === 'string') {
8906 return fromString(value, encodingOrOffset)
8907 }
8908
8909 if (ArrayBuffer.isView(value)) {
8910 return fromArrayLike(value)
8911 }
8912
8913 if (value == null) {
8914 throw TypeError(
8915 'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' +
8916 'or Array-like Object. Received type ' + (typeof value)
8917 )
8918 }
8919
8920 if (isInstance(value, ArrayBuffer) ||
8921 (value && isInstance(value.buffer, ArrayBuffer))) {
8922 return fromArrayBuffer(value, encodingOrOffset, length)
8923 }
8924
8925 if (typeof value === 'number') {
8926 throw new TypeError(
8927 'The "value" argument must not be of type number. Received type number'
8928 )
8929 }
8930
8931 var valueOf = value.valueOf && value.valueOf()
8932 if (valueOf != null && valueOf !== value) {
8933 return Buffer.from(valueOf, encodingOrOffset, length)
8934 }
8935
8936 var b = fromObject(value)
8937 if (b) return b
8938
8939 if (typeof Symbol !== 'undefined' && Symbol.toPrimitive != null &&
8940 typeof value[Symbol.toPrimitive] === 'function') {
8941 return Buffer.from(
8942 value[Symbol.toPrimitive]('string'), encodingOrOffset, length
8943 )
8944 }
8945
8946 throw new TypeError(
8947 'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' +
8948 'or Array-like Object. Received type ' + (typeof value)
8949 )
8950}
8951
8952/**
8953 * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError
8954 * if value is a number.
8955 * Buffer.from(str[, encoding])
8956 * Buffer.from(array)
8957 * Buffer.from(buffer)
8958 * Buffer.from(arrayBuffer[, byteOffset[, length]])
8959 **/
8960Buffer.from = function (value, encodingOrOffset, length) {
8961 return from(value, encodingOrOffset, length)
8962}
8963
8964// Note: Change prototype *after* Buffer.from is defined to workaround Chrome bug:
8965// https://github.com/feross/buffer/pull/148
8966Buffer.prototype.__proto__ = Uint8Array.prototype
8967Buffer.__proto__ = Uint8Array
8968
8969function assertSize (size) {
8970 if (typeof size !== 'number') {
8971 throw new TypeError('"size" argument must be of type number')
8972 } else if (size < 0) {
8973 throw new RangeError('The value "' + size + '" is invalid for option "size"')
8974 }
8975}
8976
8977function alloc (size, fill, encoding) {
8978 assertSize(size)
8979 if (size <= 0) {
8980 return createBuffer(size)
8981 }
8982 if (fill !== undefined) {
8983 // Only pay attention to encoding if it's a string. This
8984 // prevents accidentally sending in a number that would
8985 // be interpretted as a start offset.
8986 return typeof encoding === 'string'
8987 ? createBuffer(size).fill(fill, encoding)
8988 : createBuffer(size).fill(fill)
8989 }
8990 return createBuffer(size)
8991}
8992
8993/**
8994 * Creates a new filled Buffer instance.
8995 * alloc(size[, fill[, encoding]])
8996 **/
8997Buffer.alloc = function (size, fill, encoding) {
8998 return alloc(size, fill, encoding)
8999}
9000
9001function allocUnsafe (size) {
9002 assertSize(size)
9003 return createBuffer(size < 0 ? 0 : checked(size) | 0)
9004}
9005
9006/**
9007 * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance.
9008 * */
9009Buffer.allocUnsafe = function (size) {
9010 return allocUnsafe(size)
9011}
9012/**
9013 * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance.
9014 */
9015Buffer.allocUnsafeSlow = function (size) {
9016 return allocUnsafe(size)
9017}
9018
9019function fromString (string, encoding) {
9020 if (typeof encoding !== 'string' || encoding === '') {
9021 encoding = 'utf8'
9022 }
9023
9024 if (!Buffer.isEncoding(encoding)) {
9025 throw new TypeError('Unknown encoding: ' + encoding)
9026 }
9027
9028 var length = byteLength(string, encoding) | 0
9029 var buf = createBuffer(length)
9030
9031 var actual = buf.write(string, encoding)
9032
9033 if (actual !== length) {
9034 // Writing a hex string, for example, that contains invalid characters will
9035 // cause everything after the first invalid character to be ignored. (e.g.
9036 // 'abxxcd' will be treated as 'ab')
9037 buf = buf.slice(0, actual)
9038 }
9039
9040 return buf
9041}
9042
9043function fromArrayLike (array) {
9044 var length = array.length < 0 ? 0 : checked(array.length) | 0
9045 var buf = createBuffer(length)
9046 for (var i = 0; i < length; i += 1) {
9047 buf[i] = array[i] & 255
9048 }
9049 return buf
9050}
9051
9052function fromArrayBuffer (array, byteOffset, length) {
9053 if (byteOffset < 0 || array.byteLength < byteOffset) {
9054 throw new RangeError('"offset" is outside of buffer bounds')
9055 }
9056
9057 if (array.byteLength < byteOffset + (length || 0)) {
9058 throw new RangeError('"length" is outside of buffer bounds')
9059 }
9060
9061 var buf
9062 if (byteOffset === undefined && length === undefined) {
9063 buf = new Uint8Array(array)
9064 } else if (length === undefined) {
9065 buf = new Uint8Array(array, byteOffset)
9066 } else {
9067 buf = new Uint8Array(array, byteOffset, length)
9068 }
9069
9070 // Return an augmented `Uint8Array` instance
9071 buf.__proto__ = Buffer.prototype
9072 return buf
9073}
9074
9075function fromObject (obj) {
9076 if (Buffer.isBuffer(obj)) {
9077 var len = checked(obj.length) | 0
9078 var buf = createBuffer(len)
9079
9080 if (buf.length === 0) {
9081 return buf
9082 }
9083
9084 obj.copy(buf, 0, 0, len)
9085 return buf
9086 }
9087
9088 if (obj.length !== undefined) {
9089 if (typeof obj.length !== 'number' || numberIsNaN(obj.length)) {
9090 return createBuffer(0)
9091 }
9092 return fromArrayLike(obj)
9093 }
9094
9095 if (obj.type === 'Buffer' && Array.isArray(obj.data)) {
9096 return fromArrayLike(obj.data)
9097 }
9098}
9099
9100function checked (length) {
9101 // Note: cannot use `length < K_MAX_LENGTH` here because that fails when
9102 // length is NaN (which is otherwise coerced to zero.)
9103 if (length >= K_MAX_LENGTH) {
9104 throw new RangeError('Attempt to allocate Buffer larger than maximum ' +
9105 'size: 0x' + K_MAX_LENGTH.toString(16) + ' bytes')
9106 }
9107 return length | 0
9108}
9109
9110function SlowBuffer (length) {
9111 if (+length != length) { // eslint-disable-line eqeqeq
9112 length = 0
9113 }
9114 return Buffer.alloc(+length)
9115}
9116
9117Buffer.isBuffer = function isBuffer (b) {
9118 return b != null && b._isBuffer === true &&
9119 b !== Buffer.prototype // so Buffer.isBuffer(Buffer.prototype) will be false
9120}
9121
9122Buffer.compare = function compare (a, b) {
9123 if (isInstance(a, Uint8Array)) a = Buffer.from(a, a.offset, a.byteLength)
9124 if (isInstance(b, Uint8Array)) b = Buffer.from(b, b.offset, b.byteLength)
9125 if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) {
9126 throw new TypeError(
9127 'The "buf1", "buf2" arguments must be one of type Buffer or Uint8Array'
9128 )
9129 }
9130
9131 if (a === b) return 0
9132
9133 var x = a.length
9134 var y = b.length
9135
9136 for (var i = 0, len = Math.min(x, y); i < len; ++i) {
9137 if (a[i] !== b[i]) {
9138 x = a[i]
9139 y = b[i]
9140 break
9141 }
9142 }
9143
9144 if (x < y) return -1
9145 if (y < x) return 1
9146 return 0
9147}
9148
9149Buffer.isEncoding = function isEncoding (encoding) {
9150 switch (String(encoding).toLowerCase()) {
9151 case 'hex':
9152 case 'utf8':
9153 case 'utf-8':
9154 case 'ascii':
9155 case 'latin1':
9156 case 'binary':
9157 case 'base64':
9158 case 'ucs2':
9159 case 'ucs-2':
9160 case 'utf16le':
9161 case 'utf-16le':
9162 return true
9163 default:
9164 return false
9165 }
9166}
9167
9168Buffer.concat = function concat (list, length) {
9169 if (!Array.isArray(list)) {
9170 throw new TypeError('"list" argument must be an Array of Buffers')
9171 }
9172
9173 if (list.length === 0) {
9174 return Buffer.alloc(0)
9175 }
9176
9177 var i
9178 if (length === undefined) {
9179 length = 0
9180 for (i = 0; i < list.length; ++i) {
9181 length += list[i].length
9182 }
9183 }
9184
9185 var buffer = Buffer.allocUnsafe(length)
9186 var pos = 0
9187 for (i = 0; i < list.length; ++i) {
9188 var buf = list[i]
9189 if (isInstance(buf, Uint8Array)) {
9190 buf = Buffer.from(buf)
9191 }
9192 if (!Buffer.isBuffer(buf)) {
9193 throw new TypeError('"list" argument must be an Array of Buffers')
9194 }
9195 buf.copy(buffer, pos)
9196 pos += buf.length
9197 }
9198 return buffer
9199}
9200
9201function byteLength (string, encoding) {
9202 if (Buffer.isBuffer(string)) {
9203 return string.length
9204 }
9205 if (ArrayBuffer.isView(string) || isInstance(string, ArrayBuffer)) {
9206 return string.byteLength
9207 }
9208 if (typeof string !== 'string') {
9209 throw new TypeError(
9210 'The "string" argument must be one of type string, Buffer, or ArrayBuffer. ' +
9211 'Received type ' + typeof string
9212 )
9213 }
9214
9215 var len = string.length
9216 var mustMatch = (arguments.length > 2 && arguments[2] === true)
9217 if (!mustMatch && len === 0) return 0
9218
9219 // Use a for loop to avoid recursion
9220 var loweredCase = false
9221 for (;;) {
9222 switch (encoding) {
9223 case 'ascii':
9224 case 'latin1':
9225 case 'binary':
9226 return len
9227 case 'utf8':
9228 case 'utf-8':
9229 return utf8ToBytes(string).length
9230 case 'ucs2':
9231 case 'ucs-2':
9232 case 'utf16le':
9233 case 'utf-16le':
9234 return len * 2
9235 case 'hex':
9236 return len >>> 1
9237 case 'base64':
9238 return base64ToBytes(string).length
9239 default:
9240 if (loweredCase) {
9241 return mustMatch ? -1 : utf8ToBytes(string).length // assume utf8
9242 }
9243 encoding = ('' + encoding).toLowerCase()
9244 loweredCase = true
9245 }
9246 }
9247}
9248Buffer.byteLength = byteLength
9249
9250function slowToString (encoding, start, end) {
9251 var loweredCase = false
9252
9253 // No need to verify that "this.length <= MAX_UINT32" since it's a read-only
9254 // property of a typed array.
9255
9256 // This behaves neither like String nor Uint8Array in that we set start/end
9257 // to their upper/lower bounds if the value passed is out of range.
9258 // undefined is handled specially as per ECMA-262 6th Edition,
9259 // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization.
9260 if (start === undefined || start < 0) {
9261 start = 0
9262 }
9263 // Return early if start > this.length. Done here to prevent potential uint32
9264 // coercion fail below.
9265 if (start > this.length) {
9266 return ''
9267 }
9268
9269 if (end === undefined || end > this.length) {
9270 end = this.length
9271 }
9272
9273 if (end <= 0) {
9274 return ''
9275 }
9276
9277 // Force coersion to uint32. This will also coerce falsey/NaN values to 0.
9278 end >>>= 0
9279 start >>>= 0
9280
9281 if (end <= start) {
9282 return ''
9283 }
9284
9285 if (!encoding) encoding = 'utf8'
9286
9287 while (true) {
9288 switch (encoding) {
9289 case 'hex':
9290 return hexSlice(this, start, end)
9291
9292 case 'utf8':
9293 case 'utf-8':
9294 return utf8Slice(this, start, end)
9295
9296 case 'ascii':
9297 return asciiSlice(this, start, end)
9298
9299 case 'latin1':
9300 case 'binary':
9301 return latin1Slice(this, start, end)
9302
9303 case 'base64':
9304 return base64Slice(this, start, end)
9305
9306 case 'ucs2':
9307 case 'ucs-2':
9308 case 'utf16le':
9309 case 'utf-16le':
9310 return utf16leSlice(this, start, end)
9311
9312 default:
9313 if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)
9314 encoding = (encoding + '').toLowerCase()
9315 loweredCase = true
9316 }
9317 }
9318}
9319
9320// This property is used by `Buffer.isBuffer` (and the `is-buffer` npm package)
9321// to detect a Buffer instance. It's not possible to use `instanceof Buffer`
9322// reliably in a browserify context because there could be multiple different
9323// copies of the 'buffer' package in use. This method works even for Buffer
9324// instances that were created from another copy of the `buffer` package.
9325// See: https://github.com/feross/buffer/issues/154
9326Buffer.prototype._isBuffer = true
9327
9328function swap (b, n, m) {
9329 var i = b[n]
9330 b[n] = b[m]
9331 b[m] = i
9332}
9333
9334Buffer.prototype.swap16 = function swap16 () {
9335 var len = this.length
9336 if (len % 2 !== 0) {
9337 throw new RangeError('Buffer size must be a multiple of 16-bits')
9338 }
9339 for (var i = 0; i < len; i += 2) {
9340 swap(this, i, i + 1)
9341 }
9342 return this
9343}
9344
9345Buffer.prototype.swap32 = function swap32 () {
9346 var len = this.length
9347 if (len % 4 !== 0) {
9348 throw new RangeError('Buffer size must be a multiple of 32-bits')
9349 }
9350 for (var i = 0; i < len; i += 4) {
9351 swap(this, i, i + 3)
9352 swap(this, i + 1, i + 2)
9353 }
9354 return this
9355}
9356
9357Buffer.prototype.swap64 = function swap64 () {
9358 var len = this.length
9359 if (len % 8 !== 0) {
9360 throw new RangeError('Buffer size must be a multiple of 64-bits')
9361 }
9362 for (var i = 0; i < len; i += 8) {
9363 swap(this, i, i + 7)
9364 swap(this, i + 1, i + 6)
9365 swap(this, i + 2, i + 5)
9366 swap(this, i + 3, i + 4)
9367 }
9368 return this
9369}
9370
9371Buffer.prototype.toString = function toString () {
9372 var length = this.length
9373 if (length === 0) return ''
9374 if (arguments.length === 0) return utf8Slice(this, 0, length)
9375 return slowToString.apply(this, arguments)
9376}
9377
9378Buffer.prototype.toLocaleString = Buffer.prototype.toString
9379
9380Buffer.prototype.equals = function equals (b) {
9381 if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer')
9382 if (this === b) return true
9383 return Buffer.compare(this, b) === 0
9384}
9385
9386Buffer.prototype.inspect = function inspect () {
9387 var str = ''
9388 var max = exports.INSPECT_MAX_BYTES
9389 str = this.toString('hex', 0, max).replace(/(.{2})/g, '$1 ').trim()
9390 if (this.length > max) str += ' ... '
9391 return '<Buffer ' + str + '>'
9392}
9393
9394Buffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) {
9395 if (isInstance(target, Uint8Array)) {
9396 target = Buffer.from(target, target.offset, target.byteLength)
9397 }
9398 if (!Buffer.isBuffer(target)) {
9399 throw new TypeError(
9400 'The "target" argument must be one of type Buffer or Uint8Array. ' +
9401 'Received type ' + (typeof target)
9402 )
9403 }
9404
9405 if (start === undefined) {
9406 start = 0
9407 }
9408 if (end === undefined) {
9409 end = target ? target.length : 0
9410 }
9411 if (thisStart === undefined) {
9412 thisStart = 0
9413 }
9414 if (thisEnd === undefined) {
9415 thisEnd = this.length
9416 }
9417
9418 if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) {
9419 throw new RangeError('out of range index')
9420 }
9421
9422 if (thisStart >= thisEnd && start >= end) {
9423 return 0
9424 }
9425 if (thisStart >= thisEnd) {
9426 return -1
9427 }
9428 if (start >= end) {
9429 return 1
9430 }
9431
9432 start >>>= 0
9433 end >>>= 0
9434 thisStart >>>= 0
9435 thisEnd >>>= 0
9436
9437 if (this === target) return 0
9438
9439 var x = thisEnd - thisStart
9440 var y = end - start
9441 var len = Math.min(x, y)
9442
9443 var thisCopy = this.slice(thisStart, thisEnd)
9444 var targetCopy = target.slice(start, end)
9445
9446 for (var i = 0; i < len; ++i) {
9447 if (thisCopy[i] !== targetCopy[i]) {
9448 x = thisCopy[i]
9449 y = targetCopy[i]
9450 break
9451 }
9452 }
9453
9454 if (x < y) return -1
9455 if (y < x) return 1
9456 return 0
9457}
9458
9459// Finds either the first index of `val` in `buffer` at offset >= `byteOffset`,
9460// OR the last index of `val` in `buffer` at offset <= `byteOffset`.
9461//
9462// Arguments:
9463// - buffer - a Buffer to search
9464// - val - a string, Buffer, or number
9465// - byteOffset - an index into `buffer`; will be clamped to an int32
9466// - encoding - an optional encoding, relevant is val is a string
9467// - dir - true for indexOf, false for lastIndexOf
9468function bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) {
9469 // Empty buffer means no match
9470 if (buffer.length === 0) return -1
9471
9472 // Normalize byteOffset
9473 if (typeof byteOffset === 'string') {
9474 encoding = byteOffset
9475 byteOffset = 0
9476 } else if (byteOffset > 0x7fffffff) {
9477 byteOffset = 0x7fffffff
9478 } else if (byteOffset < -0x80000000) {
9479 byteOffset = -0x80000000
9480 }
9481 byteOffset = +byteOffset // Coerce to Number.
9482 if (numberIsNaN(byteOffset)) {
9483 // byteOffset: it it's undefined, null, NaN, "foo", etc, search whole buffer
9484 byteOffset = dir ? 0 : (buffer.length - 1)
9485 }
9486
9487 // Normalize byteOffset: negative offsets start from the end of the buffer
9488 if (byteOffset < 0) byteOffset = buffer.length + byteOffset
9489 if (byteOffset >= buffer.length) {
9490 if (dir) return -1
9491 else byteOffset = buffer.length - 1
9492 } else if (byteOffset < 0) {
9493 if (dir) byteOffset = 0
9494 else return -1
9495 }
9496
9497 // Normalize val
9498 if (typeof val === 'string') {
9499 val = Buffer.from(val, encoding)
9500 }
9501
9502 // Finally, search either indexOf (if dir is true) or lastIndexOf
9503 if (Buffer.isBuffer(val)) {
9504 // Special case: looking for empty string/buffer always fails
9505 if (val.length === 0) {
9506 return -1
9507 }
9508 return arrayIndexOf(buffer, val, byteOffset, encoding, dir)
9509 } else if (typeof val === 'number') {
9510 val = val & 0xFF // Search for a byte value [0-255]
9511 if (typeof Uint8Array.prototype.indexOf === 'function') {
9512 if (dir) {
9513 return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset)
9514 } else {
9515 return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset)
9516 }
9517 }
9518 return arrayIndexOf(buffer, [ val ], byteOffset, encoding, dir)
9519 }
9520
9521 throw new TypeError('val must be string, number or Buffer')
9522}
9523
9524function arrayIndexOf (arr, val, byteOffset, encoding, dir) {
9525 var indexSize = 1
9526 var arrLength = arr.length
9527 var valLength = val.length
9528
9529 if (encoding !== undefined) {
9530 encoding = String(encoding).toLowerCase()
9531 if (encoding === 'ucs2' || encoding === 'ucs-2' ||
9532 encoding === 'utf16le' || encoding === 'utf-16le') {
9533 if (arr.length < 2 || val.length < 2) {
9534 return -1
9535 }
9536 indexSize = 2
9537 arrLength /= 2
9538 valLength /= 2
9539 byteOffset /= 2
9540 }
9541 }
9542
9543 function read (buf, i) {
9544 if (indexSize === 1) {
9545 return buf[i]
9546 } else {
9547 return buf.readUInt16BE(i * indexSize)
9548 }
9549 }
9550
9551 var i
9552 if (dir) {
9553 var foundIndex = -1
9554 for (i = byteOffset; i < arrLength; i++) {
9555 if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) {
9556 if (foundIndex === -1) foundIndex = i
9557 if (i - foundIndex + 1 === valLength) return foundIndex * indexSize
9558 } else {
9559 if (foundIndex !== -1) i -= i - foundIndex
9560 foundIndex = -1
9561 }
9562 }
9563 } else {
9564 if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength
9565 for (i = byteOffset; i >= 0; i--) {
9566 var found = true
9567 for (var j = 0; j < valLength; j++) {
9568 if (read(arr, i + j) !== read(val, j)) {
9569 found = false
9570 break
9571 }
9572 }
9573 if (found) return i
9574 }
9575 }
9576
9577 return -1
9578}
9579
9580Buffer.prototype.includes = function includes (val, byteOffset, encoding) {
9581 return this.indexOf(val, byteOffset, encoding) !== -1
9582}
9583
9584Buffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) {
9585 return bidirectionalIndexOf(this, val, byteOffset, encoding, true)
9586}
9587
9588Buffer.prototype.lastIndexOf = function lastIndexOf (val, byteOffset, encoding) {
9589 return bidirectionalIndexOf(this, val, byteOffset, encoding, false)
9590}
9591
9592function hexWrite (buf, string, offset, length) {
9593 offset = Number(offset) || 0
9594 var remaining = buf.length - offset
9595 if (!length) {
9596 length = remaining
9597 } else {
9598 length = Number(length)
9599 if (length > remaining) {
9600 length = remaining
9601 }
9602 }
9603
9604 var strLen = string.length
9605
9606 if (length > strLen / 2) {
9607 length = strLen / 2
9608 }
9609 for (var i = 0; i < length; ++i) {
9610 var parsed = parseInt(string.substr(i * 2, 2), 16)
9611 if (numberIsNaN(parsed)) return i
9612 buf[offset + i] = parsed
9613 }
9614 return i
9615}
9616
9617function utf8Write (buf, string, offset, length) {
9618 return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length)
9619}
9620
9621function asciiWrite (buf, string, offset, length) {
9622 return blitBuffer(asciiToBytes(string), buf, offset, length)
9623}
9624
9625function latin1Write (buf, string, offset, length) {
9626 return asciiWrite(buf, string, offset, length)
9627}
9628
9629function base64Write (buf, string, offset, length) {
9630 return blitBuffer(base64ToBytes(string), buf, offset, length)
9631}
9632
9633function ucs2Write (buf, string, offset, length) {
9634 return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length)
9635}
9636
9637Buffer.prototype.write = function write (string, offset, length, encoding) {
9638 // Buffer#write(string)
9639 if (offset === undefined) {
9640 encoding = 'utf8'
9641 length = this.length
9642 offset = 0
9643 // Buffer#write(string, encoding)
9644 } else if (length === undefined && typeof offset === 'string') {
9645 encoding = offset
9646 length = this.length
9647 offset = 0
9648 // Buffer#write(string, offset[, length][, encoding])
9649 } else if (isFinite(offset)) {
9650 offset = offset >>> 0
9651 if (isFinite(length)) {
9652 length = length >>> 0
9653 if (encoding === undefined) encoding = 'utf8'
9654 } else {
9655 encoding = length
9656 length = undefined
9657 }
9658 } else {
9659 throw new Error(
9660 'Buffer.write(string, encoding, offset[, length]) is no longer supported'
9661 )
9662 }
9663
9664 var remaining = this.length - offset
9665 if (length === undefined || length > remaining) length = remaining
9666
9667 if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) {
9668 throw new RangeError('Attempt to write outside buffer bounds')
9669 }
9670
9671 if (!encoding) encoding = 'utf8'
9672
9673 var loweredCase = false
9674 for (;;) {
9675 switch (encoding) {
9676 case 'hex':
9677 return hexWrite(this, string, offset, length)
9678
9679 case 'utf8':
9680 case 'utf-8':
9681 return utf8Write(this, string, offset, length)
9682
9683 case 'ascii':
9684 return asciiWrite(this, string, offset, length)
9685
9686 case 'latin1':
9687 case 'binary':
9688 return latin1Write(this, string, offset, length)
9689
9690 case 'base64':
9691 // Warning: maxLength not taken into account in base64Write
9692 return base64Write(this, string, offset, length)
9693
9694 case 'ucs2':
9695 case 'ucs-2':
9696 case 'utf16le':
9697 case 'utf-16le':
9698 return ucs2Write(this, string, offset, length)
9699
9700 default:
9701 if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)
9702 encoding = ('' + encoding).toLowerCase()
9703 loweredCase = true
9704 }
9705 }
9706}
9707
9708Buffer.prototype.toJSON = function toJSON () {
9709 return {
9710 type: 'Buffer',
9711 data: Array.prototype.slice.call(this._arr || this, 0)
9712 }
9713}
9714
9715function base64Slice (buf, start, end) {
9716 if (start === 0 && end === buf.length) {
9717 return base64.fromByteArray(buf)
9718 } else {
9719 return base64.fromByteArray(buf.slice(start, end))
9720 }
9721}
9722
9723function utf8Slice (buf, start, end) {
9724 end = Math.min(buf.length, end)
9725 var res = []
9726
9727 var i = start
9728 while (i < end) {
9729 var firstByte = buf[i]
9730 var codePoint = null
9731 var bytesPerSequence = (firstByte > 0xEF) ? 4
9732 : (firstByte > 0xDF) ? 3
9733 : (firstByte > 0xBF) ? 2
9734 : 1
9735
9736 if (i + bytesPerSequence <= end) {
9737 var secondByte, thirdByte, fourthByte, tempCodePoint
9738
9739 switch (bytesPerSequence) {
9740 case 1:
9741 if (firstByte < 0x80) {
9742 codePoint = firstByte
9743 }
9744 break
9745 case 2:
9746 secondByte = buf[i + 1]
9747 if ((secondByte & 0xC0) === 0x80) {
9748 tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F)
9749 if (tempCodePoint > 0x7F) {
9750 codePoint = tempCodePoint
9751 }
9752 }
9753 break
9754 case 3:
9755 secondByte = buf[i + 1]
9756 thirdByte = buf[i + 2]
9757 if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) {
9758 tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F)
9759 if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) {
9760 codePoint = tempCodePoint
9761 }
9762 }
9763 break
9764 case 4:
9765 secondByte = buf[i + 1]
9766 thirdByte = buf[i + 2]
9767 fourthByte = buf[i + 3]
9768 if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) {
9769 tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F)
9770 if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) {
9771 codePoint = tempCodePoint
9772 }
9773 }
9774 }
9775 }
9776
9777 if (codePoint === null) {
9778 // we did not generate a valid codePoint so insert a
9779 // replacement char (U+FFFD) and advance only 1 byte
9780 codePoint = 0xFFFD
9781 bytesPerSequence = 1
9782 } else if (codePoint > 0xFFFF) {
9783 // encode to utf16 (surrogate pair dance)
9784 codePoint -= 0x10000
9785 res.push(codePoint >>> 10 & 0x3FF | 0xD800)
9786 codePoint = 0xDC00 | codePoint & 0x3FF
9787 }
9788
9789 res.push(codePoint)
9790 i += bytesPerSequence
9791 }
9792
9793 return decodeCodePointsArray(res)
9794}
9795
9796// Based on http://stackoverflow.com/a/22747272/680742, the browser with
9797// the lowest limit is Chrome, with 0x10000 args.
9798// We go 1 magnitude less, for safety
9799var MAX_ARGUMENTS_LENGTH = 0x1000
9800
9801function decodeCodePointsArray (codePoints) {
9802 var len = codePoints.length
9803 if (len <= MAX_ARGUMENTS_LENGTH) {
9804 return String.fromCharCode.apply(String, codePoints) // avoid extra slice()
9805 }
9806
9807 // Decode in chunks to avoid "call stack size exceeded".
9808 var res = ''
9809 var i = 0
9810 while (i < len) {
9811 res += String.fromCharCode.apply(
9812 String,
9813 codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH)
9814 )
9815 }
9816 return res
9817}
9818
9819function asciiSlice (buf, start, end) {
9820 var ret = ''
9821 end = Math.min(buf.length, end)
9822
9823 for (var i = start; i < end; ++i) {
9824 ret += String.fromCharCode(buf[i] & 0x7F)
9825 }
9826 return ret
9827}
9828
9829function latin1Slice (buf, start, end) {
9830 var ret = ''
9831 end = Math.min(buf.length, end)
9832
9833 for (var i = start; i < end; ++i) {
9834 ret += String.fromCharCode(buf[i])
9835 }
9836 return ret
9837}
9838
9839function hexSlice (buf, start, end) {
9840 var len = buf.length
9841
9842 if (!start || start < 0) start = 0
9843 if (!end || end < 0 || end > len) end = len
9844
9845 var out = ''
9846 for (var i = start; i < end; ++i) {
9847 out += toHex(buf[i])
9848 }
9849 return out
9850}
9851
9852function utf16leSlice (buf, start, end) {
9853 var bytes = buf.slice(start, end)
9854 var res = ''
9855 for (var i = 0; i < bytes.length; i += 2) {
9856 res += String.fromCharCode(bytes[i] + (bytes[i + 1] * 256))
9857 }
9858 return res
9859}
9860
9861Buffer.prototype.slice = function slice (start, end) {
9862 var len = this.length
9863 start = ~~start
9864 end = end === undefined ? len : ~~end
9865
9866 if (start < 0) {
9867 start += len
9868 if (start < 0) start = 0
9869 } else if (start > len) {
9870 start = len
9871 }
9872
9873 if (end < 0) {
9874 end += len
9875 if (end < 0) end = 0
9876 } else if (end > len) {
9877 end = len
9878 }
9879
9880 if (end < start) end = start
9881
9882 var newBuf = this.subarray(start, end)
9883 // Return an augmented `Uint8Array` instance
9884 newBuf.__proto__ = Buffer.prototype
9885 return newBuf
9886}
9887
9888/*
9889 * Need to make sure that buffer isn't trying to write out of bounds.
9890 */
9891function checkOffset (offset, ext, length) {
9892 if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint')
9893 if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length')
9894}
9895
9896Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) {
9897 offset = offset >>> 0
9898 byteLength = byteLength >>> 0
9899 if (!noAssert) checkOffset(offset, byteLength, this.length)
9900
9901 var val = this[offset]
9902 var mul = 1
9903 var i = 0
9904 while (++i < byteLength && (mul *= 0x100)) {
9905 val += this[offset + i] * mul
9906 }
9907
9908 return val
9909}
9910
9911Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) {
9912 offset = offset >>> 0
9913 byteLength = byteLength >>> 0
9914 if (!noAssert) {
9915 checkOffset(offset, byteLength, this.length)
9916 }
9917
9918 var val = this[offset + --byteLength]
9919 var mul = 1
9920 while (byteLength > 0 && (mul *= 0x100)) {
9921 val += this[offset + --byteLength] * mul
9922 }
9923
9924 return val
9925}
9926
9927Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) {
9928 offset = offset >>> 0
9929 if (!noAssert) checkOffset(offset, 1, this.length)
9930 return this[offset]
9931}
9932
9933Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) {
9934 offset = offset >>> 0
9935 if (!noAssert) checkOffset(offset, 2, this.length)
9936 return this[offset] | (this[offset + 1] << 8)
9937}
9938
9939Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) {
9940 offset = offset >>> 0
9941 if (!noAssert) checkOffset(offset, 2, this.length)
9942 return (this[offset] << 8) | this[offset + 1]
9943}
9944
9945Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) {
9946 offset = offset >>> 0
9947 if (!noAssert) checkOffset(offset, 4, this.length)
9948
9949 return ((this[offset]) |
9950 (this[offset + 1] << 8) |
9951 (this[offset + 2] << 16)) +
9952 (this[offset + 3] * 0x1000000)
9953}
9954
9955Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) {
9956 offset = offset >>> 0
9957 if (!noAssert) checkOffset(offset, 4, this.length)
9958
9959 return (this[offset] * 0x1000000) +
9960 ((this[offset + 1] << 16) |
9961 (this[offset + 2] << 8) |
9962 this[offset + 3])
9963}
9964
9965Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) {
9966 offset = offset >>> 0
9967 byteLength = byteLength >>> 0
9968 if (!noAssert) checkOffset(offset, byteLength, this.length)
9969
9970 var val = this[offset]
9971 var mul = 1
9972 var i = 0
9973 while (++i < byteLength && (mul *= 0x100)) {
9974 val += this[offset + i] * mul
9975 }
9976 mul *= 0x80
9977
9978 if (val >= mul) val -= Math.pow(2, 8 * byteLength)
9979
9980 return val
9981}
9982
9983Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) {
9984 offset = offset >>> 0
9985 byteLength = byteLength >>> 0
9986 if (!noAssert) checkOffset(offset, byteLength, this.length)
9987
9988 var i = byteLength
9989 var mul = 1
9990 var val = this[offset + --i]
9991 while (i > 0 && (mul *= 0x100)) {
9992 val += this[offset + --i] * mul
9993 }
9994 mul *= 0x80
9995
9996 if (val >= mul) val -= Math.pow(2, 8 * byteLength)
9997
9998 return val
9999}
10000
10001Buffer.prototype.readInt8 = function readInt8 (offset, noAssert) {
10002 offset = offset >>> 0
10003 if (!noAssert) checkOffset(offset, 1, this.length)
10004 if (!(this[offset] & 0x80)) return (this[offset])
10005 return ((0xff - this[offset] + 1) * -1)
10006}
10007
10008Buffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) {
10009 offset = offset >>> 0
10010 if (!noAssert) checkOffset(offset, 2, this.length)
10011 var val = this[offset] | (this[offset + 1] << 8)
10012 return (val & 0x8000) ? val | 0xFFFF0000 : val
10013}
10014
10015Buffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) {
10016 offset = offset >>> 0
10017 if (!noAssert) checkOffset(offset, 2, this.length)
10018 var val = this[offset + 1] | (this[offset] << 8)
10019 return (val & 0x8000) ? val | 0xFFFF0000 : val
10020}
10021
10022Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) {
10023 offset = offset >>> 0
10024 if (!noAssert) checkOffset(offset, 4, this.length)
10025
10026 return (this[offset]) |
10027 (this[offset + 1] << 8) |
10028 (this[offset + 2] << 16) |
10029 (this[offset + 3] << 24)
10030}
10031
10032Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) {
10033 offset = offset >>> 0
10034 if (!noAssert) checkOffset(offset, 4, this.length)
10035
10036 return (this[offset] << 24) |
10037 (this[offset + 1] << 16) |
10038 (this[offset + 2] << 8) |
10039 (this[offset + 3])
10040}
10041
10042Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) {
10043 offset = offset >>> 0
10044 if (!noAssert) checkOffset(offset, 4, this.length)
10045 return ieee754.read(this, offset, true, 23, 4)
10046}
10047
10048Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) {
10049 offset = offset >>> 0
10050 if (!noAssert) checkOffset(offset, 4, this.length)
10051 return ieee754.read(this, offset, false, 23, 4)
10052}
10053
10054Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) {
10055 offset = offset >>> 0
10056 if (!noAssert) checkOffset(offset, 8, this.length)
10057 return ieee754.read(this, offset, true, 52, 8)
10058}
10059
10060Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) {
10061 offset = offset >>> 0
10062 if (!noAssert) checkOffset(offset, 8, this.length)
10063 return ieee754.read(this, offset, false, 52, 8)
10064}
10065
10066function checkInt (buf, value, offset, ext, max, min) {
10067 if (!Buffer.isBuffer(buf)) throw new TypeError('"buffer" argument must be a Buffer instance')
10068 if (value > max || value < min) throw new RangeError('"value" argument is out of bounds')
10069 if (offset + ext > buf.length) throw new RangeError('Index out of range')
10070}
10071
10072Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) {
10073 value = +value
10074 offset = offset >>> 0
10075 byteLength = byteLength >>> 0
10076 if (!noAssert) {
10077 var maxBytes = Math.pow(2, 8 * byteLength) - 1
10078 checkInt(this, value, offset, byteLength, maxBytes, 0)
10079 }
10080
10081 var mul = 1
10082 var i = 0
10083 this[offset] = value & 0xFF
10084 while (++i < byteLength && (mul *= 0x100)) {
10085 this[offset + i] = (value / mul) & 0xFF
10086 }
10087
10088 return offset + byteLength
10089}
10090
10091Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) {
10092 value = +value
10093 offset = offset >>> 0
10094 byteLength = byteLength >>> 0
10095 if (!noAssert) {
10096 var maxBytes = Math.pow(2, 8 * byteLength) - 1
10097 checkInt(this, value, offset, byteLength, maxBytes, 0)
10098 }
10099
10100 var i = byteLength - 1
10101 var mul = 1
10102 this[offset + i] = value & 0xFF
10103 while (--i >= 0 && (mul *= 0x100)) {
10104 this[offset + i] = (value / mul) & 0xFF
10105 }
10106
10107 return offset + byteLength
10108}
10109
10110Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) {
10111 value = +value
10112 offset = offset >>> 0
10113 if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0)
10114 this[offset] = (value & 0xff)
10115 return offset + 1
10116}
10117
10118Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) {
10119 value = +value
10120 offset = offset >>> 0
10121 if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)
10122 this[offset] = (value & 0xff)
10123 this[offset + 1] = (value >>> 8)
10124 return offset + 2
10125}
10126
10127Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) {
10128 value = +value
10129 offset = offset >>> 0
10130 if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)
10131 this[offset] = (value >>> 8)
10132 this[offset + 1] = (value & 0xff)
10133 return offset + 2
10134}
10135
10136Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) {
10137 value = +value
10138 offset = offset >>> 0
10139 if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)
10140 this[offset + 3] = (value >>> 24)
10141 this[offset + 2] = (value >>> 16)
10142 this[offset + 1] = (value >>> 8)
10143 this[offset] = (value & 0xff)
10144 return offset + 4
10145}
10146
10147Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) {
10148 value = +value
10149 offset = offset >>> 0
10150 if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)
10151 this[offset] = (value >>> 24)
10152 this[offset + 1] = (value >>> 16)
10153 this[offset + 2] = (value >>> 8)
10154 this[offset + 3] = (value & 0xff)
10155 return offset + 4
10156}
10157
10158Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) {
10159 value = +value
10160 offset = offset >>> 0
10161 if (!noAssert) {
10162 var limit = Math.pow(2, (8 * byteLength) - 1)
10163
10164 checkInt(this, value, offset, byteLength, limit - 1, -limit)
10165 }
10166
10167 var i = 0
10168 var mul = 1
10169 var sub = 0
10170 this[offset] = value & 0xFF
10171 while (++i < byteLength && (mul *= 0x100)) {
10172 if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) {
10173 sub = 1
10174 }
10175 this[offset + i] = ((value / mul) >> 0) - sub & 0xFF
10176 }
10177
10178 return offset + byteLength
10179}
10180
10181Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) {
10182 value = +value
10183 offset = offset >>> 0
10184 if (!noAssert) {
10185 var limit = Math.pow(2, (8 * byteLength) - 1)
10186
10187 checkInt(this, value, offset, byteLength, limit - 1, -limit)
10188 }
10189
10190 var i = byteLength - 1
10191 var mul = 1
10192 var sub = 0
10193 this[offset + i] = value & 0xFF
10194 while (--i >= 0 && (mul *= 0x100)) {
10195 if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) {
10196 sub = 1
10197 }
10198 this[offset + i] = ((value / mul) >> 0) - sub & 0xFF
10199 }
10200
10201 return offset + byteLength
10202}
10203
10204Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) {
10205 value = +value
10206 offset = offset >>> 0
10207 if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80)
10208 if (value < 0) value = 0xff + value + 1
10209 this[offset] = (value & 0xff)
10210 return offset + 1
10211}
10212
10213Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) {
10214 value = +value
10215 offset = offset >>> 0
10216 if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)
10217 this[offset] = (value & 0xff)
10218 this[offset + 1] = (value >>> 8)
10219 return offset + 2
10220}
10221
10222Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) {
10223 value = +value
10224 offset = offset >>> 0
10225 if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)
10226 this[offset] = (value >>> 8)
10227 this[offset + 1] = (value & 0xff)
10228 return offset + 2
10229}
10230
10231Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) {
10232 value = +value
10233 offset = offset >>> 0
10234 if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)
10235 this[offset] = (value & 0xff)
10236 this[offset + 1] = (value >>> 8)
10237 this[offset + 2] = (value >>> 16)
10238 this[offset + 3] = (value >>> 24)
10239 return offset + 4
10240}
10241
10242Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) {
10243 value = +value
10244 offset = offset >>> 0
10245 if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)
10246 if (value < 0) value = 0xffffffff + value + 1
10247 this[offset] = (value >>> 24)
10248 this[offset + 1] = (value >>> 16)
10249 this[offset + 2] = (value >>> 8)
10250 this[offset + 3] = (value & 0xff)
10251 return offset + 4
10252}
10253
10254function checkIEEE754 (buf, value, offset, ext, max, min) {
10255 if (offset + ext > buf.length) throw new RangeError('Index out of range')
10256 if (offset < 0) throw new RangeError('Index out of range')
10257}
10258
10259function writeFloat (buf, value, offset, littleEndian, noAssert) {
10260 value = +value
10261 offset = offset >>> 0
10262 if (!noAssert) {
10263 checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38)
10264 }
10265 ieee754.write(buf, value, offset, littleEndian, 23, 4)
10266 return offset + 4
10267}
10268
10269Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) {
10270 return writeFloat(this, value, offset, true, noAssert)
10271}
10272
10273Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) {
10274 return writeFloat(this, value, offset, false, noAssert)
10275}
10276
10277function writeDouble (buf, value, offset, littleEndian, noAssert) {
10278 value = +value
10279 offset = offset >>> 0
10280 if (!noAssert) {
10281 checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308)
10282 }
10283 ieee754.write(buf, value, offset, littleEndian, 52, 8)
10284 return offset + 8
10285}
10286
10287Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) {
10288 return writeDouble(this, value, offset, true, noAssert)
10289}
10290
10291Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) {
10292 return writeDouble(this, value, offset, false, noAssert)
10293}
10294
10295// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length)
10296Buffer.prototype.copy = function copy (target, targetStart, start, end) {
10297 if (!Buffer.isBuffer(target)) throw new TypeError('argument should be a Buffer')
10298 if (!start) start = 0
10299 if (!end && end !== 0) end = this.length
10300 if (targetStart >= target.length) targetStart = target.length
10301 if (!targetStart) targetStart = 0
10302 if (end > 0 && end < start) end = start
10303
10304 // Copy 0 bytes; we're done
10305 if (end === start) return 0
10306 if (target.length === 0 || this.length === 0) return 0
10307
10308 // Fatal error conditions
10309 if (targetStart < 0) {
10310 throw new RangeError('targetStart out of bounds')
10311 }
10312 if (start < 0 || start >= this.length) throw new RangeError('Index out of range')
10313 if (end < 0) throw new RangeError('sourceEnd out of bounds')
10314
10315 // Are we oob?
10316 if (end > this.length) end = this.length
10317 if (target.length - targetStart < end - start) {
10318 end = target.length - targetStart + start
10319 }
10320
10321 var len = end - start
10322
10323 if (this === target && typeof Uint8Array.prototype.copyWithin === 'function') {
10324 // Use built-in when available, missing from IE11
10325 this.copyWithin(targetStart, start, end)
10326 } else if (this === target && start < targetStart && targetStart < end) {
10327 // descending copy from end
10328 for (var i = len - 1; i >= 0; --i) {
10329 target[i + targetStart] = this[i + start]
10330 }
10331 } else {
10332 Uint8Array.prototype.set.call(
10333 target,
10334 this.subarray(start, end),
10335 targetStart
10336 )
10337 }
10338
10339 return len
10340}
10341
10342// Usage:
10343// buffer.fill(number[, offset[, end]])
10344// buffer.fill(buffer[, offset[, end]])
10345// buffer.fill(string[, offset[, end]][, encoding])
10346Buffer.prototype.fill = function fill (val, start, end, encoding) {
10347 // Handle string cases:
10348 if (typeof val === 'string') {
10349 if (typeof start === 'string') {
10350 encoding = start
10351 start = 0
10352 end = this.length
10353 } else if (typeof end === 'string') {
10354 encoding = end
10355 end = this.length
10356 }
10357 if (encoding !== undefined && typeof encoding !== 'string') {
10358 throw new TypeError('encoding must be a string')
10359 }
10360 if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) {
10361 throw new TypeError('Unknown encoding: ' + encoding)
10362 }
10363 if (val.length === 1) {
10364 var code = val.charCodeAt(0)
10365 if ((encoding === 'utf8' && code < 128) ||
10366 encoding === 'latin1') {
10367 // Fast path: If `val` fits into a single byte, use that numeric value.
10368 val = code
10369 }
10370 }
10371 } else if (typeof val === 'number') {
10372 val = val & 255
10373 }
10374
10375 // Invalid ranges are not set to a default, so can range check early.
10376 if (start < 0 || this.length < start || this.length < end) {
10377 throw new RangeError('Out of range index')
10378 }
10379
10380 if (end <= start) {
10381 return this
10382 }
10383
10384 start = start >>> 0
10385 end = end === undefined ? this.length : end >>> 0
10386
10387 if (!val) val = 0
10388
10389 var i
10390 if (typeof val === 'number') {
10391 for (i = start; i < end; ++i) {
10392 this[i] = val
10393 }
10394 } else {
10395 var bytes = Buffer.isBuffer(val)
10396 ? val
10397 : Buffer.from(val, encoding)
10398 var len = bytes.length
10399 if (len === 0) {
10400 throw new TypeError('The value "' + val +
10401 '" is invalid for argument "value"')
10402 }
10403 for (i = 0; i < end - start; ++i) {
10404 this[i + start] = bytes[i % len]
10405 }
10406 }
10407
10408 return this
10409}
10410
10411// HELPER FUNCTIONS
10412// ================
10413
10414var INVALID_BASE64_RE = /[^+/0-9A-Za-z-_]/g
10415
10416function base64clean (str) {
10417 // Node takes equal signs as end of the Base64 encoding
10418 str = str.split('=')[0]
10419 // Node strips out invalid characters like \n and \t from the string, base64-js does not
10420 str = str.trim().replace(INVALID_BASE64_RE, '')
10421 // Node converts strings with length < 2 to ''
10422 if (str.length < 2) return ''
10423 // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not
10424 while (str.length % 4 !== 0) {
10425 str = str + '='
10426 }
10427 return str
10428}
10429
10430function toHex (n) {
10431 if (n < 16) return '0' + n.toString(16)
10432 return n.toString(16)
10433}
10434
10435function utf8ToBytes (string, units) {
10436 units = units || Infinity
10437 var codePoint
10438 var length = string.length
10439 var leadSurrogate = null
10440 var bytes = []
10441
10442 for (var i = 0; i < length; ++i) {
10443 codePoint = string.charCodeAt(i)
10444
10445 // is surrogate component
10446 if (codePoint > 0xD7FF && codePoint < 0xE000) {
10447 // last char was a lead
10448 if (!leadSurrogate) {
10449 // no lead yet
10450 if (codePoint > 0xDBFF) {
10451 // unexpected trail
10452 if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
10453 continue
10454 } else if (i + 1 === length) {
10455 // unpaired lead
10456 if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
10457 continue
10458 }
10459
10460 // valid lead
10461 leadSurrogate = codePoint
10462
10463 continue
10464 }
10465
10466 // 2 leads in a row
10467 if (codePoint < 0xDC00) {
10468 if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
10469 leadSurrogate = codePoint
10470 continue
10471 }
10472
10473 // valid surrogate pair
10474 codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000
10475 } else if (leadSurrogate) {
10476 // valid bmp char, but last char was a lead
10477 if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
10478 }
10479
10480 leadSurrogate = null
10481
10482 // encode utf8
10483 if (codePoint < 0x80) {
10484 if ((units -= 1) < 0) break
10485 bytes.push(codePoint)
10486 } else if (codePoint < 0x800) {
10487 if ((units -= 2) < 0) break
10488 bytes.push(
10489 codePoint >> 0x6 | 0xC0,
10490 codePoint & 0x3F | 0x80
10491 )
10492 } else if (codePoint < 0x10000) {
10493 if ((units -= 3) < 0) break
10494 bytes.push(
10495 codePoint >> 0xC | 0xE0,
10496 codePoint >> 0x6 & 0x3F | 0x80,
10497 codePoint & 0x3F | 0x80
10498 )
10499 } else if (codePoint < 0x110000) {
10500 if ((units -= 4) < 0) break
10501 bytes.push(
10502 codePoint >> 0x12 | 0xF0,
10503 codePoint >> 0xC & 0x3F | 0x80,
10504 codePoint >> 0x6 & 0x3F | 0x80,
10505 codePoint & 0x3F | 0x80
10506 )
10507 } else {
10508 throw new Error('Invalid code point')
10509 }
10510 }
10511
10512 return bytes
10513}
10514
10515function asciiToBytes (str) {
10516 var byteArray = []
10517 for (var i = 0; i < str.length; ++i) {
10518 // Node's code seems to be doing this and not & 0x7F..
10519 byteArray.push(str.charCodeAt(i) & 0xFF)
10520 }
10521 return byteArray
10522}
10523
10524function utf16leToBytes (str, units) {
10525 var c, hi, lo
10526 var byteArray = []
10527 for (var i = 0; i < str.length; ++i) {
10528 if ((units -= 2) < 0) break
10529
10530 c = str.charCodeAt(i)
10531 hi = c >> 8
10532 lo = c % 256
10533 byteArray.push(lo)
10534 byteArray.push(hi)
10535 }
10536
10537 return byteArray
10538}
10539
10540function base64ToBytes (str) {
10541 return base64.toByteArray(base64clean(str))
10542}
10543
10544function blitBuffer (src, dst, offset, length) {
10545 for (var i = 0; i < length; ++i) {
10546 if ((i + offset >= dst.length) || (i >= src.length)) break
10547 dst[i + offset] = src[i]
10548 }
10549 return i
10550}
10551
10552// ArrayBuffer or Uint8Array objects from other contexts (i.e. iframes) do not pass
10553// the `instanceof` check but they should be treated as of that type.
10554// See: https://github.com/feross/buffer/issues/166
10555function isInstance (obj, type) {
10556 return obj instanceof type ||
10557 (obj != null && obj.constructor != null && obj.constructor.name != null &&
10558 obj.constructor.name === type.name)
10559}
10560function numberIsNaN (obj) {
10561 // For IE11 support
10562 return obj !== obj // eslint-disable-line no-self-compare
10563}
10564
10565}).call(this)}).call(this,_dereq_("buffer").Buffer)
10566},{"base64-js":25,"buffer":28,"ieee754":66}],29:[function(_dereq_,module,exports){
10567// https://d3js.org/d3-format/ v1.4.5 Copyright 2020 Mike Bostock
10568(function (global, factory) {
10569typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
10570typeof define === 'function' && define.amd ? define(['exports'], factory) :
10571(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.d3 = global.d3 || {}));
10572}(this, (function (exports) { 'use strict';
10573
10574function formatDecimal(x) {
10575 return Math.abs(x = Math.round(x)) >= 1e21
10576 ? x.toLocaleString("en").replace(/,/g, "")
10577 : x.toString(10);
10578}
10579
10580// Computes the decimal coefficient and exponent of the specified number x with
10581// significant digits p, where x is positive and p is in [1, 21] or undefined.
10582// For example, formatDecimalParts(1.23) returns ["123", 0].
10583function formatDecimalParts(x, p) {
10584 if ((i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf("e")) < 0) return null; // NaN, ±Infinity
10585 var i, coefficient = x.slice(0, i);
10586
10587 // The string returned by toExponential either has the form \d\.\d+e[-+]\d+
10588 // (e.g., 1.2e+3) or the form \de[-+]\d+ (e.g., 1e+3).
10589 return [
10590 coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient,
10591 +x.slice(i + 1)
10592 ];
10593}
10594
10595function exponent(x) {
10596 return x = formatDecimalParts(Math.abs(x)), x ? x[1] : NaN;
10597}
10598
10599function formatGroup(grouping, thousands) {
10600 return function(value, width) {
10601 var i = value.length,
10602 t = [],
10603 j = 0,
10604 g = grouping[0],
10605 length = 0;
10606
10607 while (i > 0 && g > 0) {
10608 if (length + g + 1 > width) g = Math.max(1, width - length);
10609 t.push(value.substring(i -= g, i + g));
10610 if ((length += g + 1) > width) break;
10611 g = grouping[j = (j + 1) % grouping.length];
10612 }
10613
10614 return t.reverse().join(thousands);
10615 };
10616}
10617
10618function formatNumerals(numerals) {
10619 return function(value) {
10620 return value.replace(/[0-9]/g, function(i) {
10621 return numerals[+i];
10622 });
10623 };
10624}
10625
10626// [[fill]align][sign][symbol][0][width][,][.precision][~][type]
10627var re = /^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;
10628
10629function formatSpecifier(specifier) {
10630 if (!(match = re.exec(specifier))) throw new Error("invalid format: " + specifier);
10631 var match;
10632 return new FormatSpecifier({
10633 fill: match[1],
10634 align: match[2],
10635 sign: match[3],
10636 symbol: match[4],
10637 zero: match[5],
10638 width: match[6],
10639 comma: match[7],
10640 precision: match[8] && match[8].slice(1),
10641 trim: match[9],
10642 type: match[10]
10643 });
10644}
10645
10646formatSpecifier.prototype = FormatSpecifier.prototype; // instanceof
10647
10648function FormatSpecifier(specifier) {
10649 this.fill = specifier.fill === undefined ? " " : specifier.fill + "";
10650 this.align = specifier.align === undefined ? ">" : specifier.align + "";
10651 this.sign = specifier.sign === undefined ? "-" : specifier.sign + "";
10652 this.symbol = specifier.symbol === undefined ? "" : specifier.symbol + "";
10653 this.zero = !!specifier.zero;
10654 this.width = specifier.width === undefined ? undefined : +specifier.width;
10655 this.comma = !!specifier.comma;
10656 this.precision = specifier.precision === undefined ? undefined : +specifier.precision;
10657 this.trim = !!specifier.trim;
10658 this.type = specifier.type === undefined ? "" : specifier.type + "";
10659}
10660
10661FormatSpecifier.prototype.toString = function() {
10662 return this.fill
10663 + this.align
10664 + this.sign
10665 + this.symbol
10666 + (this.zero ? "0" : "")
10667 + (this.width === undefined ? "" : Math.max(1, this.width | 0))
10668 + (this.comma ? "," : "")
10669 + (this.precision === undefined ? "" : "." + Math.max(0, this.precision | 0))
10670 + (this.trim ? "~" : "")
10671 + this.type;
10672};
10673
10674// Trims insignificant zeros, e.g., replaces 1.2000k with 1.2k.
10675function formatTrim(s) {
10676 out: for (var n = s.length, i = 1, i0 = -1, i1; i < n; ++i) {
10677 switch (s[i]) {
10678 case ".": i0 = i1 = i; break;
10679 case "0": if (i0 === 0) i0 = i; i1 = i; break;
10680 default: if (!+s[i]) break out; if (i0 > 0) i0 = 0; break;
10681 }
10682 }
10683 return i0 > 0 ? s.slice(0, i0) + s.slice(i1 + 1) : s;
10684}
10685
10686var prefixExponent;
10687
10688function formatPrefixAuto(x, p) {
10689 var d = formatDecimalParts(x, p);
10690 if (!d) return x + "";
10691 var coefficient = d[0],
10692 exponent = d[1],
10693 i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1,
10694 n = coefficient.length;
10695 return i === n ? coefficient
10696 : i > n ? coefficient + new Array(i - n + 1).join("0")
10697 : i > 0 ? coefficient.slice(0, i) + "." + coefficient.slice(i)
10698 : "0." + new Array(1 - i).join("0") + formatDecimalParts(x, Math.max(0, p + i - 1))[0]; // less than 1y!
10699}
10700
10701function formatRounded(x, p) {
10702 var d = formatDecimalParts(x, p);
10703 if (!d) return x + "";
10704 var coefficient = d[0],
10705 exponent = d[1];
10706 return exponent < 0 ? "0." + new Array(-exponent).join("0") + coefficient
10707 : coefficient.length > exponent + 1 ? coefficient.slice(0, exponent + 1) + "." + coefficient.slice(exponent + 1)
10708 : coefficient + new Array(exponent - coefficient.length + 2).join("0");
10709}
10710
10711var formatTypes = {
10712 "%": function(x, p) { return (x * 100).toFixed(p); },
10713 "b": function(x) { return Math.round(x).toString(2); },
10714 "c": function(x) { return x + ""; },
10715 "d": formatDecimal,
10716 "e": function(x, p) { return x.toExponential(p); },
10717 "f": function(x, p) { return x.toFixed(p); },
10718 "g": function(x, p) { return x.toPrecision(p); },
10719 "o": function(x) { return Math.round(x).toString(8); },
10720 "p": function(x, p) { return formatRounded(x * 100, p); },
10721 "r": formatRounded,
10722 "s": formatPrefixAuto,
10723 "X": function(x) { return Math.round(x).toString(16).toUpperCase(); },
10724 "x": function(x) { return Math.round(x).toString(16); }
10725};
10726
10727function identity(x) {
10728 return x;
10729}
10730
10731var map = Array.prototype.map,
10732 prefixes = ["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"];
10733
10734function formatLocale(locale) {
10735 var group = locale.grouping === undefined || locale.thousands === undefined ? identity : formatGroup(map.call(locale.grouping, Number), locale.thousands + ""),
10736 currencyPrefix = locale.currency === undefined ? "" : locale.currency[0] + "",
10737 currencySuffix = locale.currency === undefined ? "" : locale.currency[1] + "",
10738 decimal = locale.decimal === undefined ? "." : locale.decimal + "",
10739 numerals = locale.numerals === undefined ? identity : formatNumerals(map.call(locale.numerals, String)),
10740 percent = locale.percent === undefined ? "%" : locale.percent + "",
10741 minus = locale.minus === undefined ? "-" : locale.minus + "",
10742 nan = locale.nan === undefined ? "NaN" : locale.nan + "";
10743
10744 function newFormat(specifier) {
10745 specifier = formatSpecifier(specifier);
10746
10747 var fill = specifier.fill,
10748 align = specifier.align,
10749 sign = specifier.sign,
10750 symbol = specifier.symbol,
10751 zero = specifier.zero,
10752 width = specifier.width,
10753 comma = specifier.comma,
10754 precision = specifier.precision,
10755 trim = specifier.trim,
10756 type = specifier.type;
10757
10758 // The "n" type is an alias for ",g".
10759 if (type === "n") comma = true, type = "g";
10760
10761 // The "" type, and any invalid type, is an alias for ".12~g".
10762 else if (!formatTypes[type]) precision === undefined && (precision = 12), trim = true, type = "g";
10763
10764 // If zero fill is specified, padding goes after sign and before digits.
10765 if (zero || (fill === "0" && align === "=")) zero = true, fill = "0", align = "=";
10766
10767 // Compute the prefix and suffix.
10768 // For SI-prefix, the suffix is lazily computed.
10769 var prefix = symbol === "$" ? currencyPrefix : symbol === "#" && /[boxX]/.test(type) ? "0" + type.toLowerCase() : "",
10770 suffix = symbol === "$" ? currencySuffix : /[%p]/.test(type) ? percent : "";
10771
10772 // What format function should we use?
10773 // Is this an integer type?
10774 // Can this type generate exponential notation?
10775 var formatType = formatTypes[type],
10776 maybeSuffix = /[defgprs%]/.test(type);
10777
10778 // Set the default precision if not specified,
10779 // or clamp the specified precision to the supported range.
10780 // For significant precision, it must be in [1, 21].
10781 // For fixed precision, it must be in [0, 20].
10782 precision = precision === undefined ? 6
10783 : /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision))
10784 : Math.max(0, Math.min(20, precision));
10785
10786 function format(value) {
10787 var valuePrefix = prefix,
10788 valueSuffix = suffix,
10789 i, n, c;
10790
10791 if (type === "c") {
10792 valueSuffix = formatType(value) + valueSuffix;
10793 value = "";
10794 } else {
10795 value = +value;
10796
10797 // Determine the sign. -0 is not less than 0, but 1 / -0 is!
10798 var valueNegative = value < 0 || 1 / value < 0;
10799
10800 // Perform the initial formatting.
10801 value = isNaN(value) ? nan : formatType(Math.abs(value), precision);
10802
10803 // Trim insignificant zeros.
10804 if (trim) value = formatTrim(value);
10805
10806 // If a negative value rounds to zero after formatting, and no explicit positive sign is requested, hide the sign.
10807 if (valueNegative && +value === 0 && sign !== "+") valueNegative = false;
10808
10809 // Compute the prefix and suffix.
10810 valuePrefix = (valueNegative ? (sign === "(" ? sign : minus) : sign === "-" || sign === "(" ? "" : sign) + valuePrefix;
10811 valueSuffix = (type === "s" ? prefixes[8 + prefixExponent / 3] : "") + valueSuffix + (valueNegative && sign === "(" ? ")" : "");
10812
10813 // Break the formatted value into the integer “value” part that can be
10814 // grouped, and fractional or exponential “suffix” part that is not.
10815 if (maybeSuffix) {
10816 i = -1, n = value.length;
10817 while (++i < n) {
10818 if (c = value.charCodeAt(i), 48 > c || c > 57) {
10819 valueSuffix = (c === 46 ? decimal + value.slice(i + 1) : value.slice(i)) + valueSuffix;
10820 value = value.slice(0, i);
10821 break;
10822 }
10823 }
10824 }
10825 }
10826
10827 // If the fill character is not "0", grouping is applied before padding.
10828 if (comma && !zero) value = group(value, Infinity);
10829
10830 // Compute the padding.
10831 var length = valuePrefix.length + value.length + valueSuffix.length,
10832 padding = length < width ? new Array(width - length + 1).join(fill) : "";
10833
10834 // If the fill character is "0", grouping is applied after padding.
10835 if (comma && zero) value = group(padding + value, padding.length ? width - valueSuffix.length : Infinity), padding = "";
10836
10837 // Reconstruct the final output based on the desired alignment.
10838 switch (align) {
10839 case "<": value = valuePrefix + value + valueSuffix + padding; break;
10840 case "=": value = valuePrefix + padding + value + valueSuffix; break;
10841 case "^": value = padding.slice(0, length = padding.length >> 1) + valuePrefix + value + valueSuffix + padding.slice(length); break;
10842 default: value = padding + valuePrefix + value + valueSuffix; break;
10843 }
10844
10845 return numerals(value);
10846 }
10847
10848 format.toString = function() {
10849 return specifier + "";
10850 };
10851
10852 return format;
10853 }
10854
10855 function formatPrefix(specifier, value) {
10856 var f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier)),
10857 e = Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3,
10858 k = Math.pow(10, -e),
10859 prefix = prefixes[8 + e / 3];
10860 return function(value) {
10861 return f(k * value) + prefix;
10862 };
10863 }
10864
10865 return {
10866 format: newFormat,
10867 formatPrefix: formatPrefix
10868 };
10869}
10870
10871var locale;
10872
10873defaultLocale({
10874 decimal: ".",
10875 thousands: ",",
10876 grouping: [3],
10877 currency: ["$", ""],
10878 minus: "-"
10879});
10880
10881function defaultLocale(definition) {
10882 locale = formatLocale(definition);
10883 exports.format = locale.format;
10884 exports.formatPrefix = locale.formatPrefix;
10885 return locale;
10886}
10887
10888function precisionFixed(step) {
10889 return Math.max(0, -exponent(Math.abs(step)));
10890}
10891
10892function precisionPrefix(step, value) {
10893 return Math.max(0, Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3 - exponent(Math.abs(step)));
10894}
10895
10896function precisionRound(step, max) {
10897 step = Math.abs(step), max = Math.abs(max) - step;
10898 return Math.max(0, exponent(max) - exponent(step)) + 1;
10899}
10900
10901exports.FormatSpecifier = FormatSpecifier;
10902exports.formatDefaultLocale = defaultLocale;
10903exports.formatLocale = formatLocale;
10904exports.formatSpecifier = formatSpecifier;
10905exports.precisionFixed = precisionFixed;
10906exports.precisionPrefix = precisionPrefix;
10907exports.precisionRound = precisionRound;
10908
10909Object.defineProperty(exports, '__esModule', { value: true });
10910
10911})));
10912
10913},{}],30:[function(_dereq_,module,exports){
10914// https://d3js.org/d3-time-format/ v2.2.3 Copyright 2019 Mike Bostock
10915(function (global, factory) {
10916typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, _dereq_('d3-time')) :
10917typeof define === 'function' && define.amd ? define(['exports', 'd3-time'], factory) :
10918(global = global || self, factory(global.d3 = global.d3 || {}, global.d3));
10919}(this, function (exports, d3Time) { 'use strict';
10920
10921function localDate(d) {
10922 if (0 <= d.y && d.y < 100) {
10923 var date = new Date(-1, d.m, d.d, d.H, d.M, d.S, d.L);
10924 date.setFullYear(d.y);
10925 return date;
10926 }
10927 return new Date(d.y, d.m, d.d, d.H, d.M, d.S, d.L);
10928}
10929
10930function utcDate(d) {
10931 if (0 <= d.y && d.y < 100) {
10932 var date = new Date(Date.UTC(-1, d.m, d.d, d.H, d.M, d.S, d.L));
10933 date.setUTCFullYear(d.y);
10934 return date;
10935 }
10936 return new Date(Date.UTC(d.y, d.m, d.d, d.H, d.M, d.S, d.L));
10937}
10938
10939function newDate(y, m, d) {
10940 return {y: y, m: m, d: d, H: 0, M: 0, S: 0, L: 0};
10941}
10942
10943function formatLocale(locale) {
10944 var locale_dateTime = locale.dateTime,
10945 locale_date = locale.date,
10946 locale_time = locale.time,
10947 locale_periods = locale.periods,
10948 locale_weekdays = locale.days,
10949 locale_shortWeekdays = locale.shortDays,
10950 locale_months = locale.months,
10951 locale_shortMonths = locale.shortMonths;
10952
10953 var periodRe = formatRe(locale_periods),
10954 periodLookup = formatLookup(locale_periods),
10955 weekdayRe = formatRe(locale_weekdays),
10956 weekdayLookup = formatLookup(locale_weekdays),
10957 shortWeekdayRe = formatRe(locale_shortWeekdays),
10958 shortWeekdayLookup = formatLookup(locale_shortWeekdays),
10959 monthRe = formatRe(locale_months),
10960 monthLookup = formatLookup(locale_months),
10961 shortMonthRe = formatRe(locale_shortMonths),
10962 shortMonthLookup = formatLookup(locale_shortMonths);
10963
10964 var formats = {
10965 "a": formatShortWeekday,
10966 "A": formatWeekday,
10967 "b": formatShortMonth,
10968 "B": formatMonth,
10969 "c": null,
10970 "d": formatDayOfMonth,
10971 "e": formatDayOfMonth,
10972 "f": formatMicroseconds,
10973 "H": formatHour24,
10974 "I": formatHour12,
10975 "j": formatDayOfYear,
10976 "L": formatMilliseconds,
10977 "m": formatMonthNumber,
10978 "M": formatMinutes,
10979 "p": formatPeriod,
10980 "q": formatQuarter,
10981 "Q": formatUnixTimestamp,
10982 "s": formatUnixTimestampSeconds,
10983 "S": formatSeconds,
10984 "u": formatWeekdayNumberMonday,
10985 "U": formatWeekNumberSunday,
10986 "V": formatWeekNumberISO,
10987 "w": formatWeekdayNumberSunday,
10988 "W": formatWeekNumberMonday,
10989 "x": null,
10990 "X": null,
10991 "y": formatYear,
10992 "Y": formatFullYear,
10993 "Z": formatZone,
10994 "%": formatLiteralPercent
10995 };
10996
10997 var utcFormats = {
10998 "a": formatUTCShortWeekday,
10999 "A": formatUTCWeekday,
11000 "b": formatUTCShortMonth,
11001 "B": formatUTCMonth,
11002 "c": null,
11003 "d": formatUTCDayOfMonth,
11004 "e": formatUTCDayOfMonth,
11005 "f": formatUTCMicroseconds,
11006 "H": formatUTCHour24,
11007 "I": formatUTCHour12,
11008 "j": formatUTCDayOfYear,
11009 "L": formatUTCMilliseconds,
11010 "m": formatUTCMonthNumber,
11011 "M": formatUTCMinutes,
11012 "p": formatUTCPeriod,
11013 "q": formatUTCQuarter,
11014 "Q": formatUnixTimestamp,
11015 "s": formatUnixTimestampSeconds,
11016 "S": formatUTCSeconds,
11017 "u": formatUTCWeekdayNumberMonday,
11018 "U": formatUTCWeekNumberSunday,
11019 "V": formatUTCWeekNumberISO,
11020 "w": formatUTCWeekdayNumberSunday,
11021 "W": formatUTCWeekNumberMonday,
11022 "x": null,
11023 "X": null,
11024 "y": formatUTCYear,
11025 "Y": formatUTCFullYear,
11026 "Z": formatUTCZone,
11027 "%": formatLiteralPercent
11028 };
11029
11030 var parses = {
11031 "a": parseShortWeekday,
11032 "A": parseWeekday,
11033 "b": parseShortMonth,
11034 "B": parseMonth,
11035 "c": parseLocaleDateTime,
11036 "d": parseDayOfMonth,
11037 "e": parseDayOfMonth,
11038 "f": parseMicroseconds,
11039 "H": parseHour24,
11040 "I": parseHour24,
11041 "j": parseDayOfYear,
11042 "L": parseMilliseconds,
11043 "m": parseMonthNumber,
11044 "M": parseMinutes,
11045 "p": parsePeriod,
11046 "q": parseQuarter,
11047 "Q": parseUnixTimestamp,
11048 "s": parseUnixTimestampSeconds,
11049 "S": parseSeconds,
11050 "u": parseWeekdayNumberMonday,
11051 "U": parseWeekNumberSunday,
11052 "V": parseWeekNumberISO,
11053 "w": parseWeekdayNumberSunday,
11054 "W": parseWeekNumberMonday,
11055 "x": parseLocaleDate,
11056 "X": parseLocaleTime,
11057 "y": parseYear,
11058 "Y": parseFullYear,
11059 "Z": parseZone,
11060 "%": parseLiteralPercent
11061 };
11062
11063 // These recursive directive definitions must be deferred.
11064 formats.x = newFormat(locale_date, formats);
11065 formats.X = newFormat(locale_time, formats);
11066 formats.c = newFormat(locale_dateTime, formats);
11067 utcFormats.x = newFormat(locale_date, utcFormats);
11068 utcFormats.X = newFormat(locale_time, utcFormats);
11069 utcFormats.c = newFormat(locale_dateTime, utcFormats);
11070
11071 function newFormat(specifier, formats) {
11072 return function(date) {
11073 var string = [],
11074 i = -1,
11075 j = 0,
11076 n = specifier.length,
11077 c,
11078 pad,
11079 format;
11080
11081 if (!(date instanceof Date)) date = new Date(+date);
11082
11083 while (++i < n) {
11084 if (specifier.charCodeAt(i) === 37) {
11085 string.push(specifier.slice(j, i));
11086 if ((pad = pads[c = specifier.charAt(++i)]) != null) c = specifier.charAt(++i);
11087 else pad = c === "e" ? " " : "0";
11088 if (format = formats[c]) c = format(date, pad);
11089 string.push(c);
11090 j = i + 1;
11091 }
11092 }
11093
11094 string.push(specifier.slice(j, i));
11095 return string.join("");
11096 };
11097 }
11098
11099 function newParse(specifier, Z) {
11100 return function(string) {
11101 var d = newDate(1900, undefined, 1),
11102 i = parseSpecifier(d, specifier, string += "", 0),
11103 week, day;
11104 if (i != string.length) return null;
11105
11106 // If a UNIX timestamp is specified, return it.
11107 if ("Q" in d) return new Date(d.Q);
11108 if ("s" in d) return new Date(d.s * 1000 + ("L" in d ? d.L : 0));
11109
11110 // If this is utcParse, never use the local timezone.
11111 if (Z && !("Z" in d)) d.Z = 0;
11112
11113 // The am-pm flag is 0 for AM, and 1 for PM.
11114 if ("p" in d) d.H = d.H % 12 + d.p * 12;
11115
11116 // If the month was not specified, inherit from the quarter.
11117 if (d.m === undefined) d.m = "q" in d ? d.q : 0;
11118
11119 // Convert day-of-week and week-of-year to day-of-year.
11120 if ("V" in d) {
11121 if (d.V < 1 || d.V > 53) return null;
11122 if (!("w" in d)) d.w = 1;
11123 if ("Z" in d) {
11124 week = utcDate(newDate(d.y, 0, 1)), day = week.getUTCDay();
11125 week = day > 4 || day === 0 ? d3Time.utcMonday.ceil(week) : d3Time.utcMonday(week);
11126 week = d3Time.utcDay.offset(week, (d.V - 1) * 7);
11127 d.y = week.getUTCFullYear();
11128 d.m = week.getUTCMonth();
11129 d.d = week.getUTCDate() + (d.w + 6) % 7;
11130 } else {
11131 week = localDate(newDate(d.y, 0, 1)), day = week.getDay();
11132 week = day > 4 || day === 0 ? d3Time.timeMonday.ceil(week) : d3Time.timeMonday(week);
11133 week = d3Time.timeDay.offset(week, (d.V - 1) * 7);
11134 d.y = week.getFullYear();
11135 d.m = week.getMonth();
11136 d.d = week.getDate() + (d.w + 6) % 7;
11137 }
11138 } else if ("W" in d || "U" in d) {
11139 if (!("w" in d)) d.w = "u" in d ? d.u % 7 : "W" in d ? 1 : 0;
11140 day = "Z" in d ? utcDate(newDate(d.y, 0, 1)).getUTCDay() : localDate(newDate(d.y, 0, 1)).getDay();
11141 d.m = 0;
11142 d.d = "W" in d ? (d.w + 6) % 7 + d.W * 7 - (day + 5) % 7 : d.w + d.U * 7 - (day + 6) % 7;
11143 }
11144
11145 // If a time zone is specified, all fields are interpreted as UTC and then
11146 // offset according to the specified time zone.
11147 if ("Z" in d) {
11148 d.H += d.Z / 100 | 0;
11149 d.M += d.Z % 100;
11150 return utcDate(d);
11151 }
11152
11153 // Otherwise, all fields are in local time.
11154 return localDate(d);
11155 };
11156 }
11157
11158 function parseSpecifier(d, specifier, string, j) {
11159 var i = 0,
11160 n = specifier.length,
11161 m = string.length,
11162 c,
11163 parse;
11164
11165 while (i < n) {
11166 if (j >= m) return -1;
11167 c = specifier.charCodeAt(i++);
11168 if (c === 37) {
11169 c = specifier.charAt(i++);
11170 parse = parses[c in pads ? specifier.charAt(i++) : c];
11171 if (!parse || ((j = parse(d, string, j)) < 0)) return -1;
11172 } else if (c != string.charCodeAt(j++)) {
11173 return -1;
11174 }
11175 }
11176
11177 return j;
11178 }
11179
11180 function parsePeriod(d, string, i) {
11181 var n = periodRe.exec(string.slice(i));
11182 return n ? (d.p = periodLookup[n[0].toLowerCase()], i + n[0].length) : -1;
11183 }
11184
11185 function parseShortWeekday(d, string, i) {
11186 var n = shortWeekdayRe.exec(string.slice(i));
11187 return n ? (d.w = shortWeekdayLookup[n[0].toLowerCase()], i + n[0].length) : -1;
11188 }
11189
11190 function parseWeekday(d, string, i) {
11191 var n = weekdayRe.exec(string.slice(i));
11192 return n ? (d.w = weekdayLookup[n[0].toLowerCase()], i + n[0].length) : -1;
11193 }
11194
11195 function parseShortMonth(d, string, i) {
11196 var n = shortMonthRe.exec(string.slice(i));
11197 return n ? (d.m = shortMonthLookup[n[0].toLowerCase()], i + n[0].length) : -1;
11198 }
11199
11200 function parseMonth(d, string, i) {
11201 var n = monthRe.exec(string.slice(i));
11202 return n ? (d.m = monthLookup[n[0].toLowerCase()], i + n[0].length) : -1;
11203 }
11204
11205 function parseLocaleDateTime(d, string, i) {
11206 return parseSpecifier(d, locale_dateTime, string, i);
11207 }
11208
11209 function parseLocaleDate(d, string, i) {
11210 return parseSpecifier(d, locale_date, string, i);
11211 }
11212
11213 function parseLocaleTime(d, string, i) {
11214 return parseSpecifier(d, locale_time, string, i);
11215 }
11216
11217 function formatShortWeekday(d) {
11218 return locale_shortWeekdays[d.getDay()];
11219 }
11220
11221 function formatWeekday(d) {
11222 return locale_weekdays[d.getDay()];
11223 }
11224
11225 function formatShortMonth(d) {
11226 return locale_shortMonths[d.getMonth()];
11227 }
11228
11229 function formatMonth(d) {
11230 return locale_months[d.getMonth()];
11231 }
11232
11233 function formatPeriod(d) {
11234 return locale_periods[+(d.getHours() >= 12)];
11235 }
11236
11237 function formatQuarter(d) {
11238 return 1 + ~~(d.getMonth() / 3);
11239 }
11240
11241 function formatUTCShortWeekday(d) {
11242 return locale_shortWeekdays[d.getUTCDay()];
11243 }
11244
11245 function formatUTCWeekday(d) {
11246 return locale_weekdays[d.getUTCDay()];
11247 }
11248
11249 function formatUTCShortMonth(d) {
11250 return locale_shortMonths[d.getUTCMonth()];
11251 }
11252
11253 function formatUTCMonth(d) {
11254 return locale_months[d.getUTCMonth()];
11255 }
11256
11257 function formatUTCPeriod(d) {
11258 return locale_periods[+(d.getUTCHours() >= 12)];
11259 }
11260
11261 function formatUTCQuarter(d) {
11262 return 1 + ~~(d.getUTCMonth() / 3);
11263 }
11264
11265 return {
11266 format: function(specifier) {
11267 var f = newFormat(specifier += "", formats);
11268 f.toString = function() { return specifier; };
11269 return f;
11270 },
11271 parse: function(specifier) {
11272 var p = newParse(specifier += "", false);
11273 p.toString = function() { return specifier; };
11274 return p;
11275 },
11276 utcFormat: function(specifier) {
11277 var f = newFormat(specifier += "", utcFormats);
11278 f.toString = function() { return specifier; };
11279 return f;
11280 },
11281 utcParse: function(specifier) {
11282 var p = newParse(specifier += "", true);
11283 p.toString = function() { return specifier; };
11284 return p;
11285 }
11286 };
11287}
11288
11289var pads = {"-": "", "_": " ", "0": "0"},
11290 numberRe = /^\s*\d+/, // note: ignores next directive
11291 percentRe = /^%/,
11292 requoteRe = /[\\^$*+?|[\]().{}]/g;
11293
11294function pad(value, fill, width) {
11295 var sign = value < 0 ? "-" : "",
11296 string = (sign ? -value : value) + "",
11297 length = string.length;
11298 return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string);
11299}
11300
11301function requote(s) {
11302 return s.replace(requoteRe, "\\$&");
11303}
11304
11305function formatRe(names) {
11306 return new RegExp("^(?:" + names.map(requote).join("|") + ")", "i");
11307}
11308
11309function formatLookup(names) {
11310 var map = {}, i = -1, n = names.length;
11311 while (++i < n) map[names[i].toLowerCase()] = i;
11312 return map;
11313}
11314
11315function parseWeekdayNumberSunday(d, string, i) {
11316 var n = numberRe.exec(string.slice(i, i + 1));
11317 return n ? (d.w = +n[0], i + n[0].length) : -1;
11318}
11319
11320function parseWeekdayNumberMonday(d, string, i) {
11321 var n = numberRe.exec(string.slice(i, i + 1));
11322 return n ? (d.u = +n[0], i + n[0].length) : -1;
11323}
11324
11325function parseWeekNumberSunday(d, string, i) {
11326 var n = numberRe.exec(string.slice(i, i + 2));
11327 return n ? (d.U = +n[0], i + n[0].length) : -1;
11328}
11329
11330function parseWeekNumberISO(d, string, i) {
11331 var n = numberRe.exec(string.slice(i, i + 2));
11332 return n ? (d.V = +n[0], i + n[0].length) : -1;
11333}
11334
11335function parseWeekNumberMonday(d, string, i) {
11336 var n = numberRe.exec(string.slice(i, i + 2));
11337 return n ? (d.W = +n[0], i + n[0].length) : -1;
11338}
11339
11340function parseFullYear(d, string, i) {
11341 var n = numberRe.exec(string.slice(i, i + 4));
11342 return n ? (d.y = +n[0], i + n[0].length) : -1;
11343}
11344
11345function parseYear(d, string, i) {
11346 var n = numberRe.exec(string.slice(i, i + 2));
11347 return n ? (d.y = +n[0] + (+n[0] > 68 ? 1900 : 2000), i + n[0].length) : -1;
11348}
11349
11350function parseZone(d, string, i) {
11351 var n = /^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(string.slice(i, i + 6));
11352 return n ? (d.Z = n[1] ? 0 : -(n[2] + (n[3] || "00")), i + n[0].length) : -1;
11353}
11354
11355function parseQuarter(d, string, i) {
11356 var n = numberRe.exec(string.slice(i, i + 1));
11357 return n ? (d.q = n[0] * 3 - 3, i + n[0].length) : -1;
11358}
11359
11360function parseMonthNumber(d, string, i) {
11361 var n = numberRe.exec(string.slice(i, i + 2));
11362 return n ? (d.m = n[0] - 1, i + n[0].length) : -1;
11363}
11364
11365function parseDayOfMonth(d, string, i) {
11366 var n = numberRe.exec(string.slice(i, i + 2));
11367 return n ? (d.d = +n[0], i + n[0].length) : -1;
11368}
11369
11370function parseDayOfYear(d, string, i) {
11371 var n = numberRe.exec(string.slice(i, i + 3));
11372 return n ? (d.m = 0, d.d = +n[0], i + n[0].length) : -1;
11373}
11374
11375function parseHour24(d, string, i) {
11376 var n = numberRe.exec(string.slice(i, i + 2));
11377 return n ? (d.H = +n[0], i + n[0].length) : -1;
11378}
11379
11380function parseMinutes(d, string, i) {
11381 var n = numberRe.exec(string.slice(i, i + 2));
11382 return n ? (d.M = +n[0], i + n[0].length) : -1;
11383}
11384
11385function parseSeconds(d, string, i) {
11386 var n = numberRe.exec(string.slice(i, i + 2));
11387 return n ? (d.S = +n[0], i + n[0].length) : -1;
11388}
11389
11390function parseMilliseconds(d, string, i) {
11391 var n = numberRe.exec(string.slice(i, i + 3));
11392 return n ? (d.L = +n[0], i + n[0].length) : -1;
11393}
11394
11395function parseMicroseconds(d, string, i) {
11396 var n = numberRe.exec(string.slice(i, i + 6));
11397 return n ? (d.L = Math.floor(n[0] / 1000), i + n[0].length) : -1;
11398}
11399
11400function parseLiteralPercent(d, string, i) {
11401 var n = percentRe.exec(string.slice(i, i + 1));
11402 return n ? i + n[0].length : -1;
11403}
11404
11405function parseUnixTimestamp(d, string, i) {
11406 var n = numberRe.exec(string.slice(i));
11407 return n ? (d.Q = +n[0], i + n[0].length) : -1;
11408}
11409
11410function parseUnixTimestampSeconds(d, string, i) {
11411 var n = numberRe.exec(string.slice(i));
11412 return n ? (d.s = +n[0], i + n[0].length) : -1;
11413}
11414
11415function formatDayOfMonth(d, p) {
11416 return pad(d.getDate(), p, 2);
11417}
11418
11419function formatHour24(d, p) {
11420 return pad(d.getHours(), p, 2);
11421}
11422
11423function formatHour12(d, p) {
11424 return pad(d.getHours() % 12 || 12, p, 2);
11425}
11426
11427function formatDayOfYear(d, p) {
11428 return pad(1 + d3Time.timeDay.count(d3Time.timeYear(d), d), p, 3);
11429}
11430
11431function formatMilliseconds(d, p) {
11432 return pad(d.getMilliseconds(), p, 3);
11433}
11434
11435function formatMicroseconds(d, p) {
11436 return formatMilliseconds(d, p) + "000";
11437}
11438
11439function formatMonthNumber(d, p) {
11440 return pad(d.getMonth() + 1, p, 2);
11441}
11442
11443function formatMinutes(d, p) {
11444 return pad(d.getMinutes(), p, 2);
11445}
11446
11447function formatSeconds(d, p) {
11448 return pad(d.getSeconds(), p, 2);
11449}
11450
11451function formatWeekdayNumberMonday(d) {
11452 var day = d.getDay();
11453 return day === 0 ? 7 : day;
11454}
11455
11456function formatWeekNumberSunday(d, p) {
11457 return pad(d3Time.timeSunday.count(d3Time.timeYear(d) - 1, d), p, 2);
11458}
11459
11460function formatWeekNumberISO(d, p) {
11461 var day = d.getDay();
11462 d = (day >= 4 || day === 0) ? d3Time.timeThursday(d) : d3Time.timeThursday.ceil(d);
11463 return pad(d3Time.timeThursday.count(d3Time.timeYear(d), d) + (d3Time.timeYear(d).getDay() === 4), p, 2);
11464}
11465
11466function formatWeekdayNumberSunday(d) {
11467 return d.getDay();
11468}
11469
11470function formatWeekNumberMonday(d, p) {
11471 return pad(d3Time.timeMonday.count(d3Time.timeYear(d) - 1, d), p, 2);
11472}
11473
11474function formatYear(d, p) {
11475 return pad(d.getFullYear() % 100, p, 2);
11476}
11477
11478function formatFullYear(d, p) {
11479 return pad(d.getFullYear() % 10000, p, 4);
11480}
11481
11482function formatZone(d) {
11483 var z = d.getTimezoneOffset();
11484 return (z > 0 ? "-" : (z *= -1, "+"))
11485 + pad(z / 60 | 0, "0", 2)
11486 + pad(z % 60, "0", 2);
11487}
11488
11489function formatUTCDayOfMonth(d, p) {
11490 return pad(d.getUTCDate(), p, 2);
11491}
11492
11493function formatUTCHour24(d, p) {
11494 return pad(d.getUTCHours(), p, 2);
11495}
11496
11497function formatUTCHour12(d, p) {
11498 return pad(d.getUTCHours() % 12 || 12, p, 2);
11499}
11500
11501function formatUTCDayOfYear(d, p) {
11502 return pad(1 + d3Time.utcDay.count(d3Time.utcYear(d), d), p, 3);
11503}
11504
11505function formatUTCMilliseconds(d, p) {
11506 return pad(d.getUTCMilliseconds(), p, 3);
11507}
11508
11509function formatUTCMicroseconds(d, p) {
11510 return formatUTCMilliseconds(d, p) + "000";
11511}
11512
11513function formatUTCMonthNumber(d, p) {
11514 return pad(d.getUTCMonth() + 1, p, 2);
11515}
11516
11517function formatUTCMinutes(d, p) {
11518 return pad(d.getUTCMinutes(), p, 2);
11519}
11520
11521function formatUTCSeconds(d, p) {
11522 return pad(d.getUTCSeconds(), p, 2);
11523}
11524
11525function formatUTCWeekdayNumberMonday(d) {
11526 var dow = d.getUTCDay();
11527 return dow === 0 ? 7 : dow;
11528}
11529
11530function formatUTCWeekNumberSunday(d, p) {
11531 return pad(d3Time.utcSunday.count(d3Time.utcYear(d) - 1, d), p, 2);
11532}
11533
11534function formatUTCWeekNumberISO(d, p) {
11535 var day = d.getUTCDay();
11536 d = (day >= 4 || day === 0) ? d3Time.utcThursday(d) : d3Time.utcThursday.ceil(d);
11537 return pad(d3Time.utcThursday.count(d3Time.utcYear(d), d) + (d3Time.utcYear(d).getUTCDay() === 4), p, 2);
11538}
11539
11540function formatUTCWeekdayNumberSunday(d) {
11541 return d.getUTCDay();
11542}
11543
11544function formatUTCWeekNumberMonday(d, p) {
11545 return pad(d3Time.utcMonday.count(d3Time.utcYear(d) - 1, d), p, 2);
11546}
11547
11548function formatUTCYear(d, p) {
11549 return pad(d.getUTCFullYear() % 100, p, 2);
11550}
11551
11552function formatUTCFullYear(d, p) {
11553 return pad(d.getUTCFullYear() % 10000, p, 4);
11554}
11555
11556function formatUTCZone() {
11557 return "+0000";
11558}
11559
11560function formatLiteralPercent() {
11561 return "%";
11562}
11563
11564function formatUnixTimestamp(d) {
11565 return +d;
11566}
11567
11568function formatUnixTimestampSeconds(d) {
11569 return Math.floor(+d / 1000);
11570}
11571
11572var locale;
11573
11574defaultLocale({
11575 dateTime: "%x, %X",
11576 date: "%-m/%-d/%Y",
11577 time: "%-I:%M:%S %p",
11578 periods: ["AM", "PM"],
11579 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
11580 shortDays: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
11581 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11582 shortMonths: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
11583});
11584
11585function defaultLocale(definition) {
11586 locale = formatLocale(definition);
11587 exports.timeFormat = locale.format;
11588 exports.timeParse = locale.parse;
11589 exports.utcFormat = locale.utcFormat;
11590 exports.utcParse = locale.utcParse;
11591 return locale;
11592}
11593
11594var isoSpecifier = "%Y-%m-%dT%H:%M:%S.%LZ";
11595
11596function formatIsoNative(date) {
11597 return date.toISOString();
11598}
11599
11600var formatIso = Date.prototype.toISOString
11601 ? formatIsoNative
11602 : exports.utcFormat(isoSpecifier);
11603
11604function parseIsoNative(string) {
11605 var date = new Date(string);
11606 return isNaN(date) ? null : date;
11607}
11608
11609var parseIso = +new Date("2000-01-01T00:00:00.000Z")
11610 ? parseIsoNative
11611 : exports.utcParse(isoSpecifier);
11612
11613exports.isoFormat = formatIso;
11614exports.isoParse = parseIso;
11615exports.timeFormatDefaultLocale = defaultLocale;
11616exports.timeFormatLocale = formatLocale;
11617
11618Object.defineProperty(exports, '__esModule', { value: true });
11619
11620}));
11621
11622},{"d3-time":31}],31:[function(_dereq_,module,exports){
11623// https://d3js.org/d3-time/ v1.1.0 Copyright 2019 Mike Bostock
11624(function (global, factory) {
11625typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
11626typeof define === 'function' && define.amd ? define(['exports'], factory) :
11627(global = global || self, factory(global.d3 = global.d3 || {}));
11628}(this, function (exports) { 'use strict';
11629
11630var t0 = new Date,
11631 t1 = new Date;
11632
11633function newInterval(floori, offseti, count, field) {
11634
11635 function interval(date) {
11636 return floori(date = arguments.length === 0 ? new Date : new Date(+date)), date;
11637 }
11638
11639 interval.floor = function(date) {
11640 return floori(date = new Date(+date)), date;
11641 };
11642
11643 interval.ceil = function(date) {
11644 return floori(date = new Date(date - 1)), offseti(date, 1), floori(date), date;
11645 };
11646
11647 interval.round = function(date) {
11648 var d0 = interval(date),
11649 d1 = interval.ceil(date);
11650 return date - d0 < d1 - date ? d0 : d1;
11651 };
11652
11653 interval.offset = function(date, step) {
11654 return offseti(date = new Date(+date), step == null ? 1 : Math.floor(step)), date;
11655 };
11656
11657 interval.range = function(start, stop, step) {
11658 var range = [], previous;
11659 start = interval.ceil(start);
11660 step = step == null ? 1 : Math.floor(step);
11661 if (!(start < stop) || !(step > 0)) return range; // also handles Invalid Date
11662 do range.push(previous = new Date(+start)), offseti(start, step), floori(start);
11663 while (previous < start && start < stop);
11664 return range;
11665 };
11666
11667 interval.filter = function(test) {
11668 return newInterval(function(date) {
11669 if (date >= date) while (floori(date), !test(date)) date.setTime(date - 1);
11670 }, function(date, step) {
11671 if (date >= date) {
11672 if (step < 0) while (++step <= 0) {
11673 while (offseti(date, -1), !test(date)) {} // eslint-disable-line no-empty
11674 } else while (--step >= 0) {
11675 while (offseti(date, +1), !test(date)) {} // eslint-disable-line no-empty
11676 }
11677 }
11678 });
11679 };
11680
11681 if (count) {
11682 interval.count = function(start, end) {
11683 t0.setTime(+start), t1.setTime(+end);
11684 floori(t0), floori(t1);
11685 return Math.floor(count(t0, t1));
11686 };
11687
11688 interval.every = function(step) {
11689 step = Math.floor(step);
11690 return !isFinite(step) || !(step > 0) ? null
11691 : !(step > 1) ? interval
11692 : interval.filter(field
11693 ? function(d) { return field(d) % step === 0; }
11694 : function(d) { return interval.count(0, d) % step === 0; });
11695 };
11696 }
11697
11698 return interval;
11699}
11700
11701var millisecond = newInterval(function() {
11702 // noop
11703}, function(date, step) {
11704 date.setTime(+date + step);
11705}, function(start, end) {
11706 return end - start;
11707});
11708
11709// An optimized implementation for this simple case.
11710millisecond.every = function(k) {
11711 k = Math.floor(k);
11712 if (!isFinite(k) || !(k > 0)) return null;
11713 if (!(k > 1)) return millisecond;
11714 return newInterval(function(date) {
11715 date.setTime(Math.floor(date / k) * k);
11716 }, function(date, step) {
11717 date.setTime(+date + step * k);
11718 }, function(start, end) {
11719 return (end - start) / k;
11720 });
11721};
11722var milliseconds = millisecond.range;
11723
11724var durationSecond = 1e3;
11725var durationMinute = 6e4;
11726var durationHour = 36e5;
11727var durationDay = 864e5;
11728var durationWeek = 6048e5;
11729
11730var second = newInterval(function(date) {
11731 date.setTime(date - date.getMilliseconds());
11732}, function(date, step) {
11733 date.setTime(+date + step * durationSecond);
11734}, function(start, end) {
11735 return (end - start) / durationSecond;
11736}, function(date) {
11737 return date.getUTCSeconds();
11738});
11739var seconds = second.range;
11740
11741var minute = newInterval(function(date) {
11742 date.setTime(date - date.getMilliseconds() - date.getSeconds() * durationSecond);
11743}, function(date, step) {
11744 date.setTime(+date + step * durationMinute);
11745}, function(start, end) {
11746 return (end - start) / durationMinute;
11747}, function(date) {
11748 return date.getMinutes();
11749});
11750var minutes = minute.range;
11751
11752var hour = newInterval(function(date) {
11753 date.setTime(date - date.getMilliseconds() - date.getSeconds() * durationSecond - date.getMinutes() * durationMinute);
11754}, function(date, step) {
11755 date.setTime(+date + step * durationHour);
11756}, function(start, end) {
11757 return (end - start) / durationHour;
11758}, function(date) {
11759 return date.getHours();
11760});
11761var hours = hour.range;
11762
11763var day = newInterval(function(date) {
11764 date.setHours(0, 0, 0, 0);
11765}, function(date, step) {
11766 date.setDate(date.getDate() + step);
11767}, function(start, end) {
11768 return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute) / durationDay;
11769}, function(date) {
11770 return date.getDate() - 1;
11771});
11772var days = day.range;
11773
11774function weekday(i) {
11775 return newInterval(function(date) {
11776 date.setDate(date.getDate() - (date.getDay() + 7 - i) % 7);
11777 date.setHours(0, 0, 0, 0);
11778 }, function(date, step) {
11779 date.setDate(date.getDate() + step * 7);
11780 }, function(start, end) {
11781 return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute) / durationWeek;
11782 });
11783}
11784
11785var sunday = weekday(0);
11786var monday = weekday(1);
11787var tuesday = weekday(2);
11788var wednesday = weekday(3);
11789var thursday = weekday(4);
11790var friday = weekday(5);
11791var saturday = weekday(6);
11792
11793var sundays = sunday.range;
11794var mondays = monday.range;
11795var tuesdays = tuesday.range;
11796var wednesdays = wednesday.range;
11797var thursdays = thursday.range;
11798var fridays = friday.range;
11799var saturdays = saturday.range;
11800
11801var month = newInterval(function(date) {
11802 date.setDate(1);
11803 date.setHours(0, 0, 0, 0);
11804}, function(date, step) {
11805 date.setMonth(date.getMonth() + step);
11806}, function(start, end) {
11807 return end.getMonth() - start.getMonth() + (end.getFullYear() - start.getFullYear()) * 12;
11808}, function(date) {
11809 return date.getMonth();
11810});
11811var months = month.range;
11812
11813var year = newInterval(function(date) {
11814 date.setMonth(0, 1);
11815 date.setHours(0, 0, 0, 0);
11816}, function(date, step) {
11817 date.setFullYear(date.getFullYear() + step);
11818}, function(start, end) {
11819 return end.getFullYear() - start.getFullYear();
11820}, function(date) {
11821 return date.getFullYear();
11822});
11823
11824// An optimized implementation for this simple case.
11825year.every = function(k) {
11826 return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : newInterval(function(date) {
11827 date.setFullYear(Math.floor(date.getFullYear() / k) * k);
11828 date.setMonth(0, 1);
11829 date.setHours(0, 0, 0, 0);
11830 }, function(date, step) {
11831 date.setFullYear(date.getFullYear() + step * k);
11832 });
11833};
11834var years = year.range;
11835
11836var utcMinute = newInterval(function(date) {
11837 date.setUTCSeconds(0, 0);
11838}, function(date, step) {
11839 date.setTime(+date + step * durationMinute);
11840}, function(start, end) {
11841 return (end - start) / durationMinute;
11842}, function(date) {
11843 return date.getUTCMinutes();
11844});
11845var utcMinutes = utcMinute.range;
11846
11847var utcHour = newInterval(function(date) {
11848 date.setUTCMinutes(0, 0, 0);
11849}, function(date, step) {
11850 date.setTime(+date + step * durationHour);
11851}, function(start, end) {
11852 return (end - start) / durationHour;
11853}, function(date) {
11854 return date.getUTCHours();
11855});
11856var utcHours = utcHour.range;
11857
11858var utcDay = newInterval(function(date) {
11859 date.setUTCHours(0, 0, 0, 0);
11860}, function(date, step) {
11861 date.setUTCDate(date.getUTCDate() + step);
11862}, function(start, end) {
11863 return (end - start) / durationDay;
11864}, function(date) {
11865 return date.getUTCDate() - 1;
11866});
11867var utcDays = utcDay.range;
11868
11869function utcWeekday(i) {
11870 return newInterval(function(date) {
11871 date.setUTCDate(date.getUTCDate() - (date.getUTCDay() + 7 - i) % 7);
11872 date.setUTCHours(0, 0, 0, 0);
11873 }, function(date, step) {
11874 date.setUTCDate(date.getUTCDate() + step * 7);
11875 }, function(start, end) {
11876 return (end - start) / durationWeek;
11877 });
11878}
11879
11880var utcSunday = utcWeekday(0);
11881var utcMonday = utcWeekday(1);
11882var utcTuesday = utcWeekday(2);
11883var utcWednesday = utcWeekday(3);
11884var utcThursday = utcWeekday(4);
11885var utcFriday = utcWeekday(5);
11886var utcSaturday = utcWeekday(6);
11887
11888var utcSundays = utcSunday.range;
11889var utcMondays = utcMonday.range;
11890var utcTuesdays = utcTuesday.range;
11891var utcWednesdays = utcWednesday.range;
11892var utcThursdays = utcThursday.range;
11893var utcFridays = utcFriday.range;
11894var utcSaturdays = utcSaturday.range;
11895
11896var utcMonth = newInterval(function(date) {
11897 date.setUTCDate(1);
11898 date.setUTCHours(0, 0, 0, 0);
11899}, function(date, step) {
11900 date.setUTCMonth(date.getUTCMonth() + step);
11901}, function(start, end) {
11902 return end.getUTCMonth() - start.getUTCMonth() + (end.getUTCFullYear() - start.getUTCFullYear()) * 12;
11903}, function(date) {
11904 return date.getUTCMonth();
11905});
11906var utcMonths = utcMonth.range;
11907
11908var utcYear = newInterval(function(date) {
11909 date.setUTCMonth(0, 1);
11910 date.setUTCHours(0, 0, 0, 0);
11911}, function(date, step) {
11912 date.setUTCFullYear(date.getUTCFullYear() + step);
11913}, function(start, end) {
11914 return end.getUTCFullYear() - start.getUTCFullYear();
11915}, function(date) {
11916 return date.getUTCFullYear();
11917});
11918
11919// An optimized implementation for this simple case.
11920utcYear.every = function(k) {
11921 return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : newInterval(function(date) {
11922 date.setUTCFullYear(Math.floor(date.getUTCFullYear() / k) * k);
11923 date.setUTCMonth(0, 1);
11924 date.setUTCHours(0, 0, 0, 0);
11925 }, function(date, step) {
11926 date.setUTCFullYear(date.getUTCFullYear() + step * k);
11927 });
11928};
11929var utcYears = utcYear.range;
11930
11931exports.timeDay = day;
11932exports.timeDays = days;
11933exports.timeFriday = friday;
11934exports.timeFridays = fridays;
11935exports.timeHour = hour;
11936exports.timeHours = hours;
11937exports.timeInterval = newInterval;
11938exports.timeMillisecond = millisecond;
11939exports.timeMilliseconds = milliseconds;
11940exports.timeMinute = minute;
11941exports.timeMinutes = minutes;
11942exports.timeMonday = monday;
11943exports.timeMondays = mondays;
11944exports.timeMonth = month;
11945exports.timeMonths = months;
11946exports.timeSaturday = saturday;
11947exports.timeSaturdays = saturdays;
11948exports.timeSecond = second;
11949exports.timeSeconds = seconds;
11950exports.timeSunday = sunday;
11951exports.timeSundays = sundays;
11952exports.timeThursday = thursday;
11953exports.timeThursdays = thursdays;
11954exports.timeTuesday = tuesday;
11955exports.timeTuesdays = tuesdays;
11956exports.timeWednesday = wednesday;
11957exports.timeWednesdays = wednesdays;
11958exports.timeWeek = sunday;
11959exports.timeWeeks = sundays;
11960exports.timeYear = year;
11961exports.timeYears = years;
11962exports.utcDay = utcDay;
11963exports.utcDays = utcDays;
11964exports.utcFriday = utcFriday;
11965exports.utcFridays = utcFridays;
11966exports.utcHour = utcHour;
11967exports.utcHours = utcHours;
11968exports.utcMillisecond = millisecond;
11969exports.utcMilliseconds = milliseconds;
11970exports.utcMinute = utcMinute;
11971exports.utcMinutes = utcMinutes;
11972exports.utcMonday = utcMonday;
11973exports.utcMondays = utcMondays;
11974exports.utcMonth = utcMonth;
11975exports.utcMonths = utcMonths;
11976exports.utcSaturday = utcSaturday;
11977exports.utcSaturdays = utcSaturdays;
11978exports.utcSecond = second;
11979exports.utcSeconds = seconds;
11980exports.utcSunday = utcSunday;
11981exports.utcSundays = utcSundays;
11982exports.utcThursday = utcThursday;
11983exports.utcThursdays = utcThursdays;
11984exports.utcTuesday = utcTuesday;
11985exports.utcTuesdays = utcTuesdays;
11986exports.utcWednesday = utcWednesday;
11987exports.utcWednesdays = utcWednesdays;
11988exports.utcWeek = utcSunday;
11989exports.utcWeeks = utcSundays;
11990exports.utcYear = utcYear;
11991exports.utcYears = utcYears;
11992
11993Object.defineProperty(exports, '__esModule', { value: true });
11994
11995}));
11996
11997},{}],32:[function(_dereq_,module,exports){
11998arguments[4][31][0].apply(exports,arguments)
11999},{"dup":31}],33:[function(_dereq_,module,exports){
12000/**
12001 * inspired by is-number <https://github.com/jonschlinkert/is-number>
12002 * but significantly simplified and sped up by ignoring number and string constructors
12003 * ie these return false:
12004 * new Number(1)
12005 * new String('1')
12006 */
12007
12008'use strict';
12009
12010var allBlankCharCodes = _dereq_('is-string-blank');
12011
12012module.exports = function(n) {
12013 var type = typeof n;
12014 if(type === 'string') {
12015 var original = n;
12016 n = +n;
12017 // whitespace strings cast to zero - filter them out
12018 if(n===0 && allBlankCharCodes(original)) return false;
12019 }
12020 else if(type !== 'number') return false;
12021
12022 return n - n < 1;
12023};
12024
12025},{"is-string-blank":70}],34:[function(_dereq_,module,exports){
12026module.exports = adjoint;
12027
12028/**
12029 * Calculates the adjugate of a mat4
12030 *
12031 * @param {mat4} out the receiving matrix
12032 * @param {mat4} a the source matrix
12033 * @returns {mat4} out
12034 */
12035function adjoint(out, a) {
12036 var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3],
12037 a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7],
12038 a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11],
12039 a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
12040
12041 out[0] = (a11 * (a22 * a33 - a23 * a32) - a21 * (a12 * a33 - a13 * a32) + a31 * (a12 * a23 - a13 * a22));
12042 out[1] = -(a01 * (a22 * a33 - a23 * a32) - a21 * (a02 * a33 - a03 * a32) + a31 * (a02 * a23 - a03 * a22));
12043 out[2] = (a01 * (a12 * a33 - a13 * a32) - a11 * (a02 * a33 - a03 * a32) + a31 * (a02 * a13 - a03 * a12));
12044 out[3] = -(a01 * (a12 * a23 - a13 * a22) - a11 * (a02 * a23 - a03 * a22) + a21 * (a02 * a13 - a03 * a12));
12045 out[4] = -(a10 * (a22 * a33 - a23 * a32) - a20 * (a12 * a33 - a13 * a32) + a30 * (a12 * a23 - a13 * a22));
12046 out[5] = (a00 * (a22 * a33 - a23 * a32) - a20 * (a02 * a33 - a03 * a32) + a30 * (a02 * a23 - a03 * a22));
12047 out[6] = -(a00 * (a12 * a33 - a13 * a32) - a10 * (a02 * a33 - a03 * a32) + a30 * (a02 * a13 - a03 * a12));
12048 out[7] = (a00 * (a12 * a23 - a13 * a22) - a10 * (a02 * a23 - a03 * a22) + a20 * (a02 * a13 - a03 * a12));
12049 out[8] = (a10 * (a21 * a33 - a23 * a31) - a20 * (a11 * a33 - a13 * a31) + a30 * (a11 * a23 - a13 * a21));
12050 out[9] = -(a00 * (a21 * a33 - a23 * a31) - a20 * (a01 * a33 - a03 * a31) + a30 * (a01 * a23 - a03 * a21));
12051 out[10] = (a00 * (a11 * a33 - a13 * a31) - a10 * (a01 * a33 - a03 * a31) + a30 * (a01 * a13 - a03 * a11));
12052 out[11] = -(a00 * (a11 * a23 - a13 * a21) - a10 * (a01 * a23 - a03 * a21) + a20 * (a01 * a13 - a03 * a11));
12053 out[12] = -(a10 * (a21 * a32 - a22 * a31) - a20 * (a11 * a32 - a12 * a31) + a30 * (a11 * a22 - a12 * a21));
12054 out[13] = (a00 * (a21 * a32 - a22 * a31) - a20 * (a01 * a32 - a02 * a31) + a30 * (a01 * a22 - a02 * a21));
12055 out[14] = -(a00 * (a11 * a32 - a12 * a31) - a10 * (a01 * a32 - a02 * a31) + a30 * (a01 * a12 - a02 * a11));
12056 out[15] = (a00 * (a11 * a22 - a12 * a21) - a10 * (a01 * a22 - a02 * a21) + a20 * (a01 * a12 - a02 * a11));
12057 return out;
12058};
12059},{}],35:[function(_dereq_,module,exports){
12060module.exports = clone;
12061
12062/**
12063 * Creates a new mat4 initialized with values from an existing matrix
12064 *
12065 * @param {mat4} a matrix to clone
12066 * @returns {mat4} a new 4x4 matrix
12067 */
12068function clone(a) {
12069 var out = new Float32Array(16);
12070 out[0] = a[0];
12071 out[1] = a[1];
12072 out[2] = a[2];
12073 out[3] = a[3];
12074 out[4] = a[4];
12075 out[5] = a[5];
12076 out[6] = a[6];
12077 out[7] = a[7];
12078 out[8] = a[8];
12079 out[9] = a[9];
12080 out[10] = a[10];
12081 out[11] = a[11];
12082 out[12] = a[12];
12083 out[13] = a[13];
12084 out[14] = a[14];
12085 out[15] = a[15];
12086 return out;
12087};
12088},{}],36:[function(_dereq_,module,exports){
12089module.exports = copy;
12090
12091/**
12092 * Copy the values from one mat4 to another
12093 *
12094 * @param {mat4} out the receiving matrix
12095 * @param {mat4} a the source matrix
12096 * @returns {mat4} out
12097 */
12098function copy(out, a) {
12099 out[0] = a[0];
12100 out[1] = a[1];
12101 out[2] = a[2];
12102 out[3] = a[3];
12103 out[4] = a[4];
12104 out[5] = a[5];
12105 out[6] = a[6];
12106 out[7] = a[7];
12107 out[8] = a[8];
12108 out[9] = a[9];
12109 out[10] = a[10];
12110 out[11] = a[11];
12111 out[12] = a[12];
12112 out[13] = a[13];
12113 out[14] = a[14];
12114 out[15] = a[15];
12115 return out;
12116};
12117},{}],37:[function(_dereq_,module,exports){
12118module.exports = create;
12119
12120/**
12121 * Creates a new identity mat4
12122 *
12123 * @returns {mat4} a new 4x4 matrix
12124 */
12125function create() {
12126 var out = new Float32Array(16);
12127 out[0] = 1;
12128 out[1] = 0;
12129 out[2] = 0;
12130 out[3] = 0;
12131 out[4] = 0;
12132 out[5] = 1;
12133 out[6] = 0;
12134 out[7] = 0;
12135 out[8] = 0;
12136 out[9] = 0;
12137 out[10] = 1;
12138 out[11] = 0;
12139 out[12] = 0;
12140 out[13] = 0;
12141 out[14] = 0;
12142 out[15] = 1;
12143 return out;
12144};
12145},{}],38:[function(_dereq_,module,exports){
12146module.exports = determinant;
12147
12148/**
12149 * Calculates the determinant of a mat4
12150 *
12151 * @param {mat4} a the source matrix
12152 * @returns {Number} determinant of a
12153 */
12154function determinant(a) {
12155 var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3],
12156 a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7],
12157 a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11],
12158 a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15],
12159
12160 b00 = a00 * a11 - a01 * a10,
12161 b01 = a00 * a12 - a02 * a10,
12162 b02 = a00 * a13 - a03 * a10,
12163 b03 = a01 * a12 - a02 * a11,
12164 b04 = a01 * a13 - a03 * a11,
12165 b05 = a02 * a13 - a03 * a12,
12166 b06 = a20 * a31 - a21 * a30,
12167 b07 = a20 * a32 - a22 * a30,
12168 b08 = a20 * a33 - a23 * a30,
12169 b09 = a21 * a32 - a22 * a31,
12170 b10 = a21 * a33 - a23 * a31,
12171 b11 = a22 * a33 - a23 * a32;
12172
12173 // Calculate the determinant
12174 return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
12175};
12176},{}],39:[function(_dereq_,module,exports){
12177module.exports = fromQuat;
12178
12179/**
12180 * Creates a matrix from a quaternion rotation.
12181 *
12182 * @param {mat4} out mat4 receiving operation result
12183 * @param {quat4} q Rotation quaternion
12184 * @returns {mat4} out
12185 */
12186function fromQuat(out, q) {
12187 var x = q[0], y = q[1], z = q[2], w = q[3],
12188 x2 = x + x,
12189 y2 = y + y,
12190 z2 = z + z,
12191
12192 xx = x * x2,
12193 yx = y * x2,
12194 yy = y * y2,
12195 zx = z * x2,
12196 zy = z * y2,
12197 zz = z * z2,
12198 wx = w * x2,
12199 wy = w * y2,
12200 wz = w * z2;
12201
12202 out[0] = 1 - yy - zz;
12203 out[1] = yx + wz;
12204 out[2] = zx - wy;
12205 out[3] = 0;
12206
12207 out[4] = yx - wz;
12208 out[5] = 1 - xx - zz;
12209 out[6] = zy + wx;
12210 out[7] = 0;
12211
12212 out[8] = zx + wy;
12213 out[9] = zy - wx;
12214 out[10] = 1 - xx - yy;
12215 out[11] = 0;
12216
12217 out[12] = 0;
12218 out[13] = 0;
12219 out[14] = 0;
12220 out[15] = 1;
12221
12222 return out;
12223};
12224},{}],40:[function(_dereq_,module,exports){
12225module.exports = fromRotation
12226
12227/**
12228 * Creates a matrix from a given angle around a given axis
12229 * This is equivalent to (but much faster than):
12230 *
12231 * mat4.identity(dest)
12232 * mat4.rotate(dest, dest, rad, axis)
12233 *
12234 * @param {mat4} out mat4 receiving operation result
12235 * @param {Number} rad the angle to rotate the matrix by
12236 * @param {vec3} axis the axis to rotate around
12237 * @returns {mat4} out
12238 */
12239function fromRotation(out, rad, axis) {
12240 var s, c, t
12241 var x = axis[0]
12242 var y = axis[1]
12243 var z = axis[2]
12244 var len = Math.sqrt(x * x + y * y + z * z)
12245
12246 if (Math.abs(len) < 0.000001) {
12247 return null
12248 }
12249
12250 len = 1 / len
12251 x *= len
12252 y *= len
12253 z *= len
12254
12255 s = Math.sin(rad)
12256 c = Math.cos(rad)
12257 t = 1 - c
12258
12259 // Perform rotation-specific matrix multiplication
12260 out[0] = x * x * t + c
12261 out[1] = y * x * t + z * s
12262 out[2] = z * x * t - y * s
12263 out[3] = 0
12264 out[4] = x * y * t - z * s
12265 out[5] = y * y * t + c
12266 out[6] = z * y * t + x * s
12267 out[7] = 0
12268 out[8] = x * z * t + y * s
12269 out[9] = y * z * t - x * s
12270 out[10] = z * z * t + c
12271 out[11] = 0
12272 out[12] = 0
12273 out[13] = 0
12274 out[14] = 0
12275 out[15] = 1
12276 return out
12277}
12278
12279},{}],41:[function(_dereq_,module,exports){
12280module.exports = fromRotationTranslation;
12281
12282/**
12283 * Creates a matrix from a quaternion rotation and vector translation
12284 * This is equivalent to (but much faster than):
12285 *
12286 * mat4.identity(dest);
12287 * mat4.translate(dest, vec);
12288 * var quatMat = mat4.create();
12289 * quat4.toMat4(quat, quatMat);
12290 * mat4.multiply(dest, quatMat);
12291 *
12292 * @param {mat4} out mat4 receiving operation result
12293 * @param {quat4} q Rotation quaternion
12294 * @param {vec3} v Translation vector
12295 * @returns {mat4} out
12296 */
12297function fromRotationTranslation(out, q, v) {
12298 // Quaternion math
12299 var x = q[0], y = q[1], z = q[2], w = q[3],
12300 x2 = x + x,
12301 y2 = y + y,
12302 z2 = z + z,
12303
12304 xx = x * x2,
12305 xy = x * y2,
12306 xz = x * z2,
12307 yy = y * y2,
12308 yz = y * z2,
12309 zz = z * z2,
12310 wx = w * x2,
12311 wy = w * y2,
12312 wz = w * z2;
12313
12314 out[0] = 1 - (yy + zz);
12315 out[1] = xy + wz;
12316 out[2] = xz - wy;
12317 out[3] = 0;
12318 out[4] = xy - wz;
12319 out[5] = 1 - (xx + zz);
12320 out[6] = yz + wx;
12321 out[7] = 0;
12322 out[8] = xz + wy;
12323 out[9] = yz - wx;
12324 out[10] = 1 - (xx + yy);
12325 out[11] = 0;
12326 out[12] = v[0];
12327 out[13] = v[1];
12328 out[14] = v[2];
12329 out[15] = 1;
12330
12331 return out;
12332};
12333},{}],42:[function(_dereq_,module,exports){
12334module.exports = fromScaling
12335
12336/**
12337 * Creates a matrix from a vector scaling
12338 * This is equivalent to (but much faster than):
12339 *
12340 * mat4.identity(dest)
12341 * mat4.scale(dest, dest, vec)
12342 *
12343 * @param {mat4} out mat4 receiving operation result
12344 * @param {vec3} v Scaling vector
12345 * @returns {mat4} out
12346 */
12347function fromScaling(out, v) {
12348 out[0] = v[0]
12349 out[1] = 0
12350 out[2] = 0
12351 out[3] = 0
12352 out[4] = 0
12353 out[5] = v[1]
12354 out[6] = 0
12355 out[7] = 0
12356 out[8] = 0
12357 out[9] = 0
12358 out[10] = v[2]
12359 out[11] = 0
12360 out[12] = 0
12361 out[13] = 0
12362 out[14] = 0
12363 out[15] = 1
12364 return out
12365}
12366
12367},{}],43:[function(_dereq_,module,exports){
12368module.exports = fromTranslation
12369
12370/**
12371 * Creates a matrix from a vector translation
12372 * This is equivalent to (but much faster than):
12373 *
12374 * mat4.identity(dest)
12375 * mat4.translate(dest, dest, vec)
12376 *
12377 * @param {mat4} out mat4 receiving operation result
12378 * @param {vec3} v Translation vector
12379 * @returns {mat4} out
12380 */
12381function fromTranslation(out, v) {
12382 out[0] = 1
12383 out[1] = 0
12384 out[2] = 0
12385 out[3] = 0
12386 out[4] = 0
12387 out[5] = 1
12388 out[6] = 0
12389 out[7] = 0
12390 out[8] = 0
12391 out[9] = 0
12392 out[10] = 1
12393 out[11] = 0
12394 out[12] = v[0]
12395 out[13] = v[1]
12396 out[14] = v[2]
12397 out[15] = 1
12398 return out
12399}
12400
12401},{}],44:[function(_dereq_,module,exports){
12402module.exports = fromXRotation
12403
12404/**
12405 * Creates a matrix from the given angle around the X axis
12406 * This is equivalent to (but much faster than):
12407 *
12408 * mat4.identity(dest)
12409 * mat4.rotateX(dest, dest, rad)
12410 *
12411 * @param {mat4} out mat4 receiving operation result
12412 * @param {Number} rad the angle to rotate the matrix by
12413 * @returns {mat4} out
12414 */
12415function fromXRotation(out, rad) {
12416 var s = Math.sin(rad),
12417 c = Math.cos(rad)
12418
12419 // Perform axis-specific matrix multiplication
12420 out[0] = 1
12421 out[1] = 0
12422 out[2] = 0
12423 out[3] = 0
12424 out[4] = 0
12425 out[5] = c
12426 out[6] = s
12427 out[7] = 0
12428 out[8] = 0
12429 out[9] = -s
12430 out[10] = c
12431 out[11] = 0
12432 out[12] = 0
12433 out[13] = 0
12434 out[14] = 0
12435 out[15] = 1
12436 return out
12437}
12438},{}],45:[function(_dereq_,module,exports){
12439module.exports = fromYRotation
12440
12441/**
12442 * Creates a matrix from the given angle around the Y axis
12443 * This is equivalent to (but much faster than):
12444 *
12445 * mat4.identity(dest)
12446 * mat4.rotateY(dest, dest, rad)
12447 *
12448 * @param {mat4} out mat4 receiving operation result
12449 * @param {Number} rad the angle to rotate the matrix by
12450 * @returns {mat4} out
12451 */
12452function fromYRotation(out, rad) {
12453 var s = Math.sin(rad),
12454 c = Math.cos(rad)
12455
12456 // Perform axis-specific matrix multiplication
12457 out[0] = c
12458 out[1] = 0
12459 out[2] = -s
12460 out[3] = 0
12461 out[4] = 0
12462 out[5] = 1
12463 out[6] = 0
12464 out[7] = 0
12465 out[8] = s
12466 out[9] = 0
12467 out[10] = c
12468 out[11] = 0
12469 out[12] = 0
12470 out[13] = 0
12471 out[14] = 0
12472 out[15] = 1
12473 return out
12474}
12475},{}],46:[function(_dereq_,module,exports){
12476module.exports = fromZRotation
12477
12478/**
12479 * Creates a matrix from the given angle around the Z axis
12480 * This is equivalent to (but much faster than):
12481 *
12482 * mat4.identity(dest)
12483 * mat4.rotateZ(dest, dest, rad)
12484 *
12485 * @param {mat4} out mat4 receiving operation result
12486 * @param {Number} rad the angle to rotate the matrix by
12487 * @returns {mat4} out
12488 */
12489function fromZRotation(out, rad) {
12490 var s = Math.sin(rad),
12491 c = Math.cos(rad)
12492
12493 // Perform axis-specific matrix multiplication
12494 out[0] = c
12495 out[1] = s
12496 out[2] = 0
12497 out[3] = 0
12498 out[4] = -s
12499 out[5] = c
12500 out[6] = 0
12501 out[7] = 0
12502 out[8] = 0
12503 out[9] = 0
12504 out[10] = 1
12505 out[11] = 0
12506 out[12] = 0
12507 out[13] = 0
12508 out[14] = 0
12509 out[15] = 1
12510 return out
12511}
12512},{}],47:[function(_dereq_,module,exports){
12513module.exports = frustum;
12514
12515/**
12516 * Generates a frustum matrix with the given bounds
12517 *
12518 * @param {mat4} out mat4 frustum matrix will be written into
12519 * @param {Number} left Left bound of the frustum
12520 * @param {Number} right Right bound of the frustum
12521 * @param {Number} bottom Bottom bound of the frustum
12522 * @param {Number} top Top bound of the frustum
12523 * @param {Number} near Near bound of the frustum
12524 * @param {Number} far Far bound of the frustum
12525 * @returns {mat4} out
12526 */
12527function frustum(out, left, right, bottom, top, near, far) {
12528 var rl = 1 / (right - left),
12529 tb = 1 / (top - bottom),
12530 nf = 1 / (near - far);
12531 out[0] = (near * 2) * rl;
12532 out[1] = 0;
12533 out[2] = 0;
12534 out[3] = 0;
12535 out[4] = 0;
12536 out[5] = (near * 2) * tb;
12537 out[6] = 0;
12538 out[7] = 0;
12539 out[8] = (right + left) * rl;
12540 out[9] = (top + bottom) * tb;
12541 out[10] = (far + near) * nf;
12542 out[11] = -1;
12543 out[12] = 0;
12544 out[13] = 0;
12545 out[14] = (far * near * 2) * nf;
12546 out[15] = 0;
12547 return out;
12548};
12549},{}],48:[function(_dereq_,module,exports){
12550module.exports = identity;
12551
12552/**
12553 * Set a mat4 to the identity matrix
12554 *
12555 * @param {mat4} out the receiving matrix
12556 * @returns {mat4} out
12557 */
12558function identity(out) {
12559 out[0] = 1;
12560 out[1] = 0;
12561 out[2] = 0;
12562 out[3] = 0;
12563 out[4] = 0;
12564 out[5] = 1;
12565 out[6] = 0;
12566 out[7] = 0;
12567 out[8] = 0;
12568 out[9] = 0;
12569 out[10] = 1;
12570 out[11] = 0;
12571 out[12] = 0;
12572 out[13] = 0;
12573 out[14] = 0;
12574 out[15] = 1;
12575 return out;
12576};
12577},{}],49:[function(_dereq_,module,exports){
12578module.exports = {
12579 create: _dereq_('./create')
12580 , clone: _dereq_('./clone')
12581 , copy: _dereq_('./copy')
12582 , identity: _dereq_('./identity')
12583 , transpose: _dereq_('./transpose')
12584 , invert: _dereq_('./invert')
12585 , adjoint: _dereq_('./adjoint')
12586 , determinant: _dereq_('./determinant')
12587 , multiply: _dereq_('./multiply')
12588 , translate: _dereq_('./translate')
12589 , scale: _dereq_('./scale')
12590 , rotate: _dereq_('./rotate')
12591 , rotateX: _dereq_('./rotateX')
12592 , rotateY: _dereq_('./rotateY')
12593 , rotateZ: _dereq_('./rotateZ')
12594 , fromRotation: _dereq_('./fromRotation')
12595 , fromRotationTranslation: _dereq_('./fromRotationTranslation')
12596 , fromScaling: _dereq_('./fromScaling')
12597 , fromTranslation: _dereq_('./fromTranslation')
12598 , fromXRotation: _dereq_('./fromXRotation')
12599 , fromYRotation: _dereq_('./fromYRotation')
12600 , fromZRotation: _dereq_('./fromZRotation')
12601 , fromQuat: _dereq_('./fromQuat')
12602 , frustum: _dereq_('./frustum')
12603 , perspective: _dereq_('./perspective')
12604 , perspectiveFromFieldOfView: _dereq_('./perspectiveFromFieldOfView')
12605 , ortho: _dereq_('./ortho')
12606 , lookAt: _dereq_('./lookAt')
12607 , str: _dereq_('./str')
12608}
12609
12610},{"./adjoint":34,"./clone":35,"./copy":36,"./create":37,"./determinant":38,"./fromQuat":39,"./fromRotation":40,"./fromRotationTranslation":41,"./fromScaling":42,"./fromTranslation":43,"./fromXRotation":44,"./fromYRotation":45,"./fromZRotation":46,"./frustum":47,"./identity":48,"./invert":50,"./lookAt":51,"./multiply":52,"./ortho":53,"./perspective":54,"./perspectiveFromFieldOfView":55,"./rotate":56,"./rotateX":57,"./rotateY":58,"./rotateZ":59,"./scale":60,"./str":61,"./translate":62,"./transpose":63}],50:[function(_dereq_,module,exports){
12611module.exports = invert;
12612
12613/**
12614 * Inverts a mat4
12615 *
12616 * @param {mat4} out the receiving matrix
12617 * @param {mat4} a the source matrix
12618 * @returns {mat4} out
12619 */
12620function invert(out, a) {
12621 var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3],
12622 a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7],
12623 a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11],
12624 a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15],
12625
12626 b00 = a00 * a11 - a01 * a10,
12627 b01 = a00 * a12 - a02 * a10,
12628 b02 = a00 * a13 - a03 * a10,
12629 b03 = a01 * a12 - a02 * a11,
12630 b04 = a01 * a13 - a03 * a11,
12631 b05 = a02 * a13 - a03 * a12,
12632 b06 = a20 * a31 - a21 * a30,
12633 b07 = a20 * a32 - a22 * a30,
12634 b08 = a20 * a33 - a23 * a30,
12635 b09 = a21 * a32 - a22 * a31,
12636 b10 = a21 * a33 - a23 * a31,
12637 b11 = a22 * a33 - a23 * a32,
12638
12639 // Calculate the determinant
12640 det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
12641
12642 if (!det) {
12643 return null;
12644 }
12645 det = 1.0 / det;
12646
12647 out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
12648 out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
12649 out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
12650 out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det;
12651 out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
12652 out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
12653 out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
12654 out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det;
12655 out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
12656 out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
12657 out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
12658 out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det;
12659 out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det;
12660 out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det;
12661 out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det;
12662 out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det;
12663
12664 return out;
12665};
12666},{}],51:[function(_dereq_,module,exports){
12667var identity = _dereq_('./identity');
12668
12669module.exports = lookAt;
12670
12671/**
12672 * Generates a look-at matrix with the given eye position, focal point, and up axis
12673 *
12674 * @param {mat4} out mat4 frustum matrix will be written into
12675 * @param {vec3} eye Position of the viewer
12676 * @param {vec3} center Point the viewer is looking at
12677 * @param {vec3} up vec3 pointing up
12678 * @returns {mat4} out
12679 */
12680function lookAt(out, eye, center, up) {
12681 var x0, x1, x2, y0, y1, y2, z0, z1, z2, len,
12682 eyex = eye[0],
12683 eyey = eye[1],
12684 eyez = eye[2],
12685 upx = up[0],
12686 upy = up[1],
12687 upz = up[2],
12688 centerx = center[0],
12689 centery = center[1],
12690 centerz = center[2];
12691
12692 if (Math.abs(eyex - centerx) < 0.000001 &&
12693 Math.abs(eyey - centery) < 0.000001 &&
12694 Math.abs(eyez - centerz) < 0.000001) {
12695 return identity(out);
12696 }
12697
12698 z0 = eyex - centerx;
12699 z1 = eyey - centery;
12700 z2 = eyez - centerz;
12701
12702 len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2);
12703 z0 *= len;
12704 z1 *= len;
12705 z2 *= len;
12706
12707 x0 = upy * z2 - upz * z1;
12708 x1 = upz * z0 - upx * z2;
12709 x2 = upx * z1 - upy * z0;
12710 len = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2);
12711 if (!len) {
12712 x0 = 0;
12713 x1 = 0;
12714 x2 = 0;
12715 } else {
12716 len = 1 / len;
12717 x0 *= len;
12718 x1 *= len;
12719 x2 *= len;
12720 }
12721
12722 y0 = z1 * x2 - z2 * x1;
12723 y1 = z2 * x0 - z0 * x2;
12724 y2 = z0 * x1 - z1 * x0;
12725
12726 len = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2);
12727 if (!len) {
12728 y0 = 0;
12729 y1 = 0;
12730 y2 = 0;
12731 } else {
12732 len = 1 / len;
12733 y0 *= len;
12734 y1 *= len;
12735 y2 *= len;
12736 }
12737
12738 out[0] = x0;
12739 out[1] = y0;
12740 out[2] = z0;
12741 out[3] = 0;
12742 out[4] = x1;
12743 out[5] = y1;
12744 out[6] = z1;
12745 out[7] = 0;
12746 out[8] = x2;
12747 out[9] = y2;
12748 out[10] = z2;
12749 out[11] = 0;
12750 out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez);
12751 out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez);
12752 out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez);
12753 out[15] = 1;
12754
12755 return out;
12756};
12757},{"./identity":48}],52:[function(_dereq_,module,exports){
12758module.exports = multiply;
12759
12760/**
12761 * Multiplies two mat4's
12762 *
12763 * @param {mat4} out the receiving matrix
12764 * @param {mat4} a the first operand
12765 * @param {mat4} b the second operand
12766 * @returns {mat4} out
12767 */
12768function multiply(out, a, b) {
12769 var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3],
12770 a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7],
12771 a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11],
12772 a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
12773
12774 // Cache only the current line of the second matrix
12775 var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3];
12776 out[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
12777 out[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
12778 out[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
12779 out[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33;
12780
12781 b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7];
12782 out[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
12783 out[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
12784 out[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
12785 out[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33;
12786
12787 b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11];
12788 out[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
12789 out[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
12790 out[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
12791 out[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33;
12792
12793 b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15];
12794 out[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
12795 out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
12796 out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
12797 out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33;
12798 return out;
12799};
12800},{}],53:[function(_dereq_,module,exports){
12801module.exports = ortho;
12802
12803/**
12804 * Generates a orthogonal projection matrix with the given bounds
12805 *
12806 * @param {mat4} out mat4 frustum matrix will be written into
12807 * @param {number} left Left bound of the frustum
12808 * @param {number} right Right bound of the frustum
12809 * @param {number} bottom Bottom bound of the frustum
12810 * @param {number} top Top bound of the frustum
12811 * @param {number} near Near bound of the frustum
12812 * @param {number} far Far bound of the frustum
12813 * @returns {mat4} out
12814 */
12815function ortho(out, left, right, bottom, top, near, far) {
12816 var lr = 1 / (left - right),
12817 bt = 1 / (bottom - top),
12818 nf = 1 / (near - far);
12819 out[0] = -2 * lr;
12820 out[1] = 0;
12821 out[2] = 0;
12822 out[3] = 0;
12823 out[4] = 0;
12824 out[5] = -2 * bt;
12825 out[6] = 0;
12826 out[7] = 0;
12827 out[8] = 0;
12828 out[9] = 0;
12829 out[10] = 2 * nf;
12830 out[11] = 0;
12831 out[12] = (left + right) * lr;
12832 out[13] = (top + bottom) * bt;
12833 out[14] = (far + near) * nf;
12834 out[15] = 1;
12835 return out;
12836};
12837},{}],54:[function(_dereq_,module,exports){
12838module.exports = perspective;
12839
12840/**
12841 * Generates a perspective projection matrix with the given bounds
12842 *
12843 * @param {mat4} out mat4 frustum matrix will be written into
12844 * @param {number} fovy Vertical field of view in radians
12845 * @param {number} aspect Aspect ratio. typically viewport width/height
12846 * @param {number} near Near bound of the frustum
12847 * @param {number} far Far bound of the frustum
12848 * @returns {mat4} out
12849 */
12850function perspective(out, fovy, aspect, near, far) {
12851 var f = 1.0 / Math.tan(fovy / 2),
12852 nf = 1 / (near - far);
12853 out[0] = f / aspect;
12854 out[1] = 0;
12855 out[2] = 0;
12856 out[3] = 0;
12857 out[4] = 0;
12858 out[5] = f;
12859 out[6] = 0;
12860 out[7] = 0;
12861 out[8] = 0;
12862 out[9] = 0;
12863 out[10] = (far + near) * nf;
12864 out[11] = -1;
12865 out[12] = 0;
12866 out[13] = 0;
12867 out[14] = (2 * far * near) * nf;
12868 out[15] = 0;
12869 return out;
12870};
12871},{}],55:[function(_dereq_,module,exports){
12872module.exports = perspectiveFromFieldOfView;
12873
12874/**
12875 * Generates a perspective projection matrix with the given field of view.
12876 * This is primarily useful for generating projection matrices to be used
12877 * with the still experiemental WebVR API.
12878 *
12879 * @param {mat4} out mat4 frustum matrix will be written into
12880 * @param {number} fov Object containing the following values: upDegrees, downDegrees, leftDegrees, rightDegrees
12881 * @param {number} near Near bound of the frustum
12882 * @param {number} far Far bound of the frustum
12883 * @returns {mat4} out
12884 */
12885function perspectiveFromFieldOfView(out, fov, near, far) {
12886 var upTan = Math.tan(fov.upDegrees * Math.PI/180.0),
12887 downTan = Math.tan(fov.downDegrees * Math.PI/180.0),
12888 leftTan = Math.tan(fov.leftDegrees * Math.PI/180.0),
12889 rightTan = Math.tan(fov.rightDegrees * Math.PI/180.0),
12890 xScale = 2.0 / (leftTan + rightTan),
12891 yScale = 2.0 / (upTan + downTan);
12892
12893 out[0] = xScale;
12894 out[1] = 0.0;
12895 out[2] = 0.0;
12896 out[3] = 0.0;
12897 out[4] = 0.0;
12898 out[5] = yScale;
12899 out[6] = 0.0;
12900 out[7] = 0.0;
12901 out[8] = -((leftTan - rightTan) * xScale * 0.5);
12902 out[9] = ((upTan - downTan) * yScale * 0.5);
12903 out[10] = far / (near - far);
12904 out[11] = -1.0;
12905 out[12] = 0.0;
12906 out[13] = 0.0;
12907 out[14] = (far * near) / (near - far);
12908 out[15] = 0.0;
12909 return out;
12910}
12911
12912
12913},{}],56:[function(_dereq_,module,exports){
12914module.exports = rotate;
12915
12916/**
12917 * Rotates a mat4 by the given angle
12918 *
12919 * @param {mat4} out the receiving matrix
12920 * @param {mat4} a the matrix to rotate
12921 * @param {Number} rad the angle to rotate the matrix by
12922 * @param {vec3} axis the axis to rotate around
12923 * @returns {mat4} out
12924 */
12925function rotate(out, a, rad, axis) {
12926 var x = axis[0], y = axis[1], z = axis[2],
12927 len = Math.sqrt(x * x + y * y + z * z),
12928 s, c, t,
12929 a00, a01, a02, a03,
12930 a10, a11, a12, a13,
12931 a20, a21, a22, a23,
12932 b00, b01, b02,
12933 b10, b11, b12,
12934 b20, b21, b22;
12935
12936 if (Math.abs(len) < 0.000001) { return null; }
12937
12938 len = 1 / len;
12939 x *= len;
12940 y *= len;
12941 z *= len;
12942
12943 s = Math.sin(rad);
12944 c = Math.cos(rad);
12945 t = 1 - c;
12946
12947 a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3];
12948 a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7];
12949 a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11];
12950
12951 // Construct the elements of the rotation matrix
12952 b00 = x * x * t + c; b01 = y * x * t + z * s; b02 = z * x * t - y * s;
12953 b10 = x * y * t - z * s; b11 = y * y * t + c; b12 = z * y * t + x * s;
12954 b20 = x * z * t + y * s; b21 = y * z * t - x * s; b22 = z * z * t + c;
12955
12956 // Perform rotation-specific matrix multiplication
12957 out[0] = a00 * b00 + a10 * b01 + a20 * b02;
12958 out[1] = a01 * b00 + a11 * b01 + a21 * b02;
12959 out[2] = a02 * b00 + a12 * b01 + a22 * b02;
12960 out[3] = a03 * b00 + a13 * b01 + a23 * b02;
12961 out[4] = a00 * b10 + a10 * b11 + a20 * b12;
12962 out[5] = a01 * b10 + a11 * b11 + a21 * b12;
12963 out[6] = a02 * b10 + a12 * b11 + a22 * b12;
12964 out[7] = a03 * b10 + a13 * b11 + a23 * b12;
12965 out[8] = a00 * b20 + a10 * b21 + a20 * b22;
12966 out[9] = a01 * b20 + a11 * b21 + a21 * b22;
12967 out[10] = a02 * b20 + a12 * b21 + a22 * b22;
12968 out[11] = a03 * b20 + a13 * b21 + a23 * b22;
12969
12970 if (a !== out) { // If the source and destination differ, copy the unchanged last row
12971 out[12] = a[12];
12972 out[13] = a[13];
12973 out[14] = a[14];
12974 out[15] = a[15];
12975 }
12976 return out;
12977};
12978},{}],57:[function(_dereq_,module,exports){
12979module.exports = rotateX;
12980
12981/**
12982 * Rotates a matrix by the given angle around the X axis
12983 *
12984 * @param {mat4} out the receiving matrix
12985 * @param {mat4} a the matrix to rotate
12986 * @param {Number} rad the angle to rotate the matrix by
12987 * @returns {mat4} out
12988 */
12989function rotateX(out, a, rad) {
12990 var s = Math.sin(rad),
12991 c = Math.cos(rad),
12992 a10 = a[4],
12993 a11 = a[5],
12994 a12 = a[6],
12995 a13 = a[7],
12996 a20 = a[8],
12997 a21 = a[9],
12998 a22 = a[10],
12999 a23 = a[11];
13000
13001 if (a !== out) { // If the source and destination differ, copy the unchanged rows
13002 out[0] = a[0];
13003 out[1] = a[1];
13004 out[2] = a[2];
13005 out[3] = a[3];
13006 out[12] = a[12];
13007 out[13] = a[13];
13008 out[14] = a[14];
13009 out[15] = a[15];
13010 }
13011
13012 // Perform axis-specific matrix multiplication
13013 out[4] = a10 * c + a20 * s;
13014 out[5] = a11 * c + a21 * s;
13015 out[6] = a12 * c + a22 * s;
13016 out[7] = a13 * c + a23 * s;
13017 out[8] = a20 * c - a10 * s;
13018 out[9] = a21 * c - a11 * s;
13019 out[10] = a22 * c - a12 * s;
13020 out[11] = a23 * c - a13 * s;
13021 return out;
13022};
13023},{}],58:[function(_dereq_,module,exports){
13024module.exports = rotateY;
13025
13026/**
13027 * Rotates a matrix by the given angle around the Y axis
13028 *
13029 * @param {mat4} out the receiving matrix
13030 * @param {mat4} a the matrix to rotate
13031 * @param {Number} rad the angle to rotate the matrix by
13032 * @returns {mat4} out
13033 */
13034function rotateY(out, a, rad) {
13035 var s = Math.sin(rad),
13036 c = Math.cos(rad),
13037 a00 = a[0],
13038 a01 = a[1],
13039 a02 = a[2],
13040 a03 = a[3],
13041 a20 = a[8],
13042 a21 = a[9],
13043 a22 = a[10],
13044 a23 = a[11];
13045
13046 if (a !== out) { // If the source and destination differ, copy the unchanged rows
13047 out[4] = a[4];
13048 out[5] = a[5];
13049 out[6] = a[6];
13050 out[7] = a[7];
13051 out[12] = a[12];
13052 out[13] = a[13];
13053 out[14] = a[14];
13054 out[15] = a[15];
13055 }
13056
13057 // Perform axis-specific matrix multiplication
13058 out[0] = a00 * c - a20 * s;
13059 out[1] = a01 * c - a21 * s;
13060 out[2] = a02 * c - a22 * s;
13061 out[3] = a03 * c - a23 * s;
13062 out[8] = a00 * s + a20 * c;
13063 out[9] = a01 * s + a21 * c;
13064 out[10] = a02 * s + a22 * c;
13065 out[11] = a03 * s + a23 * c;
13066 return out;
13067};
13068},{}],59:[function(_dereq_,module,exports){
13069module.exports = rotateZ;
13070
13071/**
13072 * Rotates a matrix by the given angle around the Z axis
13073 *
13074 * @param {mat4} out the receiving matrix
13075 * @param {mat4} a the matrix to rotate
13076 * @param {Number} rad the angle to rotate the matrix by
13077 * @returns {mat4} out
13078 */
13079function rotateZ(out, a, rad) {
13080 var s = Math.sin(rad),
13081 c = Math.cos(rad),
13082 a00 = a[0],
13083 a01 = a[1],
13084 a02 = a[2],
13085 a03 = a[3],
13086 a10 = a[4],
13087 a11 = a[5],
13088 a12 = a[6],
13089 a13 = a[7];
13090
13091 if (a !== out) { // If the source and destination differ, copy the unchanged last row
13092 out[8] = a[8];
13093 out[9] = a[9];
13094 out[10] = a[10];
13095 out[11] = a[11];
13096 out[12] = a[12];
13097 out[13] = a[13];
13098 out[14] = a[14];
13099 out[15] = a[15];
13100 }
13101
13102 // Perform axis-specific matrix multiplication
13103 out[0] = a00 * c + a10 * s;
13104 out[1] = a01 * c + a11 * s;
13105 out[2] = a02 * c + a12 * s;
13106 out[3] = a03 * c + a13 * s;
13107 out[4] = a10 * c - a00 * s;
13108 out[5] = a11 * c - a01 * s;
13109 out[6] = a12 * c - a02 * s;
13110 out[7] = a13 * c - a03 * s;
13111 return out;
13112};
13113},{}],60:[function(_dereq_,module,exports){
13114module.exports = scale;
13115
13116/**
13117 * Scales the mat4 by the dimensions in the given vec3
13118 *
13119 * @param {mat4} out the receiving matrix
13120 * @param {mat4} a the matrix to scale
13121 * @param {vec3} v the vec3 to scale the matrix by
13122 * @returns {mat4} out
13123 **/
13124function scale(out, a, v) {
13125 var x = v[0], y = v[1], z = v[2];
13126
13127 out[0] = a[0] * x;
13128 out[1] = a[1] * x;
13129 out[2] = a[2] * x;
13130 out[3] = a[3] * x;
13131 out[4] = a[4] * y;
13132 out[5] = a[5] * y;
13133 out[6] = a[6] * y;
13134 out[7] = a[7] * y;
13135 out[8] = a[8] * z;
13136 out[9] = a[9] * z;
13137 out[10] = a[10] * z;
13138 out[11] = a[11] * z;
13139 out[12] = a[12];
13140 out[13] = a[13];
13141 out[14] = a[14];
13142 out[15] = a[15];
13143 return out;
13144};
13145},{}],61:[function(_dereq_,module,exports){
13146module.exports = str;
13147
13148/**
13149 * Returns a string representation of a mat4
13150 *
13151 * @param {mat4} mat matrix to represent as a string
13152 * @returns {String} string representation of the matrix
13153 */
13154function str(a) {
13155 return 'mat4(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ', ' +
13156 a[4] + ', ' + a[5] + ', ' + a[6] + ', ' + a[7] + ', ' +
13157 a[8] + ', ' + a[9] + ', ' + a[10] + ', ' + a[11] + ', ' +
13158 a[12] + ', ' + a[13] + ', ' + a[14] + ', ' + a[15] + ')';
13159};
13160},{}],62:[function(_dereq_,module,exports){
13161module.exports = translate;
13162
13163/**
13164 * Translate a mat4 by the given vector
13165 *
13166 * @param {mat4} out the receiving matrix
13167 * @param {mat4} a the matrix to translate
13168 * @param {vec3} v vector to translate by
13169 * @returns {mat4} out
13170 */
13171function translate(out, a, v) {
13172 var x = v[0], y = v[1], z = v[2],
13173 a00, a01, a02, a03,
13174 a10, a11, a12, a13,
13175 a20, a21, a22, a23;
13176
13177 if (a === out) {
13178 out[12] = a[0] * x + a[4] * y + a[8] * z + a[12];
13179 out[13] = a[1] * x + a[5] * y + a[9] * z + a[13];
13180 out[14] = a[2] * x + a[6] * y + a[10] * z + a[14];
13181 out[15] = a[3] * x + a[7] * y + a[11] * z + a[15];
13182 } else {
13183 a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3];
13184 a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7];
13185 a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11];
13186
13187 out[0] = a00; out[1] = a01; out[2] = a02; out[3] = a03;
13188 out[4] = a10; out[5] = a11; out[6] = a12; out[7] = a13;
13189 out[8] = a20; out[9] = a21; out[10] = a22; out[11] = a23;
13190
13191 out[12] = a00 * x + a10 * y + a20 * z + a[12];
13192 out[13] = a01 * x + a11 * y + a21 * z + a[13];
13193 out[14] = a02 * x + a12 * y + a22 * z + a[14];
13194 out[15] = a03 * x + a13 * y + a23 * z + a[15];
13195 }
13196
13197 return out;
13198};
13199},{}],63:[function(_dereq_,module,exports){
13200module.exports = transpose;
13201
13202/**
13203 * Transpose the values of a mat4
13204 *
13205 * @param {mat4} out the receiving matrix
13206 * @param {mat4} a the source matrix
13207 * @returns {mat4} out
13208 */
13209function transpose(out, a) {
13210 // If we are transposing ourselves we can skip a few steps but have to cache some values
13211 if (out === a) {
13212 var a01 = a[1], a02 = a[2], a03 = a[3],
13213 a12 = a[6], a13 = a[7],
13214 a23 = a[11];
13215
13216 out[1] = a[4];
13217 out[2] = a[8];
13218 out[3] = a[12];
13219 out[4] = a01;
13220 out[6] = a[9];
13221 out[7] = a[13];
13222 out[8] = a02;
13223 out[9] = a12;
13224 out[11] = a[14];
13225 out[12] = a03;
13226 out[13] = a13;
13227 out[14] = a23;
13228 } else {
13229 out[0] = a[0];
13230 out[1] = a[4];
13231 out[2] = a[8];
13232 out[3] = a[12];
13233 out[4] = a[1];
13234 out[5] = a[5];
13235 out[6] = a[9];
13236 out[7] = a[13];
13237 out[8] = a[2];
13238 out[9] = a[6];
13239 out[10] = a[10];
13240 out[11] = a[14];
13241 out[12] = a[3];
13242 out[13] = a[7];
13243 out[14] = a[11];
13244 out[15] = a[15];
13245 }
13246
13247 return out;
13248};
13249},{}],64:[function(_dereq_,module,exports){
13250(function (global){(function (){
13251'use strict'
13252
13253var isBrowser = _dereq_('is-browser')
13254var hasHover
13255
13256if (typeof global.matchMedia === 'function') {
13257 hasHover = !global.matchMedia('(hover: none)').matches
13258}
13259else {
13260 hasHover = isBrowser
13261}
13262
13263module.exports = hasHover
13264
13265}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
13266},{"is-browser":68}],65:[function(_dereq_,module,exports){
13267'use strict'
13268
13269var isBrowser = _dereq_('is-browser')
13270
13271function detect() {
13272 var supported = false
13273
13274 try {
13275 var opts = Object.defineProperty({}, 'passive', {
13276 get: function() {
13277 supported = true
13278 }
13279 })
13280
13281 window.addEventListener('test', null, opts)
13282 window.removeEventListener('test', null, opts)
13283 } catch(e) {
13284 supported = false
13285 }
13286
13287 return supported
13288}
13289
13290module.exports = isBrowser && detect()
13291
13292},{"is-browser":68}],66:[function(_dereq_,module,exports){
13293exports.read = function (buffer, offset, isLE, mLen, nBytes) {
13294 var e, m
13295 var eLen = (nBytes * 8) - mLen - 1
13296 var eMax = (1 << eLen) - 1
13297 var eBias = eMax >> 1
13298 var nBits = -7
13299 var i = isLE ? (nBytes - 1) : 0
13300 var d = isLE ? -1 : 1
13301 var s = buffer[offset + i]
13302
13303 i += d
13304
13305 e = s & ((1 << (-nBits)) - 1)
13306 s >>= (-nBits)
13307 nBits += eLen
13308 for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {}
13309
13310 m = e & ((1 << (-nBits)) - 1)
13311 e >>= (-nBits)
13312 nBits += mLen
13313 for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {}
13314
13315 if (e === 0) {
13316 e = 1 - eBias
13317 } else if (e === eMax) {
13318 return m ? NaN : ((s ? -1 : 1) * Infinity)
13319 } else {
13320 m = m + Math.pow(2, mLen)
13321 e = e - eBias
13322 }
13323 return (s ? -1 : 1) * m * Math.pow(2, e - mLen)
13324}
13325
13326exports.write = function (buffer, value, offset, isLE, mLen, nBytes) {
13327 var e, m, c
13328 var eLen = (nBytes * 8) - mLen - 1
13329 var eMax = (1 << eLen) - 1
13330 var eBias = eMax >> 1
13331 var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0)
13332 var i = isLE ? 0 : (nBytes - 1)
13333 var d = isLE ? 1 : -1
13334 var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0
13335
13336 value = Math.abs(value)
13337
13338 if (isNaN(value) || value === Infinity) {
13339 m = isNaN(value) ? 1 : 0
13340 e = eMax
13341 } else {
13342 e = Math.floor(Math.log(value) / Math.LN2)
13343 if (value * (c = Math.pow(2, -e)) < 1) {
13344 e--
13345 c *= 2
13346 }
13347 if (e + eBias >= 1) {
13348 value += rt / c
13349 } else {
13350 value += rt * Math.pow(2, 1 - eBias)
13351 }
13352 if (value * c >= 2) {
13353 e++
13354 c /= 2
13355 }
13356
13357 if (e + eBias >= eMax) {
13358 m = 0
13359 e = eMax
13360 } else if (e + eBias >= 1) {
13361 m = ((value * c) - 1) * Math.pow(2, mLen)
13362 e = e + eBias
13363 } else {
13364 m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen)
13365 e = 0
13366 }
13367 }
13368
13369 for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {}
13370
13371 e = (e << mLen) | m
13372 eLen += mLen
13373 for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {}
13374
13375 buffer[offset + i - d] |= s * 128
13376}
13377
13378},{}],67:[function(_dereq_,module,exports){
13379if (typeof Object.create === 'function') {
13380 // implementation from standard node.js 'util' module
13381 module.exports = function inherits(ctor, superCtor) {
13382 if (superCtor) {
13383 ctor.super_ = superCtor
13384 ctor.prototype = Object.create(superCtor.prototype, {
13385 constructor: {
13386 value: ctor,
13387 enumerable: false,
13388 writable: true,
13389 configurable: true
13390 }
13391 })
13392 }
13393 };
13394} else {
13395 // old school shim for old browsers
13396 module.exports = function inherits(ctor, superCtor) {
13397 if (superCtor) {
13398 ctor.super_ = superCtor
13399 var TempCtor = function () {}
13400 TempCtor.prototype = superCtor.prototype
13401 ctor.prototype = new TempCtor()
13402 ctor.prototype.constructor = ctor
13403 }
13404 }
13405}
13406
13407},{}],68:[function(_dereq_,module,exports){
13408module.exports = true;
13409},{}],69:[function(_dereq_,module,exports){
13410'use strict'
13411
13412module.exports = isMobile
13413module.exports.isMobile = isMobile
13414module.exports.default = isMobile
13415
13416var mobileRE = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series[46]0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i
13417
13418var tabletRE = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series[46]0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino|android|ipad|playbook|silk/i
13419
13420function isMobile (opts) {
13421 if (!opts) opts = {}
13422 var ua = opts.ua
13423 if (!ua && typeof navigator !== 'undefined') ua = navigator.userAgent
13424 if (ua && ua.headers && typeof ua.headers['user-agent'] === 'string') {
13425 ua = ua.headers['user-agent']
13426 }
13427 if (typeof ua !== 'string') return false
13428
13429 var result = opts.tablet ? tabletRE.test(ua) : mobileRE.test(ua)
13430
13431 if (
13432 !result &&
13433 opts.tablet &&
13434 opts.featureDetect &&
13435 navigator &&
13436 navigator.maxTouchPoints > 1 &&
13437 ua.indexOf('Macintosh') !== -1 &&
13438 ua.indexOf('Safari') !== -1
13439 ) {
13440 result = true
13441 }
13442
13443 return result
13444}
13445
13446},{}],70:[function(_dereq_,module,exports){
13447'use strict';
13448
13449/**
13450 * Is this string all whitespace?
13451 * This solution kind of makes my brain hurt, but it's significantly faster
13452 * than !str.trim() or any other solution I could find.
13453 *
13454 * whitespace codes from: http://en.wikipedia.org/wiki/Whitespace_character
13455 * and verified with:
13456 *
13457 * for(var i = 0; i < 65536; i++) {
13458 * var s = String.fromCharCode(i);
13459 * if(+s===0 && !s.trim()) console.log(i, s);
13460 * }
13461 *
13462 * which counts a couple of these as *not* whitespace, but finds nothing else
13463 * that *is* whitespace. Note that charCodeAt stops at 16 bits, but it appears
13464 * that there are no whitespace characters above this, and code points above
13465 * this do not map onto white space characters.
13466 */
13467
13468module.exports = function(str){
13469 var l = str.length,
13470 a;
13471 for(var i = 0; i < l; i++) {
13472 a = str.charCodeAt(i);
13473 if((a < 9 || a > 13) && (a !== 32) && (a !== 133) && (a !== 160) &&
13474 (a !== 5760) && (a !== 6158) && (a < 8192 || a > 8205) &&
13475 (a !== 8232) && (a !== 8233) && (a !== 8239) && (a !== 8287) &&
13476 (a !== 8288) && (a !== 12288) && (a !== 65279)) {
13477 return false;
13478 }
13479 }
13480 return true;
13481}
13482
13483},{}],71:[function(_dereq_,module,exports){
13484var rootPosition = { left: 0, top: 0 }
13485
13486module.exports = mouseEventOffset
13487function mouseEventOffset (ev, target, out) {
13488 target = target || ev.currentTarget || ev.srcElement
13489 if (!Array.isArray(out)) {
13490 out = [ 0, 0 ]
13491 }
13492 var cx = ev.clientX || 0
13493 var cy = ev.clientY || 0
13494 var rect = getBoundingClientOffset(target)
13495 out[0] = cx - rect.left
13496 out[1] = cy - rect.top
13497 return out
13498}
13499
13500function getBoundingClientOffset (element) {
13501 if (element === window ||
13502 element === document ||
13503 element === document.body) {
13504 return rootPosition
13505 } else {
13506 return element.getBoundingClientRect()
13507 }
13508}
13509
13510},{}],72:[function(_dereq_,module,exports){
13511(function (global,setImmediate){(function (){
13512/*! Native Promise Only
13513 v0.8.1 (c) Kyle Simpson
13514 MIT License: http://getify.mit-license.org
13515*/
13516
13517(function UMD(name,context,definition){
13518 // special form of UMD for polyfilling across evironments
13519 context[name] = context[name] || definition();
13520 if (typeof module != "undefined" && module.exports) { module.exports = context[name]; }
13521 else if (typeof define == "function" && define.amd) { define(function $AMD$(){ return context[name]; }); }
13522})("Promise",typeof global != "undefined" ? global : this,function DEF(){
13523 /*jshint validthis:true */
13524 "use strict";
13525
13526 var builtInProp, cycle, scheduling_queue,
13527 ToString = Object.prototype.toString,
13528 timer = (typeof setImmediate != "undefined") ?
13529 function timer(fn) { return setImmediate(fn); } :
13530 setTimeout
13531 ;
13532
13533 // dammit, IE8.
13534 try {
13535 Object.defineProperty({},"x",{});
13536 builtInProp = function builtInProp(obj,name,val,config) {
13537 return Object.defineProperty(obj,name,{
13538 value: val,
13539 writable: true,
13540 configurable: config !== false
13541 });
13542 };
13543 }
13544 catch (err) {
13545 builtInProp = function builtInProp(obj,name,val) {
13546 obj[name] = val;
13547 return obj;
13548 };
13549 }
13550
13551 // Note: using a queue instead of array for efficiency
13552 scheduling_queue = (function Queue() {
13553 var first, last, item;
13554
13555 function Item(fn,self) {
13556 this.fn = fn;
13557 this.self = self;
13558 this.next = void 0;
13559 }
13560
13561 return {
13562 add: function add(fn,self) {
13563 item = new Item(fn,self);
13564 if (last) {
13565 last.next = item;
13566 }
13567 else {
13568 first = item;
13569 }
13570 last = item;
13571 item = void 0;
13572 },
13573 drain: function drain() {
13574 var f = first;
13575 first = last = cycle = void 0;
13576
13577 while (f) {
13578 f.fn.call(f.self);
13579 f = f.next;
13580 }
13581 }
13582 };
13583 })();
13584
13585 function schedule(fn,self) {
13586 scheduling_queue.add(fn,self);
13587 if (!cycle) {
13588 cycle = timer(scheduling_queue.drain);
13589 }
13590 }
13591
13592 // promise duck typing
13593 function isThenable(o) {
13594 var _then, o_type = typeof o;
13595
13596 if (o != null &&
13597 (
13598 o_type == "object" || o_type == "function"
13599 )
13600 ) {
13601 _then = o.then;
13602 }
13603 return typeof _then == "function" ? _then : false;
13604 }
13605
13606 function notify() {
13607 for (var i=0; i<this.chain.length; i++) {
13608 notifyIsolated(
13609 this,
13610 (this.state === 1) ? this.chain[i].success : this.chain[i].failure,
13611 this.chain[i]
13612 );
13613 }
13614 this.chain.length = 0;
13615 }
13616
13617 // NOTE: This is a separate function to isolate
13618 // the `try..catch` so that other code can be
13619 // optimized better
13620 function notifyIsolated(self,cb,chain) {
13621 var ret, _then;
13622 try {
13623 if (cb === false) {
13624 chain.reject(self.msg);
13625 }
13626 else {
13627 if (cb === true) {
13628 ret = self.msg;
13629 }
13630 else {
13631 ret = cb.call(void 0,self.msg);
13632 }
13633
13634 if (ret === chain.promise) {
13635 chain.reject(TypeError("Promise-chain cycle"));
13636 }
13637 else if (_then = isThenable(ret)) {
13638 _then.call(ret,chain.resolve,chain.reject);
13639 }
13640 else {
13641 chain.resolve(ret);
13642 }
13643 }
13644 }
13645 catch (err) {
13646 chain.reject(err);
13647 }
13648 }
13649
13650 function resolve(msg) {
13651 var _then, self = this;
13652
13653 // already triggered?
13654 if (self.triggered) { return; }
13655
13656 self.triggered = true;
13657
13658 // unwrap
13659 if (self.def) {
13660 self = self.def;
13661 }
13662
13663 try {
13664 if (_then = isThenable(msg)) {
13665 schedule(function(){
13666 var def_wrapper = new MakeDefWrapper(self);
13667 try {
13668 _then.call(msg,
13669 function $resolve$(){ resolve.apply(def_wrapper,arguments); },
13670 function $reject$(){ reject.apply(def_wrapper,arguments); }
13671 );
13672 }
13673 catch (err) {
13674 reject.call(def_wrapper,err);
13675 }
13676 })
13677 }
13678 else {
13679 self.msg = msg;
13680 self.state = 1;
13681 if (self.chain.length > 0) {
13682 schedule(notify,self);
13683 }
13684 }
13685 }
13686 catch (err) {
13687 reject.call(new MakeDefWrapper(self),err);
13688 }
13689 }
13690
13691 function reject(msg) {
13692 var self = this;
13693
13694 // already triggered?
13695 if (self.triggered) { return; }
13696
13697 self.triggered = true;
13698
13699 // unwrap
13700 if (self.def) {
13701 self = self.def;
13702 }
13703
13704 self.msg = msg;
13705 self.state = 2;
13706 if (self.chain.length > 0) {
13707 schedule(notify,self);
13708 }
13709 }
13710
13711 function iteratePromises(Constructor,arr,resolver,rejecter) {
13712 for (var idx=0; idx<arr.length; idx++) {
13713 (function IIFE(idx){
13714 Constructor.resolve(arr[idx])
13715 .then(
13716 function $resolver$(msg){
13717 resolver(idx,msg);
13718 },
13719 rejecter
13720 );
13721 })(idx);
13722 }
13723 }
13724
13725 function MakeDefWrapper(self) {
13726 this.def = self;
13727 this.triggered = false;
13728 }
13729
13730 function MakeDef(self) {
13731 this.promise = self;
13732 this.state = 0;
13733 this.triggered = false;
13734 this.chain = [];
13735 this.msg = void 0;
13736 }
13737
13738 function Promise(executor) {
13739 if (typeof executor != "function") {
13740 throw TypeError("Not a function");
13741 }
13742
13743 if (this.__NPO__ !== 0) {
13744 throw TypeError("Not a promise");
13745 }
13746
13747 // instance shadowing the inherited "brand"
13748 // to signal an already "initialized" promise
13749 this.__NPO__ = 1;
13750
13751 var def = new MakeDef(this);
13752
13753 this["then"] = function then(success,failure) {
13754 var o = {
13755 success: typeof success == "function" ? success : true,
13756 failure: typeof failure == "function" ? failure : false
13757 };
13758 // Note: `then(..)` itself can be borrowed to be used against
13759 // a different promise constructor for making the chained promise,
13760 // by substituting a different `this` binding.
13761 o.promise = new this.constructor(function extractChain(resolve,reject) {
13762 if (typeof resolve != "function" || typeof reject != "function") {
13763 throw TypeError("Not a function");
13764 }
13765
13766 o.resolve = resolve;
13767 o.reject = reject;
13768 });
13769 def.chain.push(o);
13770
13771 if (def.state !== 0) {
13772 schedule(notify,def);
13773 }
13774
13775 return o.promise;
13776 };
13777 this["catch"] = function $catch$(failure) {
13778 return this.then(void 0,failure);
13779 };
13780
13781 try {
13782 executor.call(
13783 void 0,
13784 function publicResolve(msg){
13785 resolve.call(def,msg);
13786 },
13787 function publicReject(msg) {
13788 reject.call(def,msg);
13789 }
13790 );
13791 }
13792 catch (err) {
13793 reject.call(def,err);
13794 }
13795 }
13796
13797 var PromisePrototype = builtInProp({},"constructor",Promise,
13798 /*configurable=*/false
13799 );
13800
13801 // Note: Android 4 cannot use `Object.defineProperty(..)` here
13802 Promise.prototype = PromisePrototype;
13803
13804 // built-in "brand" to signal an "uninitialized" promise
13805 builtInProp(PromisePrototype,"__NPO__",0,
13806 /*configurable=*/false
13807 );
13808
13809 builtInProp(Promise,"resolve",function Promise$resolve(msg) {
13810 var Constructor = this;
13811
13812 // spec mandated checks
13813 // note: best "isPromise" check that's practical for now
13814 if (msg && typeof msg == "object" && msg.__NPO__ === 1) {
13815 return msg;
13816 }
13817
13818 return new Constructor(function executor(resolve,reject){
13819 if (typeof resolve != "function" || typeof reject != "function") {
13820 throw TypeError("Not a function");
13821 }
13822
13823 resolve(msg);
13824 });
13825 });
13826
13827 builtInProp(Promise,"reject",function Promise$reject(msg) {
13828 return new this(function executor(resolve,reject){
13829 if (typeof resolve != "function" || typeof reject != "function") {
13830 throw TypeError("Not a function");
13831 }
13832
13833 reject(msg);
13834 });
13835 });
13836
13837 builtInProp(Promise,"all",function Promise$all(arr) {
13838 var Constructor = this;
13839
13840 // spec mandated checks
13841 if (ToString.call(arr) != "[object Array]") {
13842 return Constructor.reject(TypeError("Not an array"));
13843 }
13844 if (arr.length === 0) {
13845 return Constructor.resolve([]);
13846 }
13847
13848 return new Constructor(function executor(resolve,reject){
13849 if (typeof resolve != "function" || typeof reject != "function") {
13850 throw TypeError("Not a function");
13851 }
13852
13853 var len = arr.length, msgs = Array(len), count = 0;
13854
13855 iteratePromises(Constructor,arr,function resolver(idx,msg) {
13856 msgs[idx] = msg;
13857 if (++count === len) {
13858 resolve(msgs);
13859 }
13860 },reject);
13861 });
13862 });
13863
13864 builtInProp(Promise,"race",function Promise$race(arr) {
13865 var Constructor = this;
13866
13867 // spec mandated checks
13868 if (ToString.call(arr) != "[object Array]") {
13869 return Constructor.reject(TypeError("Not an array"));
13870 }
13871
13872 return new Constructor(function executor(resolve,reject){
13873 if (typeof resolve != "function" || typeof reject != "function") {
13874 throw TypeError("Not a function");
13875 }
13876
13877 iteratePromises(Constructor,arr,function resolver(idx,msg){
13878 resolve(msg);
13879 },reject);
13880 });
13881 });
13882
13883 return Promise;
13884});
13885
13886}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("timers").setImmediate)
13887},{"timers":120}],73:[function(_dereq_,module,exports){
13888/*
13889object-assign
13890(c) Sindre Sorhus
13891@license MIT
13892*/
13893
13894'use strict';
13895/* eslint-disable no-unused-vars */
13896var getOwnPropertySymbols = Object.getOwnPropertySymbols;
13897var hasOwnProperty = Object.prototype.hasOwnProperty;
13898var propIsEnumerable = Object.prototype.propertyIsEnumerable;
13899
13900function toObject(val) {
13901 if (val === null || val === undefined) {
13902 throw new TypeError('Object.assign cannot be called with null or undefined');
13903 }
13904
13905 return Object(val);
13906}
13907
13908function shouldUseNative() {
13909 try {
13910 if (!Object.assign) {
13911 return false;
13912 }
13913
13914 // Detect buggy property enumeration order in older V8 versions.
13915
13916 // https://bugs.chromium.org/p/v8/issues/detail?id=4118
13917 var test1 = new String('abc'); // eslint-disable-line no-new-wrappers
13918 test1[5] = 'de';
13919 if (Object.getOwnPropertyNames(test1)[0] === '5') {
13920 return false;
13921 }
13922
13923 // https://bugs.chromium.org/p/v8/issues/detail?id=3056
13924 var test2 = {};
13925 for (var i = 0; i < 10; i++) {
13926 test2['_' + String.fromCharCode(i)] = i;
13927 }
13928 var order2 = Object.getOwnPropertyNames(test2).map(function (n) {
13929 return test2[n];
13930 });
13931 if (order2.join('') !== '0123456789') {
13932 return false;
13933 }
13934
13935 // https://bugs.chromium.org/p/v8/issues/detail?id=3056
13936 var test3 = {};
13937 'abcdefghijklmnopqrst'.split('').forEach(function (letter) {
13938 test3[letter] = letter;
13939 });
13940 if (Object.keys(Object.assign({}, test3)).join('') !==
13941 'abcdefghijklmnopqrst') {
13942 return false;
13943 }
13944
13945 return true;
13946 } catch (err) {
13947 // We don't expect any of the above to throw, but better to be safe.
13948 return false;
13949 }
13950}
13951
13952module.exports = shouldUseNative() ? Object.assign : function (target, source) {
13953 var from;
13954 var to = toObject(target);
13955 var symbols;
13956
13957 for (var s = 1; s < arguments.length; s++) {
13958 from = Object(arguments[s]);
13959
13960 for (var key in from) {
13961 if (hasOwnProperty.call(from, key)) {
13962 to[key] = from[key];
13963 }
13964 }
13965
13966 if (getOwnPropertySymbols) {
13967 symbols = getOwnPropertySymbols(from);
13968 for (var i = 0; i < symbols.length; i++) {
13969 if (propIsEnumerable.call(from, symbols[i])) {
13970 to[symbols[i]] = from[symbols[i]];
13971 }
13972 }
13973 }
13974 }
13975
13976 return to;
13977};
13978
13979},{}],74:[function(_dereq_,module,exports){
13980
13981module.exports = parse
13982
13983/**
13984 * expected argument lengths
13985 * @type {Object}
13986 */
13987
13988var length = {a: 7, c: 6, h: 1, l: 2, m: 2, q: 4, s: 4, t: 2, v: 1, z: 0}
13989
13990/**
13991 * segment pattern
13992 * @type {RegExp}
13993 */
13994
13995var segment = /([astvzqmhlc])([^astvzqmhlc]*)/ig
13996
13997/**
13998 * parse an svg path data string. Generates an Array
13999 * of commands where each command is an Array of the
14000 * form `[command, arg1, arg2, ...]`
14001 *
14002 * @param {String} path
14003 * @return {Array}
14004 */
14005
14006function parse(path) {
14007 var data = []
14008 path.replace(segment, function(_, command, args){
14009 var type = command.toLowerCase()
14010 args = parseValues(args)
14011
14012 // overloaded moveTo
14013 if (type == 'm' && args.length > 2) {
14014 data.push([command].concat(args.splice(0, 2)))
14015 type = 'l'
14016 command = command == 'm' ? 'l' : 'L'
14017 }
14018
14019 while (true) {
14020 if (args.length == length[type]) {
14021 args.unshift(command)
14022 return data.push(args)
14023 }
14024 if (args.length < length[type]) throw new Error('malformed path data')
14025 data.push([command].concat(args.splice(0, length[type])))
14026 }
14027 })
14028 return data
14029}
14030
14031var number = /-?[0-9]*\.?[0-9]+(?:e[-+]?\d+)?/ig
14032
14033function parseValues(args) {
14034 var numbers = args.match(number)
14035 return numbers ? numbers.map(Number) : []
14036}
14037
14038},{}],75:[function(_dereq_,module,exports){
14039/*
14040 * @copyright 2016 Sean Connelly (@voidqk), http://syntheti.cc
14041 * @license MIT
14042 * @preserve Project Home: https://github.com/voidqk/polybooljs
14043 */
14044
14045var BuildLog = _dereq_('./lib/build-log');
14046var Epsilon = _dereq_('./lib/epsilon');
14047var Intersecter = _dereq_('./lib/intersecter');
14048var SegmentChainer = _dereq_('./lib/segment-chainer');
14049var SegmentSelector = _dereq_('./lib/segment-selector');
14050var GeoJSON = _dereq_('./lib/geojson');
14051
14052var buildLog = false;
14053var epsilon = Epsilon();
14054
14055var PolyBool;
14056PolyBool = {
14057 // getter/setter for buildLog
14058 buildLog: function(bl){
14059 if (bl === true)
14060 buildLog = BuildLog();
14061 else if (bl === false)
14062 buildLog = false;
14063 return buildLog === false ? false : buildLog.list;
14064 },
14065 // getter/setter for epsilon
14066 epsilon: function(v){
14067 return epsilon.epsilon(v);
14068 },
14069
14070 // core API
14071 segments: function(poly){
14072 var i = Intersecter(true, epsilon, buildLog);
14073 poly.regions.forEach(i.addRegion);
14074 return {
14075 segments: i.calculate(poly.inverted),
14076 inverted: poly.inverted
14077 };
14078 },
14079 combine: function(segments1, segments2){
14080 var i3 = Intersecter(false, epsilon, buildLog);
14081 return {
14082 combined: i3.calculate(
14083 segments1.segments, segments1.inverted,
14084 segments2.segments, segments2.inverted
14085 ),
14086 inverted1: segments1.inverted,
14087 inverted2: segments2.inverted
14088 };
14089 },
14090 selectUnion: function(combined){
14091 return {
14092 segments: SegmentSelector.union(combined.combined, buildLog),
14093 inverted: combined.inverted1 || combined.inverted2
14094 }
14095 },
14096 selectIntersect: function(combined){
14097 return {
14098 segments: SegmentSelector.intersect(combined.combined, buildLog),
14099 inverted: combined.inverted1 && combined.inverted2
14100 }
14101 },
14102 selectDifference: function(combined){
14103 return {
14104 segments: SegmentSelector.difference(combined.combined, buildLog),
14105 inverted: combined.inverted1 && !combined.inverted2
14106 }
14107 },
14108 selectDifferenceRev: function(combined){
14109 return {
14110 segments: SegmentSelector.differenceRev(combined.combined, buildLog),
14111 inverted: !combined.inverted1 && combined.inverted2
14112 }
14113 },
14114 selectXor: function(combined){
14115 return {
14116 segments: SegmentSelector.xor(combined.combined, buildLog),
14117 inverted: combined.inverted1 !== combined.inverted2
14118 }
14119 },
14120 polygon: function(segments){
14121 return {
14122 regions: SegmentChainer(segments.segments, epsilon, buildLog),
14123 inverted: segments.inverted
14124 };
14125 },
14126
14127 // GeoJSON converters
14128 polygonFromGeoJSON: function(geojson){
14129 return GeoJSON.toPolygon(PolyBool, geojson);
14130 },
14131 polygonToGeoJSON: function(poly){
14132 return GeoJSON.fromPolygon(PolyBool, epsilon, poly);
14133 },
14134
14135 // helper functions for common operations
14136 union: function(poly1, poly2){
14137 return operate(poly1, poly2, PolyBool.selectUnion);
14138 },
14139 intersect: function(poly1, poly2){
14140 return operate(poly1, poly2, PolyBool.selectIntersect);
14141 },
14142 difference: function(poly1, poly2){
14143 return operate(poly1, poly2, PolyBool.selectDifference);
14144 },
14145 differenceRev: function(poly1, poly2){
14146 return operate(poly1, poly2, PolyBool.selectDifferenceRev);
14147 },
14148 xor: function(poly1, poly2){
14149 return operate(poly1, poly2, PolyBool.selectXor);
14150 }
14151};
14152
14153function operate(poly1, poly2, selector){
14154 var seg1 = PolyBool.segments(poly1);
14155 var seg2 = PolyBool.segments(poly2);
14156 var comb = PolyBool.combine(seg1, seg2);
14157 var seg3 = selector(comb);
14158 return PolyBool.polygon(seg3);
14159}
14160
14161if (typeof window === 'object')
14162 window.PolyBool = PolyBool;
14163
14164module.exports = PolyBool;
14165
14166},{"./lib/build-log":76,"./lib/epsilon":77,"./lib/geojson":78,"./lib/intersecter":79,"./lib/segment-chainer":81,"./lib/segment-selector":82}],76:[function(_dereq_,module,exports){
14167// (c) Copyright 2016, Sean Connelly (@voidqk), http://syntheti.cc
14168// MIT License
14169// Project Home: https://github.com/voidqk/polybooljs
14170
14171//
14172// used strictly for logging the processing of the algorithm... only useful if you intend on
14173// looking under the covers (for pretty UI's or debugging)
14174//
14175
14176function BuildLog(){
14177 var my;
14178 var nextSegmentId = 0;
14179 var curVert = false;
14180
14181 function push(type, data){
14182 my.list.push({
14183 type: type,
14184 data: data ? JSON.parse(JSON.stringify(data)) : void 0
14185 });
14186 return my;
14187 }
14188
14189 my = {
14190 list: [],
14191 segmentId: function(){
14192 return nextSegmentId++;
14193 },
14194 checkIntersection: function(seg1, seg2){
14195 return push('check', { seg1: seg1, seg2: seg2 });
14196 },
14197 segmentChop: function(seg, end){
14198 push('div_seg', { seg: seg, pt: end });
14199 return push('chop', { seg: seg, pt: end });
14200 },
14201 statusRemove: function(seg){
14202 return push('pop_seg', { seg: seg });
14203 },
14204 segmentUpdate: function(seg){
14205 return push('seg_update', { seg: seg });
14206 },
14207 segmentNew: function(seg, primary){
14208 return push('new_seg', { seg: seg, primary: primary });
14209 },
14210 segmentRemove: function(seg){
14211 return push('rem_seg', { seg: seg });
14212 },
14213 tempStatus: function(seg, above, below){
14214 return push('temp_status', { seg: seg, above: above, below: below });
14215 },
14216 rewind: function(seg){
14217 return push('rewind', { seg: seg });
14218 },
14219 status: function(seg, above, below){
14220 return push('status', { seg: seg, above: above, below: below });
14221 },
14222 vert: function(x){
14223 if (x === curVert)
14224 return my;
14225 curVert = x;
14226 return push('vert', { x: x });
14227 },
14228 log: function(data){
14229 if (typeof data !== 'string')
14230 data = JSON.stringify(data, false, ' ');
14231 return push('log', { txt: data });
14232 },
14233 reset: function(){
14234 return push('reset');
14235 },
14236 selected: function(segs){
14237 return push('selected', { segs: segs });
14238 },
14239 chainStart: function(seg){
14240 return push('chain_start', { seg: seg });
14241 },
14242 chainRemoveHead: function(index, pt){
14243 return push('chain_rem_head', { index: index, pt: pt });
14244 },
14245 chainRemoveTail: function(index, pt){
14246 return push('chain_rem_tail', { index: index, pt: pt });
14247 },
14248 chainNew: function(pt1, pt2){
14249 return push('chain_new', { pt1: pt1, pt2: pt2 });
14250 },
14251 chainMatch: function(index){
14252 return push('chain_match', { index: index });
14253 },
14254 chainClose: function(index){
14255 return push('chain_close', { index: index });
14256 },
14257 chainAddHead: function(index, pt){
14258 return push('chain_add_head', { index: index, pt: pt });
14259 },
14260 chainAddTail: function(index, pt){
14261 return push('chain_add_tail', { index: index, pt: pt, });
14262 },
14263 chainConnect: function(index1, index2){
14264 return push('chain_con', { index1: index1, index2: index2 });
14265 },
14266 chainReverse: function(index){
14267 return push('chain_rev', { index: index });
14268 },
14269 chainJoin: function(index1, index2){
14270 return push('chain_join', { index1: index1, index2: index2 });
14271 },
14272 done: function(){
14273 return push('done');
14274 }
14275 };
14276 return my;
14277}
14278
14279module.exports = BuildLog;
14280
14281},{}],77:[function(_dereq_,module,exports){
14282// (c) Copyright 2016, Sean Connelly (@voidqk), http://syntheti.cc
14283// MIT License
14284// Project Home: https://github.com/voidqk/polybooljs
14285
14286//
14287// provides the raw computation functions that takes epsilon into account
14288//
14289// zero is defined to be between (-epsilon, epsilon) exclusive
14290//
14291
14292function Epsilon(eps){
14293 if (typeof eps !== 'number')
14294 eps = 0.0000000001; // sane default? sure why not
14295 var my = {
14296 epsilon: function(v){
14297 if (typeof v === 'number')
14298 eps = v;
14299 return eps;
14300 },
14301 pointAboveOrOnLine: function(pt, left, right){
14302 var Ax = left[0];
14303 var Ay = left[1];
14304 var Bx = right[0];
14305 var By = right[1];
14306 var Cx = pt[0];
14307 var Cy = pt[1];
14308 return (Bx - Ax) * (Cy - Ay) - (By - Ay) * (Cx - Ax) >= -eps;
14309 },
14310 pointBetween: function(p, left, right){
14311 // p must be collinear with left->right
14312 // returns false if p == left, p == right, or left == right
14313 var d_py_ly = p[1] - left[1];
14314 var d_rx_lx = right[0] - left[0];
14315 var d_px_lx = p[0] - left[0];
14316 var d_ry_ly = right[1] - left[1];
14317
14318 var dot = d_px_lx * d_rx_lx + d_py_ly * d_ry_ly;
14319 // if `dot` is 0, then `p` == `left` or `left` == `right` (reject)
14320 // if `dot` is less than 0, then `p` is to the left of `left` (reject)
14321 if (dot < eps)
14322 return false;
14323
14324 var sqlen = d_rx_lx * d_rx_lx + d_ry_ly * d_ry_ly;
14325 // if `dot` > `sqlen`, then `p` is to the right of `right` (reject)
14326 // therefore, if `dot - sqlen` is greater than 0, then `p` is to the right of `right` (reject)
14327 if (dot - sqlen > -eps)
14328 return false;
14329
14330 return true;
14331 },
14332 pointsSameX: function(p1, p2){
14333 return Math.abs(p1[0] - p2[0]) < eps;
14334 },
14335 pointsSameY: function(p1, p2){
14336 return Math.abs(p1[1] - p2[1]) < eps;
14337 },
14338 pointsSame: function(p1, p2){
14339 return my.pointsSameX(p1, p2) && my.pointsSameY(p1, p2);
14340 },
14341 pointsCompare: function(p1, p2){
14342 // returns -1 if p1 is smaller, 1 if p2 is smaller, 0 if equal
14343 if (my.pointsSameX(p1, p2))
14344 return my.pointsSameY(p1, p2) ? 0 : (p1[1] < p2[1] ? -1 : 1);
14345 return p1[0] < p2[0] ? -1 : 1;
14346 },
14347 pointsCollinear: function(pt1, pt2, pt3){
14348 // does pt1->pt2->pt3 make a straight line?
14349 // essentially this is just checking to see if the slope(pt1->pt2) === slope(pt2->pt3)
14350 // if slopes are equal, then they must be collinear, because they share pt2
14351 var dx1 = pt1[0] - pt2[0];
14352 var dy1 = pt1[1] - pt2[1];
14353 var dx2 = pt2[0] - pt3[0];
14354 var dy2 = pt2[1] - pt3[1];
14355 return Math.abs(dx1 * dy2 - dx2 * dy1) < eps;
14356 },
14357 linesIntersect: function(a0, a1, b0, b1){
14358 // returns false if the lines are coincident (e.g., parallel or on top of each other)
14359 //
14360 // returns an object if the lines intersect:
14361 // {
14362 // pt: [x, y], where the intersection point is at
14363 // alongA: where intersection point is along A,
14364 // alongB: where intersection point is along B
14365 // }
14366 //
14367 // alongA and alongB will each be one of: -2, -1, 0, 1, 2
14368 //
14369 // with the following meaning:
14370 //
14371 // -2 intersection point is before segment's first point
14372 // -1 intersection point is directly on segment's first point
14373 // 0 intersection point is between segment's first and second points (exclusive)
14374 // 1 intersection point is directly on segment's second point
14375 // 2 intersection point is after segment's second point
14376 var adx = a1[0] - a0[0];
14377 var ady = a1[1] - a0[1];
14378 var bdx = b1[0] - b0[0];
14379 var bdy = b1[1] - b0[1];
14380
14381 var axb = adx * bdy - ady * bdx;
14382 if (Math.abs(axb) < eps)
14383 return false; // lines are coincident
14384
14385 var dx = a0[0] - b0[0];
14386 var dy = a0[1] - b0[1];
14387
14388 var A = (bdx * dy - bdy * dx) / axb;
14389 var B = (adx * dy - ady * dx) / axb;
14390
14391 var ret = {
14392 alongA: 0,
14393 alongB: 0,
14394 pt: [
14395 a0[0] + A * adx,
14396 a0[1] + A * ady
14397 ]
14398 };
14399
14400 // categorize where intersection point is along A and B
14401
14402 if (A <= -eps)
14403 ret.alongA = -2;
14404 else if (A < eps)
14405 ret.alongA = -1;
14406 else if (A - 1 <= -eps)
14407 ret.alongA = 0;
14408 else if (A - 1 < eps)
14409 ret.alongA = 1;
14410 else
14411 ret.alongA = 2;
14412
14413 if (B <= -eps)
14414 ret.alongB = -2;
14415 else if (B < eps)
14416 ret.alongB = -1;
14417 else if (B - 1 <= -eps)
14418 ret.alongB = 0;
14419 else if (B - 1 < eps)
14420 ret.alongB = 1;
14421 else
14422 ret.alongB = 2;
14423
14424 return ret;
14425 },
14426 pointInsideRegion: function(pt, region){
14427 var x = pt[0];
14428 var y = pt[1];
14429 var last_x = region[region.length - 1][0];
14430 var last_y = region[region.length - 1][1];
14431 var inside = false;
14432 for (var i = 0; i < region.length; i++){
14433 var curr_x = region[i][0];
14434 var curr_y = region[i][1];
14435
14436 // if y is between curr_y and last_y, and
14437 // x is to the right of the boundary created by the line
14438 if ((curr_y - y > eps) != (last_y - y > eps) &&
14439 (last_x - curr_x) * (y - curr_y) / (last_y - curr_y) + curr_x - x > eps)
14440 inside = !inside
14441
14442 last_x = curr_x;
14443 last_y = curr_y;
14444 }
14445 return inside;
14446 }
14447 };
14448 return my;
14449}
14450
14451module.exports = Epsilon;
14452
14453},{}],78:[function(_dereq_,module,exports){
14454// (c) Copyright 2017, Sean Connelly (@voidqk), http://syntheti.cc
14455// MIT License
14456// Project Home: https://github.com/voidqk/polybooljs
14457
14458//
14459// convert between PolyBool polygon format and GeoJSON formats (Polygon and MultiPolygon)
14460//
14461
14462var GeoJSON = {
14463 // convert a GeoJSON object to a PolyBool polygon
14464 toPolygon: function(PolyBool, geojson){
14465
14466 // converts list of LineString's to segments
14467 function GeoPoly(coords){
14468 // check for empty coords
14469 if (coords.length <= 0)
14470 return PolyBool.segments({ inverted: false, regions: [] });
14471
14472 // convert LineString to segments
14473 function LineString(ls){
14474 // remove tail which should be the same as head
14475 var reg = ls.slice(0, ls.length - 1);
14476 return PolyBool.segments({ inverted: false, regions: [reg] });
14477 }
14478
14479 // the first LineString is considered the outside
14480 var out = LineString(coords[0]);
14481
14482 // the rest of the LineStrings are considered interior holes, so subtract them from the
14483 // current result
14484 for (var i = 1; i < coords.length; i++)
14485 out = PolyBool.selectDifference(PolyBool.combine(out, LineString(coords[i])));
14486
14487 return out;
14488 }
14489
14490 if (geojson.type === 'Polygon'){
14491 // single polygon, so just convert it and we're done
14492 return PolyBool.polygon(GeoPoly(geojson.coordinates));
14493 }
14494 else if (geojson.type === 'MultiPolygon'){
14495 // multiple polygons, so union all the polygons together
14496 var out = PolyBool.segments({ inverted: false, regions: [] });
14497 for (var i = 0; i < geojson.coordinates.length; i++)
14498 out = PolyBool.selectUnion(PolyBool.combine(out, GeoPoly(geojson.coordinates[i])));
14499 return PolyBool.polygon(out);
14500 }
14501 throw new Error('PolyBool: Cannot convert GeoJSON object to PolyBool polygon');
14502 },
14503
14504 // convert a PolyBool polygon to a GeoJSON object
14505 fromPolygon: function(PolyBool, eps, poly){
14506 // make sure out polygon is clean
14507 poly = PolyBool.polygon(PolyBool.segments(poly));
14508
14509 // test if r1 is inside r2
14510 function regionInsideRegion(r1, r2){
14511 // we're guaranteed no lines intersect (because the polygon is clean), but a vertex
14512 // could be on the edge -- so we just average pt[0] and pt[1] to produce a point on the
14513 // edge of the first line, which cannot be on an edge
14514 return eps.pointInsideRegion([
14515 (r1[0][0] + r1[1][0]) * 0.5,
14516 (r1[0][1] + r1[1][1]) * 0.5
14517 ], r2);
14518 }
14519
14520 // calculate inside heirarchy
14521 //
14522 // _____________________ _______ roots -> A -> F
14523 // | A | | F | | |
14524 // | _______ _______ | | ___ | +-- B +-- G
14525 // | | B | | C | | | | | | | |
14526 // | | ___ | | ___ | | | | | | | +-- D
14527 // | | | D | | | | E | | | | | G | | |
14528 // | | |___| | | |___| | | | | | | +-- C
14529 // | |_______| |_______| | | |___| | |
14530 // |_____________________| |_______| +-- E
14531
14532 function newNode(region){
14533 return {
14534 region: region,
14535 children: []
14536 };
14537 }
14538
14539 var roots = newNode(null);
14540
14541 function addChild(root, region){
14542 // first check if we're inside any children
14543 for (var i = 0; i < root.children.length; i++){
14544 var child = root.children[i];
14545 if (regionInsideRegion(region, child.region)){
14546 // we are, so insert inside them instead
14547 addChild(child, region);
14548 return;
14549 }
14550 }
14551
14552 // not inside any children, so check to see if any children are inside us
14553 var node = newNode(region);
14554 for (var i = 0; i < root.children.length; i++){
14555 var child = root.children[i];
14556 if (regionInsideRegion(child.region, region)){
14557 // oops... move the child beneath us, and remove them from root
14558 node.children.push(child);
14559 root.children.splice(i, 1);
14560 i--;
14561 }
14562 }
14563
14564 // now we can add ourselves
14565 root.children.push(node);
14566 }
14567
14568 // add all regions to the root
14569 for (var i = 0; i < poly.regions.length; i++){
14570 var region = poly.regions[i];
14571 if (region.length < 3) // regions must have at least 3 points (sanity check)
14572 continue;
14573 addChild(roots, region);
14574 }
14575
14576 // with our heirarchy, we can distinguish between exterior borders, and interior holes
14577 // the root nodes are exterior, children are interior, children's children are exterior,
14578 // children's children's children are interior, etc
14579
14580 // while we're at it, exteriors are counter-clockwise, and interiors are clockwise
14581
14582 function forceWinding(region, clockwise){
14583 // first, see if we're clockwise or counter-clockwise
14584 // https://en.wikipedia.org/wiki/Shoelace_formula
14585 var winding = 0;
14586 var last_x = region[region.length - 1][0];
14587 var last_y = region[region.length - 1][1];
14588 var copy = [];
14589 for (var i = 0; i < region.length; i++){
14590 var curr_x = region[i][0];
14591 var curr_y = region[i][1];
14592 copy.push([curr_x, curr_y]); // create a copy while we're at it
14593 winding += curr_y * last_x - curr_x * last_y;
14594 last_x = curr_x;
14595 last_y = curr_y;
14596 }
14597 // this assumes Cartesian coordinates (Y is positive going up)
14598 var isclockwise = winding < 0;
14599 if (isclockwise !== clockwise)
14600 copy.reverse();
14601 // while we're here, the last point must be the first point...
14602 copy.push([copy[0][0], copy[0][1]]);
14603 return copy;
14604 }
14605
14606 var geopolys = [];
14607
14608 function addExterior(node){
14609 var poly = [forceWinding(node.region, false)];
14610 geopolys.push(poly);
14611 // children of exteriors are interior
14612 for (var i = 0; i < node.children.length; i++)
14613 poly.push(getInterior(node.children[i]));
14614 }
14615
14616 function getInterior(node){
14617 // children of interiors are exterior
14618 for (var i = 0; i < node.children.length; i++)
14619 addExterior(node.children[i]);
14620 // return the clockwise interior
14621 return forceWinding(node.region, true);
14622 }
14623
14624 // root nodes are exterior
14625 for (var i = 0; i < roots.children.length; i++)
14626 addExterior(roots.children[i]);
14627
14628 // lastly, construct the approrpriate GeoJSON object
14629
14630 if (geopolys.length <= 0) // empty GeoJSON Polygon
14631 return { type: 'Polygon', coordinates: [] };
14632 if (geopolys.length == 1) // use a GeoJSON Polygon
14633 return { type: 'Polygon', coordinates: geopolys[0] };
14634 return { // otherwise, use a GeoJSON MultiPolygon
14635 type: 'MultiPolygon',
14636 coordinates: geopolys
14637 };
14638 }
14639};
14640
14641module.exports = GeoJSON;
14642
14643},{}],79:[function(_dereq_,module,exports){
14644// (c) Copyright 2016, Sean Connelly (@voidqk), http://syntheti.cc
14645// MIT License
14646// Project Home: https://github.com/voidqk/polybooljs
14647
14648//
14649// this is the core work-horse
14650//
14651
14652var LinkedList = _dereq_('./linked-list');
14653
14654function Intersecter(selfIntersection, eps, buildLog){
14655 // selfIntersection is true/false depending on the phase of the overall algorithm
14656
14657 //
14658 // segment creation
14659 //
14660
14661 function segmentNew(start, end){
14662 return {
14663 id: buildLog ? buildLog.segmentId() : -1,
14664 start: start,
14665 end: end,
14666 myFill: {
14667 above: null, // is there fill above us?
14668 below: null // is there fill below us?
14669 },
14670 otherFill: null
14671 };
14672 }
14673
14674 function segmentCopy(start, end, seg){
14675 return {
14676 id: buildLog ? buildLog.segmentId() : -1,
14677 start: start,
14678 end: end,
14679 myFill: {
14680 above: seg.myFill.above,
14681 below: seg.myFill.below
14682 },
14683 otherFill: null
14684 };
14685 }
14686
14687 //
14688 // event logic
14689 //
14690
14691 var event_root = LinkedList.create();
14692
14693 function eventCompare(p1_isStart, p1_1, p1_2, p2_isStart, p2_1, p2_2){
14694 // compare the selected points first
14695 var comp = eps.pointsCompare(p1_1, p2_1);
14696 if (comp !== 0)
14697 return comp;
14698 // the selected points are the same
14699
14700 if (eps.pointsSame(p1_2, p2_2)) // if the non-selected points are the same too...
14701 return 0; // then the segments are equal
14702
14703 if (p1_isStart !== p2_isStart) // if one is a start and the other isn't...
14704 return p1_isStart ? 1 : -1; // favor the one that isn't the start
14705
14706 // otherwise, we'll have to calculate which one is below the other manually
14707 return eps.pointAboveOrOnLine(p1_2,
14708 p2_isStart ? p2_1 : p2_2, // order matters
14709 p2_isStart ? p2_2 : p2_1
14710 ) ? 1 : -1;
14711 }
14712
14713 function eventAdd(ev, other_pt){
14714 event_root.insertBefore(ev, function(here){
14715 // should ev be inserted before here?
14716 var comp = eventCompare(
14717 ev .isStart, ev .pt, other_pt,
14718 here.isStart, here.pt, here.other.pt
14719 );
14720 return comp < 0;
14721 });
14722 }
14723
14724 function eventAddSegmentStart(seg, primary){
14725 var ev_start = LinkedList.node({
14726 isStart: true,
14727 pt: seg.start,
14728 seg: seg,
14729 primary: primary,
14730 other: null,
14731 status: null
14732 });
14733 eventAdd(ev_start, seg.end);
14734 return ev_start;
14735 }
14736
14737 function eventAddSegmentEnd(ev_start, seg, primary){
14738 var ev_end = LinkedList.node({
14739 isStart: false,
14740 pt: seg.end,
14741 seg: seg,
14742 primary: primary,
14743 other: ev_start,
14744 status: null
14745 });
14746 ev_start.other = ev_end;
14747 eventAdd(ev_end, ev_start.pt);
14748 }
14749
14750 function eventAddSegment(seg, primary){
14751 var ev_start = eventAddSegmentStart(seg, primary);
14752 eventAddSegmentEnd(ev_start, seg, primary);
14753 return ev_start;
14754 }
14755
14756 function eventUpdateEnd(ev, end){
14757 // slides an end backwards
14758 // (start)------------(end) to:
14759 // (start)---(end)
14760
14761 if (buildLog)
14762 buildLog.segmentChop(ev.seg, end);
14763
14764 ev.other.remove();
14765 ev.seg.end = end;
14766 ev.other.pt = end;
14767 eventAdd(ev.other, ev.pt);
14768 }
14769
14770 function eventDivide(ev, pt){
14771 var ns = segmentCopy(pt, ev.seg.end, ev.seg);
14772 eventUpdateEnd(ev, pt);
14773 return eventAddSegment(ns, ev.primary);
14774 }
14775
14776 function calculate(primaryPolyInverted, secondaryPolyInverted){
14777 // if selfIntersection is true then there is no secondary polygon, so that isn't used
14778
14779 //
14780 // status logic
14781 //
14782
14783 var status_root = LinkedList.create();
14784
14785 function statusCompare(ev1, ev2){
14786 var a1 = ev1.seg.start;
14787 var a2 = ev1.seg.end;
14788 var b1 = ev2.seg.start;
14789 var b2 = ev2.seg.end;
14790
14791 if (eps.pointsCollinear(a1, b1, b2)){
14792 if (eps.pointsCollinear(a2, b1, b2))
14793 return 1;//eventCompare(true, a1, a2, true, b1, b2);
14794 return eps.pointAboveOrOnLine(a2, b1, b2) ? 1 : -1;
14795 }
14796 return eps.pointAboveOrOnLine(a1, b1, b2) ? 1 : -1;
14797 }
14798
14799 function statusFindSurrounding(ev){
14800 return status_root.findTransition(function(here){
14801 var comp = statusCompare(ev, here.ev);
14802 return comp > 0;
14803 });
14804 }
14805
14806 function checkIntersection(ev1, ev2){
14807 // returns the segment equal to ev1, or false if nothing equal
14808
14809 var seg1 = ev1.seg;
14810 var seg2 = ev2.seg;
14811 var a1 = seg1.start;
14812 var a2 = seg1.end;
14813 var b1 = seg2.start;
14814 var b2 = seg2.end;
14815
14816 if (buildLog)
14817 buildLog.checkIntersection(seg1, seg2);
14818
14819 var i = eps.linesIntersect(a1, a2, b1, b2);
14820
14821 if (i === false){
14822 // segments are parallel or coincident
14823
14824 // if points aren't collinear, then the segments are parallel, so no intersections
14825 if (!eps.pointsCollinear(a1, a2, b1))
14826 return false;
14827 // otherwise, segments are on top of each other somehow (aka coincident)
14828
14829 if (eps.pointsSame(a1, b2) || eps.pointsSame(a2, b1))
14830 return false; // segments touch at endpoints... no intersection
14831
14832 var a1_equ_b1 = eps.pointsSame(a1, b1);
14833 var a2_equ_b2 = eps.pointsSame(a2, b2);
14834
14835 if (a1_equ_b1 && a2_equ_b2)
14836 return ev2; // segments are exactly equal
14837
14838 var a1_between = !a1_equ_b1 && eps.pointBetween(a1, b1, b2);
14839 var a2_between = !a2_equ_b2 && eps.pointBetween(a2, b1, b2);
14840
14841 // handy for debugging:
14842 // buildLog.log({
14843 // a1_equ_b1: a1_equ_b1,
14844 // a2_equ_b2: a2_equ_b2,
14845 // a1_between: a1_between,
14846 // a2_between: a2_between
14847 // });
14848
14849 if (a1_equ_b1){
14850 if (a2_between){
14851 // (a1)---(a2)
14852 // (b1)----------(b2)
14853 eventDivide(ev2, a2);
14854 }
14855 else{
14856 // (a1)----------(a2)
14857 // (b1)---(b2)
14858 eventDivide(ev1, b2);
14859 }
14860 return ev2;
14861 }
14862 else if (a1_between){
14863 if (!a2_equ_b2){
14864 // make a2 equal to b2
14865 if (a2_between){
14866 // (a1)---(a2)
14867 // (b1)-----------------(b2)
14868 eventDivide(ev2, a2);
14869 }
14870 else{
14871 // (a1)----------(a2)
14872 // (b1)----------(b2)
14873 eventDivide(ev1, b2);
14874 }
14875 }
14876
14877 // (a1)---(a2)
14878 // (b1)----------(b2)
14879 eventDivide(ev2, a1);
14880 }
14881 }
14882 else{
14883 // otherwise, lines intersect at i.pt, which may or may not be between the endpoints
14884
14885 // is A divided between its endpoints? (exclusive)
14886 if (i.alongA === 0){
14887 if (i.alongB === -1) // yes, at exactly b1
14888 eventDivide(ev1, b1);
14889 else if (i.alongB === 0) // yes, somewhere between B's endpoints
14890 eventDivide(ev1, i.pt);
14891 else if (i.alongB === 1) // yes, at exactly b2
14892 eventDivide(ev1, b2);
14893 }
14894
14895 // is B divided between its endpoints? (exclusive)
14896 if (i.alongB === 0){
14897 if (i.alongA === -1) // yes, at exactly a1
14898 eventDivide(ev2, a1);
14899 else if (i.alongA === 0) // yes, somewhere between A's endpoints (exclusive)
14900 eventDivide(ev2, i.pt);
14901 else if (i.alongA === 1) // yes, at exactly a2
14902 eventDivide(ev2, a2);
14903 }
14904 }
14905 return false;
14906 }
14907
14908 //
14909 // main event loop
14910 //
14911 var segments = [];
14912 while (!event_root.isEmpty()){
14913 var ev = event_root.getHead();
14914
14915 if (buildLog)
14916 buildLog.vert(ev.pt[0]);
14917
14918 if (ev.isStart){
14919
14920 if (buildLog)
14921 buildLog.segmentNew(ev.seg, ev.primary);
14922
14923 var surrounding = statusFindSurrounding(ev);
14924 var above = surrounding.before ? surrounding.before.ev : null;
14925 var below = surrounding.after ? surrounding.after.ev : null;
14926
14927 if (buildLog){
14928 buildLog.tempStatus(
14929 ev.seg,
14930 above ? above.seg : false,
14931 below ? below.seg : false
14932 );
14933 }
14934
14935 function checkBothIntersections(){
14936 if (above){
14937 var eve = checkIntersection(ev, above);
14938 if (eve)
14939 return eve;
14940 }
14941 if (below)
14942 return checkIntersection(ev, below);
14943 return false;
14944 }
14945
14946 var eve = checkBothIntersections();
14947 if (eve){
14948 // ev and eve are equal
14949 // we'll keep eve and throw away ev
14950
14951 // merge ev.seg's fill information into eve.seg
14952
14953 if (selfIntersection){
14954 var toggle; // are we a toggling edge?
14955 if (ev.seg.myFill.below === null)
14956 toggle = true;
14957 else
14958 toggle = ev.seg.myFill.above !== ev.seg.myFill.below;
14959
14960 // merge two segments that belong to the same polygon
14961 // think of this as sandwiching two segments together, where `eve.seg` is
14962 // the bottom -- this will cause the above fill flag to toggle
14963 if (toggle)
14964 eve.seg.myFill.above = !eve.seg.myFill.above;
14965 }
14966 else{
14967 // merge two segments that belong to different polygons
14968 // each segment has distinct knowledge, so no special logic is needed
14969 // note that this can only happen once per segment in this phase, because we
14970 // are guaranteed that all self-intersections are gone
14971 eve.seg.otherFill = ev.seg.myFill;
14972 }
14973
14974 if (buildLog)
14975 buildLog.segmentUpdate(eve.seg);
14976
14977 ev.other.remove();
14978 ev.remove();
14979 }
14980
14981 if (event_root.getHead() !== ev){
14982 // something was inserted before us in the event queue, so loop back around and
14983 // process it before continuing
14984 if (buildLog)
14985 buildLog.rewind(ev.seg);
14986 continue;
14987 }
14988
14989 //
14990 // calculate fill flags
14991 //
14992 if (selfIntersection){
14993 var toggle; // are we a toggling edge?
14994 if (ev.seg.myFill.below === null) // if we are a new segment...
14995 toggle = true; // then we toggle
14996 else // we are a segment that has previous knowledge from a division
14997 toggle = ev.seg.myFill.above !== ev.seg.myFill.below; // calculate toggle
14998
14999 // next, calculate whether we are filled below us
15000 if (!below){ // if nothing is below us...
15001 // we are filled below us if the polygon is inverted
15002 ev.seg.myFill.below = primaryPolyInverted;
15003 }
15004 else{
15005 // otherwise, we know the answer -- it's the same if whatever is below
15006 // us is filled above it
15007 ev.seg.myFill.below = below.seg.myFill.above;
15008 }
15009
15010 // since now we know if we're filled below us, we can calculate whether
15011 // we're filled above us by applying toggle to whatever is below us
15012 if (toggle)
15013 ev.seg.myFill.above = !ev.seg.myFill.below;
15014 else
15015 ev.seg.myFill.above = ev.seg.myFill.below;
15016 }
15017 else{
15018 // now we fill in any missing transition information, since we are all-knowing
15019 // at this point
15020
15021 if (ev.seg.otherFill === null){
15022 // if we don't have other information, then we need to figure out if we're
15023 // inside the other polygon
15024 var inside;
15025 if (!below){
15026 // if nothing is below us, then we're inside if the other polygon is
15027 // inverted
15028 inside =
15029 ev.primary ? secondaryPolyInverted : primaryPolyInverted;
15030 }
15031 else{ // otherwise, something is below us
15032 // so copy the below segment's other polygon's above
15033 if (ev.primary === below.primary)
15034 inside = below.seg.otherFill.above;
15035 else
15036 inside = below.seg.myFill.above;
15037 }
15038 ev.seg.otherFill = {
15039 above: inside,
15040 below: inside
15041 };
15042 }
15043 }
15044
15045 if (buildLog){
15046 buildLog.status(
15047 ev.seg,
15048 above ? above.seg : false,
15049 below ? below.seg : false
15050 );
15051 }
15052
15053 // insert the status and remember it for later removal
15054 ev.other.status = surrounding.insert(LinkedList.node({ ev: ev }));
15055 }
15056 else{
15057 var st = ev.status;
15058
15059 if (st === null){
15060 throw new Error('PolyBool: Zero-length segment detected; your epsilon is ' +
15061 'probably too small or too large');
15062 }
15063
15064 // removing the status will create two new adjacent edges, so we'll need to check
15065 // for those
15066 if (status_root.exists(st.prev) && status_root.exists(st.next))
15067 checkIntersection(st.prev.ev, st.next.ev);
15068
15069 if (buildLog)
15070 buildLog.statusRemove(st.ev.seg);
15071
15072 // remove the status
15073 st.remove();
15074
15075 // if we've reached this point, we've calculated everything there is to know, so
15076 // save the segment for reporting
15077 if (!ev.primary){
15078 // make sure `seg.myFill` actually points to the primary polygon though
15079 var s = ev.seg.myFill;
15080 ev.seg.myFill = ev.seg.otherFill;
15081 ev.seg.otherFill = s;
15082 }
15083 segments.push(ev.seg);
15084 }
15085
15086 // remove the event and continue
15087 event_root.getHead().remove();
15088 }
15089
15090 if (buildLog)
15091 buildLog.done();
15092
15093 return segments;
15094 }
15095
15096 // return the appropriate API depending on what we're doing
15097 if (!selfIntersection){
15098 // performing combination of polygons, so only deal with already-processed segments
15099 return {
15100 calculate: function(segments1, inverted1, segments2, inverted2){
15101 // segmentsX come from the self-intersection API, or this API
15102 // invertedX is whether we treat that list of segments as an inverted polygon or not
15103 // returns segments that can be used for further operations
15104 segments1.forEach(function(seg){
15105 eventAddSegment(segmentCopy(seg.start, seg.end, seg), true);
15106 });
15107 segments2.forEach(function(seg){
15108 eventAddSegment(segmentCopy(seg.start, seg.end, seg), false);
15109 });
15110 return calculate(inverted1, inverted2);
15111 }
15112 };
15113 }
15114
15115 // otherwise, performing self-intersection, so deal with regions
15116 return {
15117 addRegion: function(region){
15118 // regions are a list of points:
15119 // [ [0, 0], [100, 0], [50, 100] ]
15120 // you can add multiple regions before running calculate
15121 var pt1;
15122 var pt2 = region[region.length - 1];
15123 for (var i = 0; i < region.length; i++){
15124 pt1 = pt2;
15125 pt2 = region[i];
15126
15127 var forward = eps.pointsCompare(pt1, pt2);
15128 if (forward === 0) // points are equal, so we have a zero-length segment
15129 continue; // just skip it
15130
15131 eventAddSegment(
15132 segmentNew(
15133 forward < 0 ? pt1 : pt2,
15134 forward < 0 ? pt2 : pt1
15135 ),
15136 true
15137 );
15138 }
15139 },
15140 calculate: function(inverted){
15141 // is the polygon inverted?
15142 // returns segments
15143 return calculate(inverted, false);
15144 }
15145 };
15146}
15147
15148module.exports = Intersecter;
15149
15150},{"./linked-list":80}],80:[function(_dereq_,module,exports){
15151// (c) Copyright 2016, Sean Connelly (@voidqk), http://syntheti.cc
15152// MIT License
15153// Project Home: https://github.com/voidqk/polybooljs
15154
15155//
15156// simple linked list implementation that allows you to traverse down nodes and save positions
15157//
15158
15159var LinkedList = {
15160 create: function(){
15161 var my = {
15162 root: { root: true, next: null },
15163 exists: function(node){
15164 if (node === null || node === my.root)
15165 return false;
15166 return true;
15167 },
15168 isEmpty: function(){
15169 return my.root.next === null;
15170 },
15171 getHead: function(){
15172 return my.root.next;
15173 },
15174 insertBefore: function(node, check){
15175 var last = my.root;
15176 var here = my.root.next;
15177 while (here !== null){
15178 if (check(here)){
15179 node.prev = here.prev;
15180 node.next = here;
15181 here.prev.next = node;
15182 here.prev = node;
15183 return;
15184 }
15185 last = here;
15186 here = here.next;
15187 }
15188 last.next = node;
15189 node.prev = last;
15190 node.next = null;
15191 },
15192 findTransition: function(check){
15193 var prev = my.root;
15194 var here = my.root.next;
15195 while (here !== null){
15196 if (check(here))
15197 break;
15198 prev = here;
15199 here = here.next;
15200 }
15201 return {
15202 before: prev === my.root ? null : prev,
15203 after: here,
15204 insert: function(node){
15205 node.prev = prev;
15206 node.next = here;
15207 prev.next = node;
15208 if (here !== null)
15209 here.prev = node;
15210 return node;
15211 }
15212 };
15213 }
15214 };
15215 return my;
15216 },
15217 node: function(data){
15218 data.prev = null;
15219 data.next = null;
15220 data.remove = function(){
15221 data.prev.next = data.next;
15222 if (data.next)
15223 data.next.prev = data.prev;
15224 data.prev = null;
15225 data.next = null;
15226 };
15227 return data;
15228 }
15229};
15230
15231module.exports = LinkedList;
15232
15233},{}],81:[function(_dereq_,module,exports){
15234// (c) Copyright 2016, Sean Connelly (@voidqk), http://syntheti.cc
15235// MIT License
15236// Project Home: https://github.com/voidqk/polybooljs
15237
15238//
15239// converts a list of segments into a list of regions, while also removing unnecessary verticies
15240//
15241
15242function SegmentChainer(segments, eps, buildLog){
15243 var chains = [];
15244 var regions = [];
15245
15246 segments.forEach(function(seg){
15247 var pt1 = seg.start;
15248 var pt2 = seg.end;
15249 if (eps.pointsSame(pt1, pt2)){
15250 console.warn('PolyBool: Warning: Zero-length segment detected; your epsilon is ' +
15251 'probably too small or too large');
15252 return;
15253 }
15254
15255 if (buildLog)
15256 buildLog.chainStart(seg);
15257
15258 // search for two chains that this segment matches
15259 var first_match = {
15260 index: 0,
15261 matches_head: false,
15262 matches_pt1: false
15263 };
15264 var second_match = {
15265 index: 0,
15266 matches_head: false,
15267 matches_pt1: false
15268 };
15269 var next_match = first_match;
15270 function setMatch(index, matches_head, matches_pt1){
15271 // return true if we've matched twice
15272 next_match.index = index;
15273 next_match.matches_head = matches_head;
15274 next_match.matches_pt1 = matches_pt1;
15275 if (next_match === first_match){
15276 next_match = second_match;
15277 return false;
15278 }
15279 next_match = null;
15280 return true; // we've matched twice, we're done here
15281 }
15282 for (var i = 0; i < chains.length; i++){
15283 var chain = chains[i];
15284 var head = chain[0];
15285 var head2 = chain[1];
15286 var tail = chain[chain.length - 1];
15287 var tail2 = chain[chain.length - 2];
15288 if (eps.pointsSame(head, pt1)){
15289 if (setMatch(i, true, true))
15290 break;
15291 }
15292 else if (eps.pointsSame(head, pt2)){
15293 if (setMatch(i, true, false))
15294 break;
15295 }
15296 else if (eps.pointsSame(tail, pt1)){
15297 if (setMatch(i, false, true))
15298 break;
15299 }
15300 else if (eps.pointsSame(tail, pt2)){
15301 if (setMatch(i, false, false))
15302 break;
15303 }
15304 }
15305
15306 if (next_match === first_match){
15307 // we didn't match anything, so create a new chain
15308 chains.push([ pt1, pt2 ]);
15309 if (buildLog)
15310 buildLog.chainNew(pt1, pt2);
15311 return;
15312 }
15313
15314 if (next_match === second_match){
15315 // we matched a single chain
15316
15317 if (buildLog)
15318 buildLog.chainMatch(first_match.index);
15319
15320 // add the other point to the apporpriate end, and check to see if we've closed the
15321 // chain into a loop
15322
15323 var index = first_match.index;
15324 var pt = first_match.matches_pt1 ? pt2 : pt1; // if we matched pt1, then we add pt2, etc
15325 var addToHead = first_match.matches_head; // if we matched at head, then add to the head
15326
15327 var chain = chains[index];
15328 var grow = addToHead ? chain[0] : chain[chain.length - 1];
15329 var grow2 = addToHead ? chain[1] : chain[chain.length - 2];
15330 var oppo = addToHead ? chain[chain.length - 1] : chain[0];
15331 var oppo2 = addToHead ? chain[chain.length - 2] : chain[1];
15332
15333 if (eps.pointsCollinear(grow2, grow, pt)){
15334 // grow isn't needed because it's directly between grow2 and pt:
15335 // grow2 ---grow---> pt
15336 if (addToHead){
15337 if (buildLog)
15338 buildLog.chainRemoveHead(first_match.index, pt);
15339 chain.shift();
15340 }
15341 else{
15342 if (buildLog)
15343 buildLog.chainRemoveTail(first_match.index, pt);
15344 chain.pop();
15345 }
15346 grow = grow2; // old grow is gone... new grow is what grow2 was
15347 }
15348
15349 if (eps.pointsSame(oppo, pt)){
15350 // we're closing the loop, so remove chain from chains
15351 chains.splice(index, 1);
15352
15353 if (eps.pointsCollinear(oppo2, oppo, grow)){
15354 // oppo isn't needed because it's directly between oppo2 and grow:
15355 // oppo2 ---oppo--->grow
15356 if (addToHead){
15357 if (buildLog)
15358 buildLog.chainRemoveTail(first_match.index, grow);
15359 chain.pop();
15360 }
15361 else{
15362 if (buildLog)
15363 buildLog.chainRemoveHead(first_match.index, grow);
15364 chain.shift();
15365 }
15366 }
15367
15368 if (buildLog)
15369 buildLog.chainClose(first_match.index);
15370
15371 // we have a closed chain!
15372 regions.push(chain);
15373 return;
15374 }
15375
15376 // not closing a loop, so just add it to the apporpriate side
15377 if (addToHead){
15378 if (buildLog)
15379 buildLog.chainAddHead(first_match.index, pt);
15380 chain.unshift(pt);
15381 }
15382 else{
15383 if (buildLog)
15384 buildLog.chainAddTail(first_match.index, pt);
15385 chain.push(pt);
15386 }
15387 return;
15388 }
15389
15390 // otherwise, we matched two chains, so we need to combine those chains together
15391
15392 function reverseChain(index){
15393 if (buildLog)
15394 buildLog.chainReverse(index);
15395 chains[index].reverse(); // gee, that's easy
15396 }
15397
15398 function appendChain(index1, index2){
15399 // index1 gets index2 appended to it, and index2 is removed
15400 var chain1 = chains[index1];
15401 var chain2 = chains[index2];
15402 var tail = chain1[chain1.length - 1];
15403 var tail2 = chain1[chain1.length - 2];
15404 var head = chain2[0];
15405 var head2 = chain2[1];
15406
15407 if (eps.pointsCollinear(tail2, tail, head)){
15408 // tail isn't needed because it's directly between tail2 and head
15409 // tail2 ---tail---> head
15410 if (buildLog)
15411 buildLog.chainRemoveTail(index1, tail);
15412 chain1.pop();
15413 tail = tail2; // old tail is gone... new tail is what tail2 was
15414 }
15415
15416 if (eps.pointsCollinear(tail, head, head2)){
15417 // head isn't needed because it's directly between tail and head2
15418 // tail ---head---> head2
15419 if (buildLog)
15420 buildLog.chainRemoveHead(index2, head);
15421 chain2.shift();
15422 }
15423
15424 if (buildLog)
15425 buildLog.chainJoin(index1, index2);
15426 chains[index1] = chain1.concat(chain2);
15427 chains.splice(index2, 1);
15428 }
15429
15430 var F = first_match.index;
15431 var S = second_match.index;
15432
15433 if (buildLog)
15434 buildLog.chainConnect(F, S);
15435
15436 var reverseF = chains[F].length < chains[S].length; // reverse the shorter chain, if needed
15437 if (first_match.matches_head){
15438 if (second_match.matches_head){
15439 if (reverseF){
15440 // <<<< F <<<< --- >>>> S >>>>
15441 reverseChain(F);
15442 // >>>> F >>>> --- >>>> S >>>>
15443 appendChain(F, S);
15444 }
15445 else{
15446 // <<<< F <<<< --- >>>> S >>>>
15447 reverseChain(S);
15448 // <<<< F <<<< --- <<<< S <<<< logically same as:
15449 // >>>> S >>>> --- >>>> F >>>>
15450 appendChain(S, F);
15451 }
15452 }
15453 else{
15454 // <<<< F <<<< --- <<<< S <<<< logically same as:
15455 // >>>> S >>>> --- >>>> F >>>>
15456 appendChain(S, F);
15457 }
15458 }
15459 else{
15460 if (second_match.matches_head){
15461 // >>>> F >>>> --- >>>> S >>>>
15462 appendChain(F, S);
15463 }
15464 else{
15465 if (reverseF){
15466 // >>>> F >>>> --- <<<< S <<<<
15467 reverseChain(F);
15468 // <<<< F <<<< --- <<<< S <<<< logically same as:
15469 // >>>> S >>>> --- >>>> F >>>>
15470 appendChain(S, F);
15471 }
15472 else{
15473 // >>>> F >>>> --- <<<< S <<<<
15474 reverseChain(S);
15475 // >>>> F >>>> --- >>>> S >>>>
15476 appendChain(F, S);
15477 }
15478 }
15479 }
15480 });
15481
15482 return regions;
15483}
15484
15485module.exports = SegmentChainer;
15486
15487},{}],82:[function(_dereq_,module,exports){
15488// (c) Copyright 2016, Sean Connelly (@voidqk), http://syntheti.cc
15489// MIT License
15490// Project Home: https://github.com/voidqk/polybooljs
15491
15492//
15493// filter a list of segments based on boolean operations
15494//
15495
15496function select(segments, selection, buildLog){
15497 var result = [];
15498 segments.forEach(function(seg){
15499 var index =
15500 (seg.myFill.above ? 8 : 0) +
15501 (seg.myFill.below ? 4 : 0) +
15502 ((seg.otherFill && seg.otherFill.above) ? 2 : 0) +
15503 ((seg.otherFill && seg.otherFill.below) ? 1 : 0);
15504 if (selection[index] !== 0){
15505 // copy the segment to the results, while also calculating the fill status
15506 result.push({
15507 id: buildLog ? buildLog.segmentId() : -1,
15508 start: seg.start,
15509 end: seg.end,
15510 myFill: {
15511 above: selection[index] === 1, // 1 if filled above
15512 below: selection[index] === 2 // 2 if filled below
15513 },
15514 otherFill: null
15515 });
15516 }
15517 });
15518
15519 if (buildLog)
15520 buildLog.selected(result);
15521
15522 return result;
15523}
15524
15525var SegmentSelector = {
15526 union: function(segments, buildLog){ // primary | secondary
15527 // above1 below1 above2 below2 Keep? Value
15528 // 0 0 0 0 => no 0
15529 // 0 0 0 1 => yes filled below 2
15530 // 0 0 1 0 => yes filled above 1
15531 // 0 0 1 1 => no 0
15532 // 0 1 0 0 => yes filled below 2
15533 // 0 1 0 1 => yes filled below 2
15534 // 0 1 1 0 => no 0
15535 // 0 1 1 1 => no 0
15536 // 1 0 0 0 => yes filled above 1
15537 // 1 0 0 1 => no 0
15538 // 1 0 1 0 => yes filled above 1
15539 // 1 0 1 1 => no 0
15540 // 1 1 0 0 => no 0
15541 // 1 1 0 1 => no 0
15542 // 1 1 1 0 => no 0
15543 // 1 1 1 1 => no 0
15544 return select(segments, [
15545 0, 2, 1, 0,
15546 2, 2, 0, 0,
15547 1, 0, 1, 0,
15548 0, 0, 0, 0
15549 ], buildLog);
15550 },
15551 intersect: function(segments, buildLog){ // primary & secondary
15552 // above1 below1 above2 below2 Keep? Value
15553 // 0 0 0 0 => no 0
15554 // 0 0 0 1 => no 0
15555 // 0 0 1 0 => no 0
15556 // 0 0 1 1 => no 0
15557 // 0 1 0 0 => no 0
15558 // 0 1 0 1 => yes filled below 2
15559 // 0 1 1 0 => no 0
15560 // 0 1 1 1 => yes filled below 2
15561 // 1 0 0 0 => no 0
15562 // 1 0 0 1 => no 0
15563 // 1 0 1 0 => yes filled above 1
15564 // 1 0 1 1 => yes filled above 1
15565 // 1 1 0 0 => no 0
15566 // 1 1 0 1 => yes filled below 2
15567 // 1 1 1 0 => yes filled above 1
15568 // 1 1 1 1 => no 0
15569 return select(segments, [
15570 0, 0, 0, 0,
15571 0, 2, 0, 2,
15572 0, 0, 1, 1,
15573 0, 2, 1, 0
15574 ], buildLog);
15575 },
15576 difference: function(segments, buildLog){ // primary - secondary
15577 // above1 below1 above2 below2 Keep? Value
15578 // 0 0 0 0 => no 0
15579 // 0 0 0 1 => no 0
15580 // 0 0 1 0 => no 0
15581 // 0 0 1 1 => no 0
15582 // 0 1 0 0 => yes filled below 2
15583 // 0 1 0 1 => no 0
15584 // 0 1 1 0 => yes filled below 2
15585 // 0 1 1 1 => no 0
15586 // 1 0 0 0 => yes filled above 1
15587 // 1 0 0 1 => yes filled above 1
15588 // 1 0 1 0 => no 0
15589 // 1 0 1 1 => no 0
15590 // 1 1 0 0 => no 0
15591 // 1 1 0 1 => yes filled above 1
15592 // 1 1 1 0 => yes filled below 2
15593 // 1 1 1 1 => no 0
15594 return select(segments, [
15595 0, 0, 0, 0,
15596 2, 0, 2, 0,
15597 1, 1, 0, 0,
15598 0, 1, 2, 0
15599 ], buildLog);
15600 },
15601 differenceRev: function(segments, buildLog){ // secondary - primary
15602 // above1 below1 above2 below2 Keep? Value
15603 // 0 0 0 0 => no 0
15604 // 0 0 0 1 => yes filled below 2
15605 // 0 0 1 0 => yes filled above 1
15606 // 0 0 1 1 => no 0
15607 // 0 1 0 0 => no 0
15608 // 0 1 0 1 => no 0
15609 // 0 1 1 0 => yes filled above 1
15610 // 0 1 1 1 => yes filled above 1
15611 // 1 0 0 0 => no 0
15612 // 1 0 0 1 => yes filled below 2
15613 // 1 0 1 0 => no 0
15614 // 1 0 1 1 => yes filled below 2
15615 // 1 1 0 0 => no 0
15616 // 1 1 0 1 => no 0
15617 // 1 1 1 0 => no 0
15618 // 1 1 1 1 => no 0
15619 return select(segments, [
15620 0, 2, 1, 0,
15621 0, 0, 1, 1,
15622 0, 2, 0, 2,
15623 0, 0, 0, 0
15624 ], buildLog);
15625 },
15626 xor: function(segments, buildLog){ // primary ^ secondary
15627 // above1 below1 above2 below2 Keep? Value
15628 // 0 0 0 0 => no 0
15629 // 0 0 0 1 => yes filled below 2
15630 // 0 0 1 0 => yes filled above 1
15631 // 0 0 1 1 => no 0
15632 // 0 1 0 0 => yes filled below 2
15633 // 0 1 0 1 => no 0
15634 // 0 1 1 0 => no 0
15635 // 0 1 1 1 => yes filled above 1
15636 // 1 0 0 0 => yes filled above 1
15637 // 1 0 0 1 => no 0
15638 // 1 0 1 0 => no 0
15639 // 1 0 1 1 => yes filled below 2
15640 // 1 1 0 0 => no 0
15641 // 1 1 0 1 => yes filled above 1
15642 // 1 1 1 0 => yes filled below 2
15643 // 1 1 1 1 => no 0
15644 return select(segments, [
15645 0, 2, 1, 0,
15646 2, 0, 0, 1,
15647 1, 0, 0, 2,
15648 0, 1, 2, 0
15649 ], buildLog);
15650 }
15651};
15652
15653module.exports = SegmentSelector;
15654
15655},{}],83:[function(_dereq_,module,exports){
15656'use strict';
15657
15658
15659var Transform = _dereq_('stream').Transform;
15660var streamParser = _dereq_('stream-parser');
15661
15662
15663function ParserStream() {
15664 Transform.call(this, { readableObjectMode: true });
15665}
15666
15667// Inherit from Transform
15668ParserStream.prototype = Object.create(Transform.prototype);
15669ParserStream.prototype.constructor = ParserStream;
15670
15671streamParser(ParserStream.prototype);
15672
15673
15674exports.ParserStream = ParserStream;
15675
15676
15677exports.sliceEq = function (src, start, dest) {
15678 for (var i = start, j = 0; j < dest.length;) {
15679 if (src[i++] !== dest[j++]) return false;
15680 }
15681 return true;
15682};
15683
15684exports.str2arr = function (str, format) {
15685 var arr = [], i = 0;
15686
15687 if (format && format === 'hex') {
15688 while (i < str.length) {
15689 arr.push(parseInt(str.slice(i, i + 2), 16));
15690 i += 2;
15691 }
15692 } else {
15693 for (; i < str.length; i++) {
15694 /* eslint-disable no-bitwise */
15695 arr.push(str.charCodeAt(i) & 0xFF);
15696 }
15697 }
15698
15699 return arr;
15700};
15701
15702exports.readUInt16LE = function (data, offset) {
15703 return data[offset] | (data[offset + 1] << 8);
15704};
15705
15706exports.readUInt16BE = function (data, offset) {
15707 return data[offset + 1] | (data[offset] << 8);
15708};
15709
15710exports.readUInt32LE = function (data, offset) {
15711 return data[offset] |
15712 (data[offset + 1] << 8) |
15713 (data[offset + 2] << 16) |
15714 (data[offset + 3] * 0x1000000);
15715};
15716
15717exports.readUInt32BE = function (data, offset) {
15718 return data[offset + 3] |
15719 (data[offset + 2] << 8) |
15720 (data[offset + 1] << 16) |
15721 (data[offset] * 0x1000000);
15722};
15723
15724
15725function ProbeError(message, code, statusCode) {
15726 Error.call(this);
15727 Error.captureStackTrace(this, this.constructor);
15728
15729 this.name = this.constructor.name;
15730
15731 this.message = message;
15732 if (code) this.code = code;
15733 if (statusCode) this.statusCode = statusCode;
15734}
15735
15736// Inherit from Error
15737ProbeError.prototype = Object.create(Error.prototype);
15738ProbeError.prototype.constructor = ProbeError;
15739
15740
15741exports.ProbeError = ProbeError;
15742
15743},{"stream":100,"stream-parser":116}],84:[function(_dereq_,module,exports){
15744
15745/* eslint-disable no-bitwise */
15746/* eslint-disable consistent-return */
15747
15748'use strict';
15749
15750//////////////////////////////////////////////////////////////////////////
15751// Helpers
15752//
15753function error(message, code) {
15754 var err = new Error(message);
15755 err.code = code;
15756 return err;
15757}
15758
15759
15760function utf8_decode(str) {
15761 try {
15762 return decodeURIComponent(escape(str));
15763 } catch (_) {
15764 return str;
15765 }
15766}
15767
15768
15769//////////////////////////////////////////////////////////////////////////
15770// Exif parser
15771//
15772// Input:
15773// - jpeg_bin: Uint8Array - jpeg file
15774// - exif_start: Number - start of TIFF header (after Exif\0\0)
15775// - exif_end: Number - end of Exif segment
15776// - on_entry: Number - callback
15777//
15778function ExifParser(jpeg_bin, exif_start, exif_end) {
15779 // Uint8Array, exif without signature (which isn't included in offsets)
15780 this.input = jpeg_bin.subarray(exif_start, exif_end);
15781
15782 // offset correction for `on_entry` callback
15783 this.start = exif_start;
15784
15785 // Check TIFF header (includes byte alignment and first IFD offset)
15786 var sig = String.fromCharCode.apply(null, this.input.subarray(0, 4));
15787
15788 if (sig !== 'II\x2A\0' && sig !== 'MM\0\x2A') {
15789 throw error('invalid TIFF signature', 'EBADDATA');
15790 }
15791
15792 // true if motorola (big endian) byte alignment, false if intel
15793 this.big_endian = sig[0] === 'M';
15794}
15795
15796
15797ExifParser.prototype.each = function (on_entry) {
15798 // allow premature exit
15799 this.aborted = false;
15800
15801 var offset = this.read_uint32(4);
15802
15803 this.ifds_to_read = [ {
15804 id: 0,
15805 offset: offset
15806 } ];
15807
15808 while (this.ifds_to_read.length > 0 && !this.aborted) {
15809 var i = this.ifds_to_read.shift();
15810 if (!i.offset) continue;
15811 this.scan_ifd(i.id, i.offset, on_entry);
15812 }
15813};
15814
15815
15816ExifParser.prototype.read_uint16 = function (offset) {
15817 var d = this.input;
15818 if (offset + 2 > d.length) throw error('unexpected EOF', 'EBADDATA');
15819
15820 return this.big_endian ?
15821 d[offset] * 0x100 + d[offset + 1] :
15822 d[offset] + d[offset + 1] * 0x100;
15823};
15824
15825
15826ExifParser.prototype.read_uint32 = function (offset) {
15827 var d = this.input;
15828 if (offset + 4 > d.length) throw error('unexpected EOF', 'EBADDATA');
15829
15830 return this.big_endian ?
15831 d[offset] * 0x1000000 + d[offset + 1] * 0x10000 + d[offset + 2] * 0x100 + d[offset + 3] :
15832 d[offset] + d[offset + 1] * 0x100 + d[offset + 2] * 0x10000 + d[offset + 3] * 0x1000000;
15833};
15834
15835
15836ExifParser.prototype.is_subifd_link = function (ifd, tag) {
15837 return (ifd === 0 && tag === 0x8769) || // SubIFD
15838 (ifd === 0 && tag === 0x8825) || // GPS Info
15839 (ifd === 0x8769 && tag === 0xA005); // Interop IFD
15840};
15841
15842
15843// Returns byte length of a single component of a given format
15844//
15845ExifParser.prototype.exif_format_length = function (format) {
15846 switch (format) {
15847 case 1: // byte
15848 case 2: // ascii
15849 case 6: // sbyte
15850 case 7: // undefined
15851 return 1;
15852
15853 case 3: // short
15854 case 8: // sshort
15855 return 2;
15856
15857 case 4: // long
15858 case 9: // slong
15859 case 11: // float
15860 return 4;
15861
15862 case 5: // rational
15863 case 10: // srational
15864 case 12: // double
15865 return 8;
15866
15867 default:
15868 // unknown type
15869 return 0;
15870 }
15871};
15872
15873
15874// Reads Exif data
15875//
15876ExifParser.prototype.exif_format_read = function (format, offset) {
15877 var v;
15878
15879 switch (format) {
15880 case 1: // byte
15881 case 2: // ascii
15882 v = this.input[offset];
15883 return v;
15884
15885 case 6: // sbyte
15886 v = this.input[offset];
15887 return v | (v & 0x80) * 0x1fffffe;
15888
15889 case 3: // short
15890 v = this.read_uint16(offset);
15891 return v;
15892
15893 case 8: // sshort
15894 v = this.read_uint16(offset);
15895 return v | (v & 0x8000) * 0x1fffe;
15896
15897 case 4: // long
15898 v = this.read_uint32(offset);
15899 return v;
15900
15901 case 9: // slong
15902 v = this.read_uint32(offset);
15903 return v | 0;
15904
15905 case 5: // rational
15906 case 10: // srational
15907 case 11: // float
15908 case 12: // double
15909 return null; // not implemented
15910
15911 case 7: // undefined
15912 return null; // blob
15913
15914 default:
15915 // unknown type
15916 return null;
15917 }
15918};
15919
15920
15921ExifParser.prototype.scan_ifd = function (ifd_no, offset, on_entry) {
15922 var entry_count = this.read_uint16(offset);
15923
15924 offset += 2;
15925
15926 for (var i = 0; i < entry_count; i++) {
15927 var tag = this.read_uint16(offset);
15928 var format = this.read_uint16(offset + 2);
15929 var count = this.read_uint32(offset + 4);
15930
15931 var comp_length = this.exif_format_length(format);
15932 var data_length = count * comp_length;
15933 var data_offset = data_length <= 4 ? offset + 8 : this.read_uint32(offset + 8);
15934 var is_subifd_link = false;
15935
15936 if (data_offset + data_length > this.input.length) {
15937 throw error('unexpected EOF', 'EBADDATA');
15938 }
15939
15940 var value = [];
15941 var comp_offset = data_offset;
15942
15943 for (var j = 0; j < count; j++, comp_offset += comp_length) {
15944 var item = this.exif_format_read(format, comp_offset);
15945 if (item === null) {
15946 value = null;
15947 break;
15948 }
15949 value.push(item);
15950 }
15951
15952 if (Array.isArray(value) && format === 2) {
15953 value = utf8_decode(String.fromCharCode.apply(null, value));
15954 if (value && value[value.length - 1] === '\0') value = value.slice(0, -1);
15955 }
15956
15957 if (this.is_subifd_link(ifd_no, tag)) {
15958 if (Array.isArray(value) && Number.isInteger(value[0]) && value[0] > 0) {
15959 this.ifds_to_read.push({
15960 id: tag,
15961 offset: value[0]
15962 });
15963 is_subifd_link = true;
15964 }
15965 }
15966
15967 var entry = {
15968 is_big_endian: this.big_endian,
15969 ifd: ifd_no,
15970 tag: tag,
15971 format: format,
15972 count: count,
15973 entry_offset: offset + this.start,
15974 data_length: data_length,
15975 data_offset: data_offset + this.start,
15976 value: value,
15977 is_subifd_link: is_subifd_link
15978 };
15979
15980 if (on_entry(entry) === false) {
15981 this.aborted = true;
15982 return;
15983 }
15984
15985 offset += 12;
15986 }
15987
15988 if (ifd_no === 0) {
15989 this.ifds_to_read.push({
15990 id: 1,
15991 offset: this.read_uint32(offset)
15992 });
15993 }
15994};
15995
15996
15997module.exports.ExifParser = ExifParser;
15998
15999// returns orientation stored in Exif (1-8), 0 if none was found, -1 if error
16000module.exports.get_orientation = function (data) {
16001 var orientation = 0;
16002 try {
16003 new ExifParser(data, 0, data.length).each(function (entry) {
16004 if (entry.ifd === 0 && entry.tag === 0x112 && Array.isArray(entry.value)) {
16005 orientation = entry.value[0];
16006 return false;
16007 }
16008 });
16009 return orientation;
16010 } catch (err) {
16011 return -1;
16012 }
16013};
16014
16015},{}],85:[function(_dereq_,module,exports){
16016// Utils used to parse miaf-based files (avif/heic/heif)
16017//
16018// ISO media file spec:
16019// https://web.archive.org/web/20180219054429/http://l.web.umkc.edu/lizhu/teaching/2016sp.video-communication/ref/mp4.pdf
16020//
16021// ISO image file format spec:
16022// https://standards.iso.org/ittf/PubliclyAvailableStandards/c066067_ISO_IEC_23008-12_2017.zip
16023//
16024
16025'use strict';
16026
16027/* eslint-disable consistent-return */
16028/* eslint-disable no-bitwise */
16029
16030var readUInt16BE = _dereq_('./common').readUInt16BE;
16031var readUInt32BE = _dereq_('./common').readUInt32BE;
16032
16033/*
16034 * interface Box {
16035 * size: uint32; // if size == 0, box lasts until EOF
16036 * boxtype: char[4];
16037 * largesize?: uint64; // only if size == 1
16038 * usertype?: char[16]; // only if boxtype == 'uuid'
16039 * }
16040 */
16041function unbox(data, offset) {
16042 if (data.length < 4 + offset) return null;
16043
16044 var size = readUInt32BE(data, offset);
16045
16046 // size includes first 4 bytes (length)
16047 if (data.length < size + offset || size < 8) return null;
16048
16049 // if size === 1, real size is following uint64 (only for big boxes, not needed)
16050 // if size === 0, real size is until the end of the file (only for big boxes, not needed)
16051
16052 return {
16053 boxtype: String.fromCharCode.apply(null, data.slice(offset + 4, offset + 8)),
16054 data: data.slice(offset + 8, offset + size),
16055 end: offset + size
16056 };
16057}
16058
16059
16060module.exports.unbox = unbox;
16061
16062
16063// parses `meta` -> `iprp` -> `ipco` box, returns:
16064// {
16065// sizes: [ { width, height } ],
16066// transforms: [ { type, value } ]
16067// }
16068function scan_ipco(data, sandbox) {
16069 var offset = 0;
16070
16071 for (;;) {
16072 var box = unbox(data, offset);
16073 if (!box) break;
16074
16075 switch (box.boxtype) {
16076 case 'ispe':
16077 sandbox.sizes.push({
16078 width: readUInt32BE(box.data, 4),
16079 height: readUInt32BE(box.data, 8)
16080 });
16081 break;
16082
16083 case 'irot':
16084 sandbox.transforms.push({
16085 type: 'irot',
16086 value: box.data[0] & 3
16087 });
16088 break;
16089
16090 case 'imir':
16091 sandbox.transforms.push({
16092 type: 'imir',
16093 value: box.data[0] & 1
16094 });
16095 break;
16096 }
16097
16098 offset = box.end;
16099 }
16100}
16101
16102
16103function readUIntBE(data, offset, size) {
16104 var result = 0;
16105
16106 for (var i = 0; i < size; i++) {
16107 result = result * 256 + (data[offset + i] || 0);
16108 }
16109
16110 return result;
16111}
16112
16113
16114// parses `meta` -> `iloc` box
16115function scan_iloc(data, sandbox) {
16116 var offset_size = (data[4] >> 4) & 0xF;
16117 var length_size = data[4] & 0xF;
16118 var base_offset_size = (data[5] >> 4) & 0xF;
16119 var item_count = readUInt16BE(data, 6);
16120 var offset = 8;
16121
16122 for (var i = 0; i < item_count; i++) {
16123 var item_ID = readUInt16BE(data, offset);
16124 offset += 2;
16125
16126 var data_reference_index = readUInt16BE(data, offset);
16127 offset += 2;
16128
16129 var base_offset = readUIntBE(data, offset, base_offset_size);
16130 offset += base_offset_size;
16131
16132 var extent_count = readUInt16BE(data, offset);
16133 offset += 2;
16134
16135 if (data_reference_index === 0 && extent_count === 1) {
16136 var first_extent_offset = readUIntBE(data, offset, offset_size);
16137 var first_extent_length = readUIntBE(data, offset + offset_size, length_size);
16138 sandbox.item_loc[item_ID] = { length: first_extent_length, offset: first_extent_offset + base_offset };
16139 }
16140
16141 offset += extent_count * (offset_size + length_size);
16142 }
16143}
16144
16145
16146// parses `meta` -> `iinf` box
16147function scan_iinf(data, sandbox) {
16148 var item_count = readUInt16BE(data, 4);
16149 var offset = 6;
16150
16151 for (var i = 0; i < item_count; i++) {
16152 var box = unbox(data, offset);
16153 if (!box) break;
16154 if (box.boxtype === 'infe') {
16155 var item_id = readUInt16BE(box.data, 4);
16156 var item_name = '';
16157
16158 for (var pos = 8; pos < box.data.length && box.data[pos]; pos++) {
16159 item_name += String.fromCharCode(box.data[pos]);
16160 }
16161
16162 sandbox.item_inf[item_name] = item_id;
16163 }
16164 offset = box.end;
16165 }
16166}
16167
16168
16169// parses `meta` -> `iprp` box
16170function scan_iprp(data, sandbox) {
16171 var offset = 0;
16172
16173 for (;;) {
16174 var box = unbox(data, offset);
16175 if (!box) break;
16176 if (box.boxtype === 'ipco') scan_ipco(box.data, sandbox);
16177 offset = box.end;
16178 }
16179}
16180
16181
16182// parses `meta` box
16183function scan_meta(data, sandbox) {
16184 var offset = 4; // version + flags
16185
16186 for (;;) {
16187 var box = unbox(data, offset);
16188 if (!box) break;
16189 if (box.boxtype === 'iprp') scan_iprp(box.data, sandbox);
16190 if (box.boxtype === 'iloc') scan_iloc(box.data, sandbox);
16191 if (box.boxtype === 'iinf') scan_iinf(box.data, sandbox);
16192 offset = box.end;
16193 }
16194}
16195
16196
16197// get image with largest single dimension as base
16198function getMaxSize(sizes) {
16199 var maxWidthSize = sizes.reduce(function (a, b) {
16200 return a.width > b.width || (a.width === b.width && a.height > b.height) ? a : b;
16201 });
16202
16203 var maxHeightSize = sizes.reduce(function (a, b) {
16204 return a.height > b.height || (a.height === b.height && a.width > b.width) ? a : b;
16205 });
16206
16207 var maxSize;
16208
16209 if (maxWidthSize.width > maxHeightSize.height ||
16210 (maxWidthSize.width === maxHeightSize.height && maxWidthSize.height > maxHeightSize.width)) {
16211 maxSize = maxWidthSize;
16212 } else {
16213 maxSize = maxHeightSize;
16214 }
16215
16216 return maxSize;
16217}
16218
16219
16220module.exports.readSizeFromMeta = function (data) {
16221 var sandbox = {
16222 sizes: [],
16223 transforms: [],
16224 item_inf: {},
16225 item_loc: {}
16226 };
16227
16228 scan_meta(data, sandbox);
16229
16230 if (!sandbox.sizes.length) return;
16231
16232 var maxSize = getMaxSize(sandbox.sizes);
16233
16234 var orientation = 1;
16235
16236 // convert imir/irot to exif orientation
16237 sandbox.transforms.forEach(function (transform) {
16238 var rotate_ccw = { 1: 6, 2: 5, 3: 8, 4: 7, 5: 4, 6: 3, 7: 2, 8: 1 };
16239 var mirror_vert = { 1: 4, 2: 3, 3: 2, 4: 1, 5: 6, 6: 5, 7: 8, 8: 7 };
16240
16241 if (transform.type === 'imir') {
16242 if (transform.value === 0) {
16243 // vertical flip
16244 orientation = mirror_vert[orientation];
16245 } else {
16246 // horizontal flip = vertical flip + 180 deg rotation
16247 orientation = mirror_vert[orientation];
16248 orientation = rotate_ccw[orientation];
16249 orientation = rotate_ccw[orientation];
16250 }
16251 }
16252
16253 if (transform.type === 'irot') {
16254 // counter-clockwise rotation 90 deg 0-3 times
16255 for (var i = 0; i < transform.value; i++) {
16256 orientation = rotate_ccw[orientation];
16257 }
16258 }
16259 });
16260
16261 var exif_location = null;
16262
16263 if (sandbox.item_inf.Exif) {
16264 exif_location = sandbox.item_loc[sandbox.item_inf.Exif];
16265 }
16266
16267 return {
16268 width: maxSize.width,
16269 height: maxSize.height,
16270 orientation: sandbox.transforms.length ? orientation : null,
16271 variants: sandbox.sizes,
16272 exif_location: exif_location
16273 };
16274};
16275
16276
16277module.exports.getMimeType = function (data) {
16278 var brand = String.fromCharCode.apply(null, data.slice(0, 4));
16279 var compat = {};
16280
16281 compat[brand] = true;
16282
16283 for (var i = 8; i < data.length; i += 4) {
16284 compat[String.fromCharCode.apply(null, data.slice(i, i + 4))] = true;
16285 }
16286
16287 // heic and avif are superset of miaf, so they should all list mif1 as compatible
16288 if (!compat.mif1 && !compat.msf1 && !compat.miaf) return;
16289
16290 if (brand === 'avif' || brand === 'avis' || brand === 'avio') {
16291 // `.avifs` and `image/avif-sequence` are removed from spec, all files have single type
16292 return { type: 'avif', mime: 'image/avif' };
16293 }
16294
16295 // https://nokiatech.github.io/heif/technical.html
16296 if (brand === 'heic' || brand === 'heix') {
16297 return { type: 'heic', mime: 'image/heic' };
16298 }
16299
16300 if (brand === 'hevc' || brand === 'hevx') {
16301 return { type: 'heic', mime: 'image/heic-sequence' };
16302 }
16303
16304 if (compat.avif || compat.avis) {
16305 return { type: 'avif', mime: 'image/avif' };
16306 }
16307
16308 if (compat.heic || compat.heix || compat.hevc || compat.hevx || compat.heis) {
16309 if (compat.msf1) {
16310 return { type: 'heif', mime: 'image/heif-sequence' };
16311 }
16312 return { type: 'heif', mime: 'image/heif' };
16313 }
16314
16315 return { type: 'avif', mime: 'image/avif' };
16316};
16317
16318},{"./common":83}],86:[function(_dereq_,module,exports){
16319// Utils used to parse miaf-based files (avif/heic/heif)
16320//
16321// - image collections are not supported (only last size is reported)
16322// - images with metadata encoded after image data are not supported
16323// - images without any `ispe` box are not supported
16324//
16325
16326/* eslint-disable consistent-return */
16327
16328'use strict';
16329
16330
16331var str2arr = _dereq_('../common').str2arr;
16332var sliceEq = _dereq_('../common').sliceEq;
16333var readUInt32BE = _dereq_('../common').readUInt32BE;
16334var miaf = _dereq_('../miaf_utils');
16335var exif = _dereq_('../exif_utils');
16336
16337var SIG_FTYP = str2arr('ftyp');
16338
16339
16340module.exports = function (data) {
16341 // ISO media file (avif format) starts with ftyp box:
16342 // 0000 0020 6674 7970 6176 6966
16343 // (length) f t y p a v i f
16344 //
16345 if (!sliceEq(data, 4, SIG_FTYP)) return;
16346
16347 var firstBox = miaf.unbox(data, 0);
16348 if (!firstBox) return;
16349
16350 var fileType = miaf.getMimeType(firstBox.data);
16351 if (!fileType) return;
16352
16353 var meta, offset = firstBox.end;
16354
16355 for (;;) {
16356 var box = miaf.unbox(data, offset);
16357 if (!box) break;
16358 offset = box.end;
16359
16360 // mdat block SHOULD be last (but not strictly required),
16361 // so it's unlikely that metadata is after it
16362 if (box.boxtype === 'mdat') return;
16363 if (box.boxtype === 'meta') {
16364 meta = box.data;
16365 break;
16366 }
16367 }
16368
16369 if (!meta) return;
16370
16371 var imgSize = miaf.readSizeFromMeta(meta);
16372
16373 if (!imgSize) return;
16374
16375 var result = {
16376 width: imgSize.width,
16377 height: imgSize.height,
16378 type: fileType.type,
16379 mime: fileType.mime,
16380 wUnits: 'px',
16381 hUnits: 'px'
16382 };
16383
16384 if (imgSize.variants.length > 1) {
16385 result.variants = imgSize.variants;
16386 }
16387
16388 if (imgSize.orientation) {
16389 result.orientation = imgSize.orientation;
16390 }
16391
16392 if (imgSize.exif_location &&
16393 imgSize.exif_location.offset + imgSize.exif_location.length <= data.length) {
16394
16395 var sig_offset = readUInt32BE(data, imgSize.exif_location.offset);
16396 var exif_data = data.slice(
16397 imgSize.exif_location.offset + sig_offset + 4,
16398 imgSize.exif_location.offset + imgSize.exif_location.length);
16399
16400 var orientation = exif.get_orientation(exif_data);
16401
16402 if (orientation > 0) result.orientation = orientation;
16403 }
16404
16405 return result;
16406};
16407
16408},{"../common":83,"../exif_utils":84,"../miaf_utils":85}],87:[function(_dereq_,module,exports){
16409'use strict';
16410
16411/* eslint-disable consistent-return */
16412
16413var str2arr = _dereq_('../common').str2arr;
16414var sliceEq = _dereq_('../common').sliceEq;
16415var readUInt16LE = _dereq_('../common').readUInt16LE;
16416
16417var SIG_BM = str2arr('BM');
16418
16419
16420module.exports = function (data) {
16421 if (data.length < 26) return;
16422
16423 if (!sliceEq(data, 0, SIG_BM)) return;
16424
16425 return {
16426 width: readUInt16LE(data, 18),
16427 height: readUInt16LE(data, 22),
16428 type: 'bmp',
16429 mime: 'image/bmp',
16430 wUnits: 'px',
16431 hUnits: 'px'
16432 };
16433};
16434
16435},{"../common":83}],88:[function(_dereq_,module,exports){
16436'use strict';
16437
16438/* eslint-disable consistent-return */
16439
16440var str2arr = _dereq_('../common').str2arr;
16441var sliceEq = _dereq_('../common').sliceEq;
16442var readUInt16LE = _dereq_('../common').readUInt16LE;
16443
16444
16445var SIG_GIF87a = str2arr('GIF87a');
16446var SIG_GIF89a = str2arr('GIF89a');
16447
16448
16449module.exports = function (data) {
16450 if (data.length < 10) return;
16451
16452 if (!sliceEq(data, 0, SIG_GIF87a) && !sliceEq(data, 0, SIG_GIF89a)) return;
16453
16454 return {
16455 width: readUInt16LE(data, 6),
16456 height: readUInt16LE(data, 8),
16457 type: 'gif',
16458 mime: 'image/gif',
16459 wUnits: 'px',
16460 hUnits: 'px'
16461 };
16462};
16463
16464},{"../common":83}],89:[function(_dereq_,module,exports){
16465'use strict';
16466
16467/* eslint-disable consistent-return */
16468
16469var readUInt16LE = _dereq_('../common').readUInt16LE;
16470
16471var HEADER = 0;
16472var TYPE_ICO = 1;
16473var INDEX_SIZE = 16;
16474
16475// Format specification:
16476// https://en.wikipedia.org/wiki/ICO_(file_format)#Icon_resource_structure
16477module.exports = function (data) {
16478 var header = readUInt16LE(data, 0);
16479 var type = readUInt16LE(data, 2);
16480 var numImages = readUInt16LE(data, 4);
16481
16482 if (header !== HEADER || type !== TYPE_ICO || !numImages) {
16483 return;
16484 }
16485
16486 var variants = [];
16487 var maxSize = { width: 0, height: 0 };
16488
16489 for (var i = 0; i < numImages; i++) {
16490 var width = data[6 + INDEX_SIZE * i] || 256;
16491 var height = data[6 + INDEX_SIZE * i + 1] || 256;
16492 var size = { width: width, height: height };
16493 variants.push(size);
16494
16495 if (width > maxSize.width || height > maxSize.height) {
16496 maxSize = size;
16497 }
16498 }
16499
16500 return {
16501 width: maxSize.width,
16502 height: maxSize.height,
16503 variants: variants,
16504 type: 'ico',
16505 mime: 'image/x-icon',
16506 wUnits: 'px',
16507 hUnits: 'px'
16508 };
16509};
16510
16511},{"../common":83}],90:[function(_dereq_,module,exports){
16512'use strict';
16513
16514/* eslint-disable consistent-return */
16515
16516var readUInt16BE = _dereq_('../common').readUInt16BE;
16517var str2arr = _dereq_('../common').str2arr;
16518var sliceEq = _dereq_('../common').sliceEq;
16519var exif = _dereq_('../exif_utils');
16520
16521
16522var SIG_EXIF = str2arr('Exif\0\0');
16523
16524
16525module.exports = function (data) {
16526 if (data.length < 2) return;
16527
16528 // first marker of the file MUST be 0xFFD8
16529 if (data[0] !== 0xFF || data[1] !== 0xD8) return;
16530
16531 var offset = 2;
16532
16533 for (;;) {
16534 if (data.length - offset < 2) return;
16535 // not a JPEG marker
16536 if (data[offset++] !== 0xFF) return;
16537
16538 var code = data[offset++];
16539 var length;
16540
16541 // skip padding bytes
16542 while (code === 0xFF) code = data[offset++];
16543
16544 // standalone markers, according to JPEG 1992,
16545 // http://www.w3.org/Graphics/JPEG/itu-t81.pdf, see Table B.1
16546 if ((0xD0 <= code && code <= 0xD9) || code === 0x01) {
16547 length = 0;
16548 } else if (0xC0 <= code && code <= 0xFE) {
16549 // the rest of the unreserved markers
16550 if (data.length - offset < 2) return;
16551
16552 length = readUInt16BE(data, offset) - 2;
16553 offset += 2;
16554 } else {
16555 // unknown markers
16556 return;
16557 }
16558
16559 if (code === 0xD9 /* EOI */ || code === 0xDA /* SOS */) {
16560 // end of the datastream
16561 return;
16562 }
16563
16564 var orientation;
16565
16566 // try to get orientation from Exif segment
16567 if (code === 0xE1 && length >= 10 && sliceEq(data, offset, SIG_EXIF)) {
16568 orientation = exif.get_orientation(data.slice(offset + 6, offset + length));
16569 }
16570
16571 if (length >= 5 &&
16572 (0xC0 <= code && code <= 0xCF) &&
16573 code !== 0xC4 && code !== 0xC8 && code !== 0xCC) {
16574
16575 if (data.length - offset < length) return;
16576
16577 var result = {
16578 width: readUInt16BE(data, offset + 3),
16579 height: readUInt16BE(data, offset + 1),
16580 type: 'jpg',
16581 mime: 'image/jpeg',
16582 wUnits: 'px',
16583 hUnits: 'px'
16584 };
16585
16586 if (orientation > 0) {
16587 result.orientation = orientation;
16588 }
16589
16590 return result;
16591 }
16592
16593 offset += length;
16594 }
16595};
16596
16597},{"../common":83,"../exif_utils":84}],91:[function(_dereq_,module,exports){
16598'use strict';
16599
16600/* eslint-disable consistent-return */
16601
16602var str2arr = _dereq_('../common').str2arr;
16603var sliceEq = _dereq_('../common').sliceEq;
16604var readUInt32BE = _dereq_('../common').readUInt32BE;
16605
16606
16607var SIG_PNG = str2arr('\x89PNG\r\n\x1a\n');
16608var SIG_IHDR = str2arr('IHDR');
16609
16610
16611module.exports = function (data) {
16612 if (data.length < 24) return;
16613
16614 // check PNG signature
16615 if (!sliceEq(data, 0, SIG_PNG)) return;
16616
16617 // check that first chunk is IHDR
16618 if (!sliceEq(data, 12, SIG_IHDR)) return;
16619
16620 return {
16621 width: readUInt32BE(data, 16),
16622 height: readUInt32BE(data, 20),
16623 type: 'png',
16624 mime: 'image/png',
16625 wUnits: 'px',
16626 hUnits: 'px'
16627 };
16628};
16629
16630},{"../common":83}],92:[function(_dereq_,module,exports){
16631'use strict';
16632
16633/* eslint-disable consistent-return */
16634
16635var str2arr = _dereq_('../common').str2arr;
16636var sliceEq = _dereq_('../common').sliceEq;
16637var readUInt32BE = _dereq_('../common').readUInt32BE;
16638
16639
16640var SIG_8BPS = str2arr('8BPS\x00\x01');
16641
16642
16643module.exports = function (data) {
16644 if (data.length < 6 + 16) return;
16645
16646 // signature + version
16647 if (!sliceEq(data, 0, SIG_8BPS)) return;
16648
16649 return {
16650 width: readUInt32BE(data, 6 + 12),
16651 height: readUInt32BE(data, 6 + 8),
16652 type: 'psd',
16653 mime: 'image/vnd.adobe.photoshop',
16654 wUnits: 'px',
16655 hUnits: 'px'
16656 };
16657};
16658
16659},{"../common":83}],93:[function(_dereq_,module,exports){
16660'use strict';
16661
16662/* eslint-disable consistent-return */
16663
16664function isWhiteSpace(chr) {
16665 return chr === 0x20 || chr === 0x09 || chr === 0x0D || chr === 0x0A;
16666}
16667
16668// Filter NaN, Infinity, < 0
16669function isFinitePositive(val) {
16670 return typeof val === 'number' && isFinite(val) && val > 0;
16671}
16672
16673function canBeSvg(buf) {
16674 var i = 0, max = buf.length;
16675
16676 // byte order mark, https://github.com/nodeca/probe-image-size/issues/57
16677 if (buf[0] === 0xEF && buf[1] === 0xBB && buf[2] === 0xBF) i = 3;
16678
16679 while (i < max && isWhiteSpace(buf[i])) i++;
16680
16681 if (i === max) return false;
16682 return buf[i] === 0x3c; /* < */
16683}
16684
16685
16686// skip `<?` (comments), `<!` (directives, cdata, doctype),
16687// looking for `<svg>` or `<NAMESPACE:svg>`
16688var SVG_HEADER_RE = /<[-_.:a-zA-Z0-9][^>]*>/;
16689
16690// test if the top level element is svg + optional namespace,
16691// used to skip svg embedded in html
16692var SVG_TAG_RE = /^<([-_.:a-zA-Z0-9]+:)?svg\s/;
16693
16694var SVG_WIDTH_RE = /[^-]\bwidth="([^%]+?)"|[^-]\bwidth='([^%]+?)'/;
16695var SVG_HEIGHT_RE = /\bheight="([^%]+?)"|\bheight='([^%]+?)'/;
16696var SVG_VIEWBOX_RE = /\bview[bB]ox="(.+?)"|\bview[bB]ox='(.+?)'/;
16697var SVG_UNITS_RE = /in$|mm$|cm$|pt$|pc$|px$|em$|ex$/;
16698
16699function svgAttrs(str) {
16700 var width = str.match(SVG_WIDTH_RE);
16701 var height = str.match(SVG_HEIGHT_RE);
16702 var viewbox = str.match(SVG_VIEWBOX_RE);
16703
16704 return {
16705 width: width && (width[1] || width[2]),
16706 height: height && (height[1] || height[2]),
16707 viewbox: viewbox && (viewbox[1] || viewbox[2])
16708 };
16709}
16710
16711
16712function units(str) {
16713 if (!SVG_UNITS_RE.test(str)) return 'px';
16714
16715 return str.match(SVG_UNITS_RE)[0];
16716}
16717
16718
16719module.exports = function (data) {
16720 if (!canBeSvg(data)) return;
16721
16722 var str = '';
16723
16724 for (var i = 0; i < data.length; i++) {
16725 // 1. We can't rely on buffer features
16726 // 2. Don't care about UTF16 because ascii is enougth for our goals
16727 str += String.fromCharCode(data[i]);
16728 }
16729
16730 // get top level element
16731 var svgTag = (str.match(SVG_HEADER_RE) || [ '' ])[0];
16732
16733 // test if top level element is <svg>
16734 if (!SVG_TAG_RE.test(svgTag)) return;
16735
16736 var attrs = svgAttrs(svgTag);
16737 var width = parseFloat(attrs.width);
16738 var height = parseFloat(attrs.height);
16739
16740 // Extract from direct values
16741
16742 if (attrs.width && attrs.height) {
16743 if (!isFinitePositive(width) || !isFinitePositive(height)) return;
16744
16745 return {
16746 width: width,
16747 height: height,
16748 type: 'svg',
16749 mime: 'image/svg+xml',
16750 wUnits: units(attrs.width),
16751 hUnits: units(attrs.height)
16752 };
16753 }
16754
16755 // Extract from viewbox
16756
16757 var parts = (attrs.viewbox || '').split(' ');
16758 var viewbox = {
16759 width: parts[2],
16760 height: parts[3]
16761 };
16762 var vbWidth = parseFloat(viewbox.width);
16763 var vbHeight = parseFloat(viewbox.height);
16764
16765 if (!isFinitePositive(vbWidth) || !isFinitePositive(vbHeight)) return;
16766 if (units(viewbox.width) !== units(viewbox.height)) return;
16767
16768 var ratio = vbWidth / vbHeight;
16769
16770 if (attrs.width) {
16771 if (!isFinitePositive(width)) return;
16772
16773 return {
16774 width: width,
16775 height: width / ratio,
16776 type: 'svg',
16777 mime: 'image/svg+xml',
16778 wUnits: units(attrs.width),
16779 hUnits: units(attrs.width)
16780 };
16781 }
16782
16783 if (attrs.height) {
16784 if (!isFinitePositive(height)) return;
16785
16786 return {
16787 width: height * ratio,
16788 height: height,
16789 type: 'svg',
16790 mime: 'image/svg+xml',
16791 wUnits: units(attrs.height),
16792 hUnits: units(attrs.height)
16793 };
16794 }
16795
16796 return {
16797 width: vbWidth,
16798 height: vbHeight,
16799 type: 'svg',
16800 mime: 'image/svg+xml',
16801 wUnits: units(viewbox.width),
16802 hUnits: units(viewbox.height)
16803 };
16804};
16805
16806},{}],94:[function(_dereq_,module,exports){
16807'use strict';
16808
16809/* eslint-disable consistent-return */
16810
16811var str2arr = _dereq_('../common').str2arr;
16812var sliceEq = _dereq_('../common').sliceEq;
16813var readUInt16LE = _dereq_('../common').readUInt16LE;
16814var readUInt16BE = _dereq_('../common').readUInt16BE;
16815var readUInt32LE = _dereq_('../common').readUInt32LE;
16816var readUInt32BE = _dereq_('../common').readUInt32BE;
16817
16818
16819var SIG_1 = str2arr('II\x2A\0');
16820var SIG_2 = str2arr('MM\0\x2A');
16821
16822
16823function readUInt16(buffer, offset, is_big_endian) {
16824 return is_big_endian ? readUInt16BE(buffer, offset) : readUInt16LE(buffer, offset);
16825}
16826
16827function readUInt32(buffer, offset, is_big_endian) {
16828 return is_big_endian ? readUInt32BE(buffer, offset) : readUInt32LE(buffer, offset);
16829}
16830
16831function readIFDValue(data, data_offset, is_big_endian) {
16832 var type = readUInt16(data, data_offset + 2, is_big_endian);
16833 var values = readUInt32(data, data_offset + 4, is_big_endian);
16834
16835 if (values !== 1 || (type !== 3 && type !== 4)) return null;
16836
16837 if (type === 3) {
16838 return readUInt16(data, data_offset + 8, is_big_endian);
16839 }
16840
16841 return readUInt32(data, data_offset + 8, is_big_endian);
16842}
16843
16844module.exports = function (data) {
16845 if (data.length < 8) return;
16846
16847 // check TIFF signature
16848 if (!sliceEq(data, 0, SIG_1) && !sliceEq(data, 0, SIG_2)) return;
16849
16850 var is_big_endian = (data[0] === 77 /* 'MM' */);
16851 var count = readUInt32(data, 4, is_big_endian) - 8;
16852
16853 if (count < 0) return;
16854
16855 // skip until IFD
16856 var offset = count + 8;
16857
16858 if (data.length - offset < 2) return;
16859
16860 // read number of IFD entries
16861 var ifd_size = readUInt16(data, offset + 0, is_big_endian) * 12;
16862
16863 if (ifd_size <= 0) return;
16864
16865 offset += 2;
16866
16867 // read all IFD entries
16868 if (data.length - offset < ifd_size) return;
16869
16870 var i, width, height, tag;
16871
16872 for (i = 0; i < ifd_size; i += 12) {
16873 tag = readUInt16(data, offset + i, is_big_endian);
16874
16875 if (tag === 256) {
16876 width = readIFDValue(data, offset + i, is_big_endian);
16877 } else if (tag === 257) {
16878 height = readIFDValue(data, offset + i, is_big_endian);
16879 }
16880 }
16881
16882 if (width && height) {
16883 return {
16884 width: width,
16885 height: height,
16886 type: 'tiff',
16887 mime: 'image/tiff',
16888 wUnits: 'px',
16889 hUnits: 'px'
16890 };
16891 }
16892};
16893
16894},{"../common":83}],95:[function(_dereq_,module,exports){
16895'use strict';
16896
16897/* eslint-disable no-bitwise */
16898/* eslint-disable consistent-return */
16899
16900var str2arr = _dereq_('../common').str2arr;
16901var sliceEq = _dereq_('../common').sliceEq;
16902var readUInt16LE = _dereq_('../common').readUInt16LE;
16903var readUInt32LE = _dereq_('../common').readUInt32LE;
16904var exif = _dereq_('../exif_utils');
16905
16906
16907var SIG_RIFF = str2arr('RIFF');
16908var SIG_WEBP = str2arr('WEBP');
16909
16910
16911function parseVP8(data, offset) {
16912 if (data[offset + 3] !== 0x9D || data[offset + 4] !== 0x01 || data[offset + 5] !== 0x2A) {
16913 // bad code block signature
16914 return;
16915 }
16916
16917 return {
16918 width: readUInt16LE(data, offset + 6) & 0x3FFF,
16919 height: readUInt16LE(data, offset + 8) & 0x3FFF,
16920 type: 'webp',
16921 mime: 'image/webp',
16922 wUnits: 'px',
16923 hUnits: 'px'
16924 };
16925}
16926
16927
16928function parseVP8L(data, offset) {
16929 if (data[offset] !== 0x2F) return;
16930
16931 var bits = readUInt32LE(data, offset + 1);
16932
16933 return {
16934 width: (bits & 0x3FFF) + 1,
16935 height: ((bits >> 14) & 0x3FFF) + 1,
16936 type: 'webp',
16937 mime: 'image/webp',
16938 wUnits: 'px',
16939 hUnits: 'px'
16940 };
16941}
16942
16943
16944function parseVP8X(data, offset) {
16945 return {
16946 // TODO: replace with `data.readUIntLE(8, 3) + 1`
16947 // when 0.10 support is dropped
16948 width: ((data[offset + 6] << 16) | (data[offset + 5] << 8) | data[offset + 4]) + 1,
16949 height: ((data[offset + 9] << offset) | (data[offset + 8] << 8) | data[offset + 7]) + 1,
16950 type: 'webp',
16951 mime: 'image/webp',
16952 wUnits: 'px',
16953 hUnits: 'px'
16954 };
16955}
16956
16957
16958module.exports = function (data) {
16959 if (data.length < 16) return;
16960
16961 // check /^RIFF....WEBPVP8([ LX])$/ signature
16962 if (!sliceEq(data, 0, SIG_RIFF) && !sliceEq(data, 8, SIG_WEBP)) return;
16963
16964 var offset = 12;
16965 var result = null;
16966 var exif_orientation = 0;
16967 var fileLength = readUInt32LE(data, 4) + 8;
16968
16969 if (fileLength > data.length) return;
16970
16971 while (offset + 8 < fileLength) {
16972 if (data[offset] === 0) {
16973 // after each chunk of odd size there should be 0 byte of padding, skip those
16974 offset++;
16975 continue;
16976 }
16977
16978 var header = String.fromCharCode.apply(null, data.slice(offset, offset + 4));
16979 var length = readUInt32LE(data, offset + 4);
16980
16981 if (header === 'VP8 ' && length >= 10) {
16982 result = result || parseVP8(data, offset + 8);
16983 } else if (header === 'VP8L' && length >= 9) {
16984 result = result || parseVP8L(data, offset + 8);
16985 } else if (header === 'VP8X' && length >= 10) {
16986 result = result || parseVP8X(data, offset + 8);
16987 } else if (header === 'EXIF') {
16988 exif_orientation = exif.get_orientation(data.slice(offset + 8, offset + 8 + length));
16989
16990 // exif is the last chunk we care about, stop after it
16991 offset = Infinity;
16992 }
16993
16994 offset += 8 + length;
16995 }
16996
16997 if (!result) return;
16998
16999 if (exif_orientation > 0) {
17000 result.orientation = exif_orientation;
17001 }
17002
17003 return result;
17004};
17005
17006},{"../common":83,"../exif_utils":84}],96:[function(_dereq_,module,exports){
17007'use strict';
17008
17009
17010module.exports = {
17011 avif: _dereq_('./parse_sync/avif'),
17012 bmp: _dereq_('./parse_sync/bmp'),
17013 gif: _dereq_('./parse_sync/gif'),
17014 ico: _dereq_('./parse_sync/ico'),
17015 jpeg: _dereq_('./parse_sync/jpeg'),
17016 png: _dereq_('./parse_sync/png'),
17017 psd: _dereq_('./parse_sync/psd'),
17018 svg: _dereq_('./parse_sync/svg'),
17019 tiff: _dereq_('./parse_sync/tiff'),
17020 webp: _dereq_('./parse_sync/webp')
17021};
17022
17023},{"./parse_sync/avif":86,"./parse_sync/bmp":87,"./parse_sync/gif":88,"./parse_sync/ico":89,"./parse_sync/jpeg":90,"./parse_sync/png":91,"./parse_sync/psd":92,"./parse_sync/svg":93,"./parse_sync/tiff":94,"./parse_sync/webp":95}],97:[function(_dereq_,module,exports){
17024'use strict';
17025
17026
17027var parsers = _dereq_('./lib/parsers_sync');
17028
17029
17030function probeBuffer(buffer) {
17031 var parser_names = Object.keys(parsers);
17032
17033 for (var i = 0; i < parser_names.length; i++) {
17034 var result = parsers[parser_names[i]](buffer);
17035
17036 if (result) return result;
17037 }
17038
17039 return null;
17040}
17041
17042
17043///////////////////////////////////////////////////////////////////////
17044// Exports
17045//
17046
17047module.exports = function get_image_size(src) {
17048 return probeBuffer(src);
17049};
17050
17051module.exports.parsers = parsers;
17052
17053},{"./lib/parsers_sync":96}],98:[function(_dereq_,module,exports){
17054// shim for using process in browser
17055var process = module.exports = {};
17056
17057// cached from whatever global is present so that test runners that stub it
17058// don't break things. But we need to wrap it in a try catch in case it is
17059// wrapped in strict mode code which doesn't define any globals. It's inside a
17060// function because try/catches deoptimize in certain engines.
17061
17062var cachedSetTimeout;
17063var cachedClearTimeout;
17064
17065function defaultSetTimout() {
17066 throw new Error('setTimeout has not been defined');
17067}
17068function defaultClearTimeout () {
17069 throw new Error('clearTimeout has not been defined');
17070}
17071(function () {
17072 try {
17073 if (typeof setTimeout === 'function') {
17074 cachedSetTimeout = setTimeout;
17075 } else {
17076 cachedSetTimeout = defaultSetTimout;
17077 }
17078 } catch (e) {
17079 cachedSetTimeout = defaultSetTimout;
17080 }
17081 try {
17082 if (typeof clearTimeout === 'function') {
17083 cachedClearTimeout = clearTimeout;
17084 } else {
17085 cachedClearTimeout = defaultClearTimeout;
17086 }
17087 } catch (e) {
17088 cachedClearTimeout = defaultClearTimeout;
17089 }
17090} ())
17091function runTimeout(fun) {
17092 if (cachedSetTimeout === setTimeout) {
17093 //normal enviroments in sane situations
17094 return setTimeout(fun, 0);
17095 }
17096 // if setTimeout wasn't available but was latter defined
17097 if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
17098 cachedSetTimeout = setTimeout;
17099 return setTimeout(fun, 0);
17100 }
17101 try {
17102 // when when somebody has screwed with setTimeout but no I.E. maddness
17103 return cachedSetTimeout(fun, 0);
17104 } catch(e){
17105 try {
17106 // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
17107 return cachedSetTimeout.call(null, fun, 0);
17108 } catch(e){
17109 // 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
17110 return cachedSetTimeout.call(this, fun, 0);
17111 }
17112 }
17113
17114
17115}
17116function runClearTimeout(marker) {
17117 if (cachedClearTimeout === clearTimeout) {
17118 //normal enviroments in sane situations
17119 return clearTimeout(marker);
17120 }
17121 // if clearTimeout wasn't available but was latter defined
17122 if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
17123 cachedClearTimeout = clearTimeout;
17124 return clearTimeout(marker);
17125 }
17126 try {
17127 // when when somebody has screwed with setTimeout but no I.E. maddness
17128 return cachedClearTimeout(marker);
17129 } catch (e){
17130 try {
17131 // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
17132 return cachedClearTimeout.call(null, marker);
17133 } catch (e){
17134 // 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.
17135 // Some versions of I.E. have different rules for clearTimeout vs setTimeout
17136 return cachedClearTimeout.call(this, marker);
17137 }
17138 }
17139
17140
17141
17142}
17143var queue = [];
17144var draining = false;
17145var currentQueue;
17146var queueIndex = -1;
17147
17148function cleanUpNextTick() {
17149 if (!draining || !currentQueue) {
17150 return;
17151 }
17152 draining = false;
17153 if (currentQueue.length) {
17154 queue = currentQueue.concat(queue);
17155 } else {
17156 queueIndex = -1;
17157 }
17158 if (queue.length) {
17159 drainQueue();
17160 }
17161}
17162
17163function drainQueue() {
17164 if (draining) {
17165 return;
17166 }
17167 var timeout = runTimeout(cleanUpNextTick);
17168 draining = true;
17169
17170 var len = queue.length;
17171 while(len) {
17172 currentQueue = queue;
17173 queue = [];
17174 while (++queueIndex < len) {
17175 if (currentQueue) {
17176 currentQueue[queueIndex].run();
17177 }
17178 }
17179 queueIndex = -1;
17180 len = queue.length;
17181 }
17182 currentQueue = null;
17183 draining = false;
17184 runClearTimeout(timeout);
17185}
17186
17187process.nextTick = function (fun) {
17188 var args = new Array(arguments.length - 1);
17189 if (arguments.length > 1) {
17190 for (var i = 1; i < arguments.length; i++) {
17191 args[i - 1] = arguments[i];
17192 }
17193 }
17194 queue.push(new Item(fun, args));
17195 if (queue.length === 1 && !draining) {
17196 runTimeout(drainQueue);
17197 }
17198};
17199
17200// v8 likes predictible objects
17201function Item(fun, array) {
17202 this.fun = fun;
17203 this.array = array;
17204}
17205Item.prototype.run = function () {
17206 this.fun.apply(null, this.array);
17207};
17208process.title = 'browser';
17209process.browser = true;
17210process.env = {};
17211process.argv = [];
17212process.version = ''; // empty string to avoid regexp issues
17213process.versions = {};
17214
17215function noop() {}
17216
17217process.on = noop;
17218process.addListener = noop;
17219process.once = noop;
17220process.off = noop;
17221process.removeListener = noop;
17222process.removeAllListeners = noop;
17223process.emit = noop;
17224process.prependListener = noop;
17225process.prependOnceListener = noop;
17226
17227process.listeners = function (name) { return [] }
17228
17229process.binding = function (name) {
17230 throw new Error('process.binding is not supported');
17231};
17232
17233process.cwd = function () { return '/' };
17234process.chdir = function (dir) {
17235 throw new Error('process.chdir is not supported');
17236};
17237process.umask = function() { return 0; };
17238
17239},{}],99:[function(_dereq_,module,exports){
17240/* eslint-disable node/no-deprecated-api */
17241var buffer = _dereq_('buffer')
17242var Buffer = buffer.Buffer
17243
17244// alternative to using Object.keys for old browsers
17245function copyProps (src, dst) {
17246 for (var key in src) {
17247 dst[key] = src[key]
17248 }
17249}
17250if (Buffer.from && Buffer.alloc && Buffer.allocUnsafe && Buffer.allocUnsafeSlow) {
17251 module.exports = buffer
17252} else {
17253 // Copy properties from require('buffer')
17254 copyProps(buffer, exports)
17255 exports.Buffer = SafeBuffer
17256}
17257
17258function SafeBuffer (arg, encodingOrOffset, length) {
17259 return Buffer(arg, encodingOrOffset, length)
17260}
17261
17262SafeBuffer.prototype = Object.create(Buffer.prototype)
17263
17264// Copy static methods from Buffer
17265copyProps(Buffer, SafeBuffer)
17266
17267SafeBuffer.from = function (arg, encodingOrOffset, length) {
17268 if (typeof arg === 'number') {
17269 throw new TypeError('Argument must not be a number')
17270 }
17271 return Buffer(arg, encodingOrOffset, length)
17272}
17273
17274SafeBuffer.alloc = function (size, fill, encoding) {
17275 if (typeof size !== 'number') {
17276 throw new TypeError('Argument must be a number')
17277 }
17278 var buf = Buffer(size)
17279 if (fill !== undefined) {
17280 if (typeof encoding === 'string') {
17281 buf.fill(fill, encoding)
17282 } else {
17283 buf.fill(fill)
17284 }
17285 } else {
17286 buf.fill(0)
17287 }
17288 return buf
17289}
17290
17291SafeBuffer.allocUnsafe = function (size) {
17292 if (typeof size !== 'number') {
17293 throw new TypeError('Argument must be a number')
17294 }
17295 return Buffer(size)
17296}
17297
17298SafeBuffer.allocUnsafeSlow = function (size) {
17299 if (typeof size !== 'number') {
17300 throw new TypeError('Argument must be a number')
17301 }
17302 return buffer.SlowBuffer(size)
17303}
17304
17305},{"buffer":28}],100:[function(_dereq_,module,exports){
17306// Copyright Joyent, Inc. and other Node contributors.
17307//
17308// Permission is hereby granted, free of charge, to any person obtaining a
17309// copy of this software and associated documentation files (the
17310// "Software"), to deal in the Software without restriction, including
17311// without limitation the rights to use, copy, modify, merge, publish,
17312// distribute, sublicense, and/or sell copies of the Software, and to permit
17313// persons to whom the Software is furnished to do so, subject to the
17314// following conditions:
17315//
17316// The above copyright notice and this permission notice shall be included
17317// in all copies or substantial portions of the Software.
17318//
17319// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17320// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17321// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17322// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17323// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
17324// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
17325// USE OR OTHER DEALINGS IN THE SOFTWARE.
17326
17327module.exports = Stream;
17328
17329var EE = _dereq_('events').EventEmitter;
17330var inherits = _dereq_('inherits');
17331
17332inherits(Stream, EE);
17333Stream.Readable = _dereq_('readable-stream/lib/_stream_readable.js');
17334Stream.Writable = _dereq_('readable-stream/lib/_stream_writable.js');
17335Stream.Duplex = _dereq_('readable-stream/lib/_stream_duplex.js');
17336Stream.Transform = _dereq_('readable-stream/lib/_stream_transform.js');
17337Stream.PassThrough = _dereq_('readable-stream/lib/_stream_passthrough.js');
17338Stream.finished = _dereq_('readable-stream/lib/internal/streams/end-of-stream.js')
17339Stream.pipeline = _dereq_('readable-stream/lib/internal/streams/pipeline.js')
17340
17341// Backwards-compat with node 0.4.x
17342Stream.Stream = Stream;
17343
17344
17345
17346// old-style streams. Note that the pipe method (the only relevant
17347// part of this class) is overridden in the Readable class.
17348
17349function Stream() {
17350 EE.call(this);
17351}
17352
17353Stream.prototype.pipe = function(dest, options) {
17354 var source = this;
17355
17356 function ondata(chunk) {
17357 if (dest.writable) {
17358 if (false === dest.write(chunk) && source.pause) {
17359 source.pause();
17360 }
17361 }
17362 }
17363
17364 source.on('data', ondata);
17365
17366 function ondrain() {
17367 if (source.readable && source.resume) {
17368 source.resume();
17369 }
17370 }
17371
17372 dest.on('drain', ondrain);
17373
17374 // If the 'end' option is not supplied, dest.end() will be called when
17375 // source gets the 'end' or 'close' events. Only dest.end() once.
17376 if (!dest._isStdio && (!options || options.end !== false)) {
17377 source.on('end', onend);
17378 source.on('close', onclose);
17379 }
17380
17381 var didOnEnd = false;
17382 function onend() {
17383 if (didOnEnd) return;
17384 didOnEnd = true;
17385
17386 dest.end();
17387 }
17388
17389
17390 function onclose() {
17391 if (didOnEnd) return;
17392 didOnEnd = true;
17393
17394 if (typeof dest.destroy === 'function') dest.destroy();
17395 }
17396
17397 // don't leave dangling pipes when there are errors.
17398 function onerror(er) {
17399 cleanup();
17400 if (EE.listenerCount(this, 'error') === 0) {
17401 throw er; // Unhandled stream error in pipe.
17402 }
17403 }
17404
17405 source.on('error', onerror);
17406 dest.on('error', onerror);
17407
17408 // remove all the event listeners that were added.
17409 function cleanup() {
17410 source.removeListener('data', ondata);
17411 dest.removeListener('drain', ondrain);
17412
17413 source.removeListener('end', onend);
17414 source.removeListener('close', onclose);
17415
17416 source.removeListener('error', onerror);
17417 dest.removeListener('error', onerror);
17418
17419 source.removeListener('end', cleanup);
17420 source.removeListener('close', cleanup);
17421
17422 dest.removeListener('close', cleanup);
17423 }
17424
17425 source.on('end', cleanup);
17426 source.on('close', cleanup);
17427
17428 dest.on('close', cleanup);
17429
17430 dest.emit('pipe', source);
17431
17432 // Allow for unix-like usage: A.pipe(B).pipe(C)
17433 return dest;
17434};
17435
17436},{"events":27,"inherits":67,"readable-stream/lib/_stream_duplex.js":102,"readable-stream/lib/_stream_passthrough.js":103,"readable-stream/lib/_stream_readable.js":104,"readable-stream/lib/_stream_transform.js":105,"readable-stream/lib/_stream_writable.js":106,"readable-stream/lib/internal/streams/end-of-stream.js":110,"readable-stream/lib/internal/streams/pipeline.js":112}],101:[function(_dereq_,module,exports){
17437'use strict';
17438
17439function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; }
17440
17441var codes = {};
17442
17443function createErrorType(code, message, Base) {
17444 if (!Base) {
17445 Base = Error;
17446 }
17447
17448 function getMessage(arg1, arg2, arg3) {
17449 if (typeof message === 'string') {
17450 return message;
17451 } else {
17452 return message(arg1, arg2, arg3);
17453 }
17454 }
17455
17456 var NodeError =
17457 /*#__PURE__*/
17458 function (_Base) {
17459 _inheritsLoose(NodeError, _Base);
17460
17461 function NodeError(arg1, arg2, arg3) {
17462 return _Base.call(this, getMessage(arg1, arg2, arg3)) || this;
17463 }
17464
17465 return NodeError;
17466 }(Base);
17467
17468 NodeError.prototype.name = Base.name;
17469 NodeError.prototype.code = code;
17470 codes[code] = NodeError;
17471} // https://github.com/nodejs/node/blob/v10.8.0/lib/internal/errors.js
17472
17473
17474function oneOf(expected, thing) {
17475 if (Array.isArray(expected)) {
17476 var len = expected.length;
17477 expected = expected.map(function (i) {
17478 return String(i);
17479 });
17480
17481 if (len > 2) {
17482 return "one of ".concat(thing, " ").concat(expected.slice(0, len - 1).join(', '), ", or ") + expected[len - 1];
17483 } else if (len === 2) {
17484 return "one of ".concat(thing, " ").concat(expected[0], " or ").concat(expected[1]);
17485 } else {
17486 return "of ".concat(thing, " ").concat(expected[0]);
17487 }
17488 } else {
17489 return "of ".concat(thing, " ").concat(String(expected));
17490 }
17491} // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith
17492
17493
17494function startsWith(str, search, pos) {
17495 return str.substr(!pos || pos < 0 ? 0 : +pos, search.length) === search;
17496} // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith
17497
17498
17499function endsWith(str, search, this_len) {
17500 if (this_len === undefined || this_len > str.length) {
17501 this_len = str.length;
17502 }
17503
17504 return str.substring(this_len - search.length, this_len) === search;
17505} // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes
17506
17507
17508function includes(str, search, start) {
17509 if (typeof start !== 'number') {
17510 start = 0;
17511 }
17512
17513 if (start + search.length > str.length) {
17514 return false;
17515 } else {
17516 return str.indexOf(search, start) !== -1;
17517 }
17518}
17519
17520createErrorType('ERR_INVALID_OPT_VALUE', function (name, value) {
17521 return 'The value "' + value + '" is invalid for option "' + name + '"';
17522}, TypeError);
17523createErrorType('ERR_INVALID_ARG_TYPE', function (name, expected, actual) {
17524 // determiner: 'must be' or 'must not be'
17525 var determiner;
17526
17527 if (typeof expected === 'string' && startsWith(expected, 'not ')) {
17528 determiner = 'must not be';
17529 expected = expected.replace(/^not /, '');
17530 } else {
17531 determiner = 'must be';
17532 }
17533
17534 var msg;
17535
17536 if (endsWith(name, ' argument')) {
17537 // For cases like 'first argument'
17538 msg = "The ".concat(name, " ").concat(determiner, " ").concat(oneOf(expected, 'type'));
17539 } else {
17540 var type = includes(name, '.') ? 'property' : 'argument';
17541 msg = "The \"".concat(name, "\" ").concat(type, " ").concat(determiner, " ").concat(oneOf(expected, 'type'));
17542 }
17543
17544 msg += ". Received type ".concat(typeof actual);
17545 return msg;
17546}, TypeError);
17547createErrorType('ERR_STREAM_PUSH_AFTER_EOF', 'stream.push() after EOF');
17548createErrorType('ERR_METHOD_NOT_IMPLEMENTED', function (name) {
17549 return 'The ' + name + ' method is not implemented';
17550});
17551createErrorType('ERR_STREAM_PREMATURE_CLOSE', 'Premature close');
17552createErrorType('ERR_STREAM_DESTROYED', function (name) {
17553 return 'Cannot call ' + name + ' after a stream was destroyed';
17554});
17555createErrorType('ERR_MULTIPLE_CALLBACK', 'Callback called multiple times');
17556createErrorType('ERR_STREAM_CANNOT_PIPE', 'Cannot pipe, not readable');
17557createErrorType('ERR_STREAM_WRITE_AFTER_END', 'write after end');
17558createErrorType('ERR_STREAM_NULL_VALUES', 'May not write null values to stream', TypeError);
17559createErrorType('ERR_UNKNOWN_ENCODING', function (arg) {
17560 return 'Unknown encoding: ' + arg;
17561}, TypeError);
17562createErrorType('ERR_STREAM_UNSHIFT_AFTER_END_EVENT', 'stream.unshift() after end event');
17563module.exports.codes = codes;
17564
17565},{}],102:[function(_dereq_,module,exports){
17566(function (process){(function (){
17567// Copyright Joyent, Inc. and other Node contributors.
17568//
17569// Permission is hereby granted, free of charge, to any person obtaining a
17570// copy of this software and associated documentation files (the
17571// "Software"), to deal in the Software without restriction, including
17572// without limitation the rights to use, copy, modify, merge, publish,
17573// distribute, sublicense, and/or sell copies of the Software, and to permit
17574// persons to whom the Software is furnished to do so, subject to the
17575// following conditions:
17576//
17577// The above copyright notice and this permission notice shall be included
17578// in all copies or substantial portions of the Software.
17579//
17580// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17581// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17582// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17583// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17584// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
17585// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
17586// USE OR OTHER DEALINGS IN THE SOFTWARE.
17587// a duplex stream is just a stream that is both readable and writable.
17588// Since JS doesn't have multiple prototypal inheritance, this class
17589// prototypally inherits from Readable, and then parasitically from
17590// Writable.
17591'use strict';
17592/*<replacement>*/
17593
17594var objectKeys = Object.keys || function (obj) {
17595 var keys = [];
17596
17597 for (var key in obj) {
17598 keys.push(key);
17599 }
17600
17601 return keys;
17602};
17603/*</replacement>*/
17604
17605
17606module.exports = Duplex;
17607
17608var Readable = _dereq_('./_stream_readable');
17609
17610var Writable = _dereq_('./_stream_writable');
17611
17612_dereq_('inherits')(Duplex, Readable);
17613
17614{
17615 // Allow the keys array to be GC'ed.
17616 var keys = objectKeys(Writable.prototype);
17617
17618 for (var v = 0; v < keys.length; v++) {
17619 var method = keys[v];
17620 if (!Duplex.prototype[method]) Duplex.prototype[method] = Writable.prototype[method];
17621 }
17622}
17623
17624function Duplex(options) {
17625 if (!(this instanceof Duplex)) return new Duplex(options);
17626 Readable.call(this, options);
17627 Writable.call(this, options);
17628 this.allowHalfOpen = true;
17629
17630 if (options) {
17631 if (options.readable === false) this.readable = false;
17632 if (options.writable === false) this.writable = false;
17633
17634 if (options.allowHalfOpen === false) {
17635 this.allowHalfOpen = false;
17636 this.once('end', onend);
17637 }
17638 }
17639}
17640
17641Object.defineProperty(Duplex.prototype, 'writableHighWaterMark', {
17642 // making it explicit this property is not enumerable
17643 // because otherwise some prototype manipulation in
17644 // userland will fail
17645 enumerable: false,
17646 get: function get() {
17647 return this._writableState.highWaterMark;
17648 }
17649});
17650Object.defineProperty(Duplex.prototype, 'writableBuffer', {
17651 // making it explicit this property is not enumerable
17652 // because otherwise some prototype manipulation in
17653 // userland will fail
17654 enumerable: false,
17655 get: function get() {
17656 return this._writableState && this._writableState.getBuffer();
17657 }
17658});
17659Object.defineProperty(Duplex.prototype, 'writableLength', {
17660 // making it explicit this property is not enumerable
17661 // because otherwise some prototype manipulation in
17662 // userland will fail
17663 enumerable: false,
17664 get: function get() {
17665 return this._writableState.length;
17666 }
17667}); // the no-half-open enforcer
17668
17669function onend() {
17670 // If the writable side ended, then we're ok.
17671 if (this._writableState.ended) return; // no more data can be written.
17672 // But allow more writes to happen in this tick.
17673
17674 process.nextTick(onEndNT, this);
17675}
17676
17677function onEndNT(self) {
17678 self.end();
17679}
17680
17681Object.defineProperty(Duplex.prototype, 'destroyed', {
17682 // making it explicit this property is not enumerable
17683 // because otherwise some prototype manipulation in
17684 // userland will fail
17685 enumerable: false,
17686 get: function get() {
17687 if (this._readableState === undefined || this._writableState === undefined) {
17688 return false;
17689 }
17690
17691 return this._readableState.destroyed && this._writableState.destroyed;
17692 },
17693 set: function set(value) {
17694 // we ignore the value if the stream
17695 // has not been initialized yet
17696 if (this._readableState === undefined || this._writableState === undefined) {
17697 return;
17698 } // backward compatibility, the user is explicitly
17699 // managing destroyed
17700
17701
17702 this._readableState.destroyed = value;
17703 this._writableState.destroyed = value;
17704 }
17705});
17706}).call(this)}).call(this,_dereq_('_process'))
17707},{"./_stream_readable":104,"./_stream_writable":106,"_process":98,"inherits":67}],103:[function(_dereq_,module,exports){
17708// Copyright Joyent, Inc. and other Node contributors.
17709//
17710// Permission is hereby granted, free of charge, to any person obtaining a
17711// copy of this software and associated documentation files (the
17712// "Software"), to deal in the Software without restriction, including
17713// without limitation the rights to use, copy, modify, merge, publish,
17714// distribute, sublicense, and/or sell copies of the Software, and to permit
17715// persons to whom the Software is furnished to do so, subject to the
17716// following conditions:
17717//
17718// The above copyright notice and this permission notice shall be included
17719// in all copies or substantial portions of the Software.
17720//
17721// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17722// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17723// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17724// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17725// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
17726// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
17727// USE OR OTHER DEALINGS IN THE SOFTWARE.
17728// a passthrough stream.
17729// basically just the most minimal sort of Transform stream.
17730// Every written chunk gets output as-is.
17731'use strict';
17732
17733module.exports = PassThrough;
17734
17735var Transform = _dereq_('./_stream_transform');
17736
17737_dereq_('inherits')(PassThrough, Transform);
17738
17739function PassThrough(options) {
17740 if (!(this instanceof PassThrough)) return new PassThrough(options);
17741 Transform.call(this, options);
17742}
17743
17744PassThrough.prototype._transform = function (chunk, encoding, cb) {
17745 cb(null, chunk);
17746};
17747},{"./_stream_transform":105,"inherits":67}],104:[function(_dereq_,module,exports){
17748(function (process,global){(function (){
17749// Copyright Joyent, Inc. and other Node contributors.
17750//
17751// Permission is hereby granted, free of charge, to any person obtaining a
17752// copy of this software and associated documentation files (the
17753// "Software"), to deal in the Software without restriction, including
17754// without limitation the rights to use, copy, modify, merge, publish,
17755// distribute, sublicense, and/or sell copies of the Software, and to permit
17756// persons to whom the Software is furnished to do so, subject to the
17757// following conditions:
17758//
17759// The above copyright notice and this permission notice shall be included
17760// in all copies or substantial portions of the Software.
17761//
17762// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17763// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17764// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17765// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17766// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
17767// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
17768// USE OR OTHER DEALINGS IN THE SOFTWARE.
17769'use strict';
17770
17771module.exports = Readable;
17772/*<replacement>*/
17773
17774var Duplex;
17775/*</replacement>*/
17776
17777Readable.ReadableState = ReadableState;
17778/*<replacement>*/
17779
17780var EE = _dereq_('events').EventEmitter;
17781
17782var EElistenerCount = function EElistenerCount(emitter, type) {
17783 return emitter.listeners(type).length;
17784};
17785/*</replacement>*/
17786
17787/*<replacement>*/
17788
17789
17790var Stream = _dereq_('./internal/streams/stream');
17791/*</replacement>*/
17792
17793
17794var Buffer = _dereq_('buffer').Buffer;
17795
17796var OurUint8Array = global.Uint8Array || function () {};
17797
17798function _uint8ArrayToBuffer(chunk) {
17799 return Buffer.from(chunk);
17800}
17801
17802function _isUint8Array(obj) {
17803 return Buffer.isBuffer(obj) || obj instanceof OurUint8Array;
17804}
17805/*<replacement>*/
17806
17807
17808var debugUtil = _dereq_('util');
17809
17810var debug;
17811
17812if (debugUtil && debugUtil.debuglog) {
17813 debug = debugUtil.debuglog('stream');
17814} else {
17815 debug = function debug() {};
17816}
17817/*</replacement>*/
17818
17819
17820var BufferList = _dereq_('./internal/streams/buffer_list');
17821
17822var destroyImpl = _dereq_('./internal/streams/destroy');
17823
17824var _require = _dereq_('./internal/streams/state'),
17825 getHighWaterMark = _require.getHighWaterMark;
17826
17827var _require$codes = _dereq_('../errors').codes,
17828 ERR_INVALID_ARG_TYPE = _require$codes.ERR_INVALID_ARG_TYPE,
17829 ERR_STREAM_PUSH_AFTER_EOF = _require$codes.ERR_STREAM_PUSH_AFTER_EOF,
17830 ERR_METHOD_NOT_IMPLEMENTED = _require$codes.ERR_METHOD_NOT_IMPLEMENTED,
17831 ERR_STREAM_UNSHIFT_AFTER_END_EVENT = _require$codes.ERR_STREAM_UNSHIFT_AFTER_END_EVENT; // Lazy loaded to improve the startup performance.
17832
17833
17834var StringDecoder;
17835var createReadableStreamAsyncIterator;
17836var from;
17837
17838_dereq_('inherits')(Readable, Stream);
17839
17840var errorOrDestroy = destroyImpl.errorOrDestroy;
17841var kProxyEvents = ['error', 'close', 'destroy', 'pause', 'resume'];
17842
17843function prependListener(emitter, event, fn) {
17844 // Sadly this is not cacheable as some libraries bundle their own
17845 // event emitter implementation with them.
17846 if (typeof emitter.prependListener === 'function') return emitter.prependListener(event, fn); // This is a hack to make sure that our error handler is attached before any
17847 // userland ones. NEVER DO THIS. This is here only because this code needs
17848 // to continue to work with older versions of Node.js that do not include
17849 // the prependListener() method. The goal is to eventually remove this hack.
17850
17851 if (!emitter._events || !emitter._events[event]) emitter.on(event, fn);else if (Array.isArray(emitter._events[event])) emitter._events[event].unshift(fn);else emitter._events[event] = [fn, emitter._events[event]];
17852}
17853
17854function ReadableState(options, stream, isDuplex) {
17855 Duplex = Duplex || _dereq_('./_stream_duplex');
17856 options = options || {}; // Duplex streams are both readable and writable, but share
17857 // the same options object.
17858 // However, some cases require setting options to different
17859 // values for the readable and the writable sides of the duplex stream.
17860 // These options can be provided separately as readableXXX and writableXXX.
17861
17862 if (typeof isDuplex !== 'boolean') isDuplex = stream instanceof Duplex; // object stream flag. Used to make read(n) ignore n and to
17863 // make all the buffer merging and length checks go away
17864
17865 this.objectMode = !!options.objectMode;
17866 if (isDuplex) this.objectMode = this.objectMode || !!options.readableObjectMode; // the point at which it stops calling _read() to fill the buffer
17867 // Note: 0 is a valid value, means "don't call _read preemptively ever"
17868
17869 this.highWaterMark = getHighWaterMark(this, options, 'readableHighWaterMark', isDuplex); // A linked list is used to store data chunks instead of an array because the
17870 // linked list can remove elements from the beginning faster than
17871 // array.shift()
17872
17873 this.buffer = new BufferList();
17874 this.length = 0;
17875 this.pipes = null;
17876 this.pipesCount = 0;
17877 this.flowing = null;
17878 this.ended = false;
17879 this.endEmitted = false;
17880 this.reading = false; // a flag to be able to tell if the event 'readable'/'data' is emitted
17881 // immediately, or on a later tick. We set this to true at first, because
17882 // any actions that shouldn't happen until "later" should generally also
17883 // not happen before the first read call.
17884
17885 this.sync = true; // whenever we return null, then we set a flag to say
17886 // that we're awaiting a 'readable' event emission.
17887
17888 this.needReadable = false;
17889 this.emittedReadable = false;
17890 this.readableListening = false;
17891 this.resumeScheduled = false;
17892 this.paused = true; // Should close be emitted on destroy. Defaults to true.
17893
17894 this.emitClose = options.emitClose !== false; // Should .destroy() be called after 'end' (and potentially 'finish')
17895
17896 this.autoDestroy = !!options.autoDestroy; // has it been destroyed
17897
17898 this.destroyed = false; // Crypto is kind of old and crusty. Historically, its default string
17899 // encoding is 'binary' so we have to make this configurable.
17900 // Everything else in the universe uses 'utf8', though.
17901
17902 this.defaultEncoding = options.defaultEncoding || 'utf8'; // the number of writers that are awaiting a drain event in .pipe()s
17903
17904 this.awaitDrain = 0; // if true, a maybeReadMore has been scheduled
17905
17906 this.readingMore = false;
17907 this.decoder = null;
17908 this.encoding = null;
17909
17910 if (options.encoding) {
17911 if (!StringDecoder) StringDecoder = _dereq_('string_decoder/').StringDecoder;
17912 this.decoder = new StringDecoder(options.encoding);
17913 this.encoding = options.encoding;
17914 }
17915}
17916
17917function Readable(options) {
17918 Duplex = Duplex || _dereq_('./_stream_duplex');
17919 if (!(this instanceof Readable)) return new Readable(options); // Checking for a Stream.Duplex instance is faster here instead of inside
17920 // the ReadableState constructor, at least with V8 6.5
17921
17922 var isDuplex = this instanceof Duplex;
17923 this._readableState = new ReadableState(options, this, isDuplex); // legacy
17924
17925 this.readable = true;
17926
17927 if (options) {
17928 if (typeof options.read === 'function') this._read = options.read;
17929 if (typeof options.destroy === 'function') this._destroy = options.destroy;
17930 }
17931
17932 Stream.call(this);
17933}
17934
17935Object.defineProperty(Readable.prototype, 'destroyed', {
17936 // making it explicit this property is not enumerable
17937 // because otherwise some prototype manipulation in
17938 // userland will fail
17939 enumerable: false,
17940 get: function get() {
17941 if (this._readableState === undefined) {
17942 return false;
17943 }
17944
17945 return this._readableState.destroyed;
17946 },
17947 set: function set(value) {
17948 // we ignore the value if the stream
17949 // has not been initialized yet
17950 if (!this._readableState) {
17951 return;
17952 } // backward compatibility, the user is explicitly
17953 // managing destroyed
17954
17955
17956 this._readableState.destroyed = value;
17957 }
17958});
17959Readable.prototype.destroy = destroyImpl.destroy;
17960Readable.prototype._undestroy = destroyImpl.undestroy;
17961
17962Readable.prototype._destroy = function (err, cb) {
17963 cb(err);
17964}; // Manually shove something into the read() buffer.
17965// This returns true if the highWaterMark has not been hit yet,
17966// similar to how Writable.write() returns true if you should
17967// write() some more.
17968
17969
17970Readable.prototype.push = function (chunk, encoding) {
17971 var state = this._readableState;
17972 var skipChunkCheck;
17973
17974 if (!state.objectMode) {
17975 if (typeof chunk === 'string') {
17976 encoding = encoding || state.defaultEncoding;
17977
17978 if (encoding !== state.encoding) {
17979 chunk = Buffer.from(chunk, encoding);
17980 encoding = '';
17981 }
17982
17983 skipChunkCheck = true;
17984 }
17985 } else {
17986 skipChunkCheck = true;
17987 }
17988
17989 return readableAddChunk(this, chunk, encoding, false, skipChunkCheck);
17990}; // Unshift should *always* be something directly out of read()
17991
17992
17993Readable.prototype.unshift = function (chunk) {
17994 return readableAddChunk(this, chunk, null, true, false);
17995};
17996
17997function readableAddChunk(stream, chunk, encoding, addToFront, skipChunkCheck) {
17998 debug('readableAddChunk', chunk);
17999 var state = stream._readableState;
18000
18001 if (chunk === null) {
18002 state.reading = false;
18003 onEofChunk(stream, state);
18004 } else {
18005 var er;
18006 if (!skipChunkCheck) er = chunkInvalid(state, chunk);
18007
18008 if (er) {
18009 errorOrDestroy(stream, er);
18010 } else if (state.objectMode || chunk && chunk.length > 0) {
18011 if (typeof chunk !== 'string' && !state.objectMode && Object.getPrototypeOf(chunk) !== Buffer.prototype) {
18012 chunk = _uint8ArrayToBuffer(chunk);
18013 }
18014
18015 if (addToFront) {
18016 if (state.endEmitted) errorOrDestroy(stream, new ERR_STREAM_UNSHIFT_AFTER_END_EVENT());else addChunk(stream, state, chunk, true);
18017 } else if (state.ended) {
18018 errorOrDestroy(stream, new ERR_STREAM_PUSH_AFTER_EOF());
18019 } else if (state.destroyed) {
18020 return false;
18021 } else {
18022 state.reading = false;
18023
18024 if (state.decoder && !encoding) {
18025 chunk = state.decoder.write(chunk);
18026 if (state.objectMode || chunk.length !== 0) addChunk(stream, state, chunk, false);else maybeReadMore(stream, state);
18027 } else {
18028 addChunk(stream, state, chunk, false);
18029 }
18030 }
18031 } else if (!addToFront) {
18032 state.reading = false;
18033 maybeReadMore(stream, state);
18034 }
18035 } // We can push more data if we are below the highWaterMark.
18036 // Also, if we have no data yet, we can stand some more bytes.
18037 // This is to work around cases where hwm=0, such as the repl.
18038
18039
18040 return !state.ended && (state.length < state.highWaterMark || state.length === 0);
18041}
18042
18043function addChunk(stream, state, chunk, addToFront) {
18044 if (state.flowing && state.length === 0 && !state.sync) {
18045 state.awaitDrain = 0;
18046 stream.emit('data', chunk);
18047 } else {
18048 // update the buffer info.
18049 state.length += state.objectMode ? 1 : chunk.length;
18050 if (addToFront) state.buffer.unshift(chunk);else state.buffer.push(chunk);
18051 if (state.needReadable) emitReadable(stream);
18052 }
18053
18054 maybeReadMore(stream, state);
18055}
18056
18057function chunkInvalid(state, chunk) {
18058 var er;
18059
18060 if (!_isUint8Array(chunk) && typeof chunk !== 'string' && chunk !== undefined && !state.objectMode) {
18061 er = new ERR_INVALID_ARG_TYPE('chunk', ['string', 'Buffer', 'Uint8Array'], chunk);
18062 }
18063
18064 return er;
18065}
18066
18067Readable.prototype.isPaused = function () {
18068 return this._readableState.flowing === false;
18069}; // backwards compatibility.
18070
18071
18072Readable.prototype.setEncoding = function (enc) {
18073 if (!StringDecoder) StringDecoder = _dereq_('string_decoder/').StringDecoder;
18074 var decoder = new StringDecoder(enc);
18075 this._readableState.decoder = decoder; // If setEncoding(null), decoder.encoding equals utf8
18076
18077 this._readableState.encoding = this._readableState.decoder.encoding; // Iterate over current buffer to convert already stored Buffers:
18078
18079 var p = this._readableState.buffer.head;
18080 var content = '';
18081
18082 while (p !== null) {
18083 content += decoder.write(p.data);
18084 p = p.next;
18085 }
18086
18087 this._readableState.buffer.clear();
18088
18089 if (content !== '') this._readableState.buffer.push(content);
18090 this._readableState.length = content.length;
18091 return this;
18092}; // Don't raise the hwm > 1GB
18093
18094
18095var MAX_HWM = 0x40000000;
18096
18097function computeNewHighWaterMark(n) {
18098 if (n >= MAX_HWM) {
18099 // TODO(ronag): Throw ERR_VALUE_OUT_OF_RANGE.
18100 n = MAX_HWM;
18101 } else {
18102 // Get the next highest power of 2 to prevent increasing hwm excessively in
18103 // tiny amounts
18104 n--;
18105 n |= n >>> 1;
18106 n |= n >>> 2;
18107 n |= n >>> 4;
18108 n |= n >>> 8;
18109 n |= n >>> 16;
18110 n++;
18111 }
18112
18113 return n;
18114} // This function is designed to be inlinable, so please take care when making
18115// changes to the function body.
18116
18117
18118function howMuchToRead(n, state) {
18119 if (n <= 0 || state.length === 0 && state.ended) return 0;
18120 if (state.objectMode) return 1;
18121
18122 if (n !== n) {
18123 // Only flow one buffer at a time
18124 if (state.flowing && state.length) return state.buffer.head.data.length;else return state.length;
18125 } // If we're asking for more than the current hwm, then raise the hwm.
18126
18127
18128 if (n > state.highWaterMark) state.highWaterMark = computeNewHighWaterMark(n);
18129 if (n <= state.length) return n; // Don't have enough
18130
18131 if (!state.ended) {
18132 state.needReadable = true;
18133 return 0;
18134 }
18135
18136 return state.length;
18137} // you can override either this method, or the async _read(n) below.
18138
18139
18140Readable.prototype.read = function (n) {
18141 debug('read', n);
18142 n = parseInt(n, 10);
18143 var state = this._readableState;
18144 var nOrig = n;
18145 if (n !== 0) state.emittedReadable = false; // if we're doing read(0) to trigger a readable event, but we
18146 // already have a bunch of data in the buffer, then just trigger
18147 // the 'readable' event and move on.
18148
18149 if (n === 0 && state.needReadable && ((state.highWaterMark !== 0 ? state.length >= state.highWaterMark : state.length > 0) || state.ended)) {
18150 debug('read: emitReadable', state.length, state.ended);
18151 if (state.length === 0 && state.ended) endReadable(this);else emitReadable(this);
18152 return null;
18153 }
18154
18155 n = howMuchToRead(n, state); // if we've ended, and we're now clear, then finish it up.
18156
18157 if (n === 0 && state.ended) {
18158 if (state.length === 0) endReadable(this);
18159 return null;
18160 } // All the actual chunk generation logic needs to be
18161 // *below* the call to _read. The reason is that in certain
18162 // synthetic stream cases, such as passthrough streams, _read
18163 // may be a completely synchronous operation which may change
18164 // the state of the read buffer, providing enough data when
18165 // before there was *not* enough.
18166 //
18167 // So, the steps are:
18168 // 1. Figure out what the state of things will be after we do
18169 // a read from the buffer.
18170 //
18171 // 2. If that resulting state will trigger a _read, then call _read.
18172 // Note that this may be asynchronous, or synchronous. Yes, it is
18173 // deeply ugly to write APIs this way, but that still doesn't mean
18174 // that the Readable class should behave improperly, as streams are
18175 // designed to be sync/async agnostic.
18176 // Take note if the _read call is sync or async (ie, if the read call
18177 // has returned yet), so that we know whether or not it's safe to emit
18178 // 'readable' etc.
18179 //
18180 // 3. Actually pull the requested chunks out of the buffer and return.
18181 // if we need a readable event, then we need to do some reading.
18182
18183
18184 var doRead = state.needReadable;
18185 debug('need readable', doRead); // if we currently have less than the highWaterMark, then also read some
18186
18187 if (state.length === 0 || state.length - n < state.highWaterMark) {
18188 doRead = true;
18189 debug('length less than watermark', doRead);
18190 } // however, if we've ended, then there's no point, and if we're already
18191 // reading, then it's unnecessary.
18192
18193
18194 if (state.ended || state.reading) {
18195 doRead = false;
18196 debug('reading or ended', doRead);
18197 } else if (doRead) {
18198 debug('do read');
18199 state.reading = true;
18200 state.sync = true; // if the length is currently zero, then we *need* a readable event.
18201
18202 if (state.length === 0) state.needReadable = true; // call internal read method
18203
18204 this._read(state.highWaterMark);
18205
18206 state.sync = false; // If _read pushed data synchronously, then `reading` will be false,
18207 // and we need to re-evaluate how much data we can return to the user.
18208
18209 if (!state.reading) n = howMuchToRead(nOrig, state);
18210 }
18211
18212 var ret;
18213 if (n > 0) ret = fromList(n, state);else ret = null;
18214
18215 if (ret === null) {
18216 state.needReadable = state.length <= state.highWaterMark;
18217 n = 0;
18218 } else {
18219 state.length -= n;
18220 state.awaitDrain = 0;
18221 }
18222
18223 if (state.length === 0) {
18224 // If we have nothing in the buffer, then we want to know
18225 // as soon as we *do* get something into the buffer.
18226 if (!state.ended) state.needReadable = true; // If we tried to read() past the EOF, then emit end on the next tick.
18227
18228 if (nOrig !== n && state.ended) endReadable(this);
18229 }
18230
18231 if (ret !== null) this.emit('data', ret);
18232 return ret;
18233};
18234
18235function onEofChunk(stream, state) {
18236 debug('onEofChunk');
18237 if (state.ended) return;
18238
18239 if (state.decoder) {
18240 var chunk = state.decoder.end();
18241
18242 if (chunk && chunk.length) {
18243 state.buffer.push(chunk);
18244 state.length += state.objectMode ? 1 : chunk.length;
18245 }
18246 }
18247
18248 state.ended = true;
18249
18250 if (state.sync) {
18251 // if we are sync, wait until next tick to emit the data.
18252 // Otherwise we risk emitting data in the flow()
18253 // the readable code triggers during a read() call
18254 emitReadable(stream);
18255 } else {
18256 // emit 'readable' now to make sure it gets picked up.
18257 state.needReadable = false;
18258
18259 if (!state.emittedReadable) {
18260 state.emittedReadable = true;
18261 emitReadable_(stream);
18262 }
18263 }
18264} // Don't emit readable right away in sync mode, because this can trigger
18265// another read() call => stack overflow. This way, it might trigger
18266// a nextTick recursion warning, but that's not so bad.
18267
18268
18269function emitReadable(stream) {
18270 var state = stream._readableState;
18271 debug('emitReadable', state.needReadable, state.emittedReadable);
18272 state.needReadable = false;
18273
18274 if (!state.emittedReadable) {
18275 debug('emitReadable', state.flowing);
18276 state.emittedReadable = true;
18277 process.nextTick(emitReadable_, stream);
18278 }
18279}
18280
18281function emitReadable_(stream) {
18282 var state = stream._readableState;
18283 debug('emitReadable_', state.destroyed, state.length, state.ended);
18284
18285 if (!state.destroyed && (state.length || state.ended)) {
18286 stream.emit('readable');
18287 state.emittedReadable = false;
18288 } // The stream needs another readable event if
18289 // 1. It is not flowing, as the flow mechanism will take
18290 // care of it.
18291 // 2. It is not ended.
18292 // 3. It is below the highWaterMark, so we can schedule
18293 // another readable later.
18294
18295
18296 state.needReadable = !state.flowing && !state.ended && state.length <= state.highWaterMark;
18297 flow(stream);
18298} // at this point, the user has presumably seen the 'readable' event,
18299// and called read() to consume some data. that may have triggered
18300// in turn another _read(n) call, in which case reading = true if
18301// it's in progress.
18302// However, if we're not ended, or reading, and the length < hwm,
18303// then go ahead and try to read some more preemptively.
18304
18305
18306function maybeReadMore(stream, state) {
18307 if (!state.readingMore) {
18308 state.readingMore = true;
18309 process.nextTick(maybeReadMore_, stream, state);
18310 }
18311}
18312
18313function maybeReadMore_(stream, state) {
18314 // Attempt to read more data if we should.
18315 //
18316 // The conditions for reading more data are (one of):
18317 // - Not enough data buffered (state.length < state.highWaterMark). The loop
18318 // is responsible for filling the buffer with enough data if such data
18319 // is available. If highWaterMark is 0 and we are not in the flowing mode
18320 // we should _not_ attempt to buffer any extra data. We'll get more data
18321 // when the stream consumer calls read() instead.
18322 // - No data in the buffer, and the stream is in flowing mode. In this mode
18323 // the loop below is responsible for ensuring read() is called. Failing to
18324 // call read here would abort the flow and there's no other mechanism for
18325 // continuing the flow if the stream consumer has just subscribed to the
18326 // 'data' event.
18327 //
18328 // In addition to the above conditions to keep reading data, the following
18329 // conditions prevent the data from being read:
18330 // - The stream has ended (state.ended).
18331 // - There is already a pending 'read' operation (state.reading). This is a
18332 // case where the the stream has called the implementation defined _read()
18333 // method, but they are processing the call asynchronously and have _not_
18334 // called push() with new data. In this case we skip performing more
18335 // read()s. The execution ends in this method again after the _read() ends
18336 // up calling push() with more data.
18337 while (!state.reading && !state.ended && (state.length < state.highWaterMark || state.flowing && state.length === 0)) {
18338 var len = state.length;
18339 debug('maybeReadMore read 0');
18340 stream.read(0);
18341 if (len === state.length) // didn't get any data, stop spinning.
18342 break;
18343 }
18344
18345 state.readingMore = false;
18346} // abstract method. to be overridden in specific implementation classes.
18347// call cb(er, data) where data is <= n in length.
18348// for virtual (non-string, non-buffer) streams, "length" is somewhat
18349// arbitrary, and perhaps not very meaningful.
18350
18351
18352Readable.prototype._read = function (n) {
18353 errorOrDestroy(this, new ERR_METHOD_NOT_IMPLEMENTED('_read()'));
18354};
18355
18356Readable.prototype.pipe = function (dest, pipeOpts) {
18357 var src = this;
18358 var state = this._readableState;
18359
18360 switch (state.pipesCount) {
18361 case 0:
18362 state.pipes = dest;
18363 break;
18364
18365 case 1:
18366 state.pipes = [state.pipes, dest];
18367 break;
18368
18369 default:
18370 state.pipes.push(dest);
18371 break;
18372 }
18373
18374 state.pipesCount += 1;
18375 debug('pipe count=%d opts=%j', state.pipesCount, pipeOpts);
18376 var doEnd = (!pipeOpts || pipeOpts.end !== false) && dest !== process.stdout && dest !== process.stderr;
18377 var endFn = doEnd ? onend : unpipe;
18378 if (state.endEmitted) process.nextTick(endFn);else src.once('end', endFn);
18379 dest.on('unpipe', onunpipe);
18380
18381 function onunpipe(readable, unpipeInfo) {
18382 debug('onunpipe');
18383
18384 if (readable === src) {
18385 if (unpipeInfo && unpipeInfo.hasUnpiped === false) {
18386 unpipeInfo.hasUnpiped = true;
18387 cleanup();
18388 }
18389 }
18390 }
18391
18392 function onend() {
18393 debug('onend');
18394 dest.end();
18395 } // when the dest drains, it reduces the awaitDrain counter
18396 // on the source. This would be more elegant with a .once()
18397 // handler in flow(), but adding and removing repeatedly is
18398 // too slow.
18399
18400
18401 var ondrain = pipeOnDrain(src);
18402 dest.on('drain', ondrain);
18403 var cleanedUp = false;
18404
18405 function cleanup() {
18406 debug('cleanup'); // cleanup event handlers once the pipe is broken
18407
18408 dest.removeListener('close', onclose);
18409 dest.removeListener('finish', onfinish);
18410 dest.removeListener('drain', ondrain);
18411 dest.removeListener('error', onerror);
18412 dest.removeListener('unpipe', onunpipe);
18413 src.removeListener('end', onend);
18414 src.removeListener('end', unpipe);
18415 src.removeListener('data', ondata);
18416 cleanedUp = true; // if the reader is waiting for a drain event from this
18417 // specific writer, then it would cause it to never start
18418 // flowing again.
18419 // So, if this is awaiting a drain, then we just call it now.
18420 // If we don't know, then assume that we are waiting for one.
18421
18422 if (state.awaitDrain && (!dest._writableState || dest._writableState.needDrain)) ondrain();
18423 }
18424
18425 src.on('data', ondata);
18426
18427 function ondata(chunk) {
18428 debug('ondata');
18429 var ret = dest.write(chunk);
18430 debug('dest.write', ret);
18431
18432 if (ret === false) {
18433 // If the user unpiped during `dest.write()`, it is possible
18434 // to get stuck in a permanently paused state if that write
18435 // also returned false.
18436 // => Check whether `dest` is still a piping destination.
18437 if ((state.pipesCount === 1 && state.pipes === dest || state.pipesCount > 1 && indexOf(state.pipes, dest) !== -1) && !cleanedUp) {
18438 debug('false write response, pause', state.awaitDrain);
18439 state.awaitDrain++;
18440 }
18441
18442 src.pause();
18443 }
18444 } // if the dest has an error, then stop piping into it.
18445 // however, don't suppress the throwing behavior for this.
18446
18447
18448 function onerror(er) {
18449 debug('onerror', er);
18450 unpipe();
18451 dest.removeListener('error', onerror);
18452 if (EElistenerCount(dest, 'error') === 0) errorOrDestroy(dest, er);
18453 } // Make sure our error handler is attached before userland ones.
18454
18455
18456 prependListener(dest, 'error', onerror); // Both close and finish should trigger unpipe, but only once.
18457
18458 function onclose() {
18459 dest.removeListener('finish', onfinish);
18460 unpipe();
18461 }
18462
18463 dest.once('close', onclose);
18464
18465 function onfinish() {
18466 debug('onfinish');
18467 dest.removeListener('close', onclose);
18468 unpipe();
18469 }
18470
18471 dest.once('finish', onfinish);
18472
18473 function unpipe() {
18474 debug('unpipe');
18475 src.unpipe(dest);
18476 } // tell the dest that it's being piped to
18477
18478
18479 dest.emit('pipe', src); // start the flow if it hasn't been started already.
18480
18481 if (!state.flowing) {
18482 debug('pipe resume');
18483 src.resume();
18484 }
18485
18486 return dest;
18487};
18488
18489function pipeOnDrain(src) {
18490 return function pipeOnDrainFunctionResult() {
18491 var state = src._readableState;
18492 debug('pipeOnDrain', state.awaitDrain);
18493 if (state.awaitDrain) state.awaitDrain--;
18494
18495 if (state.awaitDrain === 0 && EElistenerCount(src, 'data')) {
18496 state.flowing = true;
18497 flow(src);
18498 }
18499 };
18500}
18501
18502Readable.prototype.unpipe = function (dest) {
18503 var state = this._readableState;
18504 var unpipeInfo = {
18505 hasUnpiped: false
18506 }; // if we're not piping anywhere, then do nothing.
18507
18508 if (state.pipesCount === 0) return this; // just one destination. most common case.
18509
18510 if (state.pipesCount === 1) {
18511 // passed in one, but it's not the right one.
18512 if (dest && dest !== state.pipes) return this;
18513 if (!dest) dest = state.pipes; // got a match.
18514
18515 state.pipes = null;
18516 state.pipesCount = 0;
18517 state.flowing = false;
18518 if (dest) dest.emit('unpipe', this, unpipeInfo);
18519 return this;
18520 } // slow case. multiple pipe destinations.
18521
18522
18523 if (!dest) {
18524 // remove all.
18525 var dests = state.pipes;
18526 var len = state.pipesCount;
18527 state.pipes = null;
18528 state.pipesCount = 0;
18529 state.flowing = false;
18530
18531 for (var i = 0; i < len; i++) {
18532 dests[i].emit('unpipe', this, {
18533 hasUnpiped: false
18534 });
18535 }
18536
18537 return this;
18538 } // try to find the right one.
18539
18540
18541 var index = indexOf(state.pipes, dest);
18542 if (index === -1) return this;
18543 state.pipes.splice(index, 1);
18544 state.pipesCount -= 1;
18545 if (state.pipesCount === 1) state.pipes = state.pipes[0];
18546 dest.emit('unpipe', this, unpipeInfo);
18547 return this;
18548}; // set up data events if they are asked for
18549// Ensure readable listeners eventually get something
18550
18551
18552Readable.prototype.on = function (ev, fn) {
18553 var res = Stream.prototype.on.call(this, ev, fn);
18554 var state = this._readableState;
18555
18556 if (ev === 'data') {
18557 // update readableListening so that resume() may be a no-op
18558 // a few lines down. This is needed to support once('readable').
18559 state.readableListening = this.listenerCount('readable') > 0; // Try start flowing on next tick if stream isn't explicitly paused
18560
18561 if (state.flowing !== false) this.resume();
18562 } else if (ev === 'readable') {
18563 if (!state.endEmitted && !state.readableListening) {
18564 state.readableListening = state.needReadable = true;
18565 state.flowing = false;
18566 state.emittedReadable = false;
18567 debug('on readable', state.length, state.reading);
18568
18569 if (state.length) {
18570 emitReadable(this);
18571 } else if (!state.reading) {
18572 process.nextTick(nReadingNextTick, this);
18573 }
18574 }
18575 }
18576
18577 return res;
18578};
18579
18580Readable.prototype.addListener = Readable.prototype.on;
18581
18582Readable.prototype.removeListener = function (ev, fn) {
18583 var res = Stream.prototype.removeListener.call(this, ev, fn);
18584
18585 if (ev === 'readable') {
18586 // We need to check if there is someone still listening to
18587 // readable and reset the state. However this needs to happen
18588 // after readable has been emitted but before I/O (nextTick) to
18589 // support once('readable', fn) cycles. This means that calling
18590 // resume within the same tick will have no
18591 // effect.
18592 process.nextTick(updateReadableListening, this);
18593 }
18594
18595 return res;
18596};
18597
18598Readable.prototype.removeAllListeners = function (ev) {
18599 var res = Stream.prototype.removeAllListeners.apply(this, arguments);
18600
18601 if (ev === 'readable' || ev === undefined) {
18602 // We need to check if there is someone still listening to
18603 // readable and reset the state. However this needs to happen
18604 // after readable has been emitted but before I/O (nextTick) to
18605 // support once('readable', fn) cycles. This means that calling
18606 // resume within the same tick will have no
18607 // effect.
18608 process.nextTick(updateReadableListening, this);
18609 }
18610
18611 return res;
18612};
18613
18614function updateReadableListening(self) {
18615 var state = self._readableState;
18616 state.readableListening = self.listenerCount('readable') > 0;
18617
18618 if (state.resumeScheduled && !state.paused) {
18619 // flowing needs to be set to true now, otherwise
18620 // the upcoming resume will not flow.
18621 state.flowing = true; // crude way to check if we should resume
18622 } else if (self.listenerCount('data') > 0) {
18623 self.resume();
18624 }
18625}
18626
18627function nReadingNextTick(self) {
18628 debug('readable nexttick read 0');
18629 self.read(0);
18630} // pause() and resume() are remnants of the legacy readable stream API
18631// If the user uses them, then switch into old mode.
18632
18633
18634Readable.prototype.resume = function () {
18635 var state = this._readableState;
18636
18637 if (!state.flowing) {
18638 debug('resume'); // we flow only if there is no one listening
18639 // for readable, but we still have to call
18640 // resume()
18641
18642 state.flowing = !state.readableListening;
18643 resume(this, state);
18644 }
18645
18646 state.paused = false;
18647 return this;
18648};
18649
18650function resume(stream, state) {
18651 if (!state.resumeScheduled) {
18652 state.resumeScheduled = true;
18653 process.nextTick(resume_, stream, state);
18654 }
18655}
18656
18657function resume_(stream, state) {
18658 debug('resume', state.reading);
18659
18660 if (!state.reading) {
18661 stream.read(0);
18662 }
18663
18664 state.resumeScheduled = false;
18665 stream.emit('resume');
18666 flow(stream);
18667 if (state.flowing && !state.reading) stream.read(0);
18668}
18669
18670Readable.prototype.pause = function () {
18671 debug('call pause flowing=%j', this._readableState.flowing);
18672
18673 if (this._readableState.flowing !== false) {
18674 debug('pause');
18675 this._readableState.flowing = false;
18676 this.emit('pause');
18677 }
18678
18679 this._readableState.paused = true;
18680 return this;
18681};
18682
18683function flow(stream) {
18684 var state = stream._readableState;
18685 debug('flow', state.flowing);
18686
18687 while (state.flowing && stream.read() !== null) {
18688 ;
18689 }
18690} // wrap an old-style stream as the async data source.
18691// This is *not* part of the readable stream interface.
18692// It is an ugly unfortunate mess of history.
18693
18694
18695Readable.prototype.wrap = function (stream) {
18696 var _this = this;
18697
18698 var state = this._readableState;
18699 var paused = false;
18700 stream.on('end', function () {
18701 debug('wrapped end');
18702
18703 if (state.decoder && !state.ended) {
18704 var chunk = state.decoder.end();
18705 if (chunk && chunk.length) _this.push(chunk);
18706 }
18707
18708 _this.push(null);
18709 });
18710 stream.on('data', function (chunk) {
18711 debug('wrapped data');
18712 if (state.decoder) chunk = state.decoder.write(chunk); // don't skip over falsy values in objectMode
18713
18714 if (state.objectMode && (chunk === null || chunk === undefined)) return;else if (!state.objectMode && (!chunk || !chunk.length)) return;
18715
18716 var ret = _this.push(chunk);
18717
18718 if (!ret) {
18719 paused = true;
18720 stream.pause();
18721 }
18722 }); // proxy all the other methods.
18723 // important when wrapping filters and duplexes.
18724
18725 for (var i in stream) {
18726 if (this[i] === undefined && typeof stream[i] === 'function') {
18727 this[i] = function methodWrap(method) {
18728 return function methodWrapReturnFunction() {
18729 return stream[method].apply(stream, arguments);
18730 };
18731 }(i);
18732 }
18733 } // proxy certain important events.
18734
18735
18736 for (var n = 0; n < kProxyEvents.length; n++) {
18737 stream.on(kProxyEvents[n], this.emit.bind(this, kProxyEvents[n]));
18738 } // when we try to consume some more bytes, simply unpause the
18739 // underlying stream.
18740
18741
18742 this._read = function (n) {
18743 debug('wrapped _read', n);
18744
18745 if (paused) {
18746 paused = false;
18747 stream.resume();
18748 }
18749 };
18750
18751 return this;
18752};
18753
18754if (typeof Symbol === 'function') {
18755 Readable.prototype[Symbol.asyncIterator] = function () {
18756 if (createReadableStreamAsyncIterator === undefined) {
18757 createReadableStreamAsyncIterator = _dereq_('./internal/streams/async_iterator');
18758 }
18759
18760 return createReadableStreamAsyncIterator(this);
18761 };
18762}
18763
18764Object.defineProperty(Readable.prototype, 'readableHighWaterMark', {
18765 // making it explicit this property is not enumerable
18766 // because otherwise some prototype manipulation in
18767 // userland will fail
18768 enumerable: false,
18769 get: function get() {
18770 return this._readableState.highWaterMark;
18771 }
18772});
18773Object.defineProperty(Readable.prototype, 'readableBuffer', {
18774 // making it explicit this property is not enumerable
18775 // because otherwise some prototype manipulation in
18776 // userland will fail
18777 enumerable: false,
18778 get: function get() {
18779 return this._readableState && this._readableState.buffer;
18780 }
18781});
18782Object.defineProperty(Readable.prototype, 'readableFlowing', {
18783 // making it explicit this property is not enumerable
18784 // because otherwise some prototype manipulation in
18785 // userland will fail
18786 enumerable: false,
18787 get: function get() {
18788 return this._readableState.flowing;
18789 },
18790 set: function set(state) {
18791 if (this._readableState) {
18792 this._readableState.flowing = state;
18793 }
18794 }
18795}); // exposed for testing purposes only.
18796
18797Readable._fromList = fromList;
18798Object.defineProperty(Readable.prototype, 'readableLength', {
18799 // making it explicit this property is not enumerable
18800 // because otherwise some prototype manipulation in
18801 // userland will fail
18802 enumerable: false,
18803 get: function get() {
18804 return this._readableState.length;
18805 }
18806}); // Pluck off n bytes from an array of buffers.
18807// Length is the combined lengths of all the buffers in the list.
18808// This function is designed to be inlinable, so please take care when making
18809// changes to the function body.
18810
18811function fromList(n, state) {
18812 // nothing buffered
18813 if (state.length === 0) return null;
18814 var ret;
18815 if (state.objectMode) ret = state.buffer.shift();else if (!n || n >= state.length) {
18816 // read it all, truncate the list
18817 if (state.decoder) ret = state.buffer.join('');else if (state.buffer.length === 1) ret = state.buffer.first();else ret = state.buffer.concat(state.length);
18818 state.buffer.clear();
18819 } else {
18820 // read part of list
18821 ret = state.buffer.consume(n, state.decoder);
18822 }
18823 return ret;
18824}
18825
18826function endReadable(stream) {
18827 var state = stream._readableState;
18828 debug('endReadable', state.endEmitted);
18829
18830 if (!state.endEmitted) {
18831 state.ended = true;
18832 process.nextTick(endReadableNT, state, stream);
18833 }
18834}
18835
18836function endReadableNT(state, stream) {
18837 debug('endReadableNT', state.endEmitted, state.length); // Check that we didn't get one last unshift.
18838
18839 if (!state.endEmitted && state.length === 0) {
18840 state.endEmitted = true;
18841 stream.readable = false;
18842 stream.emit('end');
18843
18844 if (state.autoDestroy) {
18845 // In case of duplex streams we need a way to detect
18846 // if the writable side is ready for autoDestroy as well
18847 var wState = stream._writableState;
18848
18849 if (!wState || wState.autoDestroy && wState.finished) {
18850 stream.destroy();
18851 }
18852 }
18853 }
18854}
18855
18856if (typeof Symbol === 'function') {
18857 Readable.from = function (iterable, opts) {
18858 if (from === undefined) {
18859 from = _dereq_('./internal/streams/from');
18860 }
18861
18862 return from(Readable, iterable, opts);
18863 };
18864}
18865
18866function indexOf(xs, x) {
18867 for (var i = 0, l = xs.length; i < l; i++) {
18868 if (xs[i] === x) return i;
18869 }
18870
18871 return -1;
18872}
18873}).call(this)}).call(this,_dereq_('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
18874},{"../errors":101,"./_stream_duplex":102,"./internal/streams/async_iterator":107,"./internal/streams/buffer_list":108,"./internal/streams/destroy":109,"./internal/streams/from":111,"./internal/streams/state":113,"./internal/streams/stream":114,"_process":98,"buffer":28,"events":27,"inherits":67,"string_decoder/":115,"util":26}],105:[function(_dereq_,module,exports){
18875// Copyright Joyent, Inc. and other Node contributors.
18876//
18877// Permission is hereby granted, free of charge, to any person obtaining a
18878// copy of this software and associated documentation files (the
18879// "Software"), to deal in the Software without restriction, including
18880// without limitation the rights to use, copy, modify, merge, publish,
18881// distribute, sublicense, and/or sell copies of the Software, and to permit
18882// persons to whom the Software is furnished to do so, subject to the
18883// following conditions:
18884//
18885// The above copyright notice and this permission notice shall be included
18886// in all copies or substantial portions of the Software.
18887//
18888// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18889// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18890// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
18891// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18892// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
18893// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
18894// USE OR OTHER DEALINGS IN THE SOFTWARE.
18895// a transform stream is a readable/writable stream where you do
18896// something with the data. Sometimes it's called a "filter",
18897// but that's not a great name for it, since that implies a thing where
18898// some bits pass through, and others are simply ignored. (That would
18899// be a valid example of a transform, of course.)
18900//
18901// While the output is causally related to the input, it's not a
18902// necessarily symmetric or synchronous transformation. For example,
18903// a zlib stream might take multiple plain-text writes(), and then
18904// emit a single compressed chunk some time in the future.
18905//
18906// Here's how this works:
18907//
18908// The Transform stream has all the aspects of the readable and writable
18909// stream classes. When you write(chunk), that calls _write(chunk,cb)
18910// internally, and returns false if there's a lot of pending writes
18911// buffered up. When you call read(), that calls _read(n) until
18912// there's enough pending readable data buffered up.
18913//
18914// In a transform stream, the written data is placed in a buffer. When
18915// _read(n) is called, it transforms the queued up data, calling the
18916// buffered _write cb's as it consumes chunks. If consuming a single
18917// written chunk would result in multiple output chunks, then the first
18918// outputted bit calls the readcb, and subsequent chunks just go into
18919// the read buffer, and will cause it to emit 'readable' if necessary.
18920//
18921// This way, back-pressure is actually determined by the reading side,
18922// since _read has to be called to start processing a new chunk. However,
18923// a pathological inflate type of transform can cause excessive buffering
18924// here. For example, imagine a stream where every byte of input is
18925// interpreted as an integer from 0-255, and then results in that many
18926// bytes of output. Writing the 4 bytes {ff,ff,ff,ff} would result in
18927// 1kb of data being output. In this case, you could write a very small
18928// amount of input, and end up with a very large amount of output. In
18929// such a pathological inflating mechanism, there'd be no way to tell
18930// the system to stop doing the transform. A single 4MB write could
18931// cause the system to run out of memory.
18932//
18933// However, even in such a pathological case, only a single written chunk
18934// would be consumed, and then the rest would wait (un-transformed) until
18935// the results of the previous transformed chunk were consumed.
18936'use strict';
18937
18938module.exports = Transform;
18939
18940var _require$codes = _dereq_('../errors').codes,
18941 ERR_METHOD_NOT_IMPLEMENTED = _require$codes.ERR_METHOD_NOT_IMPLEMENTED,
18942 ERR_MULTIPLE_CALLBACK = _require$codes.ERR_MULTIPLE_CALLBACK,
18943 ERR_TRANSFORM_ALREADY_TRANSFORMING = _require$codes.ERR_TRANSFORM_ALREADY_TRANSFORMING,
18944 ERR_TRANSFORM_WITH_LENGTH_0 = _require$codes.ERR_TRANSFORM_WITH_LENGTH_0;
18945
18946var Duplex = _dereq_('./_stream_duplex');
18947
18948_dereq_('inherits')(Transform, Duplex);
18949
18950function afterTransform(er, data) {
18951 var ts = this._transformState;
18952 ts.transforming = false;
18953 var cb = ts.writecb;
18954
18955 if (cb === null) {
18956 return this.emit('error', new ERR_MULTIPLE_CALLBACK());
18957 }
18958
18959 ts.writechunk = null;
18960 ts.writecb = null;
18961 if (data != null) // single equals check for both `null` and `undefined`
18962 this.push(data);
18963 cb(er);
18964 var rs = this._readableState;
18965 rs.reading = false;
18966
18967 if (rs.needReadable || rs.length < rs.highWaterMark) {
18968 this._read(rs.highWaterMark);
18969 }
18970}
18971
18972function Transform(options) {
18973 if (!(this instanceof Transform)) return new Transform(options);
18974 Duplex.call(this, options);
18975 this._transformState = {
18976 afterTransform: afterTransform.bind(this),
18977 needTransform: false,
18978 transforming: false,
18979 writecb: null,
18980 writechunk: null,
18981 writeencoding: null
18982 }; // start out asking for a readable event once data is transformed.
18983
18984 this._readableState.needReadable = true; // we have implemented the _read method, and done the other things
18985 // that Readable wants before the first _read call, so unset the
18986 // sync guard flag.
18987
18988 this._readableState.sync = false;
18989
18990 if (options) {
18991 if (typeof options.transform === 'function') this._transform = options.transform;
18992 if (typeof options.flush === 'function') this._flush = options.flush;
18993 } // When the writable side finishes, then flush out anything remaining.
18994
18995
18996 this.on('prefinish', prefinish);
18997}
18998
18999function prefinish() {
19000 var _this = this;
19001
19002 if (typeof this._flush === 'function' && !this._readableState.destroyed) {
19003 this._flush(function (er, data) {
19004 done(_this, er, data);
19005 });
19006 } else {
19007 done(this, null, null);
19008 }
19009}
19010
19011Transform.prototype.push = function (chunk, encoding) {
19012 this._transformState.needTransform = false;
19013 return Duplex.prototype.push.call(this, chunk, encoding);
19014}; // This is the part where you do stuff!
19015// override this function in implementation classes.
19016// 'chunk' is an input chunk.
19017//
19018// Call `push(newChunk)` to pass along transformed output
19019// to the readable side. You may call 'push' zero or more times.
19020//
19021// Call `cb(err)` when you are done with this chunk. If you pass
19022// an error, then that'll put the hurt on the whole operation. If you
19023// never call cb(), then you'll never get another chunk.
19024
19025
19026Transform.prototype._transform = function (chunk, encoding, cb) {
19027 cb(new ERR_METHOD_NOT_IMPLEMENTED('_transform()'));
19028};
19029
19030Transform.prototype._write = function (chunk, encoding, cb) {
19031 var ts = this._transformState;
19032 ts.writecb = cb;
19033 ts.writechunk = chunk;
19034 ts.writeencoding = encoding;
19035
19036 if (!ts.transforming) {
19037 var rs = this._readableState;
19038 if (ts.needTransform || rs.needReadable || rs.length < rs.highWaterMark) this._read(rs.highWaterMark);
19039 }
19040}; // Doesn't matter what the args are here.
19041// _transform does all the work.
19042// That we got here means that the readable side wants more data.
19043
19044
19045Transform.prototype._read = function (n) {
19046 var ts = this._transformState;
19047
19048 if (ts.writechunk !== null && !ts.transforming) {
19049 ts.transforming = true;
19050
19051 this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform);
19052 } else {
19053 // mark that we need a transform, so that any data that comes in
19054 // will get processed, now that we've asked for it.
19055 ts.needTransform = true;
19056 }
19057};
19058
19059Transform.prototype._destroy = function (err, cb) {
19060 Duplex.prototype._destroy.call(this, err, function (err2) {
19061 cb(err2);
19062 });
19063};
19064
19065function done(stream, er, data) {
19066 if (er) return stream.emit('error', er);
19067 if (data != null) // single equals check for both `null` and `undefined`
19068 stream.push(data); // TODO(BridgeAR): Write a test for these two error cases
19069 // if there's nothing in the write buffer, then that means
19070 // that nothing more will ever be provided
19071
19072 if (stream._writableState.length) throw new ERR_TRANSFORM_WITH_LENGTH_0();
19073 if (stream._transformState.transforming) throw new ERR_TRANSFORM_ALREADY_TRANSFORMING();
19074 return stream.push(null);
19075}
19076},{"../errors":101,"./_stream_duplex":102,"inherits":67}],106:[function(_dereq_,module,exports){
19077(function (process,global){(function (){
19078// Copyright Joyent, Inc. and other Node contributors.
19079//
19080// Permission is hereby granted, free of charge, to any person obtaining a
19081// copy of this software and associated documentation files (the
19082// "Software"), to deal in the Software without restriction, including
19083// without limitation the rights to use, copy, modify, merge, publish,
19084// distribute, sublicense, and/or sell copies of the Software, and to permit
19085// persons to whom the Software is furnished to do so, subject to the
19086// following conditions:
19087//
19088// The above copyright notice and this permission notice shall be included
19089// in all copies or substantial portions of the Software.
19090//
19091// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19092// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19093// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
19094// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
19095// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19096// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
19097// USE OR OTHER DEALINGS IN THE SOFTWARE.
19098// A bit simpler than readable streams.
19099// Implement an async ._write(chunk, encoding, cb), and it'll handle all
19100// the drain event emission and buffering.
19101'use strict';
19102
19103module.exports = Writable;
19104/* <replacement> */
19105
19106function WriteReq(chunk, encoding, cb) {
19107 this.chunk = chunk;
19108 this.encoding = encoding;
19109 this.callback = cb;
19110 this.next = null;
19111} // It seems a linked list but it is not
19112// there will be only 2 of these for each stream
19113
19114
19115function CorkedRequest(state) {
19116 var _this = this;
19117
19118 this.next = null;
19119 this.entry = null;
19120
19121 this.finish = function () {
19122 onCorkedFinish(_this, state);
19123 };
19124}
19125/* </replacement> */
19126
19127/*<replacement>*/
19128
19129
19130var Duplex;
19131/*</replacement>*/
19132
19133Writable.WritableState = WritableState;
19134/*<replacement>*/
19135
19136var internalUtil = {
19137 deprecate: _dereq_('util-deprecate')
19138};
19139/*</replacement>*/
19140
19141/*<replacement>*/
19142
19143var Stream = _dereq_('./internal/streams/stream');
19144/*</replacement>*/
19145
19146
19147var Buffer = _dereq_('buffer').Buffer;
19148
19149var OurUint8Array = global.Uint8Array || function () {};
19150
19151function _uint8ArrayToBuffer(chunk) {
19152 return Buffer.from(chunk);
19153}
19154
19155function _isUint8Array(obj) {
19156 return Buffer.isBuffer(obj) || obj instanceof OurUint8Array;
19157}
19158
19159var destroyImpl = _dereq_('./internal/streams/destroy');
19160
19161var _require = _dereq_('./internal/streams/state'),
19162 getHighWaterMark = _require.getHighWaterMark;
19163
19164var _require$codes = _dereq_('../errors').codes,
19165 ERR_INVALID_ARG_TYPE = _require$codes.ERR_INVALID_ARG_TYPE,
19166 ERR_METHOD_NOT_IMPLEMENTED = _require$codes.ERR_METHOD_NOT_IMPLEMENTED,
19167 ERR_MULTIPLE_CALLBACK = _require$codes.ERR_MULTIPLE_CALLBACK,
19168 ERR_STREAM_CANNOT_PIPE = _require$codes.ERR_STREAM_CANNOT_PIPE,
19169 ERR_STREAM_DESTROYED = _require$codes.ERR_STREAM_DESTROYED,
19170 ERR_STREAM_NULL_VALUES = _require$codes.ERR_STREAM_NULL_VALUES,
19171 ERR_STREAM_WRITE_AFTER_END = _require$codes.ERR_STREAM_WRITE_AFTER_END,
19172 ERR_UNKNOWN_ENCODING = _require$codes.ERR_UNKNOWN_ENCODING;
19173
19174var errorOrDestroy = destroyImpl.errorOrDestroy;
19175
19176_dereq_('inherits')(Writable, Stream);
19177
19178function nop() {}
19179
19180function WritableState(options, stream, isDuplex) {
19181 Duplex = Duplex || _dereq_('./_stream_duplex');
19182 options = options || {}; // Duplex streams are both readable and writable, but share
19183 // the same options object.
19184 // However, some cases require setting options to different
19185 // values for the readable and the writable sides of the duplex stream,
19186 // e.g. options.readableObjectMode vs. options.writableObjectMode, etc.
19187
19188 if (typeof isDuplex !== 'boolean') isDuplex = stream instanceof Duplex; // object stream flag to indicate whether or not this stream
19189 // contains buffers or objects.
19190
19191 this.objectMode = !!options.objectMode;
19192 if (isDuplex) this.objectMode = this.objectMode || !!options.writableObjectMode; // the point at which write() starts returning false
19193 // Note: 0 is a valid value, means that we always return false if
19194 // the entire buffer is not flushed immediately on write()
19195
19196 this.highWaterMark = getHighWaterMark(this, options, 'writableHighWaterMark', isDuplex); // if _final has been called
19197
19198 this.finalCalled = false; // drain event flag.
19199
19200 this.needDrain = false; // at the start of calling end()
19201
19202 this.ending = false; // when end() has been called, and returned
19203
19204 this.ended = false; // when 'finish' is emitted
19205
19206 this.finished = false; // has it been destroyed
19207
19208 this.destroyed = false; // should we decode strings into buffers before passing to _write?
19209 // this is here so that some node-core streams can optimize string
19210 // handling at a lower level.
19211
19212 var noDecode = options.decodeStrings === false;
19213 this.decodeStrings = !noDecode; // Crypto is kind of old and crusty. Historically, its default string
19214 // encoding is 'binary' so we have to make this configurable.
19215 // Everything else in the universe uses 'utf8', though.
19216
19217 this.defaultEncoding = options.defaultEncoding || 'utf8'; // not an actual buffer we keep track of, but a measurement
19218 // of how much we're waiting to get pushed to some underlying
19219 // socket or file.
19220
19221 this.length = 0; // a flag to see when we're in the middle of a write.
19222
19223 this.writing = false; // when true all writes will be buffered until .uncork() call
19224
19225 this.corked = 0; // a flag to be able to tell if the onwrite cb is called immediately,
19226 // or on a later tick. We set this to true at first, because any
19227 // actions that shouldn't happen until "later" should generally also
19228 // not happen before the first write call.
19229
19230 this.sync = true; // a flag to know if we're processing previously buffered items, which
19231 // may call the _write() callback in the same tick, so that we don't
19232 // end up in an overlapped onwrite situation.
19233
19234 this.bufferProcessing = false; // the callback that's passed to _write(chunk,cb)
19235
19236 this.onwrite = function (er) {
19237 onwrite(stream, er);
19238 }; // the callback that the user supplies to write(chunk,encoding,cb)
19239
19240
19241 this.writecb = null; // the amount that is being written when _write is called.
19242
19243 this.writelen = 0;
19244 this.bufferedRequest = null;
19245 this.lastBufferedRequest = null; // number of pending user-supplied write callbacks
19246 // this must be 0 before 'finish' can be emitted
19247
19248 this.pendingcb = 0; // emit prefinish if the only thing we're waiting for is _write cbs
19249 // This is relevant for synchronous Transform streams
19250
19251 this.prefinished = false; // True if the error was already emitted and should not be thrown again
19252
19253 this.errorEmitted = false; // Should close be emitted on destroy. Defaults to true.
19254
19255 this.emitClose = options.emitClose !== false; // Should .destroy() be called after 'finish' (and potentially 'end')
19256
19257 this.autoDestroy = !!options.autoDestroy; // count buffered requests
19258
19259 this.bufferedRequestCount = 0; // allocate the first CorkedRequest, there is always
19260 // one allocated and free to use, and we maintain at most two
19261
19262 this.corkedRequestsFree = new CorkedRequest(this);
19263}
19264
19265WritableState.prototype.getBuffer = function getBuffer() {
19266 var current = this.bufferedRequest;
19267 var out = [];
19268
19269 while (current) {
19270 out.push(current);
19271 current = current.next;
19272 }
19273
19274 return out;
19275};
19276
19277(function () {
19278 try {
19279 Object.defineProperty(WritableState.prototype, 'buffer', {
19280 get: internalUtil.deprecate(function writableStateBufferGetter() {
19281 return this.getBuffer();
19282 }, '_writableState.buffer is deprecated. Use _writableState.getBuffer ' + 'instead.', 'DEP0003')
19283 });
19284 } catch (_) {}
19285})(); // Test _writableState for inheritance to account for Duplex streams,
19286// whose prototype chain only points to Readable.
19287
19288
19289var realHasInstance;
19290
19291if (typeof Symbol === 'function' && Symbol.hasInstance && typeof Function.prototype[Symbol.hasInstance] === 'function') {
19292 realHasInstance = Function.prototype[Symbol.hasInstance];
19293 Object.defineProperty(Writable, Symbol.hasInstance, {
19294 value: function value(object) {
19295 if (realHasInstance.call(this, object)) return true;
19296 if (this !== Writable) return false;
19297 return object && object._writableState instanceof WritableState;
19298 }
19299 });
19300} else {
19301 realHasInstance = function realHasInstance(object) {
19302 return object instanceof this;
19303 };
19304}
19305
19306function Writable(options) {
19307 Duplex = Duplex || _dereq_('./_stream_duplex'); // Writable ctor is applied to Duplexes, too.
19308 // `realHasInstance` is necessary because using plain `instanceof`
19309 // would return false, as no `_writableState` property is attached.
19310 // Trying to use the custom `instanceof` for Writable here will also break the
19311 // Node.js LazyTransform implementation, which has a non-trivial getter for
19312 // `_writableState` that would lead to infinite recursion.
19313 // Checking for a Stream.Duplex instance is faster here instead of inside
19314 // the WritableState constructor, at least with V8 6.5
19315
19316 var isDuplex = this instanceof Duplex;
19317 if (!isDuplex && !realHasInstance.call(Writable, this)) return new Writable(options);
19318 this._writableState = new WritableState(options, this, isDuplex); // legacy.
19319
19320 this.writable = true;
19321
19322 if (options) {
19323 if (typeof options.write === 'function') this._write = options.write;
19324 if (typeof options.writev === 'function') this._writev = options.writev;
19325 if (typeof options.destroy === 'function') this._destroy = options.destroy;
19326 if (typeof options.final === 'function') this._final = options.final;
19327 }
19328
19329 Stream.call(this);
19330} // Otherwise people can pipe Writable streams, which is just wrong.
19331
19332
19333Writable.prototype.pipe = function () {
19334 errorOrDestroy(this, new ERR_STREAM_CANNOT_PIPE());
19335};
19336
19337function writeAfterEnd(stream, cb) {
19338 var er = new ERR_STREAM_WRITE_AFTER_END(); // TODO: defer error events consistently everywhere, not just the cb
19339
19340 errorOrDestroy(stream, er);
19341 process.nextTick(cb, er);
19342} // Checks that a user-supplied chunk is valid, especially for the particular
19343// mode the stream is in. Currently this means that `null` is never accepted
19344// and undefined/non-string values are only allowed in object mode.
19345
19346
19347function validChunk(stream, state, chunk, cb) {
19348 var er;
19349
19350 if (chunk === null) {
19351 er = new ERR_STREAM_NULL_VALUES();
19352 } else if (typeof chunk !== 'string' && !state.objectMode) {
19353 er = new ERR_INVALID_ARG_TYPE('chunk', ['string', 'Buffer'], chunk);
19354 }
19355
19356 if (er) {
19357 errorOrDestroy(stream, er);
19358 process.nextTick(cb, er);
19359 return false;
19360 }
19361
19362 return true;
19363}
19364
19365Writable.prototype.write = function (chunk, encoding, cb) {
19366 var state = this._writableState;
19367 var ret = false;
19368
19369 var isBuf = !state.objectMode && _isUint8Array(chunk);
19370
19371 if (isBuf && !Buffer.isBuffer(chunk)) {
19372 chunk = _uint8ArrayToBuffer(chunk);
19373 }
19374
19375 if (typeof encoding === 'function') {
19376 cb = encoding;
19377 encoding = null;
19378 }
19379
19380 if (isBuf) encoding = 'buffer';else if (!encoding) encoding = state.defaultEncoding;
19381 if (typeof cb !== 'function') cb = nop;
19382 if (state.ending) writeAfterEnd(this, cb);else if (isBuf || validChunk(this, state, chunk, cb)) {
19383 state.pendingcb++;
19384 ret = writeOrBuffer(this, state, isBuf, chunk, encoding, cb);
19385 }
19386 return ret;
19387};
19388
19389Writable.prototype.cork = function () {
19390 this._writableState.corked++;
19391};
19392
19393Writable.prototype.uncork = function () {
19394 var state = this._writableState;
19395
19396 if (state.corked) {
19397 state.corked--;
19398 if (!state.writing && !state.corked && !state.bufferProcessing && state.bufferedRequest) clearBuffer(this, state);
19399 }
19400};
19401
19402Writable.prototype.setDefaultEncoding = function setDefaultEncoding(encoding) {
19403 // node::ParseEncoding() requires lower case.
19404 if (typeof encoding === 'string') encoding = encoding.toLowerCase();
19405 if (!(['hex', 'utf8', 'utf-8', 'ascii', 'binary', 'base64', 'ucs2', 'ucs-2', 'utf16le', 'utf-16le', 'raw'].indexOf((encoding + '').toLowerCase()) > -1)) throw new ERR_UNKNOWN_ENCODING(encoding);
19406 this._writableState.defaultEncoding = encoding;
19407 return this;
19408};
19409
19410Object.defineProperty(Writable.prototype, 'writableBuffer', {
19411 // making it explicit this property is not enumerable
19412 // because otherwise some prototype manipulation in
19413 // userland will fail
19414 enumerable: false,
19415 get: function get() {
19416 return this._writableState && this._writableState.getBuffer();
19417 }
19418});
19419
19420function decodeChunk(state, chunk, encoding) {
19421 if (!state.objectMode && state.decodeStrings !== false && typeof chunk === 'string') {
19422 chunk = Buffer.from(chunk, encoding);
19423 }
19424
19425 return chunk;
19426}
19427
19428Object.defineProperty(Writable.prototype, 'writableHighWaterMark', {
19429 // making it explicit this property is not enumerable
19430 // because otherwise some prototype manipulation in
19431 // userland will fail
19432 enumerable: false,
19433 get: function get() {
19434 return this._writableState.highWaterMark;
19435 }
19436}); // if we're already writing something, then just put this
19437// in the queue, and wait our turn. Otherwise, call _write
19438// If we return false, then we need a drain event, so set that flag.
19439
19440function writeOrBuffer(stream, state, isBuf, chunk, encoding, cb) {
19441 if (!isBuf) {
19442 var newChunk = decodeChunk(state, chunk, encoding);
19443
19444 if (chunk !== newChunk) {
19445 isBuf = true;
19446 encoding = 'buffer';
19447 chunk = newChunk;
19448 }
19449 }
19450
19451 var len = state.objectMode ? 1 : chunk.length;
19452 state.length += len;
19453 var ret = state.length < state.highWaterMark; // we must ensure that previous needDrain will not be reset to false.
19454
19455 if (!ret) state.needDrain = true;
19456
19457 if (state.writing || state.corked) {
19458 var last = state.lastBufferedRequest;
19459 state.lastBufferedRequest = {
19460 chunk: chunk,
19461 encoding: encoding,
19462 isBuf: isBuf,
19463 callback: cb,
19464 next: null
19465 };
19466
19467 if (last) {
19468 last.next = state.lastBufferedRequest;
19469 } else {
19470 state.bufferedRequest = state.lastBufferedRequest;
19471 }
19472
19473 state.bufferedRequestCount += 1;
19474 } else {
19475 doWrite(stream, state, false, len, chunk, encoding, cb);
19476 }
19477
19478 return ret;
19479}
19480
19481function doWrite(stream, state, writev, len, chunk, encoding, cb) {
19482 state.writelen = len;
19483 state.writecb = cb;
19484 state.writing = true;
19485 state.sync = true;
19486 if (state.destroyed) state.onwrite(new ERR_STREAM_DESTROYED('write'));else if (writev) stream._writev(chunk, state.onwrite);else stream._write(chunk, encoding, state.onwrite);
19487 state.sync = false;
19488}
19489
19490function onwriteError(stream, state, sync, er, cb) {
19491 --state.pendingcb;
19492
19493 if (sync) {
19494 // defer the callback if we are being called synchronously
19495 // to avoid piling up things on the stack
19496 process.nextTick(cb, er); // this can emit finish, and it will always happen
19497 // after error
19498
19499 process.nextTick(finishMaybe, stream, state);
19500 stream._writableState.errorEmitted = true;
19501 errorOrDestroy(stream, er);
19502 } else {
19503 // the caller expect this to happen before if
19504 // it is async
19505 cb(er);
19506 stream._writableState.errorEmitted = true;
19507 errorOrDestroy(stream, er); // this can emit finish, but finish must
19508 // always follow error
19509
19510 finishMaybe(stream, state);
19511 }
19512}
19513
19514function onwriteStateUpdate(state) {
19515 state.writing = false;
19516 state.writecb = null;
19517 state.length -= state.writelen;
19518 state.writelen = 0;
19519}
19520
19521function onwrite(stream, er) {
19522 var state = stream._writableState;
19523 var sync = state.sync;
19524 var cb = state.writecb;
19525 if (typeof cb !== 'function') throw new ERR_MULTIPLE_CALLBACK();
19526 onwriteStateUpdate(state);
19527 if (er) onwriteError(stream, state, sync, er, cb);else {
19528 // Check if we're actually ready to finish, but don't emit yet
19529 var finished = needFinish(state) || stream.destroyed;
19530
19531 if (!finished && !state.corked && !state.bufferProcessing && state.bufferedRequest) {
19532 clearBuffer(stream, state);
19533 }
19534
19535 if (sync) {
19536 process.nextTick(afterWrite, stream, state, finished, cb);
19537 } else {
19538 afterWrite(stream, state, finished, cb);
19539 }
19540 }
19541}
19542
19543function afterWrite(stream, state, finished, cb) {
19544 if (!finished) onwriteDrain(stream, state);
19545 state.pendingcb--;
19546 cb();
19547 finishMaybe(stream, state);
19548} // Must force callback to be called on nextTick, so that we don't
19549// emit 'drain' before the write() consumer gets the 'false' return
19550// value, and has a chance to attach a 'drain' listener.
19551
19552
19553function onwriteDrain(stream, state) {
19554 if (state.length === 0 && state.needDrain) {
19555 state.needDrain = false;
19556 stream.emit('drain');
19557 }
19558} // if there's something in the buffer waiting, then process it
19559
19560
19561function clearBuffer(stream, state) {
19562 state.bufferProcessing = true;
19563 var entry = state.bufferedRequest;
19564
19565 if (stream._writev && entry && entry.next) {
19566 // Fast case, write everything using _writev()
19567 var l = state.bufferedRequestCount;
19568 var buffer = new Array(l);
19569 var holder = state.corkedRequestsFree;
19570 holder.entry = entry;
19571 var count = 0;
19572 var allBuffers = true;
19573
19574 while (entry) {
19575 buffer[count] = entry;
19576 if (!entry.isBuf) allBuffers = false;
19577 entry = entry.next;
19578 count += 1;
19579 }
19580
19581 buffer.allBuffers = allBuffers;
19582 doWrite(stream, state, true, state.length, buffer, '', holder.finish); // doWrite is almost always async, defer these to save a bit of time
19583 // as the hot path ends with doWrite
19584
19585 state.pendingcb++;
19586 state.lastBufferedRequest = null;
19587
19588 if (holder.next) {
19589 state.corkedRequestsFree = holder.next;
19590 holder.next = null;
19591 } else {
19592 state.corkedRequestsFree = new CorkedRequest(state);
19593 }
19594
19595 state.bufferedRequestCount = 0;
19596 } else {
19597 // Slow case, write chunks one-by-one
19598 while (entry) {
19599 var chunk = entry.chunk;
19600 var encoding = entry.encoding;
19601 var cb = entry.callback;
19602 var len = state.objectMode ? 1 : chunk.length;
19603 doWrite(stream, state, false, len, chunk, encoding, cb);
19604 entry = entry.next;
19605 state.bufferedRequestCount--; // if we didn't call the onwrite immediately, then
19606 // it means that we need to wait until it does.
19607 // also, that means that the chunk and cb are currently
19608 // being processed, so move the buffer counter past them.
19609
19610 if (state.writing) {
19611 break;
19612 }
19613 }
19614
19615 if (entry === null) state.lastBufferedRequest = null;
19616 }
19617
19618 state.bufferedRequest = entry;
19619 state.bufferProcessing = false;
19620}
19621
19622Writable.prototype._write = function (chunk, encoding, cb) {
19623 cb(new ERR_METHOD_NOT_IMPLEMENTED('_write()'));
19624};
19625
19626Writable.prototype._writev = null;
19627
19628Writable.prototype.end = function (chunk, encoding, cb) {
19629 var state = this._writableState;
19630
19631 if (typeof chunk === 'function') {
19632 cb = chunk;
19633 chunk = null;
19634 encoding = null;
19635 } else if (typeof encoding === 'function') {
19636 cb = encoding;
19637 encoding = null;
19638 }
19639
19640 if (chunk !== null && chunk !== undefined) this.write(chunk, encoding); // .end() fully uncorks
19641
19642 if (state.corked) {
19643 state.corked = 1;
19644 this.uncork();
19645 } // ignore unnecessary end() calls.
19646
19647
19648 if (!state.ending) endWritable(this, state, cb);
19649 return this;
19650};
19651
19652Object.defineProperty(Writable.prototype, 'writableLength', {
19653 // making it explicit this property is not enumerable
19654 // because otherwise some prototype manipulation in
19655 // userland will fail
19656 enumerable: false,
19657 get: function get() {
19658 return this._writableState.length;
19659 }
19660});
19661
19662function needFinish(state) {
19663 return state.ending && state.length === 0 && state.bufferedRequest === null && !state.finished && !state.writing;
19664}
19665
19666function callFinal(stream, state) {
19667 stream._final(function (err) {
19668 state.pendingcb--;
19669
19670 if (err) {
19671 errorOrDestroy(stream, err);
19672 }
19673
19674 state.prefinished = true;
19675 stream.emit('prefinish');
19676 finishMaybe(stream, state);
19677 });
19678}
19679
19680function prefinish(stream, state) {
19681 if (!state.prefinished && !state.finalCalled) {
19682 if (typeof stream._final === 'function' && !state.destroyed) {
19683 state.pendingcb++;
19684 state.finalCalled = true;
19685 process.nextTick(callFinal, stream, state);
19686 } else {
19687 state.prefinished = true;
19688 stream.emit('prefinish');
19689 }
19690 }
19691}
19692
19693function finishMaybe(stream, state) {
19694 var need = needFinish(state);
19695
19696 if (need) {
19697 prefinish(stream, state);
19698
19699 if (state.pendingcb === 0) {
19700 state.finished = true;
19701 stream.emit('finish');
19702
19703 if (state.autoDestroy) {
19704 // In case of duplex streams we need a way to detect
19705 // if the readable side is ready for autoDestroy as well
19706 var rState = stream._readableState;
19707
19708 if (!rState || rState.autoDestroy && rState.endEmitted) {
19709 stream.destroy();
19710 }
19711 }
19712 }
19713 }
19714
19715 return need;
19716}
19717
19718function endWritable(stream, state, cb) {
19719 state.ending = true;
19720 finishMaybe(stream, state);
19721
19722 if (cb) {
19723 if (state.finished) process.nextTick(cb);else stream.once('finish', cb);
19724 }
19725
19726 state.ended = true;
19727 stream.writable = false;
19728}
19729
19730function onCorkedFinish(corkReq, state, err) {
19731 var entry = corkReq.entry;
19732 corkReq.entry = null;
19733
19734 while (entry) {
19735 var cb = entry.callback;
19736 state.pendingcb--;
19737 cb(err);
19738 entry = entry.next;
19739 } // reuse the free corkReq.
19740
19741
19742 state.corkedRequestsFree.next = corkReq;
19743}
19744
19745Object.defineProperty(Writable.prototype, 'destroyed', {
19746 // making it explicit this property is not enumerable
19747 // because otherwise some prototype manipulation in
19748 // userland will fail
19749 enumerable: false,
19750 get: function get() {
19751 if (this._writableState === undefined) {
19752 return false;
19753 }
19754
19755 return this._writableState.destroyed;
19756 },
19757 set: function set(value) {
19758 // we ignore the value if the stream
19759 // has not been initialized yet
19760 if (!this._writableState) {
19761 return;
19762 } // backward compatibility, the user is explicitly
19763 // managing destroyed
19764
19765
19766 this._writableState.destroyed = value;
19767 }
19768});
19769Writable.prototype.destroy = destroyImpl.destroy;
19770Writable.prototype._undestroy = destroyImpl.undestroy;
19771
19772Writable.prototype._destroy = function (err, cb) {
19773 cb(err);
19774};
19775}).call(this)}).call(this,_dereq_('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
19776},{"../errors":101,"./_stream_duplex":102,"./internal/streams/destroy":109,"./internal/streams/state":113,"./internal/streams/stream":114,"_process":98,"buffer":28,"inherits":67,"util-deprecate":122}],107:[function(_dereq_,module,exports){
19777(function (process){(function (){
19778'use strict';
19779
19780var _Object$setPrototypeO;
19781
19782function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
19783
19784var finished = _dereq_('./end-of-stream');
19785
19786var kLastResolve = Symbol('lastResolve');
19787var kLastReject = Symbol('lastReject');
19788var kError = Symbol('error');
19789var kEnded = Symbol('ended');
19790var kLastPromise = Symbol('lastPromise');
19791var kHandlePromise = Symbol('handlePromise');
19792var kStream = Symbol('stream');
19793
19794function createIterResult(value, done) {
19795 return {
19796 value: value,
19797 done: done
19798 };
19799}
19800
19801function readAndResolve(iter) {
19802 var resolve = iter[kLastResolve];
19803
19804 if (resolve !== null) {
19805 var data = iter[kStream].read(); // we defer if data is null
19806 // we can be expecting either 'end' or
19807 // 'error'
19808
19809 if (data !== null) {
19810 iter[kLastPromise] = null;
19811 iter[kLastResolve] = null;
19812 iter[kLastReject] = null;
19813 resolve(createIterResult(data, false));
19814 }
19815 }
19816}
19817
19818function onReadable(iter) {
19819 // we wait for the next tick, because it might
19820 // emit an error with process.nextTick
19821 process.nextTick(readAndResolve, iter);
19822}
19823
19824function wrapForNext(lastPromise, iter) {
19825 return function (resolve, reject) {
19826 lastPromise.then(function () {
19827 if (iter[kEnded]) {
19828 resolve(createIterResult(undefined, true));
19829 return;
19830 }
19831
19832 iter[kHandlePromise](resolve, reject);
19833 }, reject);
19834 };
19835}
19836
19837var AsyncIteratorPrototype = Object.getPrototypeOf(function () {});
19838var ReadableStreamAsyncIteratorPrototype = Object.setPrototypeOf((_Object$setPrototypeO = {
19839 get stream() {
19840 return this[kStream];
19841 },
19842
19843 next: function next() {
19844 var _this = this;
19845
19846 // if we have detected an error in the meanwhile
19847 // reject straight away
19848 var error = this[kError];
19849
19850 if (error !== null) {
19851 return Promise.reject(error);
19852 }
19853
19854 if (this[kEnded]) {
19855 return Promise.resolve(createIterResult(undefined, true));
19856 }
19857
19858 if (this[kStream].destroyed) {
19859 // We need to defer via nextTick because if .destroy(err) is
19860 // called, the error will be emitted via nextTick, and
19861 // we cannot guarantee that there is no error lingering around
19862 // waiting to be emitted.
19863 return new Promise(function (resolve, reject) {
19864 process.nextTick(function () {
19865 if (_this[kError]) {
19866 reject(_this[kError]);
19867 } else {
19868 resolve(createIterResult(undefined, true));
19869 }
19870 });
19871 });
19872 } // if we have multiple next() calls
19873 // we will wait for the previous Promise to finish
19874 // this logic is optimized to support for await loops,
19875 // where next() is only called once at a time
19876
19877
19878 var lastPromise = this[kLastPromise];
19879 var promise;
19880
19881 if (lastPromise) {
19882 promise = new Promise(wrapForNext(lastPromise, this));
19883 } else {
19884 // fast path needed to support multiple this.push()
19885 // without triggering the next() queue
19886 var data = this[kStream].read();
19887
19888 if (data !== null) {
19889 return Promise.resolve(createIterResult(data, false));
19890 }
19891
19892 promise = new Promise(this[kHandlePromise]);
19893 }
19894
19895 this[kLastPromise] = promise;
19896 return promise;
19897 }
19898}, _defineProperty(_Object$setPrototypeO, Symbol.asyncIterator, function () {
19899 return this;
19900}), _defineProperty(_Object$setPrototypeO, "return", function _return() {
19901 var _this2 = this;
19902
19903 // destroy(err, cb) is a private API
19904 // we can guarantee we have that here, because we control the
19905 // Readable class this is attached to
19906 return new Promise(function (resolve, reject) {
19907 _this2[kStream].destroy(null, function (err) {
19908 if (err) {
19909 reject(err);
19910 return;
19911 }
19912
19913 resolve(createIterResult(undefined, true));
19914 });
19915 });
19916}), _Object$setPrototypeO), AsyncIteratorPrototype);
19917
19918var createReadableStreamAsyncIterator = function createReadableStreamAsyncIterator(stream) {
19919 var _Object$create;
19920
19921 var iterator = Object.create(ReadableStreamAsyncIteratorPrototype, (_Object$create = {}, _defineProperty(_Object$create, kStream, {
19922 value: stream,
19923 writable: true
19924 }), _defineProperty(_Object$create, kLastResolve, {
19925 value: null,
19926 writable: true
19927 }), _defineProperty(_Object$create, kLastReject, {
19928 value: null,
19929 writable: true
19930 }), _defineProperty(_Object$create, kError, {
19931 value: null,
19932 writable: true
19933 }), _defineProperty(_Object$create, kEnded, {
19934 value: stream._readableState.endEmitted,
19935 writable: true
19936 }), _defineProperty(_Object$create, kHandlePromise, {
19937 value: function value(resolve, reject) {
19938 var data = iterator[kStream].read();
19939
19940 if (data) {
19941 iterator[kLastPromise] = null;
19942 iterator[kLastResolve] = null;
19943 iterator[kLastReject] = null;
19944 resolve(createIterResult(data, false));
19945 } else {
19946 iterator[kLastResolve] = resolve;
19947 iterator[kLastReject] = reject;
19948 }
19949 },
19950 writable: true
19951 }), _Object$create));
19952 iterator[kLastPromise] = null;
19953 finished(stream, function (err) {
19954 if (err && err.code !== 'ERR_STREAM_PREMATURE_CLOSE') {
19955 var reject = iterator[kLastReject]; // reject if we are waiting for data in the Promise
19956 // returned by next() and store the error
19957
19958 if (reject !== null) {
19959 iterator[kLastPromise] = null;
19960 iterator[kLastResolve] = null;
19961 iterator[kLastReject] = null;
19962 reject(err);
19963 }
19964
19965 iterator[kError] = err;
19966 return;
19967 }
19968
19969 var resolve = iterator[kLastResolve];
19970
19971 if (resolve !== null) {
19972 iterator[kLastPromise] = null;
19973 iterator[kLastResolve] = null;
19974 iterator[kLastReject] = null;
19975 resolve(createIterResult(undefined, true));
19976 }
19977
19978 iterator[kEnded] = true;
19979 });
19980 stream.on('readable', onReadable.bind(null, iterator));
19981 return iterator;
19982};
19983
19984module.exports = createReadableStreamAsyncIterator;
19985}).call(this)}).call(this,_dereq_('_process'))
19986},{"./end-of-stream":110,"_process":98}],108:[function(_dereq_,module,exports){
19987'use strict';
19988
19989function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
19990
19991function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
19992
19993function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
19994
19995function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
19996
19997function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
19998
19999function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
20000
20001var _require = _dereq_('buffer'),
20002 Buffer = _require.Buffer;
20003
20004var _require2 = _dereq_('util'),
20005 inspect = _require2.inspect;
20006
20007var custom = inspect && inspect.custom || 'inspect';
20008
20009function copyBuffer(src, target, offset) {
20010 Buffer.prototype.copy.call(src, target, offset);
20011}
20012
20013module.exports =
20014/*#__PURE__*/
20015function () {
20016 function BufferList() {
20017 _classCallCheck(this, BufferList);
20018
20019 this.head = null;
20020 this.tail = null;
20021 this.length = 0;
20022 }
20023
20024 _createClass(BufferList, [{
20025 key: "push",
20026 value: function push(v) {
20027 var entry = {
20028 data: v,
20029 next: null
20030 };
20031 if (this.length > 0) this.tail.next = entry;else this.head = entry;
20032 this.tail = entry;
20033 ++this.length;
20034 }
20035 }, {
20036 key: "unshift",
20037 value: function unshift(v) {
20038 var entry = {
20039 data: v,
20040 next: this.head
20041 };
20042 if (this.length === 0) this.tail = entry;
20043 this.head = entry;
20044 ++this.length;
20045 }
20046 }, {
20047 key: "shift",
20048 value: function shift() {
20049 if (this.length === 0) return;
20050 var ret = this.head.data;
20051 if (this.length === 1) this.head = this.tail = null;else this.head = this.head.next;
20052 --this.length;
20053 return ret;
20054 }
20055 }, {
20056 key: "clear",
20057 value: function clear() {
20058 this.head = this.tail = null;
20059 this.length = 0;
20060 }
20061 }, {
20062 key: "join",
20063 value: function join(s) {
20064 if (this.length === 0) return '';
20065 var p = this.head;
20066 var ret = '' + p.data;
20067
20068 while (p = p.next) {
20069 ret += s + p.data;
20070 }
20071
20072 return ret;
20073 }
20074 }, {
20075 key: "concat",
20076 value: function concat(n) {
20077 if (this.length === 0) return Buffer.alloc(0);
20078 var ret = Buffer.allocUnsafe(n >>> 0);
20079 var p = this.head;
20080 var i = 0;
20081
20082 while (p) {
20083 copyBuffer(p.data, ret, i);
20084 i += p.data.length;
20085 p = p.next;
20086 }
20087
20088 return ret;
20089 } // Consumes a specified amount of bytes or characters from the buffered data.
20090
20091 }, {
20092 key: "consume",
20093 value: function consume(n, hasStrings) {
20094 var ret;
20095
20096 if (n < this.head.data.length) {
20097 // `slice` is the same for buffers and strings.
20098 ret = this.head.data.slice(0, n);
20099 this.head.data = this.head.data.slice(n);
20100 } else if (n === this.head.data.length) {
20101 // First chunk is a perfect match.
20102 ret = this.shift();
20103 } else {
20104 // Result spans more than one buffer.
20105 ret = hasStrings ? this._getString(n) : this._getBuffer(n);
20106 }
20107
20108 return ret;
20109 }
20110 }, {
20111 key: "first",
20112 value: function first() {
20113 return this.head.data;
20114 } // Consumes a specified amount of characters from the buffered data.
20115
20116 }, {
20117 key: "_getString",
20118 value: function _getString(n) {
20119 var p = this.head;
20120 var c = 1;
20121 var ret = p.data;
20122 n -= ret.length;
20123
20124 while (p = p.next) {
20125 var str = p.data;
20126 var nb = n > str.length ? str.length : n;
20127 if (nb === str.length) ret += str;else ret += str.slice(0, n);
20128 n -= nb;
20129
20130 if (n === 0) {
20131 if (nb === str.length) {
20132 ++c;
20133 if (p.next) this.head = p.next;else this.head = this.tail = null;
20134 } else {
20135 this.head = p;
20136 p.data = str.slice(nb);
20137 }
20138
20139 break;
20140 }
20141
20142 ++c;
20143 }
20144
20145 this.length -= c;
20146 return ret;
20147 } // Consumes a specified amount of bytes from the buffered data.
20148
20149 }, {
20150 key: "_getBuffer",
20151 value: function _getBuffer(n) {
20152 var ret = Buffer.allocUnsafe(n);
20153 var p = this.head;
20154 var c = 1;
20155 p.data.copy(ret);
20156 n -= p.data.length;
20157
20158 while (p = p.next) {
20159 var buf = p.data;
20160 var nb = n > buf.length ? buf.length : n;
20161 buf.copy(ret, ret.length - n, 0, nb);
20162 n -= nb;
20163
20164 if (n === 0) {
20165 if (nb === buf.length) {
20166 ++c;
20167 if (p.next) this.head = p.next;else this.head = this.tail = null;
20168 } else {
20169 this.head = p;
20170 p.data = buf.slice(nb);
20171 }
20172
20173 break;
20174 }
20175
20176 ++c;
20177 }
20178
20179 this.length -= c;
20180 return ret;
20181 } // Make sure the linked list only shows the minimal necessary information.
20182
20183 }, {
20184 key: custom,
20185 value: function value(_, options) {
20186 return inspect(this, _objectSpread({}, options, {
20187 // Only inspect one level.
20188 depth: 0,
20189 // It should not recurse.
20190 customInspect: false
20191 }));
20192 }
20193 }]);
20194
20195 return BufferList;
20196}();
20197},{"buffer":28,"util":26}],109:[function(_dereq_,module,exports){
20198(function (process){(function (){
20199'use strict'; // undocumented cb() API, needed for core, not for public API
20200
20201function destroy(err, cb) {
20202 var _this = this;
20203
20204 var readableDestroyed = this._readableState && this._readableState.destroyed;
20205 var writableDestroyed = this._writableState && this._writableState.destroyed;
20206
20207 if (readableDestroyed || writableDestroyed) {
20208 if (cb) {
20209 cb(err);
20210 } else if (err) {
20211 if (!this._writableState) {
20212 process.nextTick(emitErrorNT, this, err);
20213 } else if (!this._writableState.errorEmitted) {
20214 this._writableState.errorEmitted = true;
20215 process.nextTick(emitErrorNT, this, err);
20216 }
20217 }
20218
20219 return this;
20220 } // we set destroyed to true before firing error callbacks in order
20221 // to make it re-entrance safe in case destroy() is called within callbacks
20222
20223
20224 if (this._readableState) {
20225 this._readableState.destroyed = true;
20226 } // if this is a duplex stream mark the writable part as destroyed as well
20227
20228
20229 if (this._writableState) {
20230 this._writableState.destroyed = true;
20231 }
20232
20233 this._destroy(err || null, function (err) {
20234 if (!cb && err) {
20235 if (!_this._writableState) {
20236 process.nextTick(emitErrorAndCloseNT, _this, err);
20237 } else if (!_this._writableState.errorEmitted) {
20238 _this._writableState.errorEmitted = true;
20239 process.nextTick(emitErrorAndCloseNT, _this, err);
20240 } else {
20241 process.nextTick(emitCloseNT, _this);
20242 }
20243 } else if (cb) {
20244 process.nextTick(emitCloseNT, _this);
20245 cb(err);
20246 } else {
20247 process.nextTick(emitCloseNT, _this);
20248 }
20249 });
20250
20251 return this;
20252}
20253
20254function emitErrorAndCloseNT(self, err) {
20255 emitErrorNT(self, err);
20256 emitCloseNT(self);
20257}
20258
20259function emitCloseNT(self) {
20260 if (self._writableState && !self._writableState.emitClose) return;
20261 if (self._readableState && !self._readableState.emitClose) return;
20262 self.emit('close');
20263}
20264
20265function undestroy() {
20266 if (this._readableState) {
20267 this._readableState.destroyed = false;
20268 this._readableState.reading = false;
20269 this._readableState.ended = false;
20270 this._readableState.endEmitted = false;
20271 }
20272
20273 if (this._writableState) {
20274 this._writableState.destroyed = false;
20275 this._writableState.ended = false;
20276 this._writableState.ending = false;
20277 this._writableState.finalCalled = false;
20278 this._writableState.prefinished = false;
20279 this._writableState.finished = false;
20280 this._writableState.errorEmitted = false;
20281 }
20282}
20283
20284function emitErrorNT(self, err) {
20285 self.emit('error', err);
20286}
20287
20288function errorOrDestroy(stream, err) {
20289 // We have tests that rely on errors being emitted
20290 // in the same tick, so changing this is semver major.
20291 // For now when you opt-in to autoDestroy we allow
20292 // the error to be emitted nextTick. In a future
20293 // semver major update we should change the default to this.
20294 var rState = stream._readableState;
20295 var wState = stream._writableState;
20296 if (rState && rState.autoDestroy || wState && wState.autoDestroy) stream.destroy(err);else stream.emit('error', err);
20297}
20298
20299module.exports = {
20300 destroy: destroy,
20301 undestroy: undestroy,
20302 errorOrDestroy: errorOrDestroy
20303};
20304}).call(this)}).call(this,_dereq_('_process'))
20305},{"_process":98}],110:[function(_dereq_,module,exports){
20306// Ported from https://github.com/mafintosh/end-of-stream with
20307// permission from the author, Mathias Buus (@mafintosh).
20308'use strict';
20309
20310var ERR_STREAM_PREMATURE_CLOSE = _dereq_('../../../errors').codes.ERR_STREAM_PREMATURE_CLOSE;
20311
20312function once(callback) {
20313 var called = false;
20314 return function () {
20315 if (called) return;
20316 called = true;
20317
20318 for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
20319 args[_key] = arguments[_key];
20320 }
20321
20322 callback.apply(this, args);
20323 };
20324}
20325
20326function noop() {}
20327
20328function isRequest(stream) {
20329 return stream.setHeader && typeof stream.abort === 'function';
20330}
20331
20332function eos(stream, opts, callback) {
20333 if (typeof opts === 'function') return eos(stream, null, opts);
20334 if (!opts) opts = {};
20335 callback = once(callback || noop);
20336 var readable = opts.readable || opts.readable !== false && stream.readable;
20337 var writable = opts.writable || opts.writable !== false && stream.writable;
20338
20339 var onlegacyfinish = function onlegacyfinish() {
20340 if (!stream.writable) onfinish();
20341 };
20342
20343 var writableEnded = stream._writableState && stream._writableState.finished;
20344
20345 var onfinish = function onfinish() {
20346 writable = false;
20347 writableEnded = true;
20348 if (!readable) callback.call(stream);
20349 };
20350
20351 var readableEnded = stream._readableState && stream._readableState.endEmitted;
20352
20353 var onend = function onend() {
20354 readable = false;
20355 readableEnded = true;
20356 if (!writable) callback.call(stream);
20357 };
20358
20359 var onerror = function onerror(err) {
20360 callback.call(stream, err);
20361 };
20362
20363 var onclose = function onclose() {
20364 var err;
20365
20366 if (readable && !readableEnded) {
20367 if (!stream._readableState || !stream._readableState.ended) err = new ERR_STREAM_PREMATURE_CLOSE();
20368 return callback.call(stream, err);
20369 }
20370
20371 if (writable && !writableEnded) {
20372 if (!stream._writableState || !stream._writableState.ended) err = new ERR_STREAM_PREMATURE_CLOSE();
20373 return callback.call(stream, err);
20374 }
20375 };
20376
20377 var onrequest = function onrequest() {
20378 stream.req.on('finish', onfinish);
20379 };
20380
20381 if (isRequest(stream)) {
20382 stream.on('complete', onfinish);
20383 stream.on('abort', onclose);
20384 if (stream.req) onrequest();else stream.on('request', onrequest);
20385 } else if (writable && !stream._writableState) {
20386 // legacy streams
20387 stream.on('end', onlegacyfinish);
20388 stream.on('close', onlegacyfinish);
20389 }
20390
20391 stream.on('end', onend);
20392 stream.on('finish', onfinish);
20393 if (opts.error !== false) stream.on('error', onerror);
20394 stream.on('close', onclose);
20395 return function () {
20396 stream.removeListener('complete', onfinish);
20397 stream.removeListener('abort', onclose);
20398 stream.removeListener('request', onrequest);
20399 if (stream.req) stream.req.removeListener('finish', onfinish);
20400 stream.removeListener('end', onlegacyfinish);
20401 stream.removeListener('close', onlegacyfinish);
20402 stream.removeListener('finish', onfinish);
20403 stream.removeListener('end', onend);
20404 stream.removeListener('error', onerror);
20405 stream.removeListener('close', onclose);
20406 };
20407}
20408
20409module.exports = eos;
20410},{"../../../errors":101}],111:[function(_dereq_,module,exports){
20411module.exports = function () {
20412 throw new Error('Readable.from is not available in the browser')
20413};
20414
20415},{}],112:[function(_dereq_,module,exports){
20416// Ported from https://github.com/mafintosh/pump with
20417// permission from the author, Mathias Buus (@mafintosh).
20418'use strict';
20419
20420var eos;
20421
20422function once(callback) {
20423 var called = false;
20424 return function () {
20425 if (called) return;
20426 called = true;
20427 callback.apply(void 0, arguments);
20428 };
20429}
20430
20431var _require$codes = _dereq_('../../../errors').codes,
20432 ERR_MISSING_ARGS = _require$codes.ERR_MISSING_ARGS,
20433 ERR_STREAM_DESTROYED = _require$codes.ERR_STREAM_DESTROYED;
20434
20435function noop(err) {
20436 // Rethrow the error if it exists to avoid swallowing it
20437 if (err) throw err;
20438}
20439
20440function isRequest(stream) {
20441 return stream.setHeader && typeof stream.abort === 'function';
20442}
20443
20444function destroyer(stream, reading, writing, callback) {
20445 callback = once(callback);
20446 var closed = false;
20447 stream.on('close', function () {
20448 closed = true;
20449 });
20450 if (eos === undefined) eos = _dereq_('./end-of-stream');
20451 eos(stream, {
20452 readable: reading,
20453 writable: writing
20454 }, function (err) {
20455 if (err) return callback(err);
20456 closed = true;
20457 callback();
20458 });
20459 var destroyed = false;
20460 return function (err) {
20461 if (closed) return;
20462 if (destroyed) return;
20463 destroyed = true; // request.destroy just do .end - .abort is what we want
20464
20465 if (isRequest(stream)) return stream.abort();
20466 if (typeof stream.destroy === 'function') return stream.destroy();
20467 callback(err || new ERR_STREAM_DESTROYED('pipe'));
20468 };
20469}
20470
20471function call(fn) {
20472 fn();
20473}
20474
20475function pipe(from, to) {
20476 return from.pipe(to);
20477}
20478
20479function popCallback(streams) {
20480 if (!streams.length) return noop;
20481 if (typeof streams[streams.length - 1] !== 'function') return noop;
20482 return streams.pop();
20483}
20484
20485function pipeline() {
20486 for (var _len = arguments.length, streams = new Array(_len), _key = 0; _key < _len; _key++) {
20487 streams[_key] = arguments[_key];
20488 }
20489
20490 var callback = popCallback(streams);
20491 if (Array.isArray(streams[0])) streams = streams[0];
20492
20493 if (streams.length < 2) {
20494 throw new ERR_MISSING_ARGS('streams');
20495 }
20496
20497 var error;
20498 var destroys = streams.map(function (stream, i) {
20499 var reading = i < streams.length - 1;
20500 var writing = i > 0;
20501 return destroyer(stream, reading, writing, function (err) {
20502 if (!error) error = err;
20503 if (err) destroys.forEach(call);
20504 if (reading) return;
20505 destroys.forEach(call);
20506 callback(error);
20507 });
20508 });
20509 return streams.reduce(pipe);
20510}
20511
20512module.exports = pipeline;
20513},{"../../../errors":101,"./end-of-stream":110}],113:[function(_dereq_,module,exports){
20514'use strict';
20515
20516var ERR_INVALID_OPT_VALUE = _dereq_('../../../errors').codes.ERR_INVALID_OPT_VALUE;
20517
20518function highWaterMarkFrom(options, isDuplex, duplexKey) {
20519 return options.highWaterMark != null ? options.highWaterMark : isDuplex ? options[duplexKey] : null;
20520}
20521
20522function getHighWaterMark(state, options, duplexKey, isDuplex) {
20523 var hwm = highWaterMarkFrom(options, isDuplex, duplexKey);
20524
20525 if (hwm != null) {
20526 if (!(isFinite(hwm) && Math.floor(hwm) === hwm) || hwm < 0) {
20527 var name = isDuplex ? duplexKey : 'highWaterMark';
20528 throw new ERR_INVALID_OPT_VALUE(name, hwm);
20529 }
20530
20531 return Math.floor(hwm);
20532 } // Default value
20533
20534
20535 return state.objectMode ? 16 : 16 * 1024;
20536}
20537
20538module.exports = {
20539 getHighWaterMark: getHighWaterMark
20540};
20541},{"../../../errors":101}],114:[function(_dereq_,module,exports){
20542module.exports = _dereq_('events').EventEmitter;
20543
20544},{"events":27}],115:[function(_dereq_,module,exports){
20545// Copyright Joyent, Inc. and other Node contributors.
20546//
20547// Permission is hereby granted, free of charge, to any person obtaining a
20548// copy of this software and associated documentation files (the
20549// "Software"), to deal in the Software without restriction, including
20550// without limitation the rights to use, copy, modify, merge, publish,
20551// distribute, sublicense, and/or sell copies of the Software, and to permit
20552// persons to whom the Software is furnished to do so, subject to the
20553// following conditions:
20554//
20555// The above copyright notice and this permission notice shall be included
20556// in all copies or substantial portions of the Software.
20557//
20558// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20559// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20560// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
20561// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
20562// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20563// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20564// USE OR OTHER DEALINGS IN THE SOFTWARE.
20565
20566'use strict';
20567
20568/*<replacement>*/
20569
20570var Buffer = _dereq_('safe-buffer').Buffer;
20571/*</replacement>*/
20572
20573var isEncoding = Buffer.isEncoding || function (encoding) {
20574 encoding = '' + encoding;
20575 switch (encoding && encoding.toLowerCase()) {
20576 case 'hex':case 'utf8':case 'utf-8':case 'ascii':case 'binary':case 'base64':case 'ucs2':case 'ucs-2':case 'utf16le':case 'utf-16le':case 'raw':
20577 return true;
20578 default:
20579 return false;
20580 }
20581};
20582
20583function _normalizeEncoding(enc) {
20584 if (!enc) return 'utf8';
20585 var retried;
20586 while (true) {
20587 switch (enc) {
20588 case 'utf8':
20589 case 'utf-8':
20590 return 'utf8';
20591 case 'ucs2':
20592 case 'ucs-2':
20593 case 'utf16le':
20594 case 'utf-16le':
20595 return 'utf16le';
20596 case 'latin1':
20597 case 'binary':
20598 return 'latin1';
20599 case 'base64':
20600 case 'ascii':
20601 case 'hex':
20602 return enc;
20603 default:
20604 if (retried) return; // undefined
20605 enc = ('' + enc).toLowerCase();
20606 retried = true;
20607 }
20608 }
20609};
20610
20611// Do not cache `Buffer.isEncoding` when checking encoding names as some
20612// modules monkey-patch it to support additional encodings
20613function normalizeEncoding(enc) {
20614 var nenc = _normalizeEncoding(enc);
20615 if (typeof nenc !== 'string' && (Buffer.isEncoding === isEncoding || !isEncoding(enc))) throw new Error('Unknown encoding: ' + enc);
20616 return nenc || enc;
20617}
20618
20619// StringDecoder provides an interface for efficiently splitting a series of
20620// buffers into a series of JS strings without breaking apart multi-byte
20621// characters.
20622exports.StringDecoder = StringDecoder;
20623function StringDecoder(encoding) {
20624 this.encoding = normalizeEncoding(encoding);
20625 var nb;
20626 switch (this.encoding) {
20627 case 'utf16le':
20628 this.text = utf16Text;
20629 this.end = utf16End;
20630 nb = 4;
20631 break;
20632 case 'utf8':
20633 this.fillLast = utf8FillLast;
20634 nb = 4;
20635 break;
20636 case 'base64':
20637 this.text = base64Text;
20638 this.end = base64End;
20639 nb = 3;
20640 break;
20641 default:
20642 this.write = simpleWrite;
20643 this.end = simpleEnd;
20644 return;
20645 }
20646 this.lastNeed = 0;
20647 this.lastTotal = 0;
20648 this.lastChar = Buffer.allocUnsafe(nb);
20649}
20650
20651StringDecoder.prototype.write = function (buf) {
20652 if (buf.length === 0) return '';
20653 var r;
20654 var i;
20655 if (this.lastNeed) {
20656 r = this.fillLast(buf);
20657 if (r === undefined) return '';
20658 i = this.lastNeed;
20659 this.lastNeed = 0;
20660 } else {
20661 i = 0;
20662 }
20663 if (i < buf.length) return r ? r + this.text(buf, i) : this.text(buf, i);
20664 return r || '';
20665};
20666
20667StringDecoder.prototype.end = utf8End;
20668
20669// Returns only complete characters in a Buffer
20670StringDecoder.prototype.text = utf8Text;
20671
20672// Attempts to complete a partial non-UTF-8 character using bytes from a Buffer
20673StringDecoder.prototype.fillLast = function (buf) {
20674 if (this.lastNeed <= buf.length) {
20675 buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, this.lastNeed);
20676 return this.lastChar.toString(this.encoding, 0, this.lastTotal);
20677 }
20678 buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, buf.length);
20679 this.lastNeed -= buf.length;
20680};
20681
20682// Checks the type of a UTF-8 byte, whether it's ASCII, a leading byte, or a
20683// continuation byte. If an invalid byte is detected, -2 is returned.
20684function utf8CheckByte(byte) {
20685 if (byte <= 0x7F) return 0;else if (byte >> 5 === 0x06) return 2;else if (byte >> 4 === 0x0E) return 3;else if (byte >> 3 === 0x1E) return 4;
20686 return byte >> 6 === 0x02 ? -1 : -2;
20687}
20688
20689// Checks at most 3 bytes at the end of a Buffer in order to detect an
20690// incomplete multi-byte UTF-8 character. The total number of bytes (2, 3, or 4)
20691// needed to complete the UTF-8 character (if applicable) are returned.
20692function utf8CheckIncomplete(self, buf, i) {
20693 var j = buf.length - 1;
20694 if (j < i) return 0;
20695 var nb = utf8CheckByte(buf[j]);
20696 if (nb >= 0) {
20697 if (nb > 0) self.lastNeed = nb - 1;
20698 return nb;
20699 }
20700 if (--j < i || nb === -2) return 0;
20701 nb = utf8CheckByte(buf[j]);
20702 if (nb >= 0) {
20703 if (nb > 0) self.lastNeed = nb - 2;
20704 return nb;
20705 }
20706 if (--j < i || nb === -2) return 0;
20707 nb = utf8CheckByte(buf[j]);
20708 if (nb >= 0) {
20709 if (nb > 0) {
20710 if (nb === 2) nb = 0;else self.lastNeed = nb - 3;
20711 }
20712 return nb;
20713 }
20714 return 0;
20715}
20716
20717// Validates as many continuation bytes for a multi-byte UTF-8 character as
20718// needed or are available. If we see a non-continuation byte where we expect
20719// one, we "replace" the validated continuation bytes we've seen so far with
20720// a single UTF-8 replacement character ('\ufffd'), to match v8's UTF-8 decoding
20721// behavior. The continuation byte check is included three times in the case
20722// where all of the continuation bytes for a character exist in the same buffer.
20723// It is also done this way as a slight performance increase instead of using a
20724// loop.
20725function utf8CheckExtraBytes(self, buf, p) {
20726 if ((buf[0] & 0xC0) !== 0x80) {
20727 self.lastNeed = 0;
20728 return '\ufffd';
20729 }
20730 if (self.lastNeed > 1 && buf.length > 1) {
20731 if ((buf[1] & 0xC0) !== 0x80) {
20732 self.lastNeed = 1;
20733 return '\ufffd';
20734 }
20735 if (self.lastNeed > 2 && buf.length > 2) {
20736 if ((buf[2] & 0xC0) !== 0x80) {
20737 self.lastNeed = 2;
20738 return '\ufffd';
20739 }
20740 }
20741 }
20742}
20743
20744// Attempts to complete a multi-byte UTF-8 character using bytes from a Buffer.
20745function utf8FillLast(buf) {
20746 var p = this.lastTotal - this.lastNeed;
20747 var r = utf8CheckExtraBytes(this, buf, p);
20748 if (r !== undefined) return r;
20749 if (this.lastNeed <= buf.length) {
20750 buf.copy(this.lastChar, p, 0, this.lastNeed);
20751 return this.lastChar.toString(this.encoding, 0, this.lastTotal);
20752 }
20753 buf.copy(this.lastChar, p, 0, buf.length);
20754 this.lastNeed -= buf.length;
20755}
20756
20757// Returns all complete UTF-8 characters in a Buffer. If the Buffer ended on a
20758// partial character, the character's bytes are buffered until the required
20759// number of bytes are available.
20760function utf8Text(buf, i) {
20761 var total = utf8CheckIncomplete(this, buf, i);
20762 if (!this.lastNeed) return buf.toString('utf8', i);
20763 this.lastTotal = total;
20764 var end = buf.length - (total - this.lastNeed);
20765 buf.copy(this.lastChar, 0, end);
20766 return buf.toString('utf8', i, end);
20767}
20768
20769// For UTF-8, a replacement character is added when ending on a partial
20770// character.
20771function utf8End(buf) {
20772 var r = buf && buf.length ? this.write(buf) : '';
20773 if (this.lastNeed) return r + '\ufffd';
20774 return r;
20775}
20776
20777// UTF-16LE typically needs two bytes per character, but even if we have an even
20778// number of bytes available, we need to check if we end on a leading/high
20779// surrogate. In that case, we need to wait for the next two bytes in order to
20780// decode the last character properly.
20781function utf16Text(buf, i) {
20782 if ((buf.length - i) % 2 === 0) {
20783 var r = buf.toString('utf16le', i);
20784 if (r) {
20785 var c = r.charCodeAt(r.length - 1);
20786 if (c >= 0xD800 && c <= 0xDBFF) {
20787 this.lastNeed = 2;
20788 this.lastTotal = 4;
20789 this.lastChar[0] = buf[buf.length - 2];
20790 this.lastChar[1] = buf[buf.length - 1];
20791 return r.slice(0, -1);
20792 }
20793 }
20794 return r;
20795 }
20796 this.lastNeed = 1;
20797 this.lastTotal = 2;
20798 this.lastChar[0] = buf[buf.length - 1];
20799 return buf.toString('utf16le', i, buf.length - 1);
20800}
20801
20802// For UTF-16LE we do not explicitly append special replacement characters if we
20803// end on a partial character, we simply let v8 handle that.
20804function utf16End(buf) {
20805 var r = buf && buf.length ? this.write(buf) : '';
20806 if (this.lastNeed) {
20807 var end = this.lastTotal - this.lastNeed;
20808 return r + this.lastChar.toString('utf16le', 0, end);
20809 }
20810 return r;
20811}
20812
20813function base64Text(buf, i) {
20814 var n = (buf.length - i) % 3;
20815 if (n === 0) return buf.toString('base64', i);
20816 this.lastNeed = 3 - n;
20817 this.lastTotal = 3;
20818 if (n === 1) {
20819 this.lastChar[0] = buf[buf.length - 1];
20820 } else {
20821 this.lastChar[0] = buf[buf.length - 2];
20822 this.lastChar[1] = buf[buf.length - 1];
20823 }
20824 return buf.toString('base64', i, buf.length - n);
20825}
20826
20827function base64End(buf) {
20828 var r = buf && buf.length ? this.write(buf) : '';
20829 if (this.lastNeed) return r + this.lastChar.toString('base64', 0, 3 - this.lastNeed);
20830 return r;
20831}
20832
20833// Pass bytes on through for single-byte encodings (e.g. ascii, latin1, hex)
20834function simpleWrite(buf) {
20835 return buf.toString(this.encoding);
20836}
20837
20838function simpleEnd(buf) {
20839 return buf && buf.length ? this.write(buf) : '';
20840}
20841},{"safe-buffer":99}],116:[function(_dereq_,module,exports){
20842(function (process,Buffer){(function (){
20843
20844/**
20845 * Module dependencies.
20846 */
20847
20848var assert = _dereq_('assert');
20849var debug = _dereq_('debug')('stream-parser');
20850
20851/**
20852 * Module exports.
20853 */
20854
20855module.exports = Parser;
20856
20857/**
20858 * Parser states.
20859 */
20860
20861var INIT = -1;
20862var BUFFERING = 0;
20863var SKIPPING = 1;
20864var PASSTHROUGH = 2;
20865
20866/**
20867 * The `Parser` stream mixin works with either `Writable` or `Transform` stream
20868 * instances/subclasses. Provides a convenient generic "parsing" API:
20869 *
20870 * _bytes(n, cb) - buffers "n" bytes and then calls "cb" with the "chunk"
20871 * _skipBytes(n, cb) - skips "n" bytes and then calls "cb" when done
20872 *
20873 * If you extend a `Transform` stream, then the `_passthrough()` function is also
20874 * added:
20875 *
20876 * _passthrough(n, cb) - passes through "n" bytes untouched and then calls "cb"
20877 *
20878 * @param {Stream} stream Transform or Writable stream instance to extend
20879 * @api public
20880 */
20881
20882function Parser (stream) {
20883 var isTransform = stream && 'function' == typeof stream._transform;
20884 var isWritable = stream && 'function' == typeof stream._write;
20885
20886 if (!isTransform && !isWritable) throw new Error('must pass a Writable or Transform stream in');
20887 debug('extending Parser into stream');
20888
20889 // Transform streams and Writable streams get `_bytes()` and `_skipBytes()`
20890 stream._bytes = _bytes;
20891 stream._skipBytes = _skipBytes;
20892
20893 // only Transform streams get the `_passthrough()` function
20894 if (isTransform) stream._passthrough = _passthrough;
20895
20896 // take control of the streams2 callback functions for this stream
20897 if (isTransform) {
20898 stream._transform = transform;
20899 } else {
20900 stream._write = write;
20901 }
20902}
20903
20904function init (stream) {
20905 debug('initializing parser stream');
20906
20907 // number of bytes left to parser for the next "chunk"
20908 stream._parserBytesLeft = 0;
20909
20910 // array of Buffer instances that make up the next "chunk"
20911 stream._parserBuffers = [];
20912
20913 // number of bytes parsed so far for the next "chunk"
20914 stream._parserBuffered = 0;
20915
20916 // flag that keeps track of if what the parser should do with bytes received
20917 stream._parserState = INIT;
20918
20919 // the callback for the next "chunk"
20920 stream._parserCallback = null;
20921
20922 // XXX: backwards compat with the old Transform API... remove at some point..
20923 if ('function' == typeof stream.push) {
20924 stream._parserOutput = stream.push.bind(stream);
20925 }
20926
20927 stream._parserInit = true;
20928}
20929
20930/**
20931 * Buffers `n` bytes and then invokes `fn` once that amount has been collected.
20932 *
20933 * @param {Number} n the number of bytes to buffer
20934 * @param {Function} fn callback function to invoke when `n` bytes are buffered
20935 * @api public
20936 */
20937
20938function _bytes (n, fn) {
20939 assert(!this._parserCallback, 'there is already a "callback" set!');
20940 assert(isFinite(n) && n > 0, 'can only buffer a finite number of bytes > 0, got "' + n + '"');
20941 if (!this._parserInit) init(this);
20942 debug('buffering %o bytes', n);
20943 this._parserBytesLeft = n;
20944 this._parserCallback = fn;
20945 this._parserState = BUFFERING;
20946}
20947
20948/**
20949 * Skips over the next `n` bytes, then invokes `fn` once that amount has
20950 * been discarded.
20951 *
20952 * @param {Number} n the number of bytes to discard
20953 * @param {Function} fn callback function to invoke when `n` bytes have been skipped
20954 * @api public
20955 */
20956
20957function _skipBytes (n, fn) {
20958 assert(!this._parserCallback, 'there is already a "callback" set!');
20959 assert(n > 0, 'can only skip > 0 bytes, got "' + n + '"');
20960 if (!this._parserInit) init(this);
20961 debug('skipping %o bytes', n);
20962 this._parserBytesLeft = n;
20963 this._parserCallback = fn;
20964 this._parserState = SKIPPING;
20965}
20966
20967/**
20968 * Passes through `n` bytes to the readable side of this stream untouched,
20969 * then invokes `fn` once that amount has been passed through.
20970 *
20971 * @param {Number} n the number of bytes to pass through
20972 * @param {Function} fn callback function to invoke when `n` bytes have passed through
20973 * @api public
20974 */
20975
20976function _passthrough (n, fn) {
20977 assert(!this._parserCallback, 'There is already a "callback" set!');
20978 assert(n > 0, 'can only pass through > 0 bytes, got "' + n + '"');
20979 if (!this._parserInit) init(this);
20980 debug('passing through %o bytes', n);
20981 this._parserBytesLeft = n;
20982 this._parserCallback = fn;
20983 this._parserState = PASSTHROUGH;
20984}
20985
20986/**
20987 * The `_write()` callback function implementation.
20988 *
20989 * @api private
20990 */
20991
20992function write (chunk, encoding, fn) {
20993 if (!this._parserInit) init(this);
20994 debug('write(%o bytes)', chunk.length);
20995
20996 // XXX: old Writable stream API compat... remove at some point...
20997 if ('function' == typeof encoding) fn = encoding;
20998
20999 data(this, chunk, null, fn);
21000}
21001
21002/**
21003 * The `_transform()` callback function implementation.
21004 *
21005 * @api private
21006 */
21007
21008
21009function transform (chunk, output, fn) {
21010 if (!this._parserInit) init(this);
21011 debug('transform(%o bytes)', chunk.length);
21012
21013 // XXX: old Transform stream API compat... remove at some point...
21014 if ('function' != typeof output) {
21015 output = this._parserOutput;
21016 }
21017
21018 data(this, chunk, output, fn);
21019}
21020
21021/**
21022 * The internal buffering/passthrough logic...
21023 *
21024 * This `_data` function get's "trampolined" to prevent stack overflows for tight
21025 * loops. This technique requires us to return a "thunk" function for any
21026 * synchronous action. Async stuff breaks the trampoline, but that's ok since it's
21027 * working with a new stack at that point anyway.
21028 *
21029 * @api private
21030 */
21031
21032function _data (stream, chunk, output, fn) {
21033 if (stream._parserBytesLeft <= 0) {
21034 return fn(new Error('got data but not currently parsing anything'));
21035 }
21036
21037 if (chunk.length <= stream._parserBytesLeft) {
21038 // small buffer fits within the "_parserBytesLeft" window
21039 return function () {
21040 return process(stream, chunk, output, fn);
21041 };
21042 } else {
21043 // large buffer needs to be sliced on "_parserBytesLeft" and processed
21044 return function () {
21045 var b = chunk.slice(0, stream._parserBytesLeft);
21046 return process(stream, b, output, function (err) {
21047 if (err) return fn(err);
21048 if (chunk.length > b.length) {
21049 return function () {
21050 return _data(stream, chunk.slice(b.length), output, fn);
21051 };
21052 }
21053 });
21054 };
21055 }
21056}
21057
21058/**
21059 * The internal `process` function gets called by the `data` function when
21060 * something "interesting" happens. This function takes care of buffering the
21061 * bytes when buffering, passing through the bytes when doing that, and invoking
21062 * the user callback when the number of bytes has been reached.
21063 *
21064 * @api private
21065 */
21066
21067function process (stream, chunk, output, fn) {
21068 stream._parserBytesLeft -= chunk.length;
21069 debug('%o bytes left for stream piece', stream._parserBytesLeft);
21070
21071 if (stream._parserState === BUFFERING) {
21072 // buffer
21073 stream._parserBuffers.push(chunk);
21074 stream._parserBuffered += chunk.length;
21075 } else if (stream._parserState === PASSTHROUGH) {
21076 // passthrough
21077 output(chunk);
21078 }
21079 // don't need to do anything for the SKIPPING case
21080
21081 if (0 === stream._parserBytesLeft) {
21082 // done with stream "piece", invoke the callback
21083 var cb = stream._parserCallback;
21084 if (cb && stream._parserState === BUFFERING && stream._parserBuffers.length > 1) {
21085 chunk = Buffer.concat(stream._parserBuffers, stream._parserBuffered);
21086 }
21087 if (stream._parserState !== BUFFERING) {
21088 chunk = null;
21089 }
21090 stream._parserCallback = null;
21091 stream._parserBuffered = 0;
21092 stream._parserState = INIT;
21093 stream._parserBuffers.splice(0); // empty
21094
21095 if (cb) {
21096 var args = [];
21097 if (chunk) {
21098 // buffered
21099 args.push(chunk);
21100 } else {
21101 // passthrough
21102 }
21103 if (output) {
21104 // on a Transform stream, has "output" function
21105 args.push(output);
21106 }
21107 var async = cb.length > args.length;
21108 if (async) {
21109 args.push(trampoline(fn));
21110 }
21111 // invoke cb
21112 var rtn = cb.apply(stream, args);
21113 if (!async || fn === rtn) return fn;
21114 }
21115 } else {
21116 // need more bytes
21117 return fn;
21118 }
21119}
21120
21121var data = trampoline(_data);
21122
21123/**
21124 * Generic thunk-based "trampoline" helper function.
21125 *
21126 * @param {Function} input function
21127 * @return {Function} "trampolined" function
21128 * @api private
21129 */
21130
21131function trampoline (fn) {
21132 return function () {
21133 var result = fn.apply(this, arguments);
21134
21135 while ('function' == typeof result) {
21136 result = result();
21137 }
21138
21139 return result;
21140 };
21141}
21142
21143}).call(this)}).call(this,_dereq_('_process'),_dereq_("buffer").Buffer)
21144},{"_process":98,"assert":21,"buffer":28,"debug":117}],117:[function(_dereq_,module,exports){
21145(function (process){(function (){
21146/**
21147 * This is the web browser implementation of `debug()`.
21148 *
21149 * Expose `debug()` as the module.
21150 */
21151
21152exports = module.exports = _dereq_('./debug');
21153exports.log = log;
21154exports.formatArgs = formatArgs;
21155exports.save = save;
21156exports.load = load;
21157exports.useColors = useColors;
21158exports.storage = 'undefined' != typeof chrome
21159 && 'undefined' != typeof chrome.storage
21160 ? chrome.storage.local
21161 : localstorage();
21162
21163/**
21164 * Colors.
21165 */
21166
21167exports.colors = [
21168 'lightseagreen',
21169 'forestgreen',
21170 'goldenrod',
21171 'dodgerblue',
21172 'darkorchid',
21173 'crimson'
21174];
21175
21176/**
21177 * Currently only WebKit-based Web Inspectors, Firefox >= v31,
21178 * and the Firebug extension (any Firefox version) are known
21179 * to support "%c" CSS customizations.
21180 *
21181 * TODO: add a `localStorage` variable to explicitly enable/disable colors
21182 */
21183
21184function useColors() {
21185 // NB: In an Electron preload script, document will be defined but not fully
21186 // initialized. Since we know we're in Chrome, we'll just detect this case
21187 // explicitly
21188 if (typeof window !== 'undefined' && window.process && window.process.type === 'renderer') {
21189 return true;
21190 }
21191
21192 // is webkit? http://stackoverflow.com/a/16459606/376773
21193 // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632
21194 return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) ||
21195 // is firebug? http://stackoverflow.com/a/398120/376773
21196 (typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) ||
21197 // is firefox >= v31?
21198 // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
21199 (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) ||
21200 // double check webkit in userAgent just in case we are in a worker
21201 (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/));
21202}
21203
21204/**
21205 * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
21206 */
21207
21208exports.formatters.j = function(v) {
21209 try {
21210 return JSON.stringify(v);
21211 } catch (err) {
21212 return '[UnexpectedJSONParseError]: ' + err.message;
21213 }
21214};
21215
21216
21217/**
21218 * Colorize log arguments if enabled.
21219 *
21220 * @api public
21221 */
21222
21223function formatArgs(args) {
21224 var useColors = this.useColors;
21225
21226 args[0] = (useColors ? '%c' : '')
21227 + this.namespace
21228 + (useColors ? ' %c' : ' ')
21229 + args[0]
21230 + (useColors ? '%c ' : ' ')
21231 + '+' + exports.humanize(this.diff);
21232
21233 if (!useColors) return;
21234
21235 var c = 'color: ' + this.color;
21236 args.splice(1, 0, c, 'color: inherit')
21237
21238 // the final "%c" is somewhat tricky, because there could be other
21239 // arguments passed either before or after the %c, so we need to
21240 // figure out the correct index to insert the CSS into
21241 var index = 0;
21242 var lastC = 0;
21243 args[0].replace(/%[a-zA-Z%]/g, function(match) {
21244 if ('%%' === match) return;
21245 index++;
21246 if ('%c' === match) {
21247 // we only are interested in the *last* %c
21248 // (the user may have provided their own)
21249 lastC = index;
21250 }
21251 });
21252
21253 args.splice(lastC, 0, c);
21254}
21255
21256/**
21257 * Invokes `console.log()` when available.
21258 * No-op when `console.log` is not a "function".
21259 *
21260 * @api public
21261 */
21262
21263function log() {
21264 // this hackery is required for IE8/9, where
21265 // the `console.log` function doesn't have 'apply'
21266 return 'object' === typeof console
21267 && console.log
21268 && Function.prototype.apply.call(console.log, console, arguments);
21269}
21270
21271/**
21272 * Save `namespaces`.
21273 *
21274 * @param {String} namespaces
21275 * @api private
21276 */
21277
21278function save(namespaces) {
21279 try {
21280 if (null == namespaces) {
21281 exports.storage.removeItem('debug');
21282 } else {
21283 exports.storage.debug = namespaces;
21284 }
21285 } catch(e) {}
21286}
21287
21288/**
21289 * Load `namespaces`.
21290 *
21291 * @return {String} returns the previously persisted debug modes
21292 * @api private
21293 */
21294
21295function load() {
21296 var r;
21297 try {
21298 r = exports.storage.debug;
21299 } catch(e) {}
21300
21301 // If debug isn't set in LS, and we're in Electron, try to load $DEBUG
21302 if (!r && typeof process !== 'undefined' && 'env' in process) {
21303 r = process.env.DEBUG;
21304 }
21305
21306 return r;
21307}
21308
21309/**
21310 * Enable namespaces listed in `localStorage.debug` initially.
21311 */
21312
21313exports.enable(load());
21314
21315/**
21316 * Localstorage attempts to return the localstorage.
21317 *
21318 * This is necessary because safari throws
21319 * when a user disables cookies/localstorage
21320 * and you attempt to access it.
21321 *
21322 * @return {LocalStorage}
21323 * @api private
21324 */
21325
21326function localstorage() {
21327 try {
21328 return window.localStorage;
21329 } catch (e) {}
21330}
21331
21332}).call(this)}).call(this,_dereq_('_process'))
21333},{"./debug":118,"_process":98}],118:[function(_dereq_,module,exports){
21334
21335/**
21336 * This is the common logic for both the Node.js and web browser
21337 * implementations of `debug()`.
21338 *
21339 * Expose `debug()` as the module.
21340 */
21341
21342exports = module.exports = createDebug.debug = createDebug['default'] = createDebug;
21343exports.coerce = coerce;
21344exports.disable = disable;
21345exports.enable = enable;
21346exports.enabled = enabled;
21347exports.humanize = _dereq_('ms');
21348
21349/**
21350 * The currently active debug mode names, and names to skip.
21351 */
21352
21353exports.names = [];
21354exports.skips = [];
21355
21356/**
21357 * Map of special "%n" handling functions, for the debug "format" argument.
21358 *
21359 * Valid key names are a single, lower or upper-case letter, i.e. "n" and "N".
21360 */
21361
21362exports.formatters = {};
21363
21364/**
21365 * Previous log timestamp.
21366 */
21367
21368var prevTime;
21369
21370/**
21371 * Select a color.
21372 * @param {String} namespace
21373 * @return {Number}
21374 * @api private
21375 */
21376
21377function selectColor(namespace) {
21378 var hash = 0, i;
21379
21380 for (i in namespace) {
21381 hash = ((hash << 5) - hash) + namespace.charCodeAt(i);
21382 hash |= 0; // Convert to 32bit integer
21383 }
21384
21385 return exports.colors[Math.abs(hash) % exports.colors.length];
21386}
21387
21388/**
21389 * Create a debugger with the given `namespace`.
21390 *
21391 * @param {String} namespace
21392 * @return {Function}
21393 * @api public
21394 */
21395
21396function createDebug(namespace) {
21397
21398 function debug() {
21399 // disabled?
21400 if (!debug.enabled) return;
21401
21402 var self = debug;
21403
21404 // set `diff` timestamp
21405 var curr = +new Date();
21406 var ms = curr - (prevTime || curr);
21407 self.diff = ms;
21408 self.prev = prevTime;
21409 self.curr = curr;
21410 prevTime = curr;
21411
21412 // turn the `arguments` into a proper Array
21413 var args = new Array(arguments.length);
21414 for (var i = 0; i < args.length; i++) {
21415 args[i] = arguments[i];
21416 }
21417
21418 args[0] = exports.coerce(args[0]);
21419
21420 if ('string' !== typeof args[0]) {
21421 // anything else let's inspect with %O
21422 args.unshift('%O');
21423 }
21424
21425 // apply any `formatters` transformations
21426 var index = 0;
21427 args[0] = args[0].replace(/%([a-zA-Z%])/g, function(match, format) {
21428 // if we encounter an escaped % then don't increase the array index
21429 if (match === '%%') return match;
21430 index++;
21431 var formatter = exports.formatters[format];
21432 if ('function' === typeof formatter) {
21433 var val = args[index];
21434 match = formatter.call(self, val);
21435
21436 // now we need to remove `args[index]` since it's inlined in the `format`
21437 args.splice(index, 1);
21438 index--;
21439 }
21440 return match;
21441 });
21442
21443 // apply env-specific formatting (colors, etc.)
21444 exports.formatArgs.call(self, args);
21445
21446 var logFn = debug.log || exports.log || console.log.bind(console);
21447 logFn.apply(self, args);
21448 }
21449
21450 debug.namespace = namespace;
21451 debug.enabled = exports.enabled(namespace);
21452 debug.useColors = exports.useColors();
21453 debug.color = selectColor(namespace);
21454
21455 // env-specific initialization logic for debug instances
21456 if ('function' === typeof exports.init) {
21457 exports.init(debug);
21458 }
21459
21460 return debug;
21461}
21462
21463/**
21464 * Enables a debug mode by namespaces. This can include modes
21465 * separated by a colon and wildcards.
21466 *
21467 * @param {String} namespaces
21468 * @api public
21469 */
21470
21471function enable(namespaces) {
21472 exports.save(namespaces);
21473
21474 exports.names = [];
21475 exports.skips = [];
21476
21477 var split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/);
21478 var len = split.length;
21479
21480 for (var i = 0; i < len; i++) {
21481 if (!split[i]) continue; // ignore empty strings
21482 namespaces = split[i].replace(/\*/g, '.*?');
21483 if (namespaces[0] === '-') {
21484 exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$'));
21485 } else {
21486 exports.names.push(new RegExp('^' + namespaces + '$'));
21487 }
21488 }
21489}
21490
21491/**
21492 * Disable debug output.
21493 *
21494 * @api public
21495 */
21496
21497function disable() {
21498 exports.enable('');
21499}
21500
21501/**
21502 * Returns true if the given mode name is enabled, false otherwise.
21503 *
21504 * @param {String} name
21505 * @return {Boolean}
21506 * @api public
21507 */
21508
21509function enabled(name) {
21510 var i, len;
21511 for (i = 0, len = exports.skips.length; i < len; i++) {
21512 if (exports.skips[i].test(name)) {
21513 return false;
21514 }
21515 }
21516 for (i = 0, len = exports.names.length; i < len; i++) {
21517 if (exports.names[i].test(name)) {
21518 return true;
21519 }
21520 }
21521 return false;
21522}
21523
21524/**
21525 * Coerce `val`.
21526 *
21527 * @param {Mixed} val
21528 * @return {Mixed}
21529 * @api private
21530 */
21531
21532function coerce(val) {
21533 if (val instanceof Error) return val.stack || val.message;
21534 return val;
21535}
21536
21537},{"ms":119}],119:[function(_dereq_,module,exports){
21538/**
21539 * Helpers.
21540 */
21541
21542var s = 1000;
21543var m = s * 60;
21544var h = m * 60;
21545var d = h * 24;
21546var y = d * 365.25;
21547
21548/**
21549 * Parse or format the given `val`.
21550 *
21551 * Options:
21552 *
21553 * - `long` verbose formatting [false]
21554 *
21555 * @param {String|Number} val
21556 * @param {Object} [options]
21557 * @throws {Error} throw an error if val is not a non-empty string or a number
21558 * @return {String|Number}
21559 * @api public
21560 */
21561
21562module.exports = function(val, options) {
21563 options = options || {};
21564 var type = typeof val;
21565 if (type === 'string' && val.length > 0) {
21566 return parse(val);
21567 } else if (type === 'number' && isNaN(val) === false) {
21568 return options.long ? fmtLong(val) : fmtShort(val);
21569 }
21570 throw new Error(
21571 'val is not a non-empty string or a valid number. val=' +
21572 JSON.stringify(val)
21573 );
21574};
21575
21576/**
21577 * Parse the given `str` and return milliseconds.
21578 *
21579 * @param {String} str
21580 * @return {Number}
21581 * @api private
21582 */
21583
21584function parse(str) {
21585 str = String(str);
21586 if (str.length > 100) {
21587 return;
21588 }
21589 var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(
21590 str
21591 );
21592 if (!match) {
21593 return;
21594 }
21595 var n = parseFloat(match[1]);
21596 var type = (match[2] || 'ms').toLowerCase();
21597 switch (type) {
21598 case 'years':
21599 case 'year':
21600 case 'yrs':
21601 case 'yr':
21602 case 'y':
21603 return n * y;
21604 case 'days':
21605 case 'day':
21606 case 'd':
21607 return n * d;
21608 case 'hours':
21609 case 'hour':
21610 case 'hrs':
21611 case 'hr':
21612 case 'h':
21613 return n * h;
21614 case 'minutes':
21615 case 'minute':
21616 case 'mins':
21617 case 'min':
21618 case 'm':
21619 return n * m;
21620 case 'seconds':
21621 case 'second':
21622 case 'secs':
21623 case 'sec':
21624 case 's':
21625 return n * s;
21626 case 'milliseconds':
21627 case 'millisecond':
21628 case 'msecs':
21629 case 'msec':
21630 case 'ms':
21631 return n;
21632 default:
21633 return undefined;
21634 }
21635}
21636
21637/**
21638 * Short format for `ms`.
21639 *
21640 * @param {Number} ms
21641 * @return {String}
21642 * @api private
21643 */
21644
21645function fmtShort(ms) {
21646 if (ms >= d) {
21647 return Math.round(ms / d) + 'd';
21648 }
21649 if (ms >= h) {
21650 return Math.round(ms / h) + 'h';
21651 }
21652 if (ms >= m) {
21653 return Math.round(ms / m) + 'm';
21654 }
21655 if (ms >= s) {
21656 return Math.round(ms / s) + 's';
21657 }
21658 return ms + 'ms';
21659}
21660
21661/**
21662 * Long format for `ms`.
21663 *
21664 * @param {Number} ms
21665 * @return {String}
21666 * @api private
21667 */
21668
21669function fmtLong(ms) {
21670 return plural(ms, d, 'day') ||
21671 plural(ms, h, 'hour') ||
21672 plural(ms, m, 'minute') ||
21673 plural(ms, s, 'second') ||
21674 ms + ' ms';
21675}
21676
21677/**
21678 * Pluralization helper.
21679 */
21680
21681function plural(ms, n, name) {
21682 if (ms < n) {
21683 return;
21684 }
21685 if (ms < n * 1.5) {
21686 return Math.floor(ms / n) + ' ' + name;
21687 }
21688 return Math.ceil(ms / n) + ' ' + name + 's';
21689}
21690
21691},{}],120:[function(_dereq_,module,exports){
21692(function (setImmediate,clearImmediate){(function (){
21693var nextTick = _dereq_('process/browser.js').nextTick;
21694var apply = Function.prototype.apply;
21695var slice = Array.prototype.slice;
21696var immediateIds = {};
21697var nextImmediateId = 0;
21698
21699// DOM APIs, for completeness
21700
21701exports.setTimeout = function() {
21702 return new Timeout(apply.call(setTimeout, window, arguments), clearTimeout);
21703};
21704exports.setInterval = function() {
21705 return new Timeout(apply.call(setInterval, window, arguments), clearInterval);
21706};
21707exports.clearTimeout =
21708exports.clearInterval = function(timeout) { timeout.close(); };
21709
21710function Timeout(id, clearFn) {
21711 this._id = id;
21712 this._clearFn = clearFn;
21713}
21714Timeout.prototype.unref = Timeout.prototype.ref = function() {};
21715Timeout.prototype.close = function() {
21716 this._clearFn.call(window, this._id);
21717};
21718
21719// Does not start the time, just sets up the members needed.
21720exports.enroll = function(item, msecs) {
21721 clearTimeout(item._idleTimeoutId);
21722 item._idleTimeout = msecs;
21723};
21724
21725exports.unenroll = function(item) {
21726 clearTimeout(item._idleTimeoutId);
21727 item._idleTimeout = -1;
21728};
21729
21730exports._unrefActive = exports.active = function(item) {
21731 clearTimeout(item._idleTimeoutId);
21732
21733 var msecs = item._idleTimeout;
21734 if (msecs >= 0) {
21735 item._idleTimeoutId = setTimeout(function onTimeout() {
21736 if (item._onTimeout)
21737 item._onTimeout();
21738 }, msecs);
21739 }
21740};
21741
21742// That's not how node.js implements it but the exposed api is the same.
21743exports.setImmediate = typeof setImmediate === "function" ? setImmediate : function(fn) {
21744 var id = nextImmediateId++;
21745 var args = arguments.length < 2 ? false : slice.call(arguments, 1);
21746
21747 immediateIds[id] = true;
21748
21749 nextTick(function onNextTick() {
21750 if (immediateIds[id]) {
21751 // fn.call() is faster so we optimize for the common use-case
21752 // @see http://jsperf.com/call-apply-segu
21753 if (args) {
21754 fn.apply(null, args);
21755 } else {
21756 fn.call(null);
21757 }
21758 // Prevent ids from leaking
21759 exports.clearImmediate(id);
21760 }
21761 });
21762
21763 return id;
21764};
21765
21766exports.clearImmediate = typeof clearImmediate === "function" ? clearImmediate : function(id) {
21767 delete immediateIds[id];
21768};
21769}).call(this)}).call(this,_dereq_("timers").setImmediate,_dereq_("timers").clearImmediate)
21770},{"process/browser.js":98,"timers":120}],121:[function(_dereq_,module,exports){
21771// TinyColor v1.4.2
21772// https://github.com/bgrins/TinyColor
21773// Brian Grinstead, MIT License
21774
21775(function(Math) {
21776
21777var trimLeft = /^\s+/,
21778 trimRight = /\s+$/,
21779 tinyCounter = 0,
21780 mathRound = Math.round,
21781 mathMin = Math.min,
21782 mathMax = Math.max,
21783 mathRandom = Math.random;
21784
21785function tinycolor (color, opts) {
21786
21787 color = (color) ? color : '';
21788 opts = opts || { };
21789
21790 // If input is already a tinycolor, return itself
21791 if (color instanceof tinycolor) {
21792 return color;
21793 }
21794 // If we are called as a function, call using new instead
21795 if (!(this instanceof tinycolor)) {
21796 return new tinycolor(color, opts);
21797 }
21798
21799 var rgb = inputToRGB(color);
21800 this._originalInput = color,
21801 this._r = rgb.r,
21802 this._g = rgb.g,
21803 this._b = rgb.b,
21804 this._a = rgb.a,
21805 this._roundA = mathRound(100*this._a) / 100,
21806 this._format = opts.format || rgb.format;
21807 this._gradientType = opts.gradientType;
21808
21809 // Don't let the range of [0,255] come back in [0,1].
21810 // Potentially lose a little bit of precision here, but will fix issues where
21811 // .5 gets interpreted as half of the total, instead of half of 1
21812 // If it was supposed to be 128, this was already taken care of by `inputToRgb`
21813 if (this._r < 1) { this._r = mathRound(this._r); }
21814 if (this._g < 1) { this._g = mathRound(this._g); }
21815 if (this._b < 1) { this._b = mathRound(this._b); }
21816
21817 this._ok = rgb.ok;
21818 this._tc_id = tinyCounter++;
21819}
21820
21821tinycolor.prototype = {
21822 isDark: function() {
21823 return this.getBrightness() < 128;
21824 },
21825 isLight: function() {
21826 return !this.isDark();
21827 },
21828 isValid: function() {
21829 return this._ok;
21830 },
21831 getOriginalInput: function() {
21832 return this._originalInput;
21833 },
21834 getFormat: function() {
21835 return this._format;
21836 },
21837 getAlpha: function() {
21838 return this._a;
21839 },
21840 getBrightness: function() {
21841 //http://www.w3.org/TR/AERT#color-contrast
21842 var rgb = this.toRgb();
21843 return (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000;
21844 },
21845 getLuminance: function() {
21846 //http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
21847 var rgb = this.toRgb();
21848 var RsRGB, GsRGB, BsRGB, R, G, B;
21849 RsRGB = rgb.r/255;
21850 GsRGB = rgb.g/255;
21851 BsRGB = rgb.b/255;
21852
21853 if (RsRGB <= 0.03928) {R = RsRGB / 12.92;} else {R = Math.pow(((RsRGB + 0.055) / 1.055), 2.4);}
21854 if (GsRGB <= 0.03928) {G = GsRGB / 12.92;} else {G = Math.pow(((GsRGB + 0.055) / 1.055), 2.4);}
21855 if (BsRGB <= 0.03928) {B = BsRGB / 12.92;} else {B = Math.pow(((BsRGB + 0.055) / 1.055), 2.4);}
21856 return (0.2126 * R) + (0.7152 * G) + (0.0722 * B);
21857 },
21858 setAlpha: function(value) {
21859 this._a = boundAlpha(value);
21860 this._roundA = mathRound(100*this._a) / 100;
21861 return this;
21862 },
21863 toHsv: function() {
21864 var hsv = rgbToHsv(this._r, this._g, this._b);
21865 return { h: hsv.h * 360, s: hsv.s, v: hsv.v, a: this._a };
21866 },
21867 toHsvString: function() {
21868 var hsv = rgbToHsv(this._r, this._g, this._b);
21869 var h = mathRound(hsv.h * 360), s = mathRound(hsv.s * 100), v = mathRound(hsv.v * 100);
21870 return (this._a == 1) ?
21871 "hsv(" + h + ", " + s + "%, " + v + "%)" :
21872 "hsva(" + h + ", " + s + "%, " + v + "%, "+ this._roundA + ")";
21873 },
21874 toHsl: function() {
21875 var hsl = rgbToHsl(this._r, this._g, this._b);
21876 return { h: hsl.h * 360, s: hsl.s, l: hsl.l, a: this._a };
21877 },
21878 toHslString: function() {
21879 var hsl = rgbToHsl(this._r, this._g, this._b);
21880 var h = mathRound(hsl.h * 360), s = mathRound(hsl.s * 100), l = mathRound(hsl.l * 100);
21881 return (this._a == 1) ?
21882 "hsl(" + h + ", " + s + "%, " + l + "%)" :
21883 "hsla(" + h + ", " + s + "%, " + l + "%, "+ this._roundA + ")";
21884 },
21885 toHex: function(allow3Char) {
21886 return rgbToHex(this._r, this._g, this._b, allow3Char);
21887 },
21888 toHexString: function(allow3Char) {
21889 return '#' + this.toHex(allow3Char);
21890 },
21891 toHex8: function(allow4Char) {
21892 return rgbaToHex(this._r, this._g, this._b, this._a, allow4Char);
21893 },
21894 toHex8String: function(allow4Char) {
21895 return '#' + this.toHex8(allow4Char);
21896 },
21897 toRgb: function() {
21898 return { r: mathRound(this._r), g: mathRound(this._g), b: mathRound(this._b), a: this._a };
21899 },
21900 toRgbString: function() {
21901 return (this._a == 1) ?
21902 "rgb(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ")" :
21903 "rgba(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ", " + this._roundA + ")";
21904 },
21905 toPercentageRgb: function() {
21906 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 };
21907 },
21908 toPercentageRgbString: function() {
21909 return (this._a == 1) ?
21910 "rgb(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%)" :
21911 "rgba(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%, " + this._roundA + ")";
21912 },
21913 toName: function() {
21914 if (this._a === 0) {
21915 return "transparent";
21916 }
21917
21918 if (this._a < 1) {
21919 return false;
21920 }
21921
21922 return hexNames[rgbToHex(this._r, this._g, this._b, true)] || false;
21923 },
21924 toFilter: function(secondColor) {
21925 var hex8String = '#' + rgbaToArgbHex(this._r, this._g, this._b, this._a);
21926 var secondHex8String = hex8String;
21927 var gradientType = this._gradientType ? "GradientType = 1, " : "";
21928
21929 if (secondColor) {
21930 var s = tinycolor(secondColor);
21931 secondHex8String = '#' + rgbaToArgbHex(s._r, s._g, s._b, s._a);
21932 }
21933
21934 return "progid:DXImageTransform.Microsoft.gradient("+gradientType+"startColorstr="+hex8String+",endColorstr="+secondHex8String+")";
21935 },
21936 toString: function(format) {
21937 var formatSet = !!format;
21938 format = format || this._format;
21939
21940 var formattedString = false;
21941 var hasAlpha = this._a < 1 && this._a >= 0;
21942 var needsAlphaFormat = !formatSet && hasAlpha && (format === "hex" || format === "hex6" || format === "hex3" || format === "hex4" || format === "hex8" || format === "name");
21943
21944 if (needsAlphaFormat) {
21945 // Special case for "transparent", all other non-alpha formats
21946 // will return rgba when there is transparency.
21947 if (format === "name" && this._a === 0) {
21948 return this.toName();
21949 }
21950 return this.toRgbString();
21951 }
21952 if (format === "rgb") {
21953 formattedString = this.toRgbString();
21954 }
21955 if (format === "prgb") {
21956 formattedString = this.toPercentageRgbString();
21957 }
21958 if (format === "hex" || format === "hex6") {
21959 formattedString = this.toHexString();
21960 }
21961 if (format === "hex3") {
21962 formattedString = this.toHexString(true);
21963 }
21964 if (format === "hex4") {
21965 formattedString = this.toHex8String(true);
21966 }
21967 if (format === "hex8") {
21968 formattedString = this.toHex8String();
21969 }
21970 if (format === "name") {
21971 formattedString = this.toName();
21972 }
21973 if (format === "hsl") {
21974 formattedString = this.toHslString();
21975 }
21976 if (format === "hsv") {
21977 formattedString = this.toHsvString();
21978 }
21979
21980 return formattedString || this.toHexString();
21981 },
21982 clone: function() {
21983 return tinycolor(this.toString());
21984 },
21985
21986 _applyModification: function(fn, args) {
21987 var color = fn.apply(null, [this].concat([].slice.call(args)));
21988 this._r = color._r;
21989 this._g = color._g;
21990 this._b = color._b;
21991 this.setAlpha(color._a);
21992 return this;
21993 },
21994 lighten: function() {
21995 return this._applyModification(lighten, arguments);
21996 },
21997 brighten: function() {
21998 return this._applyModification(brighten, arguments);
21999 },
22000 darken: function() {
22001 return this._applyModification(darken, arguments);
22002 },
22003 desaturate: function() {
22004 return this._applyModification(desaturate, arguments);
22005 },
22006 saturate: function() {
22007 return this._applyModification(saturate, arguments);
22008 },
22009 greyscale: function() {
22010 return this._applyModification(greyscale, arguments);
22011 },
22012 spin: function() {
22013 return this._applyModification(spin, arguments);
22014 },
22015
22016 _applyCombination: function(fn, args) {
22017 return fn.apply(null, [this].concat([].slice.call(args)));
22018 },
22019 analogous: function() {
22020 return this._applyCombination(analogous, arguments);
22021 },
22022 complement: function() {
22023 return this._applyCombination(complement, arguments);
22024 },
22025 monochromatic: function() {
22026 return this._applyCombination(monochromatic, arguments);
22027 },
22028 splitcomplement: function() {
22029 return this._applyCombination(splitcomplement, arguments);
22030 },
22031 triad: function() {
22032 return this._applyCombination(triad, arguments);
22033 },
22034 tetrad: function() {
22035 return this._applyCombination(tetrad, arguments);
22036 }
22037};
22038
22039// If input is an object, force 1 into "1.0" to handle ratios properly
22040// String input requires "1.0" as input, so 1 will be treated as 1
22041tinycolor.fromRatio = function(color, opts) {
22042 if (typeof color == "object") {
22043 var newColor = {};
22044 for (var i in color) {
22045 if (color.hasOwnProperty(i)) {
22046 if (i === "a") {
22047 newColor[i] = color[i];
22048 }
22049 else {
22050 newColor[i] = convertToPercentage(color[i]);
22051 }
22052 }
22053 }
22054 color = newColor;
22055 }
22056
22057 return tinycolor(color, opts);
22058};
22059
22060// Given a string or object, convert that input to RGB
22061// Possible string inputs:
22062//
22063// "red"
22064// "#f00" or "f00"
22065// "#ff0000" or "ff0000"
22066// "#ff000000" or "ff000000"
22067// "rgb 255 0 0" or "rgb (255, 0, 0)"
22068// "rgb 1.0 0 0" or "rgb (1, 0, 0)"
22069// "rgba (255, 0, 0, 1)" or "rgba 255, 0, 0, 1"
22070// "rgba (1.0, 0, 0, 1)" or "rgba 1.0, 0, 0, 1"
22071// "hsl(0, 100%, 50%)" or "hsl 0 100% 50%"
22072// "hsla(0, 100%, 50%, 1)" or "hsla 0 100% 50%, 1"
22073// "hsv(0, 100%, 100%)" or "hsv 0 100% 100%"
22074//
22075function inputToRGB(color) {
22076
22077 var rgb = { r: 0, g: 0, b: 0 };
22078 var a = 1;
22079 var s = null;
22080 var v = null;
22081 var l = null;
22082 var ok = false;
22083 var format = false;
22084
22085 if (typeof color == "string") {
22086 color = stringInputToObject(color);
22087 }
22088
22089 if (typeof color == "object") {
22090 if (isValidCSSUnit(color.r) && isValidCSSUnit(color.g) && isValidCSSUnit(color.b)) {
22091 rgb = rgbToRgb(color.r, color.g, color.b);
22092 ok = true;
22093 format = String(color.r).substr(-1) === "%" ? "prgb" : "rgb";
22094 }
22095 else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.v)) {
22096 s = convertToPercentage(color.s);
22097 v = convertToPercentage(color.v);
22098 rgb = hsvToRgb(color.h, s, v);
22099 ok = true;
22100 format = "hsv";
22101 }
22102 else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.l)) {
22103 s = convertToPercentage(color.s);
22104 l = convertToPercentage(color.l);
22105 rgb = hslToRgb(color.h, s, l);
22106 ok = true;
22107 format = "hsl";
22108 }
22109
22110 if (color.hasOwnProperty("a")) {
22111 a = color.a;
22112 }
22113 }
22114
22115 a = boundAlpha(a);
22116
22117 return {
22118 ok: ok,
22119 format: color.format || format,
22120 r: mathMin(255, mathMax(rgb.r, 0)),
22121 g: mathMin(255, mathMax(rgb.g, 0)),
22122 b: mathMin(255, mathMax(rgb.b, 0)),
22123 a: a
22124 };
22125}
22126
22127
22128// Conversion Functions
22129// --------------------
22130
22131// `rgbToHsl`, `rgbToHsv`, `hslToRgb`, `hsvToRgb` modified from:
22132// <http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript>
22133
22134// `rgbToRgb`
22135// Handle bounds / percentage checking to conform to CSS color spec
22136// <http://www.w3.org/TR/css3-color/>
22137// *Assumes:* r, g, b in [0, 255] or [0, 1]
22138// *Returns:* { r, g, b } in [0, 255]
22139function rgbToRgb(r, g, b){
22140 return {
22141 r: bound01(r, 255) * 255,
22142 g: bound01(g, 255) * 255,
22143 b: bound01(b, 255) * 255
22144 };
22145}
22146
22147// `rgbToHsl`
22148// Converts an RGB color value to HSL.
22149// *Assumes:* r, g, and b are contained in [0, 255] or [0, 1]
22150// *Returns:* { h, s, l } in [0,1]
22151function rgbToHsl(r, g, b) {
22152
22153 r = bound01(r, 255);
22154 g = bound01(g, 255);
22155 b = bound01(b, 255);
22156
22157 var max = mathMax(r, g, b), min = mathMin(r, g, b);
22158 var h, s, l = (max + min) / 2;
22159
22160 if(max == min) {
22161 h = s = 0; // achromatic
22162 }
22163 else {
22164 var d = max - min;
22165 s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
22166 switch(max) {
22167 case r: h = (g - b) / d + (g < b ? 6 : 0); break;
22168 case g: h = (b - r) / d + 2; break;
22169 case b: h = (r - g) / d + 4; break;
22170 }
22171
22172 h /= 6;
22173 }
22174
22175 return { h: h, s: s, l: l };
22176}
22177
22178// `hslToRgb`
22179// Converts an HSL color value to RGB.
22180// *Assumes:* h is contained in [0, 1] or [0, 360] and s and l are contained [0, 1] or [0, 100]
22181// *Returns:* { r, g, b } in the set [0, 255]
22182function hslToRgb(h, s, l) {
22183 var r, g, b;
22184
22185 h = bound01(h, 360);
22186 s = bound01(s, 100);
22187 l = bound01(l, 100);
22188
22189 function hue2rgb(p, q, t) {
22190 if(t < 0) t += 1;
22191 if(t > 1) t -= 1;
22192 if(t < 1/6) return p + (q - p) * 6 * t;
22193 if(t < 1/2) return q;
22194 if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
22195 return p;
22196 }
22197
22198 if(s === 0) {
22199 r = g = b = l; // achromatic
22200 }
22201 else {
22202 var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
22203 var p = 2 * l - q;
22204 r = hue2rgb(p, q, h + 1/3);
22205 g = hue2rgb(p, q, h);
22206 b = hue2rgb(p, q, h - 1/3);
22207 }
22208
22209 return { r: r * 255, g: g * 255, b: b * 255 };
22210}
22211
22212// `rgbToHsv`
22213// Converts an RGB color value to HSV
22214// *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1]
22215// *Returns:* { h, s, v } in [0,1]
22216function rgbToHsv(r, g, b) {
22217
22218 r = bound01(r, 255);
22219 g = bound01(g, 255);
22220 b = bound01(b, 255);
22221
22222 var max = mathMax(r, g, b), min = mathMin(r, g, b);
22223 var h, s, v = max;
22224
22225 var d = max - min;
22226 s = max === 0 ? 0 : d / max;
22227
22228 if(max == min) {
22229 h = 0; // achromatic
22230 }
22231 else {
22232 switch(max) {
22233 case r: h = (g - b) / d + (g < b ? 6 : 0); break;
22234 case g: h = (b - r) / d + 2; break;
22235 case b: h = (r - g) / d + 4; break;
22236 }
22237 h /= 6;
22238 }
22239 return { h: h, s: s, v: v };
22240}
22241
22242// `hsvToRgb`
22243// Converts an HSV color value to RGB.
22244// *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100]
22245// *Returns:* { r, g, b } in the set [0, 255]
22246 function hsvToRgb(h, s, v) {
22247
22248 h = bound01(h, 360) * 6;
22249 s = bound01(s, 100);
22250 v = bound01(v, 100);
22251
22252 var i = Math.floor(h),
22253 f = h - i,
22254 p = v * (1 - s),
22255 q = v * (1 - f * s),
22256 t = v * (1 - (1 - f) * s),
22257 mod = i % 6,
22258 r = [v, q, p, p, t, v][mod],
22259 g = [t, v, v, q, p, p][mod],
22260 b = [p, p, t, v, v, q][mod];
22261
22262 return { r: r * 255, g: g * 255, b: b * 255 };
22263}
22264
22265// `rgbToHex`
22266// Converts an RGB color to hex
22267// Assumes r, g, and b are contained in the set [0, 255]
22268// Returns a 3 or 6 character hex
22269function rgbToHex(r, g, b, allow3Char) {
22270
22271 var hex = [
22272 pad2(mathRound(r).toString(16)),
22273 pad2(mathRound(g).toString(16)),
22274 pad2(mathRound(b).toString(16))
22275 ];
22276
22277 // Return a 3 character hex if possible
22278 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)) {
22279 return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0);
22280 }
22281
22282 return hex.join("");
22283}
22284
22285// `rgbaToHex`
22286// Converts an RGBA color plus alpha transparency to hex
22287// Assumes r, g, b are contained in the set [0, 255] and
22288// a in [0, 1]. Returns a 4 or 8 character rgba hex
22289function rgbaToHex(r, g, b, a, allow4Char) {
22290
22291 var hex = [
22292 pad2(mathRound(r).toString(16)),
22293 pad2(mathRound(g).toString(16)),
22294 pad2(mathRound(b).toString(16)),
22295 pad2(convertDecimalToHex(a))
22296 ];
22297
22298 // Return a 4 character hex if possible
22299 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)) {
22300 return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0) + hex[3].charAt(0);
22301 }
22302
22303 return hex.join("");
22304}
22305
22306// `rgbaToArgbHex`
22307// Converts an RGBA color to an ARGB Hex8 string
22308// Rarely used, but required for "toFilter()"
22309function rgbaToArgbHex(r, g, b, a) {
22310
22311 var hex = [
22312 pad2(convertDecimalToHex(a)),
22313 pad2(mathRound(r).toString(16)),
22314 pad2(mathRound(g).toString(16)),
22315 pad2(mathRound(b).toString(16))
22316 ];
22317
22318 return hex.join("");
22319}
22320
22321// `equals`
22322// Can be called with any tinycolor input
22323tinycolor.equals = function (color1, color2) {
22324 if (!color1 || !color2) { return false; }
22325 return tinycolor(color1).toRgbString() == tinycolor(color2).toRgbString();
22326};
22327
22328tinycolor.random = function() {
22329 return tinycolor.fromRatio({
22330 r: mathRandom(),
22331 g: mathRandom(),
22332 b: mathRandom()
22333 });
22334};
22335
22336
22337// Modification Functions
22338// ----------------------
22339// Thanks to less.js for some of the basics here
22340// <https://github.com/cloudhead/less.js/blob/master/lib/less/functions.js>
22341
22342function desaturate(color, amount) {
22343 amount = (amount === 0) ? 0 : (amount || 10);
22344 var hsl = tinycolor(color).toHsl();
22345 hsl.s -= amount / 100;
22346 hsl.s = clamp01(hsl.s);
22347 return tinycolor(hsl);
22348}
22349
22350function saturate(color, amount) {
22351 amount = (amount === 0) ? 0 : (amount || 10);
22352 var hsl = tinycolor(color).toHsl();
22353 hsl.s += amount / 100;
22354 hsl.s = clamp01(hsl.s);
22355 return tinycolor(hsl);
22356}
22357
22358function greyscale(color) {
22359 return tinycolor(color).desaturate(100);
22360}
22361
22362function lighten (color, amount) {
22363 amount = (amount === 0) ? 0 : (amount || 10);
22364 var hsl = tinycolor(color).toHsl();
22365 hsl.l += amount / 100;
22366 hsl.l = clamp01(hsl.l);
22367 return tinycolor(hsl);
22368}
22369
22370function brighten(color, amount) {
22371 amount = (amount === 0) ? 0 : (amount || 10);
22372 var rgb = tinycolor(color).toRgb();
22373 rgb.r = mathMax(0, mathMin(255, rgb.r - mathRound(255 * - (amount / 100))));
22374 rgb.g = mathMax(0, mathMin(255, rgb.g - mathRound(255 * - (amount / 100))));
22375 rgb.b = mathMax(0, mathMin(255, rgb.b - mathRound(255 * - (amount / 100))));
22376 return tinycolor(rgb);
22377}
22378
22379function darken (color, amount) {
22380 amount = (amount === 0) ? 0 : (amount || 10);
22381 var hsl = tinycolor(color).toHsl();
22382 hsl.l -= amount / 100;
22383 hsl.l = clamp01(hsl.l);
22384 return tinycolor(hsl);
22385}
22386
22387// Spin takes a positive or negative amount within [-360, 360] indicating the change of hue.
22388// Values outside of this range will be wrapped into this range.
22389function spin(color, amount) {
22390 var hsl = tinycolor(color).toHsl();
22391 var hue = (hsl.h + amount) % 360;
22392 hsl.h = hue < 0 ? 360 + hue : hue;
22393 return tinycolor(hsl);
22394}
22395
22396// Combination Functions
22397// ---------------------
22398// Thanks to jQuery xColor for some of the ideas behind these
22399// <https://github.com/infusion/jQuery-xcolor/blob/master/jquery.xcolor.js>
22400
22401function complement(color) {
22402 var hsl = tinycolor(color).toHsl();
22403 hsl.h = (hsl.h + 180) % 360;
22404 return tinycolor(hsl);
22405}
22406
22407function triad(color) {
22408 var hsl = tinycolor(color).toHsl();
22409 var h = hsl.h;
22410 return [
22411 tinycolor(color),
22412 tinycolor({ h: (h + 120) % 360, s: hsl.s, l: hsl.l }),
22413 tinycolor({ h: (h + 240) % 360, s: hsl.s, l: hsl.l })
22414 ];
22415}
22416
22417function tetrad(color) {
22418 var hsl = tinycolor(color).toHsl();
22419 var h = hsl.h;
22420 return [
22421 tinycolor(color),
22422 tinycolor({ h: (h + 90) % 360, s: hsl.s, l: hsl.l }),
22423 tinycolor({ h: (h + 180) % 360, s: hsl.s, l: hsl.l }),
22424 tinycolor({ h: (h + 270) % 360, s: hsl.s, l: hsl.l })
22425 ];
22426}
22427
22428function splitcomplement(color) {
22429 var hsl = tinycolor(color).toHsl();
22430 var h = hsl.h;
22431 return [
22432 tinycolor(color),
22433 tinycolor({ h: (h + 72) % 360, s: hsl.s, l: hsl.l}),
22434 tinycolor({ h: (h + 216) % 360, s: hsl.s, l: hsl.l})
22435 ];
22436}
22437
22438function analogous(color, results, slices) {
22439 results = results || 6;
22440 slices = slices || 30;
22441
22442 var hsl = tinycolor(color).toHsl();
22443 var part = 360 / slices;
22444 var ret = [tinycolor(color)];
22445
22446 for (hsl.h = ((hsl.h - (part * results >> 1)) + 720) % 360; --results; ) {
22447 hsl.h = (hsl.h + part) % 360;
22448 ret.push(tinycolor(hsl));
22449 }
22450 return ret;
22451}
22452
22453function monochromatic(color, results) {
22454 results = results || 6;
22455 var hsv = tinycolor(color).toHsv();
22456 var h = hsv.h, s = hsv.s, v = hsv.v;
22457 var ret = [];
22458 var modification = 1 / results;
22459
22460 while (results--) {
22461 ret.push(tinycolor({ h: h, s: s, v: v}));
22462 v = (v + modification) % 1;
22463 }
22464
22465 return ret;
22466}
22467
22468// Utility Functions
22469// ---------------------
22470
22471tinycolor.mix = function(color1, color2, amount) {
22472 amount = (amount === 0) ? 0 : (amount || 50);
22473
22474 var rgb1 = tinycolor(color1).toRgb();
22475 var rgb2 = tinycolor(color2).toRgb();
22476
22477 var p = amount / 100;
22478
22479 var rgba = {
22480 r: ((rgb2.r - rgb1.r) * p) + rgb1.r,
22481 g: ((rgb2.g - rgb1.g) * p) + rgb1.g,
22482 b: ((rgb2.b - rgb1.b) * p) + rgb1.b,
22483 a: ((rgb2.a - rgb1.a) * p) + rgb1.a
22484 };
22485
22486 return tinycolor(rgba);
22487};
22488
22489
22490// Readability Functions
22491// ---------------------
22492// <http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef (WCAG Version 2)
22493
22494// `contrast`
22495// Analyze the 2 colors and returns the color contrast defined by (WCAG Version 2)
22496tinycolor.readability = function(color1, color2) {
22497 var c1 = tinycolor(color1);
22498 var c2 = tinycolor(color2);
22499 return (Math.max(c1.getLuminance(),c2.getLuminance())+0.05) / (Math.min(c1.getLuminance(),c2.getLuminance())+0.05);
22500};
22501
22502// `isReadable`
22503// Ensure that foreground and background color combinations meet WCAG2 guidelines.
22504// The third argument is an optional Object.
22505// the 'level' property states 'AA' or 'AAA' - if missing or invalid, it defaults to 'AA';
22506// the 'size' property states 'large' or 'small' - if missing or invalid, it defaults to 'small'.
22507// If the entire object is absent, isReadable defaults to {level:"AA",size:"small"}.
22508
22509// *Example*
22510// tinycolor.isReadable("#000", "#111") => false
22511// tinycolor.isReadable("#000", "#111",{level:"AA",size:"large"}) => false
22512tinycolor.isReadable = function(color1, color2, wcag2) {
22513 var readability = tinycolor.readability(color1, color2);
22514 var wcag2Parms, out;
22515
22516 out = false;
22517
22518 wcag2Parms = validateWCAG2Parms(wcag2);
22519 switch (wcag2Parms.level + wcag2Parms.size) {
22520 case "AAsmall":
22521 case "AAAlarge":
22522 out = readability >= 4.5;
22523 break;
22524 case "AAlarge":
22525 out = readability >= 3;
22526 break;
22527 case "AAAsmall":
22528 out = readability >= 7;
22529 break;
22530 }
22531 return out;
22532
22533};
22534
22535// `mostReadable`
22536// Given a base color and a list of possible foreground or background
22537// colors for that base, returns the most readable color.
22538// Optionally returns Black or White if the most readable color is unreadable.
22539// *Example*
22540// tinycolor.mostReadable(tinycolor.mostReadable("#123", ["#124", "#125"],{includeFallbackColors:false}).toHexString(); // "#112255"
22541// tinycolor.mostReadable(tinycolor.mostReadable("#123", ["#124", "#125"],{includeFallbackColors:true}).toHexString(); // "#ffffff"
22542// tinycolor.mostReadable("#a8015a", ["#faf3f3"],{includeFallbackColors:true,level:"AAA",size:"large"}).toHexString(); // "#faf3f3"
22543// tinycolor.mostReadable("#a8015a", ["#faf3f3"],{includeFallbackColors:true,level:"AAA",size:"small"}).toHexString(); // "#ffffff"
22544tinycolor.mostReadable = function(baseColor, colorList, args) {
22545 var bestColor = null;
22546 var bestScore = 0;
22547 var readability;
22548 var includeFallbackColors, level, size ;
22549 args = args || {};
22550 includeFallbackColors = args.includeFallbackColors ;
22551 level = args.level;
22552 size = args.size;
22553
22554 for (var i= 0; i < colorList.length ; i++) {
22555 readability = tinycolor.readability(baseColor, colorList[i]);
22556 if (readability > bestScore) {
22557 bestScore = readability;
22558 bestColor = tinycolor(colorList[i]);
22559 }
22560 }
22561
22562 if (tinycolor.isReadable(baseColor, bestColor, {"level":level,"size":size}) || !includeFallbackColors) {
22563 return bestColor;
22564 }
22565 else {
22566 args.includeFallbackColors=false;
22567 return tinycolor.mostReadable(baseColor,["#fff", "#000"],args);
22568 }
22569};
22570
22571
22572// Big List of Colors
22573// ------------------
22574// <http://www.w3.org/TR/css3-color/#svg-color>
22575var names = tinycolor.names = {
22576 aliceblue: "f0f8ff",
22577 antiquewhite: "faebd7",
22578 aqua: "0ff",
22579 aquamarine: "7fffd4",
22580 azure: "f0ffff",
22581 beige: "f5f5dc",
22582 bisque: "ffe4c4",
22583 black: "000",
22584 blanchedalmond: "ffebcd",
22585 blue: "00f",
22586 blueviolet: "8a2be2",
22587 brown: "a52a2a",
22588 burlywood: "deb887",
22589 burntsienna: "ea7e5d",
22590 cadetblue: "5f9ea0",
22591 chartreuse: "7fff00",
22592 chocolate: "d2691e",
22593 coral: "ff7f50",
22594 cornflowerblue: "6495ed",
22595 cornsilk: "fff8dc",
22596 crimson: "dc143c",
22597 cyan: "0ff",
22598 darkblue: "00008b",
22599 darkcyan: "008b8b",
22600 darkgoldenrod: "b8860b",
22601 darkgray: "a9a9a9",
22602 darkgreen: "006400",
22603 darkgrey: "a9a9a9",
22604 darkkhaki: "bdb76b",
22605 darkmagenta: "8b008b",
22606 darkolivegreen: "556b2f",
22607 darkorange: "ff8c00",
22608 darkorchid: "9932cc",
22609 darkred: "8b0000",
22610 darksalmon: "e9967a",
22611 darkseagreen: "8fbc8f",
22612 darkslateblue: "483d8b",
22613 darkslategray: "2f4f4f",
22614 darkslategrey: "2f4f4f",
22615 darkturquoise: "00ced1",
22616 darkviolet: "9400d3",
22617 deeppink: "ff1493",
22618 deepskyblue: "00bfff",
22619 dimgray: "696969",
22620 dimgrey: "696969",
22621 dodgerblue: "1e90ff",
22622 firebrick: "b22222",
22623 floralwhite: "fffaf0",
22624 forestgreen: "228b22",
22625 fuchsia: "f0f",
22626 gainsboro: "dcdcdc",
22627 ghostwhite: "f8f8ff",
22628 gold: "ffd700",
22629 goldenrod: "daa520",
22630 gray: "808080",
22631 green: "008000",
22632 greenyellow: "adff2f",
22633 grey: "808080",
22634 honeydew: "f0fff0",
22635 hotpink: "ff69b4",
22636 indianred: "cd5c5c",
22637 indigo: "4b0082",
22638 ivory: "fffff0",
22639 khaki: "f0e68c",
22640 lavender: "e6e6fa",
22641 lavenderblush: "fff0f5",
22642 lawngreen: "7cfc00",
22643 lemonchiffon: "fffacd",
22644 lightblue: "add8e6",
22645 lightcoral: "f08080",
22646 lightcyan: "e0ffff",
22647 lightgoldenrodyellow: "fafad2",
22648 lightgray: "d3d3d3",
22649 lightgreen: "90ee90",
22650 lightgrey: "d3d3d3",
22651 lightpink: "ffb6c1",
22652 lightsalmon: "ffa07a",
22653 lightseagreen: "20b2aa",
22654 lightskyblue: "87cefa",
22655 lightslategray: "789",
22656 lightslategrey: "789",
22657 lightsteelblue: "b0c4de",
22658 lightyellow: "ffffe0",
22659 lime: "0f0",
22660 limegreen: "32cd32",
22661 linen: "faf0e6",
22662 magenta: "f0f",
22663 maroon: "800000",
22664 mediumaquamarine: "66cdaa",
22665 mediumblue: "0000cd",
22666 mediumorchid: "ba55d3",
22667 mediumpurple: "9370db",
22668 mediumseagreen: "3cb371",
22669 mediumslateblue: "7b68ee",
22670 mediumspringgreen: "00fa9a",
22671 mediumturquoise: "48d1cc",
22672 mediumvioletred: "c71585",
22673 midnightblue: "191970",
22674 mintcream: "f5fffa",
22675 mistyrose: "ffe4e1",
22676 moccasin: "ffe4b5",
22677 navajowhite: "ffdead",
22678 navy: "000080",
22679 oldlace: "fdf5e6",
22680 olive: "808000",
22681 olivedrab: "6b8e23",
22682 orange: "ffa500",
22683 orangered: "ff4500",
22684 orchid: "da70d6",
22685 palegoldenrod: "eee8aa",
22686 palegreen: "98fb98",
22687 paleturquoise: "afeeee",
22688 palevioletred: "db7093",
22689 papayawhip: "ffefd5",
22690 peachpuff: "ffdab9",
22691 peru: "cd853f",
22692 pink: "ffc0cb",
22693 plum: "dda0dd",
22694 powderblue: "b0e0e6",
22695 purple: "800080",
22696 rebeccapurple: "663399",
22697 red: "f00",
22698 rosybrown: "bc8f8f",
22699 royalblue: "4169e1",
22700 saddlebrown: "8b4513",
22701 salmon: "fa8072",
22702 sandybrown: "f4a460",
22703 seagreen: "2e8b57",
22704 seashell: "fff5ee",
22705 sienna: "a0522d",
22706 silver: "c0c0c0",
22707 skyblue: "87ceeb",
22708 slateblue: "6a5acd",
22709 slategray: "708090",
22710 slategrey: "708090",
22711 snow: "fffafa",
22712 springgreen: "00ff7f",
22713 steelblue: "4682b4",
22714 tan: "d2b48c",
22715 teal: "008080",
22716 thistle: "d8bfd8",
22717 tomato: "ff6347",
22718 turquoise: "40e0d0",
22719 violet: "ee82ee",
22720 wheat: "f5deb3",
22721 white: "fff",
22722 whitesmoke: "f5f5f5",
22723 yellow: "ff0",
22724 yellowgreen: "9acd32"
22725};
22726
22727// Make it easy to access colors via `hexNames[hex]`
22728var hexNames = tinycolor.hexNames = flip(names);
22729
22730
22731// Utilities
22732// ---------
22733
22734// `{ 'name1': 'val1' }` becomes `{ 'val1': 'name1' }`
22735function flip(o) {
22736 var flipped = { };
22737 for (var i in o) {
22738 if (o.hasOwnProperty(i)) {
22739 flipped[o[i]] = i;
22740 }
22741 }
22742 return flipped;
22743}
22744
22745// Return a valid alpha value [0,1] with all invalid values being set to 1
22746function boundAlpha(a) {
22747 a = parseFloat(a);
22748
22749 if (isNaN(a) || a < 0 || a > 1) {
22750 a = 1;
22751 }
22752
22753 return a;
22754}
22755
22756// Take input from [0, n] and return it as [0, 1]
22757function bound01(n, max) {
22758 if (isOnePointZero(n)) { n = "100%"; }
22759
22760 var processPercent = isPercentage(n);
22761 n = mathMin(max, mathMax(0, parseFloat(n)));
22762
22763 // Automatically convert percentage into number
22764 if (processPercent) {
22765 n = parseInt(n * max, 10) / 100;
22766 }
22767
22768 // Handle floating point rounding errors
22769 if ((Math.abs(n - max) < 0.000001)) {
22770 return 1;
22771 }
22772
22773 // Convert into [0, 1] range if it isn't already
22774 return (n % max) / parseFloat(max);
22775}
22776
22777// Force a number between 0 and 1
22778function clamp01(val) {
22779 return mathMin(1, mathMax(0, val));
22780}
22781
22782// Parse a base-16 hex value into a base-10 integer
22783function parseIntFromHex(val) {
22784 return parseInt(val, 16);
22785}
22786
22787// Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1
22788// <http://stackoverflow.com/questions/7422072/javascript-how-to-detect-number-as-a-decimal-including-1-0>
22789function isOnePointZero(n) {
22790 return typeof n == "string" && n.indexOf('.') != -1 && parseFloat(n) === 1;
22791}
22792
22793// Check to see if string passed in is a percentage
22794function isPercentage(n) {
22795 return typeof n === "string" && n.indexOf('%') != -1;
22796}
22797
22798// Force a hex value to have 2 characters
22799function pad2(c) {
22800 return c.length == 1 ? '0' + c : '' + c;
22801}
22802
22803// Replace a decimal with it's percentage value
22804function convertToPercentage(n) {
22805 if (n <= 1) {
22806 n = (n * 100) + "%";
22807 }
22808
22809 return n;
22810}
22811
22812// Converts a decimal to a hex value
22813function convertDecimalToHex(d) {
22814 return Math.round(parseFloat(d) * 255).toString(16);
22815}
22816// Converts a hex value to a decimal
22817function convertHexToDecimal(h) {
22818 return (parseIntFromHex(h) / 255);
22819}
22820
22821var matchers = (function() {
22822
22823 // <http://www.w3.org/TR/css3-values/#integers>
22824 var CSS_INTEGER = "[-\\+]?\\d+%?";
22825
22826 // <http://www.w3.org/TR/css3-values/#number-value>
22827 var CSS_NUMBER = "[-\\+]?\\d*\\.\\d+%?";
22828
22829 // Allow positive/negative integer/number. Don't capture the either/or, just the entire outcome.
22830 var CSS_UNIT = "(?:" + CSS_NUMBER + ")|(?:" + CSS_INTEGER + ")";
22831
22832 // Actual matching.
22833 // Parentheses and commas are optional, but not required.
22834 // Whitespace can take the place of commas or opening paren
22835 var PERMISSIVE_MATCH3 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?";
22836 var PERMISSIVE_MATCH4 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?";
22837
22838 return {
22839 CSS_UNIT: new RegExp(CSS_UNIT),
22840 rgb: new RegExp("rgb" + PERMISSIVE_MATCH3),
22841 rgba: new RegExp("rgba" + PERMISSIVE_MATCH4),
22842 hsl: new RegExp("hsl" + PERMISSIVE_MATCH3),
22843 hsla: new RegExp("hsla" + PERMISSIVE_MATCH4),
22844 hsv: new RegExp("hsv" + PERMISSIVE_MATCH3),
22845 hsva: new RegExp("hsva" + PERMISSIVE_MATCH4),
22846 hex3: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
22847 hex6: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,
22848 hex4: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
22849 hex8: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/
22850 };
22851})();
22852
22853// `isValidCSSUnit`
22854// Take in a single string / number and check to see if it looks like a CSS unit
22855// (see `matchers` above for definition).
22856function isValidCSSUnit(color) {
22857 return !!matchers.CSS_UNIT.exec(color);
22858}
22859
22860// `stringInputToObject`
22861// Permissive string parsing. Take in a number of formats, and output an object
22862// based on detected format. Returns `{ r, g, b }` or `{ h, s, l }` or `{ h, s, v}`
22863function stringInputToObject(color) {
22864
22865 color = color.replace(trimLeft,'').replace(trimRight, '').toLowerCase();
22866 var named = false;
22867 if (names[color]) {
22868 color = names[color];
22869 named = true;
22870 }
22871 else if (color == 'transparent') {
22872 return { r: 0, g: 0, b: 0, a: 0, format: "name" };
22873 }
22874
22875 // Try to match string input using regular expressions.
22876 // Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360]
22877 // Just return an object and let the conversion functions handle that.
22878 // This way the result will be the same whether the tinycolor is initialized with string or object.
22879 var match;
22880 if ((match = matchers.rgb.exec(color))) {
22881 return { r: match[1], g: match[2], b: match[3] };
22882 }
22883 if ((match = matchers.rgba.exec(color))) {
22884 return { r: match[1], g: match[2], b: match[3], a: match[4] };
22885 }
22886 if ((match = matchers.hsl.exec(color))) {
22887 return { h: match[1], s: match[2], l: match[3] };
22888 }
22889 if ((match = matchers.hsla.exec(color))) {
22890 return { h: match[1], s: match[2], l: match[3], a: match[4] };
22891 }
22892 if ((match = matchers.hsv.exec(color))) {
22893 return { h: match[1], s: match[2], v: match[3] };
22894 }
22895 if ((match = matchers.hsva.exec(color))) {
22896 return { h: match[1], s: match[2], v: match[3], a: match[4] };
22897 }
22898 if ((match = matchers.hex8.exec(color))) {
22899 return {
22900 r: parseIntFromHex(match[1]),
22901 g: parseIntFromHex(match[2]),
22902 b: parseIntFromHex(match[3]),
22903 a: convertHexToDecimal(match[4]),
22904 format: named ? "name" : "hex8"
22905 };
22906 }
22907 if ((match = matchers.hex6.exec(color))) {
22908 return {
22909 r: parseIntFromHex(match[1]),
22910 g: parseIntFromHex(match[2]),
22911 b: parseIntFromHex(match[3]),
22912 format: named ? "name" : "hex"
22913 };
22914 }
22915 if ((match = matchers.hex4.exec(color))) {
22916 return {
22917 r: parseIntFromHex(match[1] + '' + match[1]),
22918 g: parseIntFromHex(match[2] + '' + match[2]),
22919 b: parseIntFromHex(match[3] + '' + match[3]),
22920 a: convertHexToDecimal(match[4] + '' + match[4]),
22921 format: named ? "name" : "hex8"
22922 };
22923 }
22924 if ((match = matchers.hex3.exec(color))) {
22925 return {
22926 r: parseIntFromHex(match[1] + '' + match[1]),
22927 g: parseIntFromHex(match[2] + '' + match[2]),
22928 b: parseIntFromHex(match[3] + '' + match[3]),
22929 format: named ? "name" : "hex"
22930 };
22931 }
22932
22933 return false;
22934}
22935
22936function validateWCAG2Parms(parms) {
22937 // return valid WCAG2 parms for isReadable.
22938 // If input parms are invalid, return {"level":"AA", "size":"small"}
22939 var level, size;
22940 parms = parms || {"level":"AA", "size":"small"};
22941 level = (parms.level || "AA").toUpperCase();
22942 size = (parms.size || "small").toLowerCase();
22943 if (level !== "AA" && level !== "AAA") {
22944 level = "AA";
22945 }
22946 if (size !== "small" && size !== "large") {
22947 size = "small";
22948 }
22949 return {"level":level, "size":size};
22950}
22951
22952// Node: Export function
22953if (typeof module !== "undefined" && module.exports) {
22954 module.exports = tinycolor;
22955}
22956// AMD/requirejs: Define the module
22957else if (typeof define === 'function' && define.amd) {
22958 define(function () {return tinycolor;});
22959}
22960// Browser: Expose to window
22961else {
22962 window.tinycolor = tinycolor;
22963}
22964
22965})(Math);
22966
22967},{}],122:[function(_dereq_,module,exports){
22968(function (global){(function (){
22969
22970/**
22971 * Module exports.
22972 */
22973
22974module.exports = deprecate;
22975
22976/**
22977 * Mark that a method should not be used.
22978 * Returns a modified function which warns once by default.
22979 *
22980 * If `localStorage.noDeprecation = true` is set, then it is a no-op.
22981 *
22982 * If `localStorage.throwDeprecation = true` is set, then deprecated functions
22983 * will throw an Error when invoked.
22984 *
22985 * If `localStorage.traceDeprecation = true` is set, then deprecated functions
22986 * will invoke `console.trace()` instead of `console.error()`.
22987 *
22988 * @param {Function} fn - the function to deprecate
22989 * @param {String} msg - the string to print to the console when `fn` is invoked
22990 * @returns {Function} a new "deprecated" version of `fn`
22991 * @api public
22992 */
22993
22994function deprecate (fn, msg) {
22995 if (config('noDeprecation')) {
22996 return fn;
22997 }
22998
22999 var warned = false;
23000 function deprecated() {
23001 if (!warned) {
23002 if (config('throwDeprecation')) {
23003 throw new Error(msg);
23004 } else if (config('traceDeprecation')) {
23005 console.trace(msg);
23006 } else {
23007 console.warn(msg);
23008 }
23009 warned = true;
23010 }
23011 return fn.apply(this, arguments);
23012 }
23013
23014 return deprecated;
23015}
23016
23017/**
23018 * Checks `localStorage` for boolean values for the given `name`.
23019 *
23020 * @param {String} name
23021 * @returns {Boolean}
23022 * @api private
23023 */
23024
23025function config (name) {
23026 // accessing global.localStorage can trigger a DOMException in sandboxed iframes
23027 try {
23028 if (!global.localStorage) return false;
23029 } catch (_) {
23030 return false;
23031 }
23032 var val = global.localStorage[name];
23033 if (null == val) return false;
23034 return String(val).toLowerCase() === 'true';
23035}
23036
23037}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
23038},{}],123:[function(_dereq_,module,exports){
23039/*
23040 * World Calendars
23041 * https://github.com/alexcjohnson/world-calendars
23042 *
23043 * Batch-converted from kbwood/calendars
23044 * Many thanks to Keith Wood and all of the contributors to the original project!
23045 *
23046 * This source code is licensed under the MIT license found in the
23047 * LICENSE file in the root directory of this source tree.
23048 */
23049
23050/* http://keith-wood.name/calendars.html
23051 Traditional Chinese calendar for jQuery v2.0.2.
23052 Written by Nicolas Riesco (enquiries@nicolasriesco.net) December 2016.
23053 Available under the MIT (http://keith-wood.name/licence.html) license.
23054 Please attribute the author if you use it. */
23055
23056var main = _dereq_('../main');
23057var assign = _dereq_('object-assign');
23058
23059
23060var gregorianCalendar = main.instance();
23061
23062/** Implementation of the traditional Chinese calendar.
23063 Source of calendar tables https://github.com/isee15/Lunar-Solar-Calendar-Converter .
23064 @class ChineseCalendar
23065 @param [language=''] {string} The language code (default English) for localisation. */
23066function ChineseCalendar(language) {
23067 this.local = this.regionalOptions[language || ''] || this.regionalOptions[''];
23068}
23069
23070ChineseCalendar.prototype = new main.baseCalendar;
23071
23072assign(ChineseCalendar.prototype, {
23073 /** The calendar name.
23074 @memberof ChineseCalendar */
23075 name: 'Chinese',
23076 /** Julian date of start of Gregorian epoch: 1 January 0001 CE.
23077 @memberof GregorianCalendar */
23078 jdEpoch: 1721425.5,
23079 /** <code>true</code> if has a year zero, <code>false</code> if not.
23080 @memberof ChineseCalendar */
23081 hasYearZero: false,
23082 /** The minimum month number.
23083 This calendar uses month indices to account for intercalary months.
23084 @memberof ChineseCalendar */
23085 minMonth: 0,
23086 /** The first month in the year.
23087 This calendar uses month indices to account for intercalary months.
23088 @memberof ChineseCalendar */
23089 firstMonth: 0,
23090 /** The minimum day number.
23091 @memberof ChineseCalendar */
23092 minDay: 1,
23093
23094 /** Localisations for the plugin.
23095 Entries are objects indexed by the language code ('' being the default US/English).
23096 Each object has the following attributes.
23097 @memberof ChineseCalendar
23098 @property name {string} The calendar name.
23099 @property epochs {string[]} The epoch names.
23100 @property monthNames {string[]} The long names of the months of the year.
23101 @property monthNamesShort {string[]} The short names of the months of the year.
23102 @property dayNames {string[]} The long names of the days of the week.
23103 @property dayNamesShort {string[]} The short names of the days of the week.
23104 @property dayNamesMin {string[]} The minimal names of the days of the week.
23105 @property dateFormat {string} The date format for this calendar.
23106 See the options on <a href="BaseCalendar.html#formatDate"><code>formatDate</code></a> for details.
23107 @property firstDay {number} The number of the first day of the week, starting at 0.
23108 @property isRTL {number} <code>true</code> if this localisation reads right-to-left. */
23109 regionalOptions: { // Localisations
23110 '': {
23111 name: 'Chinese',
23112 epochs: ['BEC', 'EC'],
23113 monthNumbers: function(date, padded) {
23114 if (typeof date === 'string') {
23115 var match = date.match(MONTH_NUMBER_REGEXP);
23116 return (match) ? match[0] : '';
23117 }
23118
23119 var year = this._validateYear(date);
23120 var monthIndex = date.month();
23121
23122 var month = '' + this.toChineseMonth(year, monthIndex);
23123
23124 if (padded && month.length < 2) {
23125 month = "0" + month;
23126 }
23127
23128 if (this.isIntercalaryMonth(year, monthIndex)) {
23129 month += 'i';
23130 }
23131
23132 return month;
23133 },
23134 monthNames: function(date) {
23135 if (typeof date === 'string') {
23136 var match = date.match(MONTH_NAME_REGEXP);
23137 return (match) ? match[0] : '';
23138 }
23139
23140 var year = this._validateYear(date);
23141 var monthIndex = date.month();
23142
23143 var month = this.toChineseMonth(year, monthIndex);
23144
23145 var monthName = ['一月','二月','三月','四月','五月','六月',
23146 '七月','八月','九月','十月','十一月','十二月'][month - 1];
23147
23148 if (this.isIntercalaryMonth(year, monthIndex)) {
23149 monthName = '闰' + monthName;
23150 }
23151
23152 return monthName;
23153 },
23154 monthNamesShort: function(date) {
23155 if (typeof date === 'string') {
23156 var match = date.match(MONTH_SHORT_NAME_REGEXP);
23157 return (match) ? match[0] : '';
23158 }
23159
23160 var year = this._validateYear(date);
23161 var monthIndex = date.month();
23162
23163 var month = this.toChineseMonth(year, monthIndex);
23164
23165 var monthName = ['一','二','三','四','五','六',
23166 '七','八','九','十','十一','十二'][month - 1];
23167
23168 if (this.isIntercalaryMonth(year, monthIndex)) {
23169 monthName = '闰' + monthName;
23170 }
23171
23172 return monthName;
23173 },
23174 parseMonth: function(year, monthString) {
23175 year = this._validateYear(year);
23176 var month = parseInt(monthString);
23177 var isIntercalary;
23178
23179 if (!isNaN(month)) {
23180 var i = monthString[monthString.length - 1];
23181 isIntercalary = (i === 'i' || i === 'I');
23182 } else {
23183 if (monthString[0] === '闰') {
23184 isIntercalary = true;
23185 monthString = monthString.substring(1);
23186 }
23187 if (monthString[monthString.length - 1] === '月') {
23188 monthString = monthString.substring(0, monthString.length - 1);
23189 }
23190 month = 1 +
23191 ['一','二','三','四','五','六',
23192 '七','八','九','十','十一','十二'].indexOf(monthString);
23193 }
23194
23195 var monthIndex = this.toMonthIndex(year, month, isIntercalary);
23196 return monthIndex;
23197 },
23198 dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
23199 dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
23200 dayNamesMin: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
23201 digits: null,
23202 dateFormat: 'yyyy/mm/dd',
23203 firstDay: 1,
23204 isRTL: false
23205 }
23206 },
23207
23208 /** Check that a candidate date is from the same calendar and is valid.
23209 @memberof BaseCalendar
23210 @private
23211 @param year {CDate|number} The date or the year to validate.
23212 @param error {string} Error message if invalid.
23213 @return {number} The year.
23214 @throws Error if year out of range. */
23215 _validateYear: function(year, error) {
23216 if (year.year) {
23217 year = year.year();
23218 }
23219
23220 if (typeof year !== 'number' || year < 1888 || year > 2111) {
23221 throw error.replace(/\{0\}/, this.local.name);
23222 }
23223
23224 return year;
23225 },
23226
23227 /** Retrieve the month index (i.e. accounting for intercalary months).
23228 @memberof ChineseCalendar
23229 @param year {number} The year.
23230 @param month {number} The month (1 for first month).
23231 @param [isIntercalary=false] {boolean} If month is intercalary.
23232 @return {number} The month index (0 for first month).
23233 @throws Error if an invalid month/year or a different calendar used. */
23234 toMonthIndex: function(year, month, isIntercalary) {
23235 // compute intercalary month in the year (0 if none)
23236 var intercalaryMonth = this.intercalaryMonth(year);
23237
23238 // validate month
23239 var invalidIntercalaryMonth =
23240 (isIntercalary && month !== intercalaryMonth);
23241 if (invalidIntercalaryMonth || month < 1 || month > 12) {
23242 throw main.local.invalidMonth
23243 .replace(/\{0\}/, this.local.name);
23244 }
23245
23246 // compute month index
23247 var monthIndex;
23248
23249 if (!intercalaryMonth) {
23250 monthIndex = month - 1;
23251 } else if(!isIntercalary && month <= intercalaryMonth) {
23252 monthIndex = month - 1;
23253 } else {
23254 monthIndex = month;
23255 }
23256
23257 return monthIndex;
23258 },
23259
23260 /** Retrieve the month (i.e. accounting for intercalary months).
23261 @memberof ChineseCalendar
23262 @param year {CDate|number} The date or the year to examine.
23263 @param monthIndex {number} The month index (0 for first month).
23264 @return {number} The month (1 for first month).
23265 @throws Error if an invalid month/year or a different calendar used. */
23266 toChineseMonth: function(year, monthIndex) {
23267 if (year.year) {
23268 year = year.year();
23269 monthIndex = year.month();
23270 }
23271
23272 // compute intercalary month in the year (0 if none)
23273 var intercalaryMonth = this.intercalaryMonth(year);
23274
23275 // validate month
23276 var maxMonthIndex = (intercalaryMonth) ? 12 : 11;
23277 if (monthIndex < 0 || monthIndex > maxMonthIndex) {
23278 throw main.local.invalidMonth
23279 .replace(/\{0\}/, this.local.name);
23280 }
23281
23282 // compute Chinese month
23283 var month;
23284
23285 if (!intercalaryMonth) {
23286 month = monthIndex + 1;
23287 } else if(monthIndex < intercalaryMonth) {
23288 month = monthIndex + 1;
23289 } else {
23290 month = monthIndex;
23291 }
23292
23293 return month;
23294 },
23295
23296 /** Determine the intercalary month of a year (if any).
23297 @memberof ChineseCalendar
23298 @param year {CDate|number} The date to examine or the year to examine.
23299 @return {number} The intercalary month number, or 0 if none.
23300 @throws Error if an invalid year or a different calendar used. */
23301 intercalaryMonth: function(year) {
23302 year = this._validateYear(year);
23303
23304 var monthDaysTable = LUNAR_MONTH_DAYS[year - LUNAR_MONTH_DAYS[0]];
23305 var intercalaryMonth = monthDaysTable >> 13;
23306
23307 return intercalaryMonth;
23308 },
23309
23310 /** Determine whether this date is an intercalary month.
23311 @memberof ChineseCalendar
23312 @param year {CDate|number} The date to examine or the year to examine.
23313 @param [monthIndex] {number} The month index to examine.
23314 @return {boolean} <code>true</code> if this is an intercalary month, <code>false</code> if not.
23315 @throws Error if an invalid year or a different calendar used. */
23316 isIntercalaryMonth: function(year, monthIndex) {
23317 if (year.year) {
23318 year = year.year();
23319 monthIndex = year.month();
23320 }
23321
23322 var intercalaryMonth = this.intercalaryMonth(year);
23323
23324 return !!intercalaryMonth && intercalaryMonth === monthIndex;
23325 },
23326
23327 /** Determine whether this date is in a leap year.
23328 @memberof ChineseCalendar
23329 @param year {CDate|number} The date to examine or the year to examine.
23330 @return {boolean} <code>true</code> if this is a leap year, <code>false</code> if not.
23331 @throws Error if an invalid year or a different calendar used. */
23332 leapYear: function(year) {
23333 return (this.intercalaryMonth(year) !== 0);
23334 },
23335
23336 /** Determine the week of the year for a date - ISO 8601.
23337 @memberof ChineseCalendar
23338 @param year {CDate|number} The date to examine or the year to examine.
23339 @param [monthIndex] {number} The month index to examine.
23340 @param [day] {number} The day to examine.
23341 @return {number} The week of the year.
23342 @throws Error if an invalid date or a different calendar used. */
23343 weekOfYear: function(year, monthIndex, day) {
23344 // compute Chinese new year
23345 var validatedYear =
23346 this._validateYear(year, main.local.invalidyear);
23347 var packedDate =
23348 CHINESE_NEW_YEAR[validatedYear - CHINESE_NEW_YEAR[0]];
23349
23350 var y = (packedDate >> 9) & 0xFFF;
23351 var m = (packedDate >> 5) & 0x0F;
23352 var d = packedDate & 0x1F;
23353
23354 // find first Thrusday of the year
23355 var firstThursday;
23356 firstThursday = gregorianCalendar.newDate(y, m, d);
23357 firstThursday.add(4 - (firstThursday.dayOfWeek() || 7), 'd');
23358
23359 // compute days from first Thursday
23360 var offset =
23361 this.toJD(year, monthIndex, day) - firstThursday.toJD();
23362 return 1 + Math.floor(offset / 7);
23363 },
23364
23365 /** Retrieve the number of months in a year.
23366 @memberof ChineseCalendar
23367 @param year {CDate|number} The date to examine or the year to examine.
23368 @return {number} The number of months.
23369 @throws Error if an invalid year or a different calendar used. */
23370 monthsInYear: function(year) {
23371 return (this.leapYear(year)) ? 13 : 12;
23372 },
23373
23374 /** Retrieve the number of days in a month.
23375 @memberof ChineseCalendar
23376 @param year {CDate|number} The date to examine or the year of the month.
23377 @param [monthIndex] {number} The month index.
23378 @return {number} The number of days in this month.
23379 @throws Error if an invalid month/year or a different calendar used. */
23380 daysInMonth: function(year, monthIndex) {
23381 if (year.year) {
23382 monthIndex = year.month();
23383 year = year.year();
23384 }
23385
23386 year = this._validateYear(year);
23387
23388 var monthDaysTable = LUNAR_MONTH_DAYS[year - LUNAR_MONTH_DAYS[0]];
23389
23390 var intercalaryMonth = monthDaysTable >> 13;
23391 var maxMonthIndex = (intercalaryMonth) ? 12 : 11;
23392 if (monthIndex > maxMonthIndex) {
23393 throw main.local.invalidMonth
23394 .replace(/\{0\}/, this.local.name);
23395 }
23396
23397 var daysInMonth = (monthDaysTable & (1 << (12 - monthIndex))) ?
23398 30 : 29;
23399
23400 return daysInMonth;
23401 },
23402
23403 /** Determine whether this date is a week day.
23404 @memberof ChineseCalendar
23405 @param year {CDate|number} The date to examine or the year to examine.
23406 @param [monthIndex] {number} The month index to examine.
23407 @param [day] {number} The day to examine.
23408 @return {boolean} <code>true</code> if a week day, <code>false</code> if not.
23409 @throws Error if an invalid date or a different calendar used. */
23410 weekDay: function(year, monthIndex, day) {
23411 return (this.dayOfWeek(year, monthIndex, day) || 7) < 6;
23412 },
23413
23414 /** Retrieve the Julian date equivalent for this date,
23415 i.e. days since January 1, 4713 BCE Greenwich noon.
23416 @memberof ChineseCalendar
23417 @param year {CDate|number} The date to convert or the year to convert.
23418 @param [monthIndex] {number} The month index to convert.
23419 @param [day] {number} The day to convert.
23420 @return {number} The equivalent Julian date.
23421 @throws Error if an invalid date or a different calendar used. */
23422 toJD: function(year, monthIndex, day) {
23423 var date = this._validate(year, month, day, main.local.invalidDate);
23424 year = this._validateYear(date.year());
23425 monthIndex = date.month();
23426 day = date.day();
23427
23428 var isIntercalary = this.isIntercalaryMonth(year, monthIndex);
23429 var month = this.toChineseMonth(year, monthIndex);
23430
23431 var solar = toSolar(year, month, day, isIntercalary);
23432
23433 return gregorianCalendar.toJD(solar.year, solar.month, solar.day);
23434 },
23435
23436 /** Create a new date from a Julian date.
23437 @memberof ChineseCalendar
23438 @param jd {number} The Julian date to convert.
23439 @return {CDate} The equivalent date. */
23440 fromJD: function(jd) {
23441 var date = gregorianCalendar.fromJD(jd);
23442 var lunar = toLunar(date.year(), date.month(), date.day());
23443 var monthIndex = this.toMonthIndex(
23444 lunar.year, lunar.month, lunar.isIntercalary);
23445 return this.newDate(lunar.year, monthIndex, lunar.day);
23446 },
23447
23448 /** Create a new date from a string.
23449 @memberof ChineseCalendar
23450 @param dateString {string} String representing a Chinese date
23451 @return {CDate} The new date.
23452 @throws Error if an invalid date. */
23453 fromString: function(dateString) {
23454 var match = dateString.match(DATE_REGEXP);
23455
23456 var year = this._validateYear(+match[1]);
23457
23458 var month = +match[2];
23459 var isIntercalary = !!match[3];
23460 var monthIndex = this.toMonthIndex(year, month, isIntercalary);
23461
23462 var day = +match[4];
23463
23464 return this.newDate(year, monthIndex, day);
23465 },
23466
23467 /** Add period(s) to a date.
23468 Cater for no year zero.
23469 @memberof ChineseCalendar
23470 @param date {CDate} The starting date.
23471 @param offset {number} The number of periods to adjust by.
23472 @param period {string} One of 'y' for year, 'm' for month, 'w' for week, 'd' for day.
23473 @return {CDate} The updated date.
23474 @throws Error if a different calendar used. */
23475 add: function(date, offset, period) {
23476 var year = date.year();
23477 var monthIndex = date.month();
23478 var isIntercalary = this.isIntercalaryMonth(year, monthIndex);
23479 var month = this.toChineseMonth(year, monthIndex);
23480
23481 var cdate = Object.getPrototypeOf(ChineseCalendar.prototype)
23482 .add.call(this, date, offset, period);
23483
23484 if (period === 'y') {
23485 // Resync month
23486 var resultYear = cdate.year();
23487 var resultMonthIndex = cdate.month();
23488
23489 // Using the fact the month index of an intercalary month
23490 // equals its month number:
23491 var resultCanBeIntercalaryMonth =
23492 this.isIntercalaryMonth(resultYear, month);
23493
23494 var correctedMonthIndex =
23495 (isIntercalary && resultCanBeIntercalaryMonth) ?
23496 this.toMonthIndex(resultYear, month, true) :
23497 this.toMonthIndex(resultYear, month, false);
23498
23499 if (correctedMonthIndex !== resultMonthIndex) {
23500 cdate.month(correctedMonthIndex);
23501 }
23502 }
23503
23504 return cdate;
23505 },
23506});
23507
23508// Used by ChineseCalendar.prototype.fromString
23509var DATE_REGEXP = /^\s*(-?\d\d\d\d|\d\d)[-/](\d?\d)([iI]?)[-/](\d?\d)/m;
23510var MONTH_NUMBER_REGEXP = /^\d?\d[iI]?/m;
23511var MONTH_NAME_REGEXP = /^闰?十?[一二三四五六七八九]?月/m;
23512var MONTH_SHORT_NAME_REGEXP = /^闰?十?[一二三四五六七八九]?/m;
23513
23514// Chinese calendar implementation
23515main.calendars.chinese = ChineseCalendar;
23516
23517// Chinese calendar tables from year 1888 to 2111
23518//
23519// Source:
23520// https://github.com/isee15/Lunar-Solar-Calendar-Converter.git
23521
23522// Table of intercalary months and days per month from year 1888 to 2111
23523//
23524// bit (12 - i): days in the i^th month
23525// (= 0 if i^th lunar month has 29 days)
23526// (= 1 if i^th lunar month has 30 days)
23527// (first month in lunar year is i = 0)
23528// bits (13,14,15,16): intercalary month
23529// (= 0 if lunar year has no intercalary month)
23530var LUNAR_MONTH_DAYS = [1887, 0x1694, 0x16aa, 0x4ad5,
23531 0xab6, 0xc4b7, 0x4ae, 0xa56, 0xb52a, 0x1d2a, 0xd54, 0x75aa, 0x156a,
23532 0x1096d, 0x95c, 0x14ae, 0xaa4d, 0x1a4c, 0x1b2a, 0x8d55, 0xad4,
23533 0x135a, 0x495d, 0x95c, 0xd49b, 0x149a, 0x1a4a, 0xbaa5, 0x16a8,
23534 0x1ad4, 0x52da, 0x12b6, 0xe937, 0x92e, 0x1496, 0xb64b, 0xd4a,
23535 0xda8, 0x95b5, 0x56c, 0x12ae, 0x492f, 0x92e, 0xcc96, 0x1a94,
23536 0x1d4a, 0xada9, 0xb5a, 0x56c, 0x726e, 0x125c, 0xf92d, 0x192a,
23537 0x1a94, 0xdb4a, 0x16aa, 0xad4, 0x955b, 0x4ba, 0x125a, 0x592b,
23538 0x152a, 0xf695, 0xd94, 0x16aa, 0xaab5, 0x9b4, 0x14b6, 0x6a57,
23539 0xa56, 0x1152a, 0x1d2a, 0xd54, 0xd5aa, 0x156a, 0x96c, 0x94ae,
23540 0x14ae, 0xa4c, 0x7d26, 0x1b2a, 0xeb55, 0xad4, 0x12da, 0xa95d,
23541 0x95a, 0x149a, 0x9a4d, 0x1a4a, 0x11aa5, 0x16a8, 0x16d4, 0xd2da,
23542 0x12b6, 0x936, 0x9497, 0x1496, 0x1564b, 0xd4a, 0xda8, 0xd5b4,
23543 0x156c, 0x12ae, 0xa92f, 0x92e, 0xc96, 0x6d4a, 0x1d4a, 0x10d65,
23544 0xb58, 0x156c, 0xb26d, 0x125c, 0x192c, 0x9a95, 0x1a94, 0x1b4a,
23545 0x4b55, 0xad4, 0xf55b, 0x4ba, 0x125a, 0xb92b, 0x152a, 0x1694,
23546 0x96aa, 0x15aa, 0x12ab5, 0x974, 0x14b6, 0xca57, 0xa56, 0x1526,
23547 0x8e95, 0xd54, 0x15aa, 0x49b5, 0x96c, 0xd4ae, 0x149c, 0x1a4c,
23548 0xbd26, 0x1aa6, 0xb54, 0x6d6a, 0x12da, 0x1695d, 0x95a, 0x149a,
23549 0xda4b, 0x1a4a, 0x1aa4, 0xbb54, 0x16b4, 0xada, 0x495b, 0x936,
23550 0xf497, 0x1496, 0x154a, 0xb6a5, 0xda4, 0x15b4, 0x6ab6, 0x126e,
23551 0x1092f, 0x92e, 0xc96, 0xcd4a, 0x1d4a, 0xd64, 0x956c, 0x155c,
23552 0x125c, 0x792e, 0x192c, 0xfa95, 0x1a94, 0x1b4a, 0xab55, 0xad4,
23553 0x14da, 0x8a5d, 0xa5a, 0x1152b, 0x152a, 0x1694, 0xd6aa, 0x15aa,
23554 0xab4, 0x94ba, 0x14b6, 0xa56, 0x7527, 0xd26, 0xee53, 0xd54, 0x15aa,
23555 0xa9b5, 0x96c, 0x14ae, 0x8a4e, 0x1a4c, 0x11d26, 0x1aa4, 0x1b54,
23556 0xcd6a, 0xada, 0x95c, 0x949d, 0x149a, 0x1a2a, 0x5b25, 0x1aa4,
23557 0xfb52, 0x16b4, 0xaba, 0xa95b, 0x936, 0x1496, 0x9a4b, 0x154a,
23558 0x136a5, 0xda4, 0x15ac];
23559
23560// Table of Chinese New Years from year 1888 to 2111
23561//
23562// bits (0 to 4): solar day
23563// bits (5 to 8): solar month
23564// bits (9 to 20): solar year
23565var CHINESE_NEW_YEAR = [1887, 0xec04c, 0xec23f, 0xec435, 0xec649,
23566 0xec83e, 0xeca51, 0xecc46, 0xece3a, 0xed04d, 0xed242, 0xed436,
23567 0xed64a, 0xed83f, 0xeda53, 0xedc48, 0xede3d, 0xee050, 0xee244,
23568 0xee439, 0xee64d, 0xee842, 0xeea36, 0xeec4a, 0xeee3e, 0xef052,
23569 0xef246, 0xef43a, 0xef64e, 0xef843, 0xefa37, 0xefc4b, 0xefe41,
23570 0xf0054, 0xf0248, 0xf043c, 0xf0650, 0xf0845, 0xf0a38, 0xf0c4d,
23571 0xf0e42, 0xf1037, 0xf124a, 0xf143e, 0xf1651, 0xf1846, 0xf1a3a,
23572 0xf1c4e, 0xf1e44, 0xf2038, 0xf224b, 0xf243f, 0xf2653, 0xf2848,
23573 0xf2a3b, 0xf2c4f, 0xf2e45, 0xf3039, 0xf324d, 0xf3442, 0xf3636,
23574 0xf384a, 0xf3a3d, 0xf3c51, 0xf3e46, 0xf403b, 0xf424e, 0xf4443,
23575 0xf4638, 0xf484c, 0xf4a3f, 0xf4c52, 0xf4e48, 0xf503c, 0xf524f,
23576 0xf5445, 0xf5639, 0xf584d, 0xf5a42, 0xf5c35, 0xf5e49, 0xf603e,
23577 0xf6251, 0xf6446, 0xf663b, 0xf684f, 0xf6a43, 0xf6c37, 0xf6e4b,
23578 0xf703f, 0xf7252, 0xf7447, 0xf763c, 0xf7850, 0xf7a45, 0xf7c39,
23579 0xf7e4d, 0xf8042, 0xf8254, 0xf8449, 0xf863d, 0xf8851, 0xf8a46,
23580 0xf8c3b, 0xf8e4f, 0xf9044, 0xf9237, 0xf944a, 0xf963f, 0xf9853,
23581 0xf9a47, 0xf9c3c, 0xf9e50, 0xfa045, 0xfa238, 0xfa44c, 0xfa641,
23582 0xfa836, 0xfaa49, 0xfac3d, 0xfae52, 0xfb047, 0xfb23a, 0xfb44e,
23583 0xfb643, 0xfb837, 0xfba4a, 0xfbc3f, 0xfbe53, 0xfc048, 0xfc23c,
23584 0xfc450, 0xfc645, 0xfc839, 0xfca4c, 0xfcc41, 0xfce36, 0xfd04a,
23585 0xfd23d, 0xfd451, 0xfd646, 0xfd83a, 0xfda4d, 0xfdc43, 0xfde37,
23586 0xfe04b, 0xfe23f, 0xfe453, 0xfe648, 0xfe83c, 0xfea4f, 0xfec44,
23587 0xfee38, 0xff04c, 0xff241, 0xff436, 0xff64a, 0xff83e, 0xffa51,
23588 0xffc46, 0xffe3a, 0x10004e, 0x100242, 0x100437, 0x10064b, 0x100841,
23589 0x100a53, 0x100c48, 0x100e3c, 0x10104f, 0x101244, 0x101438,
23590 0x10164c, 0x101842, 0x101a35, 0x101c49, 0x101e3d, 0x102051,
23591 0x102245, 0x10243a, 0x10264e, 0x102843, 0x102a37, 0x102c4b,
23592 0x102e3f, 0x103053, 0x103247, 0x10343b, 0x10364f, 0x103845,
23593 0x103a38, 0x103c4c, 0x103e42, 0x104036, 0x104249, 0x10443d,
23594 0x104651, 0x104846, 0x104a3a, 0x104c4e, 0x104e43, 0x105038,
23595 0x10524a, 0x10543e, 0x105652, 0x105847, 0x105a3b, 0x105c4f,
23596 0x105e45, 0x106039, 0x10624c, 0x106441, 0x106635, 0x106849,
23597 0x106a3d, 0x106c51, 0x106e47, 0x10703c, 0x10724f, 0x107444,
23598 0x107638, 0x10784c, 0x107a3f, 0x107c53, 0x107e48];
23599
23600function toLunar(yearOrDate, monthOrResult, day, result) {
23601 var solarDate;
23602 var lunarDate;
23603
23604 if(typeof yearOrDate === 'object') {
23605 solarDate = yearOrDate;
23606 lunarDate = monthOrResult || {};
23607
23608 } else {
23609 var isValidYear = (typeof yearOrDate === 'number') &&
23610 (yearOrDate >= 1888) && (yearOrDate <= 2111);
23611 if(!isValidYear)
23612 throw new Error("Solar year outside range 1888-2111");
23613
23614 var isValidMonth = (typeof monthOrResult === 'number') &&
23615 (monthOrResult >= 1) && (monthOrResult <= 12);
23616 if(!isValidMonth)
23617 throw new Error("Solar month outside range 1 - 12");
23618
23619 var isValidDay = (typeof day === 'number') && (day >= 1) && (day <= 31);
23620 if(!isValidDay)
23621 throw new Error("Solar day outside range 1 - 31");
23622
23623 solarDate = {
23624 year: yearOrDate,
23625 month: monthOrResult,
23626 day: day,
23627 };
23628 lunarDate = result || {};
23629 }
23630
23631 // Compute Chinese new year and lunar year
23632 var chineseNewYearPackedDate =
23633 CHINESE_NEW_YEAR[solarDate.year - CHINESE_NEW_YEAR[0]];
23634
23635 var packedDate = (solarDate.year << 9) | (solarDate.month << 5)
23636 | solarDate.day;
23637
23638 lunarDate.year = (packedDate >= chineseNewYearPackedDate) ?
23639 solarDate.year :
23640 solarDate.year - 1;
23641
23642 chineseNewYearPackedDate =
23643 CHINESE_NEW_YEAR[lunarDate.year - CHINESE_NEW_YEAR[0]];
23644
23645 var y = (chineseNewYearPackedDate >> 9) & 0xFFF;
23646 var m = (chineseNewYearPackedDate >> 5) & 0x0F;
23647 var d = chineseNewYearPackedDate & 0x1F;
23648
23649 // Compute days from new year
23650 var daysFromNewYear;
23651
23652 var chineseNewYearJSDate = new Date(y, m -1, d);
23653 var jsDate = new Date(solarDate.year, solarDate.month - 1, solarDate.day);
23654
23655 daysFromNewYear = Math.round(
23656 (jsDate - chineseNewYearJSDate) / (24 * 3600 * 1000));
23657
23658 // Compute lunar month and day
23659 var monthDaysTable = LUNAR_MONTH_DAYS[lunarDate.year - LUNAR_MONTH_DAYS[0]];
23660
23661 var i;
23662 for(i = 0; i < 13; i++) {
23663 var daysInMonth = (monthDaysTable & (1 << (12 - i))) ? 30 : 29;
23664
23665 if (daysFromNewYear < daysInMonth) {
23666 break;
23667 }
23668
23669 daysFromNewYear -= daysInMonth;
23670 }
23671
23672 var intercalaryMonth = monthDaysTable >> 13;
23673 if (!intercalaryMonth || i < intercalaryMonth) {
23674 lunarDate.isIntercalary = false;
23675 lunarDate.month = 1 + i;
23676 } else if (i === intercalaryMonth) {
23677 lunarDate.isIntercalary = true;
23678 lunarDate.month = i;
23679 } else {
23680 lunarDate.isIntercalary = false;
23681 lunarDate.month = i;
23682 }
23683
23684 lunarDate.day = 1 + daysFromNewYear;
23685
23686 return lunarDate;
23687}
23688
23689function toSolar(yearOrDate, monthOrResult, day, isIntercalaryOrResult, result) {
23690 var solarDate;
23691 var lunarDate;
23692
23693 if(typeof yearOrDate === 'object') {
23694 lunarDate = yearOrDate;
23695 solarDate = monthOrResult || {};
23696
23697 } else {
23698 var isValidYear = (typeof yearOrDate === 'number') &&
23699 (yearOrDate >= 1888) && (yearOrDate <= 2111);
23700 if(!isValidYear)
23701 throw new Error("Lunar year outside range 1888-2111");
23702
23703 var isValidMonth = (typeof monthOrResult === 'number') &&
23704 (monthOrResult >= 1) && (monthOrResult <= 12);
23705 if(!isValidMonth)
23706 throw new Error("Lunar month outside range 1 - 12");
23707
23708 var isValidDay = (typeof day === 'number') && (day >= 1) && (day <= 30);
23709 if(!isValidDay)
23710 throw new Error("Lunar day outside range 1 - 30");
23711
23712 var isIntercalary;
23713 if(typeof isIntercalaryOrResult === 'object') {
23714 isIntercalary = false;
23715 solarDate = isIntercalaryOrResult;
23716 } else {
23717 isIntercalary = !!isIntercalaryOrResult;
23718 solarDate = result || {};
23719 }
23720
23721 lunarDate = {
23722 year: yearOrDate,
23723 month: monthOrResult,
23724 day: day,
23725 isIntercalary: isIntercalary,
23726 };
23727 }
23728
23729 // Compute days from new year
23730 var daysFromNewYear;
23731
23732 daysFromNewYear = lunarDate.day - 1;
23733
23734 var monthDaysTable = LUNAR_MONTH_DAYS[lunarDate.year - LUNAR_MONTH_DAYS[0]];
23735 var intercalaryMonth = monthDaysTable >> 13;
23736
23737 var monthsFromNewYear;
23738 if (!intercalaryMonth) {
23739 monthsFromNewYear = lunarDate.month - 1;
23740 } else if (lunarDate.month > intercalaryMonth) {
23741 monthsFromNewYear = lunarDate.month;
23742 } else if (lunarDate.isIntercalary) {
23743 monthsFromNewYear = lunarDate.month;
23744 } else {
23745 monthsFromNewYear = lunarDate.month - 1;
23746 }
23747
23748 for(var i = 0; i < monthsFromNewYear; i++) {
23749 var daysInMonth = (monthDaysTable & (1 << (12 - i))) ? 30 : 29;
23750 daysFromNewYear += daysInMonth;
23751 }
23752
23753 // Compute Chinese new year
23754 var packedDate = CHINESE_NEW_YEAR[lunarDate.year - CHINESE_NEW_YEAR[0]];
23755
23756 var y = (packedDate >> 9) & 0xFFF;
23757 var m = (packedDate >> 5) & 0x0F;
23758 var d = packedDate & 0x1F;
23759
23760 // Compute solar date
23761 var jsDate = new Date(y, m - 1, d + daysFromNewYear);
23762
23763 solarDate.year = jsDate.getFullYear();
23764 solarDate.month = 1 + jsDate.getMonth();
23765 solarDate.day = jsDate.getDate();
23766
23767 return solarDate;
23768}
23769
23770
23771},{"../main":137,"object-assign":73}],124:[function(_dereq_,module,exports){
23772/*
23773 * World Calendars
23774 * https://github.com/alexcjohnson/world-calendars
23775 *
23776 * Batch-converted from kbwood/calendars
23777 * Many thanks to Keith Wood and all of the contributors to the original project!
23778 *
23779 * This source code is licensed under the MIT license found in the
23780 * LICENSE file in the root directory of this source tree.
23781 */
23782
23783/* http://keith-wood.name/calendars.html
23784 Coptic calendar for jQuery v2.0.2.
23785 Written by Keith Wood (wood.keith{at}optusnet.com.au) February 2010.
23786 Available under the MIT (http://keith-wood.name/licence.html) license.
23787 Please attribute the author if you use it. */
23788
23789var main = _dereq_('../main');
23790var assign = _dereq_('object-assign');
23791
23792
23793/** Implementation of the Coptic calendar.
23794 See <a href="http://en.wikipedia.org/wiki/Coptic_calendar">http://en.wikipedia.org/wiki/Coptic_calendar</a>.
23795 See also Calendrical Calculations: The Millennium Edition
23796 (<a href="http://emr.cs.iit.edu/home/reingold/calendar-book/index.shtml">http://emr.cs.iit.edu/home/reingold/calendar-book/index.shtml</a>).
23797 @class CopticCalendar
23798 @param [language=''] {string} The language code (default English) for localisation. */
23799function CopticCalendar(language) {
23800 this.local = this.regionalOptions[language || ''] || this.regionalOptions[''];
23801}
23802
23803CopticCalendar.prototype = new main.baseCalendar;
23804
23805assign(CopticCalendar.prototype, {
23806 /** The calendar name.
23807 @memberof CopticCalendar */
23808 name: 'Coptic',
23809 /** Julian date of start of Coptic epoch: 29 August 284 CE (Gregorian).
23810 @memberof CopticCalendar */
23811 jdEpoch: 1825029.5,
23812 /** Days per month in a common year.
23813 @memberof CopticCalendar */
23814 daysPerMonth: [30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 5],
23815 /** <code>true</code> if has a year zero, <code>false</code> if not.
23816 @memberof CopticCalendar */
23817 hasYearZero: false,
23818 /** The minimum month number.
23819 @memberof CopticCalendar */
23820 minMonth: 1,
23821 /** The first month in the year.
23822 @memberof CopticCalendar */
23823 firstMonth: 1,
23824 /** The minimum day number.
23825 @memberof CopticCalendar */
23826 minDay: 1,
23827
23828 /** Localisations for the plugin.
23829 Entries are objects indexed by the language code ('' being the default US/English).
23830 Each object has the following attributes.
23831 @memberof CopticCalendar
23832 @property name {string} The calendar name.
23833 @property epochs {string[]} The epoch names.
23834 @property monthNames {string[]} The long names of the months of the year.
23835 @property monthNamesShort {string[]} The short names of the months of the year.
23836 @property dayNames {string[]} The long names of the days of the week.
23837 @property dayNamesShort {string[]} The short names of the days of the week.
23838 @property dayNamesMin {string[]} The minimal names of the days of the week.
23839 @property dateFormat {string} The date format for this calendar.
23840 See the options on <a href="BaseCalendar.html#formatDate"><code>formatDate</code></a> for details.
23841 @property firstDay {number} The number of the first day of the week, starting at 0.
23842 @property isRTL {number} <code>true</code> if this localisation reads right-to-left. */
23843 regionalOptions: { // Localisations
23844 '': {
23845 name: 'Coptic',
23846 epochs: ['BAM', 'AM'],
23847 monthNames: ['Thout', 'Paopi', 'Hathor', 'Koiak', 'Tobi', 'Meshir',
23848 'Paremhat', 'Paremoude', 'Pashons', 'Paoni', 'Epip', 'Mesori', 'Pi Kogi Enavot'],
23849 monthNamesShort: ['Tho', 'Pao', 'Hath', 'Koi', 'Tob', 'Mesh',
23850 'Pat', 'Pad', 'Pash', 'Pao', 'Epi', 'Meso', 'PiK'],
23851 dayNames: ['Tkyriaka', 'Pesnau', 'Pshoment', 'Peftoou', 'Ptiou', 'Psoou', 'Psabbaton'],
23852 dayNamesShort: ['Tky', 'Pes', 'Psh', 'Pef', 'Pti', 'Pso', 'Psa'],
23853 dayNamesMin: ['Tk', 'Pes', 'Psh', 'Pef', 'Pt', 'Pso', 'Psa'],
23854 digits: null,
23855 dateFormat: 'dd/mm/yyyy',
23856 firstDay: 0,
23857 isRTL: false
23858 }
23859 },
23860
23861 /** Determine whether this date is in a leap year.
23862 @memberof CopticCalendar
23863 @param year {CDate|number} The date to examine or the year to examine.
23864 @return {boolean} <code>true</code> if this is a leap year, <code>false</code> if not.
23865 @throws Error if an invalid year or a different calendar used. */
23866 leapYear: function(year) {
23867 var date = this._validate(year, this.minMonth, this.minDay, main.local.invalidYear);
23868 var year = date.year() + (date.year() < 0 ? 1 : 0); // No year zero
23869 return year % 4 === 3 || year % 4 === -1;
23870 },
23871
23872 /** Retrieve the number of months in a year.
23873 @memberof CopticCalendar
23874 @param year {CDate|number} The date to examine or the year to examine.
23875 @return {number} The number of months.
23876 @throws Error if an invalid year or a different calendar used. */
23877 monthsInYear: function(year) {
23878 this._validate(year, this.minMonth, this.minDay,
23879 main.local.invalidYear || main.regionalOptions[''].invalidYear);
23880 return 13;
23881 },
23882
23883 /** Determine the week of the year for a date.
23884 @memberof CopticCalendar
23885 @param year {CDate|number} The date to examine or the year to examine.
23886 @param [month] {number) the month to examine.
23887 @param [day] {number} The day to examine.
23888 @return {number} The week of the year.
23889 @throws Error if an invalid date or a different calendar used. */
23890 weekOfYear: function(year, month, day) {
23891 // Find Sunday of this week starting on Sunday
23892 var checkDate = this.newDate(year, month, day);
23893 checkDate.add(-checkDate.dayOfWeek(), 'd');
23894 return Math.floor((checkDate.dayOfYear() - 1) / 7) + 1;
23895 },
23896
23897 /** Retrieve the number of days in a month.
23898 @memberof CopticCalendar
23899 @param year {CDate|number} The date to examine or the year of the month.
23900 @param [month] {number} The month.
23901 @return {number} The number of days in this month.
23902 @throws Error if an invalid month/year or a different calendar used. */
23903 daysInMonth: function(year, month) {
23904 var date = this._validate(year, month, this.minDay, main.local.invalidMonth);
23905 return this.daysPerMonth[date.month() - 1] +
23906 (date.month() === 13 && this.leapYear(date.year()) ? 1 : 0);
23907 },
23908
23909 /** Determine whether this date is a week day.
23910 @memberof CopticCalendar
23911 @param year {CDate|number} The date to examine or the year to examine.
23912 @param month {number} The month to examine.
23913 @param day {number} The day to examine.
23914 @return {boolean} <code>true</code> if a week day, <code>false</code> if not.
23915 @throws Error if an invalid date or a different calendar used. */
23916 weekDay: function(year, month, day) {
23917 return (this.dayOfWeek(year, month, day) || 7) < 6;
23918 },
23919
23920 /** Retrieve the Julian date equivalent for this date,
23921 i.e. days since January 1, 4713 BCE Greenwich noon.
23922 @memberof CopticCalendar
23923 @param year {CDate|number} The date to convert or the year to convert.
23924 @param [month] {number) the month to convert.
23925 @param [day] {number} The day to convert.
23926 @return {number} The equivalent Julian date.
23927 @throws Error if an invalid date or a different calendar used. */
23928 toJD: function(year, month, day) {
23929 var date = this._validate(year, month, day, main.local.invalidDate);
23930 year = date.year();
23931 if (year < 0) { year++; } // No year zero
23932 return date.day() + (date.month() - 1) * 30 +
23933 (year - 1) * 365 + Math.floor(year / 4) + this.jdEpoch - 1;
23934 },
23935
23936 /** Create a new date from a Julian date.
23937 @memberof CopticCalendar
23938 @param jd {number} The Julian date to convert.
23939 @return {CDate} The equivalent date. */
23940 fromJD: function(jd) {
23941 var c = Math.floor(jd) + 0.5 - this.jdEpoch;
23942 var year = Math.floor((c - Math.floor((c + 366) / 1461)) / 365) + 1;
23943 if (year <= 0) { year--; } // No year zero
23944 c = Math.floor(jd) + 0.5 - this.newDate(year, 1, 1).toJD();
23945 var month = Math.floor(c / 30) + 1;
23946 var day = c - (month - 1) * 30 + 1;
23947 return this.newDate(year, month, day);
23948 }
23949});
23950
23951// Coptic calendar implementation
23952main.calendars.coptic = CopticCalendar;
23953
23954
23955},{"../main":137,"object-assign":73}],125:[function(_dereq_,module,exports){
23956/*
23957 * World Calendars
23958 * https://github.com/alexcjohnson/world-calendars
23959 *
23960 * Batch-converted from kbwood/calendars
23961 * Many thanks to Keith Wood and all of the contributors to the original project!
23962 *
23963 * This source code is licensed under the MIT license found in the
23964 * LICENSE file in the root directory of this source tree.
23965 */
23966
23967/* http://keith-wood.name/calendars.html
23968 Discworld calendar for jQuery v2.0.2.
23969 Written by Keith Wood (wood.keith{at}optusnet.com.au) January 2016.
23970 Available under the MIT (http://keith-wood.name/licence.html) license.
23971 Please attribute the author if you use it. */
23972
23973var main = _dereq_('../main');
23974var assign = _dereq_('object-assign');
23975
23976
23977/** Implementation of the Discworld calendar - Unseen University version.
23978 See also <a href="http://wiki.lspace.org/mediawiki/Discworld_calendar">http://wiki.lspace.org/mediawiki/Discworld_calendar</a>
23979 and <a href="http://discworld.wikia.com/wiki/Discworld_calendar">http://discworld.wikia.com/wiki/Discworld_calendar</a>.
23980 @class DiscworldCalendar
23981 @param [language=''] {string} The language code (default English) for localisation. */
23982function DiscworldCalendar(language) {
23983 this.local = this.regionalOptions[language || ''] || this.regionalOptions[''];
23984}
23985
23986DiscworldCalendar.prototype = new main.baseCalendar;
23987
23988assign(DiscworldCalendar.prototype, {
23989 /** The calendar name.
23990 @memberof DiscworldCalendar */
23991 name: 'Discworld',
23992 /** Julian date of start of Discworld epoch: 1 January 0001 CE.
23993 @memberof DiscworldCalendar */
23994 jdEpoch: 1721425.5,
23995 /** Days per month in a common year.
23996 @memberof DiscworldCalendar */
23997 daysPerMonth: [16, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32],
23998 /** <code>true</code> if has a year zero, <code>false</code> if not.
23999 @memberof DiscworldCalendar */
24000 hasYearZero: false,
24001 /** The minimum month number.
24002 @memberof DiscworldCalendar */
24003 minMonth: 1,
24004 /** The first month in the year.
24005 @memberof DiscworldCalendar */
24006 firstMonth: 1,
24007 /** The minimum day number.
24008 @memberof DiscworldCalendar */
24009 minDay: 1,
24010
24011 /** Localisations for the plugin.
24012 Entries are objects indexed by the language code ('' being the default US/English).
24013 Each object has the following attributes.
24014 @memberof DiscworldCalendar
24015 @property name {string} The calendar name.
24016 @property epochs {string[]} The epoch names.
24017 @property monthNames {string[]} The long names of the months of the year.
24018 @property monthNamesShort {string[]} The short names of the months of the year.
24019 @property dayNames {string[]} The long names of the days of the week.
24020 @property dayNamesShort {string[]} The short names of the days of the week.
24021 @property dayNamesMin {string[]} The minimal names of the days of the week.
24022 @property dateFormat {string} The date format for this calendar.
24023 See the options on <a href="BaseCalendar.html#formatDate"><code>formatDate</code></a> for details.
24024 @property firstDay {number} The number of the first day of the week, starting at 0.
24025 @property isRTL {number} <code>true</code> if this localisation reads right-to-left. */
24026 regionalOptions: { // Localisations
24027 '': {
24028 name: 'Discworld',
24029 epochs: ['BUC', 'UC'],
24030 monthNames: ['Ick', 'Offle', 'February', 'March', 'April', 'May', 'June',
24031 'Grune', 'August', 'Spune', 'Sektober', 'Ember', 'December'],
24032 monthNamesShort: ['Ick', 'Off', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Gru', 'Aug', 'Spu', 'Sek', 'Emb', 'Dec'],
24033 dayNames: ['Sunday', 'Octeday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
24034 dayNamesShort: ['Sun', 'Oct', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
24035 dayNamesMin: ['Su', 'Oc', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
24036 digits: null,
24037 dateFormat: 'yyyy/mm/dd',
24038 firstDay: 2,
24039 isRTL: false
24040 }
24041 },
24042
24043 /** Determine whether this date is in a leap year.
24044 @memberof DiscworldCalendar
24045 @param year {CDate|number} The date to examine or the year to examine.
24046 @return {boolean} <code>true</code> if this is a leap year, <code>false</code> if not.
24047 @throws Error if an invalid year or a different calendar used. */
24048 leapYear: function(year) {
24049 this._validate(year, this.minMonth, this.minDay, main.local.invalidYear);
24050 return false;
24051 },
24052
24053 /** Retrieve the number of months in a year.
24054 @memberof DiscworldCalendar
24055 @param year {CDate|number} The date to examine or the year to examine.
24056 @return {number} The number of months.
24057 @throws Error if an invalid year or a different calendar used. */
24058 monthsInYear: function(year) {
24059 this._validate(year, this.minMonth, this.minDay, main.local.invalidYear);
24060 return 13;
24061 },
24062
24063 /** Retrieve the number of days in a year.
24064 @memberof DiscworldCalendar
24065 @param year {CDate|number} The date to examine or the year to examine.
24066 @return {number} The number of days.
24067 @throws Error if an invalid year or a different calendar used. */
24068 daysInYear: function(year) {
24069 this._validate(year, this.minMonth, this.minDay, main.local.invalidYear);
24070 return 400;
24071 },
24072
24073 /** Determine the week of the year for a date.
24074 @memberof DiscworldCalendar
24075 @param year {CDate|number} The date to examine or the year to examine.
24076 @param [month] {number} The month to examine.
24077 @param [day] {number} The day to examine.
24078 @return {number} The week of the year.
24079 @throws Error if an invalid date or a different calendar used. */
24080 weekOfYear: function(year, month, day) {
24081 // Find Sunday of this week starting on Sunday
24082 var checkDate = this.newDate(year, month, day);
24083 checkDate.add(-checkDate.dayOfWeek(), 'd');
24084 return Math.floor((checkDate.dayOfYear() - 1) / 8) + 1;
24085 },
24086
24087 /** Retrieve the number of days in a month.
24088 @memberof DiscworldCalendar
24089 @param year {CDate|number} The date to examine or the year of the month.
24090 @param [month] {number} The month.
24091 @return {number} The number of days in this month.
24092 @throws Error if an invalid month/year or a different calendar used. */
24093 daysInMonth: function(year, month) {
24094 var date = this._validate(year, month, this.minDay, main.local.invalidMonth);
24095 return this.daysPerMonth[date.month() - 1];
24096 },
24097
24098 /** Retrieve the number of days in a week.
24099 @memberof DiscworldCalendar
24100 @return {number} The number of days. */
24101 daysInWeek: function() {
24102 return 8;
24103 },
24104
24105 /** Retrieve the day of the week for a date.
24106 @memberof DiscworldCalendar
24107 @param year {CDate|number} The date to examine or the year to examine.
24108 @param [month] {number} The month to examine.
24109 @param [day] {number} The day to examine.
24110 @return {number} The day of the week: 0 to number of days - 1.
24111 @throws Error if an invalid date or a different calendar used. */
24112 dayOfWeek: function(year, month, day) {
24113 var date = this._validate(year, month, day, main.local.invalidDate);
24114 return (date.day() + 1) % 8;
24115 },
24116
24117 /** Determine whether this date is a week day.
24118 @memberof DiscworldCalendar
24119 @param year {CDate|number} The date to examine or the year to examine.
24120 @param [month] {number} The month to examine.
24121 @param [day] {number} The day to examine.
24122 @return {boolean} <code>true</code> if a week day, <code>false</code> if not.
24123 @throws Error if an invalid date or a different calendar used. */
24124 weekDay: function(year, month, day) {
24125 var dow = this.dayOfWeek(year, month, day);
24126 return (dow >= 2 && dow <= 6);
24127 },
24128
24129 /** Retrieve additional information about a date.
24130 @memberof DiscworldCalendar
24131 @param year {CDate|number} The date to examine or the year to examine.
24132 @param [month] {number} The month to examine.
24133 @param [day] {number} The day to examine.
24134 @return {object} Additional information - contents depends on calendar.
24135 @throws Error if an invalid date or a different calendar used. */
24136 extraInfo: function(year, month, day) {
24137 var date = this._validate(year, month, day, main.local.invalidDate);
24138 return {century: centuries[Math.floor((date.year() - 1) / 100) + 1] || ''};
24139 },
24140
24141 /** Retrieve the Julian date equivalent for this date,
24142 i.e. days since January 1, 4713 BCE Greenwich noon.
24143 @memberof DiscworldCalendar
24144 @param year {CDate|number} The date to convert or the year to convert.
24145 @param [month] {number} The month to convert.
24146 @param [day] {number} The day to convert.
24147 @return {number} The equivalent Julian date.
24148 @throws Error if an invalid date or a different calendar used. */
24149 toJD: function(year, month, day) {
24150 var date = this._validate(year, month, day, main.local.invalidDate);
24151 year = date.year() + (date.year() < 0 ? 1 : 0);
24152 month = date.month();
24153 day = date.day();
24154 return day + (month > 1 ? 16 : 0) + (month > 2 ? (month - 2) * 32 : 0) +
24155 (year - 1) * 400 + this.jdEpoch - 1;
24156 },
24157
24158 /** Create a new date from a Julian date.
24159 @memberof DiscworldCalendar
24160 @param jd {number} The Julian date to convert.
24161 @return {CDate} The equivalent date. */
24162 fromJD: function(jd) {
24163 jd = Math.floor(jd + 0.5) - Math.floor(this.jdEpoch) - 1;
24164 var year = Math.floor(jd / 400) + 1;
24165 jd -= (year - 1) * 400;
24166 jd += (jd > 15 ? 16 : 0);
24167 var month = Math.floor(jd / 32) + 1;
24168 var day = jd - (month - 1) * 32 + 1;
24169 return this.newDate(year <= 0 ? year - 1 : year, month, day);
24170 }
24171});
24172
24173// Names of the centuries
24174var centuries = {
24175 20: 'Fruitbat',
24176 21: 'Anchovy'
24177};
24178
24179// Discworld calendar implementation
24180main.calendars.discworld = DiscworldCalendar;
24181
24182
24183},{"../main":137,"object-assign":73}],126:[function(_dereq_,module,exports){
24184/*
24185 * World Calendars
24186 * https://github.com/alexcjohnson/world-calendars
24187 *
24188 * Batch-converted from kbwood/calendars
24189 * Many thanks to Keith Wood and all of the contributors to the original project!
24190 *
24191 * This source code is licensed under the MIT license found in the
24192 * LICENSE file in the root directory of this source tree.
24193 */
24194
24195/* http://keith-wood.name/calendars.html
24196 Ethiopian calendar for jQuery v2.0.2.
24197 Written by Keith Wood (wood.keith{at}optusnet.com.au) February 2010.
24198 Available under the MIT (http://keith-wood.name/licence.html) license.
24199 Please attribute the author if you use it. */
24200
24201var main = _dereq_('../main');
24202var assign = _dereq_('object-assign');
24203
24204
24205/** Implementation of the Ethiopian calendar.
24206 See <a href="http://en.wikipedia.org/wiki/Ethiopian_calendar">http://en.wikipedia.org/wiki/Ethiopian_calendar</a>.
24207 See also Calendrical Calculations: The Millennium Edition
24208 (<a href="http://emr.cs.iit.edu/home/reingold/calendar-book/index.shtml">http://emr.cs.iit.edu/home/reingold/calendar-book/index.shtml</a>).
24209 @class EthiopianCalendar
24210 @param [language=''] {string} The language code (default English) for localisation. */
24211function EthiopianCalendar(language) {
24212 this.local = this.regionalOptions[language || ''] || this.regionalOptions[''];
24213}
24214
24215EthiopianCalendar.prototype = new main.baseCalendar;
24216
24217assign(EthiopianCalendar.prototype, {
24218 /** The calendar name.
24219 @memberof EthiopianCalendar */
24220 name: 'Ethiopian',
24221 /** Julian date of start of Ethiopian epoch: 27 August 8 CE (Gregorian).
24222 @memberof EthiopianCalendar */
24223 jdEpoch: 1724220.5,
24224 /** Days per month in a common year.
24225 @memberof EthiopianCalendar */
24226 daysPerMonth: [30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 5],
24227 /** <code>true</code> if has a year zero, <code>false</code> if not.
24228 @memberof EthiopianCalendar */
24229 hasYearZero: false,
24230 /** The minimum month number.
24231 @memberof EthiopianCalendar */
24232 minMonth: 1,
24233 /** The first month in the year.
24234 @memberof EthiopianCalendar */
24235 firstMonth: 1,
24236 /** The minimum day number.
24237 @memberof EthiopianCalendar */
24238 minDay: 1,
24239
24240 /** Localisations for the plugin.
24241 Entries are objects indexed by the language code ('' being the default US/English).
24242 Each object has the following attributes.
24243 @memberof EthiopianCalendar
24244 @property name {string} The calendar name.
24245 @property epochs {string[]} The epoch names.
24246 @property monthNames {string[]} The long names of the months of the year.
24247 @property monthNamesShort {string[]} The short names of the months of the year.
24248 @property dayNames {string[]} The long names of the days of the week.
24249 @property dayNamesShort {string[]} The short names of the days of the week.
24250 @property dayNamesMin {string[]} The minimal names of the days of the week.
24251 @property dateFormat {string} The date format for this calendar.
24252 See the options on <a href="BaseCalendar.html#formatDate"><code>formatDate</code></a> for details.
24253 @property firstDay {number} The number of the first day of the week, starting at 0.
24254 @property isRTL {number} <code>true</code> if this localisation reads right-to-left. */
24255 regionalOptions: { // Localisations
24256 '': {
24257 name: 'Ethiopian',
24258 epochs: ['BEE', 'EE'],
24259 monthNames: ['Meskerem', 'Tikemet', 'Hidar', 'Tahesas', 'Tir', 'Yekatit',
24260 'Megabit', 'Miazia', 'Genbot', 'Sene', 'Hamle', 'Nehase', 'Pagume'],
24261 monthNamesShort: ['Mes', 'Tik', 'Hid', 'Tah', 'Tir', 'Yek',
24262 'Meg', 'Mia', 'Gen', 'Sen', 'Ham', 'Neh', 'Pag'],
24263 dayNames: ['Ehud', 'Segno', 'Maksegno', 'Irob', 'Hamus', 'Arb', 'Kidame'],
24264 dayNamesShort: ['Ehu', 'Seg', 'Mak', 'Iro', 'Ham', 'Arb', 'Kid'],
24265 dayNamesMin: ['Eh', 'Se', 'Ma', 'Ir', 'Ha', 'Ar', 'Ki'],
24266 digits: null,
24267 dateFormat: 'dd/mm/yyyy',
24268 firstDay: 0,
24269 isRTL: false
24270 }
24271 },
24272
24273 /** Determine whether this date is in a leap year.
24274 @memberof EthiopianCalendar
24275 @param year {CDate|number} The date to examine or the year to examine.
24276 @return {boolean} <code>true</code> if this is a leap year, <code>false</code> if not.
24277 @throws Error if an invalid year or a different calendar used. */
24278 leapYear: function(year) {
24279 var date = this._validate(year, this.minMonth, this.minDay, main.local.invalidYear);
24280 var year = date.year() + (date.year() < 0 ? 1 : 0); // No year zero
24281 return year % 4 === 3 || year % 4 === -1;
24282 },
24283
24284 /** Retrieve the number of months in a year.
24285 @memberof EthiopianCalendar
24286 @param year {CDate|number} The date to examine or the year to examine.
24287 @return {number} The number of months.
24288 @throws Error if an invalid year or a different calendar used. */
24289 monthsInYear: function(year) {
24290 this._validate(year, this.minMonth, this.minDay,
24291 main.local.invalidYear || main.regionalOptions[''].invalidYear);
24292 return 13;
24293 },
24294
24295 /** Determine the week of the year for a date.
24296 @memberof EthiopianCalendar
24297 @param year {CDate|number} The date to examine or the year to examine.
24298 @param [month] {number} The month to examine.
24299 @param [day] {number} The day to examine.
24300 @return {number} The week of the year.
24301 @throws Error if an invalid date or a different calendar used. */
24302 weekOfYear: function(year, month, day) {
24303 // Find Sunday of this week starting on Sunday
24304 var checkDate = this.newDate(year, month, day);
24305 checkDate.add(-checkDate.dayOfWeek(), 'd');
24306 return Math.floor((checkDate.dayOfYear() - 1) / 7) + 1;
24307 },
24308
24309 /** Retrieve the number of days in a month.
24310 @memberof EthiopianCalendar
24311 @param year {CDate|number} The date to examine or the year of the month.
24312 @param [month] {number} The month.
24313 @return {number} The number of days in this month.
24314 @throws Error if an invalid month/year or a different calendar used. */
24315 daysInMonth: function(year, month) {
24316 var date = this._validate(year, month, this.minDay, main.local.invalidMonth);
24317 return this.daysPerMonth[date.month() - 1] +
24318 (date.month() === 13 && this.leapYear(date.year()) ? 1 : 0);
24319 },
24320
24321 /** Determine whether this date is a week day.
24322 @memberof EthiopianCalendar
24323 @param year {CDate|number} The date to examine or the year to examine.
24324 @param [month] {number} The month to examine.
24325 @param [day] {number} The day to examine.
24326 @return {boolean} <code>true</code> if a week day, <code>false</code> if not.
24327 @throws Error if an invalid date or a different calendar used. */
24328 weekDay: function(year, month, day) {
24329 return (this.dayOfWeek(year, month, day) || 7) < 6;
24330 },
24331
24332 /** Retrieve the Julian date equivalent for this date,
24333 i.e. days since January 1, 4713 BCE Greenwich noon.
24334 @memberof EthiopianCalendar
24335 @param year {CDate|number} The date to convert or the year to convert.
24336 @param [month] {number} The month to convert.
24337 @param [day] {number} The day to convert.
24338 @return {number} The equivalent Julian date.
24339 @throws Error if an invalid date or a different calendar used. */
24340 toJD: function(year, month, day) {
24341 var date = this._validate(year, month, day, main.local.invalidDate);
24342 year = date.year();
24343 if (year < 0) { year++; } // No year zero
24344 return date.day() + (date.month() - 1) * 30 +
24345 (year - 1) * 365 + Math.floor(year / 4) + this.jdEpoch - 1;
24346 },
24347
24348 /** Create a new date from a Julian date.
24349 @memberof EthiopianCalendar
24350 @param jd {number} the Julian date to convert.
24351 @return {CDate} the equivalent date. */
24352 fromJD: function(jd) {
24353 var c = Math.floor(jd) + 0.5 - this.jdEpoch;
24354 var year = Math.floor((c - Math.floor((c + 366) / 1461)) / 365) + 1;
24355 if (year <= 0) { year--; } // No year zero
24356 c = Math.floor(jd) + 0.5 - this.newDate(year, 1, 1).toJD();
24357 var month = Math.floor(c / 30) + 1;
24358 var day = c - (month - 1) * 30 + 1;
24359 return this.newDate(year, month, day);
24360 }
24361});
24362
24363// Ethiopian calendar implementation
24364main.calendars.ethiopian = EthiopianCalendar;
24365
24366
24367},{"../main":137,"object-assign":73}],127:[function(_dereq_,module,exports){
24368/*
24369 * World Calendars
24370 * https://github.com/alexcjohnson/world-calendars
24371 *
24372 * Batch-converted from kbwood/calendars
24373 * Many thanks to Keith Wood and all of the contributors to the original project!
24374 *
24375 * This source code is licensed under the MIT license found in the
24376 * LICENSE file in the root directory of this source tree.
24377 */
24378
24379/* http://keith-wood.name/calendars.html
24380 Hebrew calendar for jQuery v2.0.2.
24381 Written by Keith Wood (wood.keith{at}optusnet.com.au) August 2009.
24382 Available under the MIT (http://keith-wood.name/licence.html) license.
24383 Please attribute the author if you use it. */
24384
24385var main = _dereq_('../main');
24386var assign = _dereq_('object-assign');
24387
24388
24389/** Implementation of the Hebrew civil calendar.
24390 Based on code from <a href="http://www.fourmilab.ch/documents/calendar/">http://www.fourmilab.ch/documents/calendar/</a>.
24391 See also <a href="http://en.wikipedia.org/wiki/Hebrew_calendar">http://en.wikipedia.org/wiki/Hebrew_calendar</a>.
24392 @class HebrewCalendar
24393 @param [language=''] {string} The language code (default English) for localisation. */
24394function HebrewCalendar(language) {
24395 this.local = this.regionalOptions[language || ''] || this.regionalOptions[''];
24396}
24397
24398HebrewCalendar.prototype = new main.baseCalendar;
24399
24400assign(HebrewCalendar.prototype, {
24401 /** The calendar name.
24402 @memberof HebrewCalendar */
24403 name: 'Hebrew',
24404 /** Julian date of start of Hebrew epoch: 7 October 3761 BCE.
24405 @memberof HebrewCalendar */
24406 jdEpoch: 347995.5,
24407 /** Days per month in a common year.
24408 @memberof HebrewCalendar */
24409 daysPerMonth: [30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 29],
24410 /** <code>true</code> if has a year zero, <code>false</code> if not.
24411 @memberof HebrewCalendar */
24412 hasYearZero: false,
24413 /** The minimum month number.
24414 @memberof HebrewCalendar */
24415 minMonth: 1,
24416 /** The first month in the year.
24417 @memberof HebrewCalendar */
24418 firstMonth: 7,
24419 /** The minimum day number.
24420 @memberof HebrewCalendar */
24421 minDay: 1,
24422
24423 /** Localisations for the plugin.
24424 Entries are objects indexed by the language code ('' being the default US/English).
24425 Each object has the following attributes.
24426 @memberof HebrewCalendar
24427 @property name {string} The calendar name.
24428 @property epochs {string[]} The epoch names.
24429 @property monthNames {string[]} The long names of the months of the year.
24430 @property monthNamesShort {string[]} The short names of the months of the year.
24431 @property dayNames {string[]} The long names of the days of the week.
24432 @property dayNamesShort {string[]} The short names of the days of the week.
24433 @property dayNamesMin {string[]} The minimal names of the days of the week.
24434 @property dateFormat {string} The date format for this calendar.
24435 See the options on <a href="BaseCalendar.html#formatDate"><code>formatDate</code></a> for details.
24436 @property firstDay {number} The number of the first day of the week, starting at 0.
24437 @property isRTL {number} <code>true</code> if this localisation reads right-to-left. */
24438 regionalOptions: { // Localisations
24439 '': {
24440 name: 'Hebrew',
24441 epochs: ['BAM', 'AM'],
24442 monthNames: ['Nisan', 'Iyar', 'Sivan', 'Tammuz', 'Av', 'Elul',
24443 'Tishrei', 'Cheshvan', 'Kislev', 'Tevet', 'Shevat', 'Adar', 'Adar II'],
24444 monthNamesShort: ['Nis', 'Iya', 'Siv', 'Tam', 'Av', 'Elu', 'Tis', 'Che', 'Kis', 'Tev', 'She', 'Ada', 'Ad2'],
24445 dayNames: ['Yom Rishon', 'Yom Sheni', 'Yom Shlishi', 'Yom Revi\'i', 'Yom Chamishi', 'Yom Shishi', 'Yom Shabbat'],
24446 dayNamesShort: ['Ris', 'She', 'Shl', 'Rev', 'Cha', 'Shi', 'Sha'],
24447 dayNamesMin: ['Ri','She','Shl','Re','Ch','Shi','Sha'],
24448 digits: null,
24449 dateFormat: 'dd/mm/yyyy',
24450 firstDay: 0,
24451 isRTL: false
24452 }
24453 },
24454
24455 /** Determine whether this date is in a leap year.
24456 @memberof HebrewCalendar
24457 @param year {CDate|number} The date to examine or the year to examine.
24458 @return {boolean} <code>true</code> if this is a leap year, <code>false</code> if not.
24459 @throws Error if an invalid year or a different calendar used. */
24460 leapYear: function(year) {
24461 var date = this._validate(year, this.minMonth, this.minDay, main.local.invalidYear);
24462 return this._leapYear(date.year());
24463 },
24464
24465 /** Determine whether this date is in a leap year.
24466 @memberof HebrewCalendar
24467 @private
24468 @param year {number} The year to examine.
24469 @return {boolean} <code>true</code> if this is a leap year, <code>false</code> if not.
24470 @throws Error if an invalid year or a different calendar used. */
24471 _leapYear: function(year) {
24472 year = (year < 0 ? year + 1 : year);
24473 return mod(year * 7 + 1, 19) < 7;
24474 },
24475
24476 /** Retrieve the number of months in a year.
24477 @memberof HebrewCalendar
24478 @param year {CDate|number} The date to examine or the year to examine.
24479 @return {number} The number of months.
24480 @throws Error if an invalid year or a different calendar used. */
24481 monthsInYear: function(year) {
24482 this._validate(year, this.minMonth, this.minDay, main.local.invalidYear);
24483 return this._leapYear(year.year ? year.year() : year) ? 13 : 12;
24484 },
24485
24486 /** Determine the week of the year for a date.
24487 @memberof HebrewCalendar
24488 @param year {CDate|number} The date to examine or the year to examine.
24489 @param [month] {number} The month to examine.
24490 @param [day] {number} The day to examine.
24491 @return {number} The week of the year.
24492 @throws Error if an invalid date or a different calendar used. */
24493 weekOfYear: function(year, month, day) {
24494 // Find Sunday of this week starting on Sunday
24495 var checkDate = this.newDate(year, month, day);
24496 checkDate.add(-checkDate.dayOfWeek(), 'd');
24497 return Math.floor((checkDate.dayOfYear() - 1) / 7) + 1;
24498 },
24499
24500 /** Retrieve the number of days in a year.
24501 @memberof HebrewCalendar
24502 @param year {CDate|number} The date to examine or the year to examine.
24503 @return {number} The number of days.
24504 @throws Error if an invalid year or a different calendar used. */
24505 daysInYear: function(year) {
24506 var date = this._validate(year, this.minMonth, this.minDay, main.local.invalidYear);
24507 year = date.year();
24508 return this.toJD((year === -1 ? +1 : year + 1), 7, 1) - this.toJD(year, 7, 1);
24509 },
24510
24511 /** Retrieve the number of days in a month.
24512 @memberof HebrewCalendar
24513 @param year {CDate|number} The date to examine or the year of the month.
24514 @param [month] {number} The month.
24515 @return {number} The number of days in this month.
24516 @throws Error if an invalid month/year or a different calendar used. */
24517 daysInMonth: function(year, month) {
24518 if (year.year) {
24519 month = year.month();
24520 year = year.year();
24521 }
24522 this._validate(year, month, this.minDay, main.local.invalidMonth);
24523 return (month === 12 && this.leapYear(year) ? 30 : // Adar I
24524 (month === 8 && mod(this.daysInYear(year), 10) === 5 ? 30 : // Cheshvan in shlemah year
24525 (month === 9 && mod(this.daysInYear(year), 10) === 3 ? 29 : // Kislev in chaserah year
24526 this.daysPerMonth[month - 1])));
24527 },
24528
24529 /** Determine whether this date is a week day.
24530 @memberof HebrewCalendar
24531 @param year {CDate|number} The date to examine or the year to examine.
24532 @param [month] {number} The month to examine.
24533 @param [day] {number} The day to examine.
24534 @return {boolean} <code>true</code> if a week day, <code>false</code> if not.
24535 @throws Error if an invalid date or a different calendar used. */
24536 weekDay: function(year, month, day) {
24537 return this.dayOfWeek(year, month, day) !== 6;
24538 },
24539
24540 /** Retrieve additional information about a date - year type.
24541 @memberof HebrewCalendar
24542 @param year {CDate|number} The date to examine or the year to examine.
24543 @param [month] {number} The month to examine.
24544 @param [day] {number} The day to examine.
24545 @return {object} Additional information - contents depends on calendar.
24546 @throws Error if an invalid date or a different calendar used. */
24547 extraInfo: function(year, month, day) {
24548 var date = this._validate(year, month, day, main.local.invalidDate);
24549 return {yearType: (this.leapYear(date) ? 'embolismic' : 'common') + ' ' +
24550 ['deficient', 'regular', 'complete'][this.daysInYear(date) % 10 - 3]};
24551 },
24552
24553 /** Retrieve the Julian date equivalent for this date,
24554 i.e. days since January 1, 4713 BCE Greenwich noon.
24555 @memberof HebrewCalendar
24556 @param year {CDate)|number} The date to convert or the year to convert.
24557 @param [month] {number} The month to convert.
24558 @param [day] {number} The day to convert.
24559 @return {number} The equivalent Julian date.
24560 @throws Error if an invalid date or a different calendar used. */
24561 toJD: function(year, month, day) {
24562 var date = this._validate(year, month, day, main.local.invalidDate);
24563 year = date.year();
24564 month = date.month();
24565 day = date.day();
24566 var adjYear = (year <= 0 ? year + 1 : year);
24567 var jd = this.jdEpoch + this._delay1(adjYear) +
24568 this._delay2(adjYear) + day + 1;
24569 if (month < 7) {
24570 for (var m = 7; m <= this.monthsInYear(year); m++) {
24571 jd += this.daysInMonth(year, m);
24572 }
24573 for (var m = 1; m < month; m++) {
24574 jd += this.daysInMonth(year, m);
24575 }
24576 }
24577 else {
24578 for (var m = 7; m < month; m++) {
24579 jd += this.daysInMonth(year, m);
24580 }
24581 }
24582 return jd;
24583 },
24584
24585 /** Test for delay of start of new year and to avoid
24586 Sunday, Wednesday, or Friday as start of the new year.
24587 @memberof HebrewCalendar
24588 @private
24589 @param year {number} The year to examine.
24590 @return {number} The days to offset by. */
24591 _delay1: function(year) {
24592 var months = Math.floor((235 * year - 234) / 19);
24593 var parts = 12084 + 13753 * months;
24594 var day = months * 29 + Math.floor(parts / 25920);
24595 if (mod(3 * (day + 1), 7) < 3) {
24596 day++;
24597 }
24598 return day;
24599 },
24600
24601 /** Check for delay in start of new year due to length of adjacent years.
24602 @memberof HebrewCalendar
24603 @private
24604 @param year {number} The year to examine.
24605 @return {number} The days to offset by. */
24606 _delay2: function(year) {
24607 var last = this._delay1(year - 1);
24608 var present = this._delay1(year);
24609 var next = this._delay1(year + 1);
24610 return ((next - present) === 356 ? 2 : ((present - last) === 382 ? 1 : 0));
24611 },
24612
24613 /** Create a new date from a Julian date.
24614 @memberof HebrewCalendar
24615 @param jd {number} The Julian date to convert.
24616 @return {CDate} The equivalent date. */
24617 fromJD: function(jd) {
24618 jd = Math.floor(jd) + 0.5;
24619 var year = Math.floor(((jd - this.jdEpoch) * 98496.0) / 35975351.0) - 1;
24620 while (jd >= this.toJD((year === -1 ? +1 : year + 1), 7, 1)) {
24621 year++;
24622 }
24623 var month = (jd < this.toJD(year, 1, 1)) ? 7 : 1;
24624 while (jd > this.toJD(year, month, this.daysInMonth(year, month))) {
24625 month++;
24626 }
24627 var day = jd - this.toJD(year, month, 1) + 1;
24628 return this.newDate(year, month, day);
24629 }
24630});
24631
24632// Modulus function which works for non-integers.
24633function mod(a, b) {
24634 return a - (b * Math.floor(a / b));
24635}
24636
24637// Hebrew calendar implementation
24638main.calendars.hebrew = HebrewCalendar;
24639
24640
24641},{"../main":137,"object-assign":73}],128:[function(_dereq_,module,exports){
24642/*
24643 * World Calendars
24644 * https://github.com/alexcjohnson/world-calendars
24645 *
24646 * Batch-converted from kbwood/calendars
24647 * Many thanks to Keith Wood and all of the contributors to the original project!
24648 *
24649 * This source code is licensed under the MIT license found in the
24650 * LICENSE file in the root directory of this source tree.
24651 */
24652
24653/* http://keith-wood.name/calendars.html
24654 Islamic calendar for jQuery v2.0.2.
24655 Written by Keith Wood (wood.keith{at}optusnet.com.au) August 2009.
24656 Available under the MIT (http://keith-wood.name/licence.html) license.
24657 Please attribute the author if you use it. */
24658
24659var main = _dereq_('../main');
24660var assign = _dereq_('object-assign');
24661
24662
24663/** Implementation of the Islamic or '16 civil' calendar.
24664 Based on code from <a href="http://www.iranchamber.com/calendar/converter/iranian_calendar_converter.php">http://www.iranchamber.com/calendar/converter/iranian_calendar_converter.php</a>.
24665 See also <a href="http://en.wikipedia.org/wiki/Islamic_calendar">http://en.wikipedia.org/wiki/Islamic_calendar</a>.
24666 @class IslamicCalendar
24667 @param [language=''] {string} The language code (default English) for localisation. */
24668function IslamicCalendar(language) {
24669 this.local = this.regionalOptions[language || ''] || this.regionalOptions[''];
24670}
24671
24672IslamicCalendar.prototype = new main.baseCalendar;
24673
24674assign(IslamicCalendar.prototype, {
24675 /** The calendar name.
24676 @memberof IslamicCalendar */
24677 name: 'Islamic',
24678 /** Julian date of start of Islamic epoch: 16 July 622 CE.
24679 @memberof IslamicCalendar */
24680 jdEpoch: 1948439.5,
24681 /** Days per month in a common year.
24682 @memberof IslamicCalendar */
24683 daysPerMonth: [30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29],
24684 /** <code>true</code> if has a year zero, <code>false</code> if not.
24685 @memberof IslamicCalendar */
24686 hasYearZero: false,
24687 /** The minimum month number.
24688 @memberof IslamicCalendar */
24689 minMonth: 1,
24690 /** The first month in the year.
24691 @memberof IslamicCalendar */
24692 firstMonth: 1,
24693 /** The minimum day number.
24694 @memberof IslamicCalendar */
24695 minDay: 1,
24696
24697 /** Localisations for the plugin.
24698 Entries are objects indexed by the language code ('' being the default US/English).
24699 Each object has the following attributes.
24700 @memberof IslamicCalendar
24701 @property name {string} The calendar name.
24702 @property epochs {string[]} The epoch names.
24703 @property monthNames {string[]} The long names of the months of the year.
24704 @property monthNamesShort {string[]} The short names of the months of the year.
24705 @property dayNames {string[]} The long names of the days of the week.
24706 @property dayNamesShort {string[]} The short names of the days of the week.
24707 @property dayNamesMin {string[]} The minimal names of the days of the week.
24708 @property dateFormat {string} The date format for this calendar.
24709 See the options on <a href="BaseCalendar.html#formatDate"><code>formatDate</code></a> for details.
24710 @property firstDay {number} The number of the first day of the week, starting at 0.
24711 @property isRTL {number} <code>true</code> if this localisation reads right-to-left. */
24712 regionalOptions: { // Localisations
24713 '': {
24714 name: 'Islamic',
24715 epochs: ['BH', 'AH'],
24716 monthNames: ['Muharram', 'Safar', 'Rabi\' al-awwal', 'Rabi\' al-thani', 'Jumada al-awwal', 'Jumada al-thani',
24717 'Rajab', 'Sha\'aban', 'Ramadan', 'Shawwal', 'Dhu al-Qi\'dah', 'Dhu al-Hijjah'],
24718 monthNamesShort: ['Muh', 'Saf', 'Rab1', 'Rab2', 'Jum1', 'Jum2', 'Raj', 'Sha\'', 'Ram', 'Shaw', 'DhuQ', 'DhuH'],
24719 dayNames: ['Yawm al-ahad', 'Yawm al-ithnayn', 'Yawm ath-thulaathaa\'',
24720 'Yawm al-arbi\'aa\'', 'Yawm al-khamīs', 'Yawm al-jum\'a', 'Yawm as-sabt'],
24721 dayNamesShort: ['Aha', 'Ith', 'Thu', 'Arb', 'Kha', 'Jum', 'Sab'],
24722 dayNamesMin: ['Ah','It','Th','Ar','Kh','Ju','Sa'],
24723 digits: null,
24724 dateFormat: 'yyyy/mm/dd',
24725 firstDay: 6,
24726 isRTL: false
24727 }
24728 },
24729
24730 /** Determine whether this date is in a leap year.
24731 @memberof IslamicCalendar
24732 @param year {CDate|number} The date to examine or the year to examine.
24733 @return {boolean} <code>true</code> if this is a leap year, <code>false</code> if not.
24734 @throws Error if an invalid year or a different calendar used. */
24735 leapYear: function(year) {
24736 var date = this._validate(year, this.minMonth, this.minDay, main.local.invalidYear);
24737 return (date.year() * 11 + 14) % 30 < 11;
24738 },
24739
24740 /** Determine the week of the year for a date.
24741 @memberof IslamicCalendar
24742 @param year {CDate|number} The date to examine or the year to examine.
24743 @param [month] {number} The month to examine.
24744 @param [day] {number} The day to examine.
24745 @return {number} The week of the year.
24746 @throws Error if an invalid date or a different calendar used. */
24747 weekOfYear: function(year, month, day) {
24748 // Find Sunday of this week starting on Sunday
24749 var checkDate = this.newDate(year, month, day);
24750 checkDate.add(-checkDate.dayOfWeek(), 'd');
24751 return Math.floor((checkDate.dayOfYear() - 1) / 7) + 1;
24752 },
24753
24754 /** Retrieve the number of days in a year.
24755 @memberof IslamicCalendar
24756 @param year {CDate|number} The date to examine or the year to examine.
24757 @return {number} The number of days.
24758 @throws Error if an invalid year or a different calendar used. */
24759 daysInYear: function(year) {
24760 return (this.leapYear(year) ? 355 : 354);
24761 },
24762
24763 /** Retrieve the number of days in a month.
24764 @memberof IslamicCalendar
24765 @param year {CDate|number} The date to examine or the year of the month.
24766 @param [month] {number} The month.
24767 @return {number} The number of days in this month.
24768 @throws Error if an invalid month/year or a different calendar used. */
24769 daysInMonth: function(year, month) {
24770 var date = this._validate(year, month, this.minDay, main.local.invalidMonth);
24771 return this.daysPerMonth[date.month() - 1] +
24772 (date.month() === 12 && this.leapYear(date.year()) ? 1 : 0);
24773 },
24774
24775 /** Determine whether this date is a week day.
24776 @memberof IslamicCalendar
24777 @param year {CDate|number} The date to examine or the year to examine.
24778 @param [month] {number} The month to examine.
24779 @param [day] {number} The day to examine.
24780 @return {boolean} <code>true</code> if a week day, <code>false</code> if not.
24781 @throws Error if an invalid date or a different calendar used. */
24782 weekDay: function(year, month, day) {
24783 return this.dayOfWeek(year, month, day) !== 5;
24784 },
24785
24786 /** Retrieve the Julian date equivalent for this date,
24787 i.e. days since January 1, 4713 BCE Greenwich noon.
24788 @memberof IslamicCalendar
24789 @param year {CDate|number} The date to convert or the year to convert.
24790 @param [month] {number} The month to convert.
24791 @param [day] {number} The day to convert.
24792 @return {number} The equivalent Julian date.
24793 @throws Error if an invalid date or a different calendar used. */
24794 toJD: function(year, month, day) {
24795 var date = this._validate(year, month, day, main.local.invalidDate);
24796 year = date.year();
24797 month = date.month();
24798 day = date.day();
24799 year = (year <= 0 ? year + 1 : year);
24800 return day + Math.ceil(29.5 * (month - 1)) + (year - 1) * 354 +
24801 Math.floor((3 + (11 * year)) / 30) + this.jdEpoch - 1;
24802 },
24803
24804 /** Create a new date from a Julian date.
24805 @memberof IslamicCalendar
24806 @param jd {number} The Julian date to convert.
24807 @return {CDate} The equivalent date. */
24808 fromJD: function(jd) {
24809 jd = Math.floor(jd) + 0.5;
24810 var year = Math.floor((30 * (jd - this.jdEpoch) + 10646) / 10631);
24811 year = (year <= 0 ? year - 1 : year);
24812 var month = Math.min(12, Math.ceil((jd - 29 - this.toJD(year, 1, 1)) / 29.5) + 1);
24813 var day = jd - this.toJD(year, month, 1) + 1;
24814 return this.newDate(year, month, day);
24815 }
24816});
24817
24818// Islamic (16 civil) calendar implementation
24819main.calendars.islamic = IslamicCalendar;
24820
24821
24822},{"../main":137,"object-assign":73}],129:[function(_dereq_,module,exports){
24823/*
24824 * World Calendars
24825 * https://github.com/alexcjohnson/world-calendars
24826 *
24827 * Batch-converted from kbwood/calendars
24828 * Many thanks to Keith Wood and all of the contributors to the original project!
24829 *
24830 * This source code is licensed under the MIT license found in the
24831 * LICENSE file in the root directory of this source tree.
24832 */
24833
24834/* http://keith-wood.name/calendars.html
24835 Julian calendar for jQuery v2.0.2.
24836 Written by Keith Wood (wood.keith{at}optusnet.com.au) August 2009.
24837 Available under the MIT (http://keith-wood.name/licence.html) license.
24838 Please attribute the author if you use it. */
24839
24840var main = _dereq_('../main');
24841var assign = _dereq_('object-assign');
24842
24843
24844/** Implementation of the Julian calendar.
24845 Based on code from <a href="http://www.fourmilab.ch/documents/calendar/">http://www.fourmilab.ch/documents/calendar/</a>.
24846 See also <a href="http://en.wikipedia.org/wiki/Julian_calendar">http://en.wikipedia.org/wiki/Julian_calendar</a>.
24847 @class JulianCalendar
24848 @augments BaseCalendar
24849 @param [language=''] {string} The language code (default English) for localisation. */
24850function JulianCalendar(language) {
24851 this.local = this.regionalOptions[language || ''] || this.regionalOptions[''];
24852}
24853
24854JulianCalendar.prototype = new main.baseCalendar;
24855
24856assign(JulianCalendar.prototype, {
24857 /** The calendar name.
24858 @memberof JulianCalendar */
24859 name: 'Julian',
24860 /** Julian date of start of Julian epoch: 1 January 0001 AD = 30 December 0001 BCE.
24861 @memberof JulianCalendar */
24862 jdEpoch: 1721423.5,
24863 /** Days per month in a common year.
24864 @memberof JulianCalendar */
24865 daysPerMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
24866 /** <code>true</code> if has a year zero, <code>false</code> if not.
24867 @memberof JulianCalendar */
24868 hasYearZero: false,
24869 /** The minimum month number.
24870 @memberof JulianCalendar */
24871 minMonth: 1,
24872 /** The first month in the year.
24873 @memberof JulianCalendar */
24874 firstMonth: 1,
24875 /** The minimum day number.
24876 @memberof JulianCalendar */
24877 minDay: 1,
24878
24879 /** Localisations for the plugin.
24880 Entries are objects indexed by the language code ('' being the default US/English).
24881 Each object has the following attributes.
24882 @memberof JulianCalendar
24883 @property name {string} The calendar name.
24884 @property epochs {string[]} The epoch names.
24885 @property monthNames {string[]} The long names of the months of the year.
24886 @property monthNamesShort {string[]} The short names of the months of the year.
24887 @property dayNames {string[]} The long names of the days of the week.
24888 @property dayNamesShort {string[]} The short names of the days of the week.
24889 @property dayNamesMin {string[]} The minimal names of the days of the week.
24890 @property dateFormat {string} The date format for this calendar.
24891 See the options on <a href="BaseCalendar.html#formatDate"><code>formatDate</code></a> for details.
24892 @property firstDay {number} The number of the first day of the week, starting at 0.
24893 @property isRTL {number} <code>true</code> if this localisation reads right-to-left. */
24894 regionalOptions: { // Localisations
24895 '': {
24896 name: 'Julian',
24897 epochs: ['BC', 'AD'],
24898 monthNames: ['January', 'February', 'March', 'April', 'May', 'June',
24899 'July', 'August', 'September', 'October', 'November', 'December'],
24900 monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
24901 dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
24902 dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
24903 dayNamesMin: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
24904 digits: null,
24905 dateFormat: 'mm/dd/yyyy',
24906 firstDay: 0,
24907 isRTL: false
24908 }
24909 },
24910
24911 /** Determine whether this date is in a leap year.
24912 @memberof JulianCalendar
24913 @param year {CDate|number} The date to examine or the year to examine.
24914 @return {boolean} <code>true</code> if this is a leap year, <code>false</code> if not.
24915 @throws Error if an invalid year or a different calendar used. */
24916 leapYear: function(year) {
24917 var date = this._validate(year, this.minMonth, this.minDay, main.local.invalidYear);
24918 var year = (date.year() < 0 ? date.year() + 1 : date.year()); // No year zero
24919 return (year % 4) === 0;
24920 },
24921
24922 /** Determine the week of the year for a date - ISO 8601.
24923 @memberof JulianCalendar
24924 @param year {CDate|number} The date to examine or the year to examine.
24925 @param [month] {number} The month to examine.
24926 @param [day] {number} The day to examine.
24927 @return {number} The week of the year.
24928 @throws Error if an invalid date or a different calendar used. */
24929 weekOfYear: function(year, month, day) {
24930 // Find Thursday of this week starting on Monday
24931 var checkDate = this.newDate(year, month, day);
24932 checkDate.add(4 - (checkDate.dayOfWeek() || 7), 'd');
24933 return Math.floor((checkDate.dayOfYear() - 1) / 7) + 1;
24934 },
24935
24936 /** Retrieve the number of days in a month.
24937 @memberof JulianCalendar
24938 @param year {CDate|number} The date to examine or the year of the month.
24939 @param [month] {number} The month.
24940 @return {number} The number of days in this month.
24941 @throws Error if an invalid month/year or a different calendar used. */
24942 daysInMonth: function(year, month) {
24943 var date = this._validate(year, month, this.minDay, main.local.invalidMonth);
24944 return this.daysPerMonth[date.month() - 1] +
24945 (date.month() === 2 && this.leapYear(date.year()) ? 1 : 0);
24946 },
24947
24948 /** Determine whether this date is a week day.
24949 @memberof JulianCalendar
24950 @param year {CDate|number} The date to examine or the year to examine.
24951 @param [month] {number} The month to examine.
24952 @param [day] {number} The day to examine.
24953 @return {boolean} True if a week day, false if not.
24954 @throws Error if an invalid date or a different calendar used. */
24955 weekDay: function(year, month, day) {
24956 return (this.dayOfWeek(year, month, day) || 7) < 6;
24957 },
24958
24959 /** Retrieve the Julian date equivalent for this date,
24960 i.e. days since January 1, 4713 BCE Greenwich noon.
24961 @memberof JulianCalendar
24962 @param year {CDate|number} The date to convert or the year to convert.
24963 @param [month] {number} The month to convert.
24964 @param [day] {number} The day to convert.
24965 @return {number} The equivalent Julian date.
24966 @throws Error if an invalid date or a different calendar used. */
24967 toJD: function(year, month, day) {
24968 var date = this._validate(year, month, day, main.local.invalidDate);
24969 year = date.year();
24970 month = date.month();
24971 day = date.day();
24972 if (year < 0) { year++; } // No year zero
24973 // Jean Meeus algorithm, "Astronomical Algorithms", 1991
24974 if (month <= 2) {
24975 year--;
24976 month += 12;
24977 }
24978 return Math.floor(365.25 * (year + 4716)) +
24979 Math.floor(30.6001 * (month + 1)) + day - 1524.5;
24980 },
24981
24982 /** Create a new date from a Julian date.
24983 @memberof JulianCalendar
24984 @param jd {number} The Julian date to convert.
24985 @return {CDate} The equivalent date. */
24986 fromJD: function(jd) {
24987 // Jean Meeus algorithm, "Astronomical Algorithms", 1991
24988 var a = Math.floor(jd + 0.5);
24989 var b = a + 1524;
24990 var c = Math.floor((b - 122.1) / 365.25);
24991 var d = Math.floor(365.25 * c);
24992 var e = Math.floor((b - d) / 30.6001);
24993 var month = e - Math.floor(e < 14 ? 1 : 13);
24994 var year = c - Math.floor(month > 2 ? 4716 : 4715);
24995 var day = b - d - Math.floor(30.6001 * e);
24996 if (year <= 0) { year--; } // No year zero
24997 return this.newDate(year, month, day);
24998 }
24999});
25000
25001// Julian calendar implementation
25002main.calendars.julian = JulianCalendar;
25003
25004
25005},{"../main":137,"object-assign":73}],130:[function(_dereq_,module,exports){
25006/*
25007 * World Calendars
25008 * https://github.com/alexcjohnson/world-calendars
25009 *
25010 * Batch-converted from kbwood/calendars
25011 * Many thanks to Keith Wood and all of the contributors to the original project!
25012 *
25013 * This source code is licensed under the MIT license found in the
25014 * LICENSE file in the root directory of this source tree.
25015 */
25016
25017/* http://keith-wood.name/calendars.html
25018 Mayan calendar for jQuery v2.0.2.
25019 Written by Keith Wood (wood.keith{at}optusnet.com.au) August 2009.
25020 Available under the MIT (http://keith-wood.name/licence.html) license.
25021 Please attribute the author if you use it. */
25022
25023var main = _dereq_('../main');
25024var assign = _dereq_('object-assign');
25025
25026
25027/** Implementation of the Mayan Long Count calendar.
25028 See also <a href="http://en.wikipedia.org/wiki/Mayan_calendar">http://en.wikipedia.org/wiki/Mayan_calendar</a>.
25029 @class MayanCalendar
25030 @param [language=''] {string} The language code (default English) for localisation. */
25031function MayanCalendar(language) {
25032 this.local = this.regionalOptions[language || ''] || this.regionalOptions[''];
25033}
25034
25035MayanCalendar.prototype = new main.baseCalendar;
25036
25037assign(MayanCalendar.prototype, {
25038 /** The calendar name.
25039 @memberof MayanCalendar */
25040 name: 'Mayan',
25041 /** Julian date of start of Mayan epoch: 11 August 3114 BCE.
25042 @memberof MayanCalendar */
25043 jdEpoch: 584282.5,
25044 /** <code>true</code> if has a year zero, <code>false</code> if not.
25045 @memberof MayanCalendar */
25046 hasYearZero: true,
25047 /** The minimum month number.
25048 @memberof MayanCalendar */
25049 minMonth: 0,
25050 /** The first month in the year.
25051 @memberof MayanCalendar */
25052 firstMonth: 0,
25053 /** The minimum day number.
25054 @memberof MayanCalendar */
25055 minDay: 0,
25056
25057 /** Localisations for the plugin.
25058 Entries are objects indexed by the language code ('' being the default US/English).
25059 Each object has the following attributes.
25060 @memberof MayanCalendar
25061 @property name {string} The calendar name.
25062 @property epochs {string[]} The epoch names.
25063 @property monthNames {string[]} The long names of the months of the year.
25064 @property monthNamesShort {string[]} The short names of the months of the year.
25065 @property dayNames {string[]} The long names of the days of the week.
25066 @property dayNamesShort {string[]} The short names of the days of the week.
25067 @property dayNamesMin {string[]} The minimal names of the days of the week.
25068 @property dateFormat {string} The date format for this calendar.
25069 See the options on <a href="BaseCalendar.html#formatDate"><code>formatDate</code></a> for details.
25070 @property firstDay {number} The number of the first day of the week, starting at 0.
25071 @property isRTL {number} <code>true</code> if this localisation reads right-to-left.
25072 @property haabMonths {string[]} The names of the Haab months.
25073 @property tzolkinMonths {string[]} The names of the Tzolkin months. */
25074 regionalOptions: { // Localisations
25075 '': {
25076 name: 'Mayan',
25077 epochs: ['', ''],
25078 monthNames: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
25079 '10', '11', '12', '13', '14', '15', '16', '17'],
25080 monthNamesShort: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
25081 '10', '11', '12', '13', '14', '15', '16', '17'],
25082 dayNames: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
25083 '10', '11', '12', '13', '14', '15', '16', '17', '18', '19'],
25084 dayNamesShort: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
25085 '10', '11', '12', '13', '14', '15', '16', '17', '18', '19'],
25086 dayNamesMin: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
25087 '10', '11', '12', '13', '14', '15', '16', '17', '18', '19'],
25088 digits: null,
25089 dateFormat: 'YYYY.m.d',
25090 firstDay: 0,
25091 isRTL: false,
25092 haabMonths: ['Pop', 'Uo', 'Zip', 'Zotz', 'Tzec', 'Xul', 'Yaxkin', 'Mol', 'Chen', 'Yax',
25093 'Zac', 'Ceh', 'Mac', 'Kankin', 'Muan', 'Pax', 'Kayab', 'Cumku', 'Uayeb'],
25094 tzolkinMonths: ['Imix', 'Ik', 'Akbal', 'Kan', 'Chicchan', 'Cimi', 'Manik', 'Lamat', 'Muluc', 'Oc',
25095 'Chuen', 'Eb', 'Ben', 'Ix', 'Men', 'Cib', 'Caban', 'Etznab', 'Cauac', 'Ahau']
25096 }
25097 },
25098
25099 /** Determine whether this date is in a leap year.
25100 @memberof MayanCalendar
25101 @param year {CDate|number} The date to examine or the year to examine.
25102 @return {boolean} <code>true</code> if this is a leap year, <code>false</code> if not.
25103 @throws Error if an invalid year or a different calendar used. */
25104 leapYear: function(year) {
25105 this._validate(year, this.minMonth, this.minDay, main.local.invalidYear);
25106 return false;
25107 },
25108
25109 /** Format the year, if not a simple sequential number.
25110 @memberof MayanCalendar
25111 @param year {CDate|number} The date to format or the year to format.
25112 @return {string} The formatted year.
25113 @throws Error if an invalid year or a different calendar used. */
25114 formatYear: function(year) {
25115 var date = this._validate(year, this.minMonth, this.minDay, main.local.invalidYear);
25116 year = date.year();
25117 var baktun = Math.floor(year / 400);
25118 year = year % 400;
25119 year += (year < 0 ? 400 : 0);
25120 var katun = Math.floor(year / 20);
25121 return baktun + '.' + katun + '.' + (year % 20);
25122 },
25123
25124 /** Convert from the formatted year back to a single number.
25125 @memberof MayanCalendar
25126 @param years {string} The year as n.n.n.
25127 @return {number} The sequential year.
25128 @throws Error if an invalid value is supplied. */
25129 forYear: function(years) {
25130 years = years.split('.');
25131 if (years.length < 3) {
25132 throw 'Invalid Mayan year';
25133 }
25134 var year = 0;
25135 for (var i = 0; i < years.length; i++) {
25136 var y = parseInt(years[i], 10);
25137 if (Math.abs(y) > 19 || (i > 0 && y < 0)) {
25138 throw 'Invalid Mayan year';
25139 }
25140 year = year * 20 + y;
25141 }
25142 return year;
25143 },
25144
25145 /** Retrieve the number of months in a year.
25146 @memberof MayanCalendar
25147 @param year {CDate|number} The date to examine or the year to examine.
25148 @return {number} The number of months.
25149 @throws Error if an invalid year or a different calendar used. */
25150 monthsInYear: function(year) {
25151 this._validate(year, this.minMonth, this.minDay, main.local.invalidYear);
25152 return 18;
25153 },
25154
25155 /** Determine the week of the year for a date.
25156 @memberof MayanCalendar
25157 @param year {CDate|number} The date to examine or the year to examine.
25158 @param [month] {number} The month to examine.
25159 @param [day] {number} The day to examine.
25160 @return {number} The week of the year.
25161 @throws Error if an invalid date or a different calendar used. */
25162 weekOfYear: function(year, month, day) {
25163 this._validate(year, month, day, main.local.invalidDate);
25164 return 0;
25165 },
25166
25167 /** Retrieve the number of days in a year.
25168 @memberof MayanCalendar
25169 @param year {CDate|number} The date to examine or the year to examine.
25170 @return {number} The number of days.
25171 @throws Error if an invalid year or a different calendar used. */
25172 daysInYear: function(year) {
25173 this._validate(year, this.minMonth, this.minDay, main.local.invalidYear);
25174 return 360;
25175 },
25176
25177 /** Retrieve the number of days in a month.
25178 @memberof MayanCalendar
25179 @param year {CDate|number} The date to examine or the year of the month.
25180 @param [month] {number} The month.
25181 @return {number} The number of days in this month.
25182 @throws Error if an invalid month/year or a different calendar used. */
25183 daysInMonth: function(year, month) {
25184 this._validate(year, month, this.minDay, main.local.invalidMonth);
25185 return 20;
25186 },
25187
25188 /** Retrieve the number of days in a week.
25189 @memberof MayanCalendar
25190 @return {number} The number of days. */
25191 daysInWeek: function() {
25192 return 5; // Just for formatting
25193 },
25194
25195 /** Retrieve the day of the week for a date.
25196 @memberof MayanCalendar
25197 @param year {CDate|number} The date to examine or the year to examine.
25198 @param [month] {number} The month to examine.
25199 @param [day] {number} The day to examine.
25200 @return {number} The day of the week: 0 to number of days - 1.
25201 @throws Error if an invalid date or a different calendar used. */
25202 dayOfWeek: function(year, month, day) {
25203 var date = this._validate(year, month, day, main.local.invalidDate);
25204 return date.day();
25205 },
25206
25207 /** Determine whether this date is a week day.
25208 @memberof MayanCalendar
25209 @param year {CDate|number} The date to examine or the year to examine.
25210 @param [month] {number} The month to examine.
25211 @param [day] {number} The day to examine.
25212 @return {boolean} <code>true</code> if a week day, <code>false</code> if not.
25213 @throws Error if an invalid date or a different calendar used. */
25214 weekDay: function(year, month, day) {
25215 this._validate(year, month, day, main.local.invalidDate);
25216 return true;
25217 },
25218
25219 /** Retrieve additional information about a date - Haab and Tzolkin equivalents.
25220 @memberof MayanCalendar
25221 @param year {CDate|number} The date to examine or the year to examine.
25222 @param [month] {number} The month to examine.
25223 @param [day] {number} The day to examine.
25224 @return {object} Additional information - contents depends on calendar.
25225 @throws Error if an invalid date or a different calendar used. */
25226 extraInfo: function(year, month, day) {
25227 var date = this._validate(year, month, day, main.local.invalidDate);
25228 var jd = date.toJD();
25229 var haab = this._toHaab(jd);
25230 var tzolkin = this._toTzolkin(jd);
25231 return {haabMonthName: this.local.haabMonths[haab[0] - 1],
25232 haabMonth: haab[0], haabDay: haab[1],
25233 tzolkinDayName: this.local.tzolkinMonths[tzolkin[0] - 1],
25234 tzolkinDay: tzolkin[0], tzolkinTrecena: tzolkin[1]};
25235 },
25236
25237 /** Retrieve Haab date from a Julian date.
25238 @memberof MayanCalendar
25239 @private
25240 @param jd {number} The Julian date.
25241 @return {number[]} Corresponding Haab month and day. */
25242 _toHaab: function(jd) {
25243 jd -= this.jdEpoch;
25244 var day = mod(jd + 8 + ((18 - 1) * 20), 365);
25245 return [Math.floor(day / 20) + 1, mod(day, 20)];
25246 },
25247
25248 /** Retrieve Tzolkin date from a Julian date.
25249 @memberof MayanCalendar
25250 @private
25251 @param jd {number} The Julian date.
25252 @return {number[]} Corresponding Tzolkin day and trecena. */
25253 _toTzolkin: function(jd) {
25254 jd -= this.jdEpoch;
25255 return [amod(jd + 20, 20), amod(jd + 4, 13)];
25256 },
25257
25258 /** Retrieve the Julian date equivalent for this date,
25259 i.e. days since January 1, 4713 BCE Greenwich noon.
25260 @memberof MayanCalendar
25261 @param year {CDate|number} The date to convert or the year to convert.
25262 @param [month] {number} The month to convert.
25263 @param [day] {number} The day to convert.
25264 @return {number} The equivalent Julian date.
25265 @throws Error if an invalid date or a different calendar used. */
25266 toJD: function(year, month, day) {
25267 var date = this._validate(year, month, day, main.local.invalidDate);
25268 return date.day() + (date.month() * 20) + (date.year() * 360) + this.jdEpoch;
25269 },
25270
25271 /** Create a new date from a Julian date.
25272 @memberof MayanCalendar
25273 @param jd {number} The Julian date to convert.
25274 @return {CDate} The equivalent date. */
25275 fromJD: function(jd) {
25276 jd = Math.floor(jd) + 0.5 - this.jdEpoch;
25277 var year = Math.floor(jd / 360);
25278 jd = jd % 360;
25279 jd += (jd < 0 ? 360 : 0);
25280 var month = Math.floor(jd / 20);
25281 var day = jd % 20;
25282 return this.newDate(year, month, day);
25283 }
25284});
25285
25286// Modulus function which works for non-integers.
25287function mod(a, b) {
25288 return a - (b * Math.floor(a / b));
25289}
25290
25291// Modulus function which returns numerator if modulus is zero.
25292function amod(a, b) {
25293 return mod(a - 1, b) + 1;
25294}
25295
25296// Mayan calendar implementation
25297main.calendars.mayan = MayanCalendar;
25298
25299
25300},{"../main":137,"object-assign":73}],131:[function(_dereq_,module,exports){
25301/*
25302 * World Calendars
25303 * https://github.com/alexcjohnson/world-calendars
25304 *
25305 * Batch-converted from kbwood/calendars
25306 * Many thanks to Keith Wood and all of the contributors to the original project!
25307 *
25308 * This source code is licensed under the MIT license found in the
25309 * LICENSE file in the root directory of this source tree.
25310 */
25311
25312/* http://keith-wood.name/calendars.html
25313 Nanakshahi calendar for jQuery v2.0.2.
25314 Written by Keith Wood (wood.keith{at}optusnet.com.au) January 2016.
25315 Available under the MIT (http://keith-wood.name/licence.html) license.
25316 Please attribute the author if you use it. */
25317
25318var main = _dereq_('../main');
25319var assign = _dereq_('object-assign');
25320
25321
25322/** Implementation of the Nanakshahi calendar.
25323 See also <a href="https://en.wikipedia.org/wiki/Nanakshahi_calendar">https://en.wikipedia.org/wiki/Nanakshahi_calendar</a>.
25324 @class NanakshahiCalendar
25325 @param [language=''] {string} The language code (default English) for localisation. */
25326function NanakshahiCalendar(language) {
25327 this.local = this.regionalOptions[language || ''] || this.regionalOptions[''];
25328}
25329
25330NanakshahiCalendar.prototype = new main.baseCalendar;
25331
25332var gregorian = main.instance('gregorian');
25333
25334assign(NanakshahiCalendar.prototype, {
25335 /** The calendar name.
25336 @memberof NanakshahiCalendar */
25337 name: 'Nanakshahi',
25338 /** Julian date of start of Nanakshahi epoch: 14 March 1469 CE.
25339 @memberof NanakshahiCalendar */
25340 jdEpoch: 2257673.5,
25341 /** Days per month in a common year.
25342 @memberof NanakshahiCalendar */
25343 daysPerMonth: [31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 30, 30],
25344 /** <code>true</code> if has a year zero, <code>false</code> if not.
25345 @memberof NanakshahiCalendar */
25346 hasYearZero: false,
25347 /** The minimum month number.
25348 @memberof NanakshahiCalendar */
25349 minMonth: 1,
25350 /** The first month in the year.
25351 @memberof NanakshahiCalendar */
25352 firstMonth: 1,
25353 /** The minimum day number.
25354 @memberof NanakshahiCalendar */
25355 minDay: 1,
25356
25357 /** Localisations for the plugin.
25358 Entries are objects indexed by the language code ('' being the default US/English).
25359 Each object has the following attributes.
25360 @memberof NanakshahiCalendar
25361 @property name {string} The calendar name.
25362 @property epochs {string[]} The epoch names.
25363 @property monthNames {string[]} The long names of the months of the year.
25364 @property monthNamesShort {string[]} The short names of the months of the year.
25365 @property dayNames {string[]} The long names of the days of the week.
25366 @property dayNamesShort {string[]} The short names of the days of the week.
25367 @property dayNamesMin {string[]} The minimal names of the days of the week.
25368 @property dateFormat {string} The date format for this calendar.
25369 See the options on <a href="BaseCalendar.html#formatDate"><code>formatDate</code></a> for details.
25370 @property firstDay {number} The number of the first day of the week, starting at 0.
25371 @property isRTL {number} <code>true</code> if this localisation reads right-to-left. */
25372 regionalOptions: { // Localisations
25373 '': {
25374 name: 'Nanakshahi',
25375 epochs: ['BN', 'AN'],
25376 monthNames: ['Chet', 'Vaisakh', 'Jeth', 'Harh', 'Sawan', 'Bhadon',
25377 'Assu', 'Katak', 'Maghar', 'Poh', 'Magh', 'Phagun'],
25378 monthNamesShort: ['Che', 'Vai', 'Jet', 'Har', 'Saw', 'Bha', 'Ass', 'Kat', 'Mgr', 'Poh', 'Mgh', 'Pha'],
25379 dayNames: ['Somvaar', 'Mangalvar', 'Budhvaar', 'Veervaar', 'Shukarvaar', 'Sanicharvaar', 'Etvaar'],
25380 dayNamesShort: ['Som', 'Mangal', 'Budh', 'Veer', 'Shukar', 'Sanichar', 'Et'],
25381 dayNamesMin: ['So', 'Ma', 'Bu', 'Ve', 'Sh', 'Sa', 'Et'],
25382 digits: null,
25383 dateFormat: 'dd-mm-yyyy',
25384 firstDay: 0,
25385 isRTL: false
25386 }
25387 },
25388
25389 /** Determine whether this date is in a leap year.
25390 @memberof NanakshahiCalendar
25391 @param year {CDate|number} The date to examine or the year to examine.
25392 @return {boolean} <code>true</code> if this is a leap year, <code>false</code> if not.
25393 @throws Error if an invalid year or a different calendar used. */
25394 leapYear: function(year) {
25395 var date = this._validate(year, this.minMonth, this.minDay,
25396 main.local.invalidYear || main.regionalOptions[''].invalidYear);
25397 return gregorian.leapYear(date.year() + (date.year() < 1 ? 1 : 0) + 1469);
25398 },
25399
25400 /** Determine the week of the year for a date.
25401 @memberof NanakshahiCalendar
25402 @param year {CDate|number} The date to examine or the year to examine.
25403 @param [month] {number} The month to examine.
25404 @param [day] {number} The day to examine.
25405 @return {number} The week of the year.
25406 @throws Error if an invalid date or a different calendar used. */
25407 weekOfYear: function(year, month, day) {
25408 // Find Monday of this week starting on Monday
25409 var checkDate = this.newDate(year, month, day);
25410 checkDate.add(1 - (checkDate.dayOfWeek() || 7), 'd');
25411 return Math.floor((checkDate.dayOfYear() - 1) / 7) + 1;
25412 },
25413
25414 /** Retrieve the number of days in a month.
25415 @memberof NanakshahiCalendar
25416 @param year {CDate|number} The date to examine or the year of the month.
25417 @param [month] {number} The month.
25418 @return {number} The number of days in this month.
25419 @throws Error if an invalid month/year or a different calendar used. */
25420 daysInMonth: function(year, month) {
25421 var date = this._validate(year, month, this.minDay, main.local.invalidMonth);
25422 return this.daysPerMonth[date.month() - 1] +
25423 (date.month() === 12 && this.leapYear(date.year()) ? 1 : 0);
25424 },
25425
25426 /** Determine whether this date is a week day.
25427 @memberof NanakshahiCalendar
25428 @param year {CDate|number} The date to examine or the year to examine.
25429 @param [month] {number} The month to examine.
25430 @param [day] {number} The day to examine.
25431 @return {boolean} <code>true</code> if a week day, <code>false</code> if not.
25432 @throws Error if an invalid date or a different calendar used. */
25433 weekDay: function(year, month, day) {
25434 return (this.dayOfWeek(year, month, day) || 7) < 6;
25435 },
25436
25437 /** Retrieve the Julian date equivalent for this date,
25438 i.e. days since January 1, 4713 BCE Greenwich noon.
25439 @memberof NanakshahiCalendar
25440 @param year {CDate|number} The date to convert or the year to convert.
25441 @param [month] {number} The month to convert.
25442 @param [day] {number} The day to convert.
25443 @return {number} The equivalent Julian date.
25444 @throws Error if an invalid date or a different calendar used. */
25445 toJD: function(year, month, day) {
25446 var date = this._validate(year, month, day, main.local.invalidMonth);
25447 var year = date.year();
25448 if (year < 0) { year++; } // No year zero
25449 var doy = date.day();
25450 for (var m = 1; m < date.month(); m++) {
25451 doy += this.daysPerMonth[m - 1];
25452 }
25453 return doy + gregorian.toJD(year + 1468, 3, 13);
25454 },
25455
25456 /** Create a new date from a Julian date.
25457 @memberof NanakshahiCalendar
25458 @param jd {number} The Julian date to convert.
25459 @return {CDate} The equivalent date. */
25460 fromJD: function(jd) {
25461 jd = Math.floor(jd + 0.5);
25462 var year = Math.floor((jd - (this.jdEpoch - 1)) / 366);
25463 while (jd >= this.toJD(year + 1, 1, 1)) {
25464 year++;
25465 }
25466 var day = jd - Math.floor(this.toJD(year, 1, 1) + 0.5) + 1;
25467 var month = 1;
25468 while (day > this.daysInMonth(year, month)) {
25469 day -= this.daysInMonth(year, month);
25470 month++;
25471 }
25472 return this.newDate(year, month, day);
25473 }
25474});
25475
25476// Nanakshahi calendar implementation
25477main.calendars.nanakshahi = NanakshahiCalendar;
25478
25479
25480},{"../main":137,"object-assign":73}],132:[function(_dereq_,module,exports){
25481/*
25482 * World Calendars
25483 * https://github.com/alexcjohnson/world-calendars
25484 *
25485 * Batch-converted from kbwood/calendars
25486 * Many thanks to Keith Wood and all of the contributors to the original project!
25487 *
25488 * This source code is licensed under the MIT license found in the
25489 * LICENSE file in the root directory of this source tree.
25490 */
25491
25492/* http://keith-wood.name/calendars.html
25493 Nepali calendar for jQuery v2.0.2.
25494 Written by Artur Neumann (ict.projects{at}nepal.inf.org) April 2013.
25495 Available under the MIT (http://keith-wood.name/licence.html) license.
25496 Please attribute the author if you use it. */
25497
25498var main = _dereq_('../main');
25499var assign = _dereq_('object-assign');
25500
25501
25502/** Implementation of the Nepali civil calendar.
25503 Based on the ideas from
25504 <a href="http://codeissue.com/articles/a04e050dea7468f/algorithm-to-convert-english-date-to-nepali-date-using-c-net">http://codeissue.com/articles/a04e050dea7468f/algorithm-to-convert-english-date-to-nepali-date-using-c-net</a>
25505 and <a href="http://birenj2ee.blogspot.com/2011/04/nepali-calendar-in-java.html">http://birenj2ee.blogspot.com/2011/04/nepali-calendar-in-java.html</a>
25506 See also <a href="http://en.wikipedia.org/wiki/Nepali_calendar">http://en.wikipedia.org/wiki/Nepali_calendar</a>
25507 and <a href="https://en.wikipedia.org/wiki/Bikram_Samwat">https://en.wikipedia.org/wiki/Bikram_Samwat</a>.
25508 @class NepaliCalendar
25509 @param [language=''] {string} The language code (default English) for localisation. */
25510function NepaliCalendar(language) {
25511 this.local = this.regionalOptions[language || ''] || this.regionalOptions[''];
25512}
25513
25514NepaliCalendar.prototype = new main.baseCalendar;
25515
25516assign(NepaliCalendar.prototype, {
25517 /** The calendar name.
25518 @memberof NepaliCalendar */
25519 name: 'Nepali',
25520 /** Julian date of start of Nepali epoch: 14 April 57 BCE.
25521 @memberof NepaliCalendar */
25522 jdEpoch: 1700709.5,
25523 /** Days per month in a common year.
25524 @memberof NepaliCalendar */
25525 daysPerMonth: [31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30],
25526 /** <code>true</code> if has a year zero, <code>false</code> if not.
25527 @memberof NepaliCalendar */
25528 hasYearZero: false,
25529 /** The minimum month number.
25530 @memberof NepaliCalendar */
25531 minMonth: 1,
25532 /** The first month in the year.
25533 @memberof NepaliCalendar */
25534 firstMonth: 1,
25535 /** The minimum day number.
25536 @memberof NepaliCalendar */
25537 minDay: 1,
25538 /** The number of days in the year.
25539 @memberof NepaliCalendar */
25540 daysPerYear: 365,
25541
25542 /** Localisations for the plugin.
25543 Entries are objects indexed by the language code ('' being the default US/English).
25544 Each object has the following attributes.
25545 @memberof NepaliCalendar
25546 @property name {string} The calendar name.
25547 @property epochs {string[]} The epoch names.
25548 @property monthNames {string[]} The long names of the months of the year.
25549 @property monthNamesShort {string[]} The short names of the months of the year.
25550 @property dayNames {string[]} The long names of the days of the week.
25551 @property dayNamesShort {string[]} The short names of the days of the week.
25552 @property dayNamesMin {string[]} The minimal names of the days of the week.
25553 @property dateFormat {string} The date format for this calendar.
25554 See the options on <a href="BaseCalendar.html#formatDate"><code>formatDate</code></a> for details.
25555 @property firstDay {number} The number of the first day of the week, starting at 0.
25556 @property isRTL {number} <code>true</code> if this localisation reads right-to-left. */
25557 regionalOptions: { // Localisations
25558 '': {
25559 name: 'Nepali',
25560 epochs: ['BBS', 'ABS'],
25561 monthNames: ['Baisakh', 'Jestha', 'Ashadh', 'Shrawan', 'Bhadra', 'Ashwin',
25562 'Kartik', 'Mangsir', 'Paush', 'Mangh', 'Falgun', 'Chaitra'],
25563 monthNamesShort: ['Bai', 'Je', 'As', 'Shra', 'Bha', 'Ash', 'Kar', 'Mang', 'Pau', 'Ma', 'Fal', 'Chai'],
25564 dayNames: ['Aaitabaar', 'Sombaar', 'Manglbaar', 'Budhabaar', 'Bihibaar', 'Shukrabaar', 'Shanibaar'],
25565 dayNamesShort: ['Aaita', 'Som', 'Mangl', 'Budha', 'Bihi', 'Shukra', 'Shani'],
25566 dayNamesMin: ['Aai', 'So', 'Man', 'Bu', 'Bi', 'Shu', 'Sha'],
25567 digits: null,
25568 dateFormat: 'dd/mm/yyyy',
25569 firstDay: 1,
25570 isRTL: false
25571 }
25572 },
25573
25574 /** Determine whether this date is in a leap year.
25575 @memberof NepaliCalendar
25576 @param year {CDate|number} The date to examine or the year to examine.
25577 @return {boolean} <code>true</code> if this is a leap year, <code>false</code> if not.
25578 @throws Error if an invalid year or a different calendar used. */
25579 leapYear: function(year) {
25580 return this.daysInYear(year) !== this.daysPerYear;
25581 },
25582
25583 /** Determine the week of the year for a date.
25584 @memberof NepaliCalendar
25585 @param year {CDate|number} The date to examine or the year to examine.
25586 @param [month] {number} The month to examine.
25587 @param [day] {number} The day to examine.
25588 @return {number} The week of the year.
25589 @throws Error if an invalid date or a different calendar used. */
25590 weekOfYear: function(year, month, day) {
25591 // Find Sunday of this week starting on Sunday
25592 var checkDate = this.newDate(year, month, day);
25593 checkDate.add(-checkDate.dayOfWeek(), 'd');
25594 return Math.floor((checkDate.dayOfYear() - 1) / 7) + 1;
25595 },
25596
25597 /** Retrieve the number of days in a year.
25598 @memberof NepaliCalendar
25599 @param year {CDate|number} The date to examine or the year to examine.
25600 @return {number} The number of days.
25601 @throws Error if an invalid year or a different calendar used. */
25602 daysInYear: function(year) {
25603 var date = this._validate(year, this.minMonth, this.minDay, main.local.invalidYear);
25604 year = date.year();
25605 if (typeof this.NEPALI_CALENDAR_DATA[year] === 'undefined') {
25606 return this.daysPerYear;
25607 }
25608 var daysPerYear = 0;
25609 for (var month_number = this.minMonth; month_number <= 12; month_number++) {
25610 daysPerYear += this.NEPALI_CALENDAR_DATA[year][month_number];
25611 }
25612 return daysPerYear;
25613 },
25614
25615 /** Retrieve the number of days in a month.
25616 @memberof NepaliCalendar
25617 @param year {CDate|number| The date to examine or the year of the month.
25618 @param [month] {number} The month.
25619 @return {number} The number of days in this month.
25620 @throws Error if an invalid month/year or a different calendar used. */
25621 daysInMonth: function(year, month) {
25622 if (year.year) {
25623 month = year.month();
25624 year = year.year();
25625 }
25626 this._validate(year, month, this.minDay, main.local.invalidMonth);
25627 return (typeof this.NEPALI_CALENDAR_DATA[year] === 'undefined' ?
25628 this.daysPerMonth[month - 1] : this.NEPALI_CALENDAR_DATA[year][month]);
25629 },
25630
25631 /** Determine whether this date is a week day.
25632 @memberof NepaliCalendar
25633 @param year {CDate|number} The date to examine or the year to examine.
25634 @param [month] {number} The month to examine.
25635 @param [day] {number} The day to examine.
25636 @return {boolean} <code>true</code> if a week day, <code>false</code> if not.
25637 @throws Error if an invalid date or a different calendar used. */
25638 weekDay: function(year, month, day) {
25639 return this.dayOfWeek(year, month, day) !== 6;
25640 },
25641
25642 /** Retrieve the Julian date equivalent for this date,
25643 i.e. days since January 1, 4713 BCE Greenwich noon.
25644 @memberof NepaliCalendar
25645 @param year {CDate|number} The date to convert or the year to convert.
25646 @param [month] {number} The month to convert.
25647 @param [day] {number} The day to convert.
25648 @return {number} The equivalent Julian date.
25649 @throws Error if an invalid date or a different calendar used. */
25650 toJD: function(nepaliYear, nepaliMonth, nepaliDay) {
25651 var date = this._validate(nepaliYear, nepaliMonth, nepaliDay, main.local.invalidDate);
25652 nepaliYear = date.year();
25653 nepaliMonth = date.month();
25654 nepaliDay = date.day();
25655 var gregorianCalendar = main.instance();
25656 var gregorianDayOfYear = 0; // We will add all the days that went by since
25657 // the 1st. January and then we can get the Gregorian Date
25658 var nepaliMonthToCheck = nepaliMonth;
25659 var nepaliYearToCheck = nepaliYear;
25660 this._createMissingCalendarData(nepaliYear);
25661 // Get the correct year
25662 var gregorianYear = nepaliYear - (nepaliMonthToCheck > 9 || (nepaliMonthToCheck === 9 &&
25663 nepaliDay >= this.NEPALI_CALENDAR_DATA[nepaliYearToCheck][0]) ? 56 : 57);
25664 // First we add the amount of days in the actual Nepali month as the day of year in the
25665 // Gregorian one because at least this days are gone since the 1st. Jan.
25666 if (nepaliMonth !== 9) {
25667 gregorianDayOfYear = nepaliDay;
25668 nepaliMonthToCheck--;
25669 }
25670 // Now we loop throw all Nepali month and add the amount of days to gregorianDayOfYear
25671 // we do this till we reach Paush (9th month). 1st. January always falls in this month
25672 while (nepaliMonthToCheck !== 9) {
25673 if (nepaliMonthToCheck <= 0) {
25674 nepaliMonthToCheck = 12;
25675 nepaliYearToCheck--;
25676 }
25677 gregorianDayOfYear += this.NEPALI_CALENDAR_DATA[nepaliYearToCheck][nepaliMonthToCheck];
25678 nepaliMonthToCheck--;
25679 }
25680 // If the date that has to be converted is in Paush (month no. 9) we have to do some other calculation
25681 if (nepaliMonth === 9) {
25682 // Add the days that are passed since the first day of Paush and substract the
25683 // amount of days that lie between 1st. Jan and 1st Paush
25684 gregorianDayOfYear += nepaliDay - this.NEPALI_CALENDAR_DATA[nepaliYearToCheck][0];
25685 // For the first days of Paush we are now in negative values,
25686 // because in the end of the gregorian year we substract
25687 // 365 / 366 days (P.S. remember math in school + - gives -)
25688 if (gregorianDayOfYear < 0) {
25689 gregorianDayOfYear += gregorianCalendar.daysInYear(gregorianYear);
25690 }
25691 }
25692 else {
25693 gregorianDayOfYear += this.NEPALI_CALENDAR_DATA[nepaliYearToCheck][9] -
25694 this.NEPALI_CALENDAR_DATA[nepaliYearToCheck][0];
25695 }
25696 return gregorianCalendar.newDate(gregorianYear, 1 ,1).add(gregorianDayOfYear, 'd').toJD();
25697 },
25698
25699 /** Create a new date from a Julian date.
25700 @memberof NepaliCalendar
25701 @param jd {number} The Julian date to convert.
25702 @return {CDate} The equivalent date. */
25703 fromJD: function(jd) {
25704 var gregorianCalendar = main.instance();
25705 var gregorianDate = gregorianCalendar.fromJD(jd);
25706 var gregorianYear = gregorianDate.year();
25707 var gregorianDayOfYear = gregorianDate.dayOfYear();
25708 var nepaliYear = gregorianYear + 56; //this is not final, it could be also +57 but +56 is always true for 1st Jan.
25709 this._createMissingCalendarData(nepaliYear);
25710 var nepaliMonth = 9; // Jan 1 always fall in Nepali month Paush which is the 9th month of Nepali calendar.
25711 // Get the Nepali day in Paush (month 9) of 1st January
25712 var dayOfFirstJanInPaush = this.NEPALI_CALENDAR_DATA[nepaliYear][0];
25713 // Check how many days are left of Paush .
25714 // Days calculated from 1st Jan till the end of the actual Nepali month,
25715 // we use this value to check if the gregorian Date is in the actual Nepali month.
25716 var daysSinceJanFirstToEndOfNepaliMonth =
25717 this.NEPALI_CALENDAR_DATA[nepaliYear][nepaliMonth] - dayOfFirstJanInPaush + 1;
25718 // If the gregorian day-of-year is smaller o equal than the sum of days between the 1st January and
25719 // the end of the actual nepali month we found the correct nepali month.
25720 // Example:
25721 // The 4th February 2011 is the gregorianDayOfYear 35 (31 days of January + 4)
25722 // 1st January 2011 is in the nepali year 2067, where 1st. January is in the 17th day of Paush (9th month)
25723 // In 2067 Paush has 30days, This means (30-17+1=14) there are 14days between 1st January and end of Paush
25724 // (including 17th January)
25725 // The gregorianDayOfYear (35) is bigger than 14, so we check the next month
25726 // The next nepali month (Mangh) has 29 days
25727 // 29+14=43, this is bigger than gregorianDayOfYear(35) so, we found the correct nepali month
25728 while (gregorianDayOfYear > daysSinceJanFirstToEndOfNepaliMonth) {
25729 nepaliMonth++;
25730 if (nepaliMonth > 12) {
25731 nepaliMonth = 1;
25732 nepaliYear++;
25733 }
25734 daysSinceJanFirstToEndOfNepaliMonth += this.NEPALI_CALENDAR_DATA[nepaliYear][nepaliMonth];
25735 }
25736 // The last step is to calculate the nepali day-of-month
25737 // to continue our example from before:
25738 // we calculated there are 43 days from 1st. January (17 Paush) till end of Mangh (29 days)
25739 // when we subtract from this 43 days the day-of-year of the the Gregorian date (35),
25740 // we know how far the searched day is away from the end of the Nepali month.
25741 // So we simply subtract this number from the amount of days in this month (30)
25742 var nepaliDayOfMonth = this.NEPALI_CALENDAR_DATA[nepaliYear][nepaliMonth] -
25743 (daysSinceJanFirstToEndOfNepaliMonth - gregorianDayOfYear);
25744 return this.newDate(nepaliYear, nepaliMonth, nepaliDayOfMonth);
25745 },
25746
25747 /** Creates missing data in the NEPALI_CALENDAR_DATA table.
25748 This data will not be correct but just give an estimated result. Mostly -/+ 1 day
25749 @private
25750 @param nepaliYear {number} The missing year number. */
25751 _createMissingCalendarData: function(nepaliYear) {
25752 var tmp_calendar_data = this.daysPerMonth.slice(0);
25753 tmp_calendar_data.unshift(17);
25754 for (var nepaliYearToCreate = (nepaliYear - 1); nepaliYearToCreate < (nepaliYear + 2); nepaliYearToCreate++) {
25755 if (typeof this.NEPALI_CALENDAR_DATA[nepaliYearToCreate] === 'undefined') {
25756 this.NEPALI_CALENDAR_DATA[nepaliYearToCreate] = tmp_calendar_data;
25757 }
25758 }
25759 },
25760
25761 NEPALI_CALENDAR_DATA: {
25762 // These data are from http://www.ashesh.com.np
25763 1970: [18, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30],
25764 1971: [18, 31, 31, 32, 31, 32, 30, 30, 29, 30, 29, 30, 30],
25765 1972: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 30],
25766 1973: [19, 30, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31],
25767 1974: [19, 31, 31, 32, 30, 31, 31, 30, 29, 30, 29, 30, 30],
25768 1975: [18, 31, 31, 32, 32, 30, 31, 30, 29, 30, 29, 30, 30],
25769 1976: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31],
25770 1977: [18, 31, 32, 31, 32, 31, 31, 29, 30, 29, 30, 29, 31],
25771 1978: [18, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30],
25772 1979: [18, 31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30],
25773 1980: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31],
25774 1981: [18, 31, 31, 31, 32, 31, 31, 29, 30, 30, 29, 30, 30],
25775 1982: [18, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30],
25776 1983: [18, 31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30],
25777 1984: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31],
25778 1985: [18, 31, 31, 31, 32, 31, 31, 29, 30, 30, 29, 30, 30],
25779 1986: [18, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30],
25780 1987: [18, 31, 32, 31, 32, 31, 30, 30, 29, 30, 29, 30, 30],
25781 1988: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31],
25782 1989: [18, 31, 31, 31, 32, 31, 31, 30, 29, 30, 29, 30, 30],
25783 1990: [18, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30],
25784 1991: [18, 31, 32, 31, 32, 31, 30, 30, 29, 30, 29, 30, 30],
25785 // These data are from http://nepalicalendar.rat32.com/index.php
25786 1992: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31],
25787 1993: [18, 31, 31, 31, 32, 31, 31, 30, 29, 30, 29, 30, 30],
25788 1994: [18, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30],
25789 1995: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 30],
25790 1996: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31],
25791 1997: [18, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30],
25792 1998: [18, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30],
25793 1999: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31],
25794 2000: [17, 30, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31],
25795 2001: [18, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30],
25796 2002: [18, 31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30],
25797 2003: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31],
25798 2004: [17, 30, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31],
25799 2005: [18, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30],
25800 2006: [18, 31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30],
25801 2007: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31],
25802 2008: [17, 31, 31, 31, 32, 31, 31, 29, 30, 30, 29, 29, 31],
25803 2009: [18, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30],
25804 2010: [18, 31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30],
25805 2011: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31],
25806 2012: [17, 31, 31, 31, 32, 31, 31, 29, 30, 30, 29, 30, 30],
25807 2013: [18, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30],
25808 2014: [18, 31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30],
25809 2015: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31],
25810 2016: [17, 31, 31, 31, 32, 31, 31, 29, 30, 30, 29, 30, 30],
25811 2017: [18, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30],
25812 2018: [18, 31, 32, 31, 32, 31, 30, 30, 29, 30, 29, 30, 30],
25813 2019: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31],
25814 2020: [17, 31, 31, 31, 32, 31, 31, 30, 29, 30, 29, 30, 30],
25815 2021: [18, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30],
25816 2022: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 30],
25817 2023: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31],
25818 2024: [17, 31, 31, 31, 32, 31, 31, 30, 29, 30, 29, 30, 30],
25819 2025: [18, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30],
25820 2026: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31],
25821 2027: [17, 30, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31],
25822 2028: [17, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30],
25823 2029: [18, 31, 31, 32, 31, 32, 30, 30, 29, 30, 29, 30, 30],
25824 2030: [17, 31, 32, 31, 32, 31, 30, 30, 30, 30, 30, 30, 31],
25825 2031: [17, 31, 32, 31, 32, 31, 31, 31, 31, 31, 31, 31, 31],
25826 2032: [17, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32],
25827 2033: [18, 31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30],
25828 2034: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31],
25829 2035: [17, 30, 32, 31, 32, 31, 31, 29, 30, 30, 29, 29, 31],
25830 2036: [17, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30],
25831 2037: [18, 31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30],
25832 2038: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31],
25833 2039: [17, 31, 31, 31, 32, 31, 31, 29, 30, 30, 29, 30, 30],
25834 2040: [17, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30],
25835 2041: [18, 31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30],
25836 2042: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31],
25837 2043: [17, 31, 31, 31, 32, 31, 31, 29, 30, 30, 29, 30, 30],
25838 2044: [17, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30],
25839 2045: [18, 31, 32, 31, 32, 31, 30, 30, 29, 30, 29, 30, 30],
25840 2046: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31],
25841 2047: [17, 31, 31, 31, 32, 31, 31, 30, 29, 30, 29, 30, 30],
25842 2048: [17, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30],
25843 2049: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 30],
25844 2050: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31],
25845 2051: [17, 31, 31, 31, 32, 31, 31, 30, 29, 30, 29, 30, 30],
25846 2052: [17, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30],
25847 2053: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 30],
25848 2054: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31],
25849 2055: [17, 31, 31, 32, 31, 31, 31, 30, 29, 30, 30, 29, 30],
25850 2056: [17, 31, 31, 32, 31, 32, 30, 30, 29, 30, 29, 30, 30],
25851 2057: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31],
25852 2058: [17, 30, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31],
25853 2059: [17, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30],
25854 2060: [17, 31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30],
25855 2061: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31],
25856 2062: [17, 30, 32, 31, 32, 31, 31, 29, 30, 29, 30, 29, 31],
25857 2063: [17, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30],
25858 2064: [17, 31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30],
25859 2065: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31],
25860 2066: [17, 31, 31, 31, 32, 31, 31, 29, 30, 30, 29, 29, 31],
25861 2067: [17, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30],
25862 2068: [17, 31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30],
25863 2069: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31],
25864 2070: [17, 31, 31, 31, 32, 31, 31, 29, 30, 30, 29, 30, 30],
25865 2071: [17, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30],
25866 2072: [17, 31, 32, 31, 32, 31, 30, 30, 29, 30, 29, 30, 30],
25867 2073: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31],
25868 2074: [17, 31, 31, 31, 32, 31, 31, 30, 29, 30, 29, 30, 30],
25869 2075: [17, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30],
25870 2076: [16, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 30],
25871 2077: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31],
25872 2078: [17, 31, 31, 31, 32, 31, 31, 30, 29, 30, 29, 30, 30],
25873 2079: [17, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30],
25874 2080: [16, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 30],
25875 // These data are from http://www.ashesh.com.np/nepali-calendar/
25876 2081: [17, 31, 31, 32, 32, 31, 30, 30, 30, 29, 30, 30, 30],
25877 2082: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 30, 30, 30],
25878 2083: [17, 31, 31, 32, 31, 31, 30, 30, 30, 29, 30, 30, 30],
25879 2084: [17, 31, 31, 32, 31, 31, 30, 30, 30, 29, 30, 30, 30],
25880 2085: [17, 31, 32, 31, 32, 31, 31, 30, 30, 29, 30, 30, 30],
25881 2086: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 30, 30, 30],
25882 2087: [16, 31, 31, 32, 31, 31, 31, 30, 30, 29, 30, 30, 30],
25883 2088: [16, 30, 31, 32, 32, 30, 31, 30, 30, 29, 30, 30, 30],
25884 2089: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 30, 30, 30],
25885 2090: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 30, 30, 30],
25886 2091: [16, 31, 31, 32, 31, 31, 31, 30, 30, 29, 30, 30, 30],
25887 2092: [16, 31, 31, 32, 32, 31, 30, 30, 30, 29, 30, 30, 30],
25888 2093: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 30, 30, 30],
25889 2094: [17, 31, 31, 32, 31, 31, 30, 30, 30, 29, 30, 30, 30],
25890 2095: [17, 31, 31, 32, 31, 31, 31, 30, 29, 30, 30, 30, 30],
25891 2096: [17, 30, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30],
25892 2097: [17, 31, 32, 31, 32, 31, 30, 30, 30, 29, 30, 30, 30],
25893 2098: [17, 31, 31, 32, 31, 31, 31, 29, 30, 29, 30, 30, 31],
25894 2099: [17, 31, 31, 32, 31, 31, 31, 30, 29, 29, 30, 30, 30],
25895 2100: [17, 31, 32, 31, 32, 30, 31, 30, 29, 30, 29, 30, 30]
25896 }
25897});
25898
25899// Nepali calendar implementation
25900main.calendars.nepali = NepaliCalendar;
25901
25902
25903},{"../main":137,"object-assign":73}],133:[function(_dereq_,module,exports){
25904/*
25905 * World Calendars
25906 * https://github.com/alexcjohnson/world-calendars
25907 *
25908 * Batch-converted from kbwood/calendars
25909 * Many thanks to Keith Wood and all of the contributors to the original project!
25910 *
25911 * This source code is licensed under the MIT license found in the
25912 * LICENSE file in the root directory of this source tree.
25913 */
25914
25915/* http://keith-wood.name/calendars.html
25916 Persian calendar for jQuery v2.0.2.
25917 Written by Keith Wood (wood.keith{at}optusnet.com.au) August 2009.
25918 Available under the MIT (http://keith-wood.name/licence.html) license.
25919 Please attribute the author if you use it. */
25920
25921var main = _dereq_('../main');
25922var assign = _dereq_('object-assign');
25923
25924
25925/** Implementation of the Persian or Jalali calendar.
25926 Based on code from <a href="http://www.iranchamber.com/calendar/converter/iranian_calendar_converter.php">http://www.iranchamber.com/calendar/converter/iranian_calendar_converter.php</a>.
25927 See also <a href="http://en.wikipedia.org/wiki/Iranian_calendar">http://en.wikipedia.org/wiki/Iranian_calendar</a>.
25928 @class PersianCalendar
25929 @param [language=''] {string} The language code (default English) for localisation. */
25930function PersianCalendar(language) {
25931 this.local = this.regionalOptions[language || ''] || this.regionalOptions[''];
25932}
25933
25934PersianCalendar.prototype = new main.baseCalendar;
25935
25936assign(PersianCalendar.prototype, {
25937 /** The calendar name.
25938 @memberof PersianCalendar */
25939 name: 'Persian',
25940 /** Julian date of start of Persian epoch: 19 March 622 CE.
25941 @memberof PersianCalendar */
25942 jdEpoch: 1948320.5,
25943 /** Days per month in a common year.
25944 @memberof PersianCalendar */
25945 daysPerMonth: [31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29],
25946 /** <code>true</code> if has a year zero, <code>false</code> if not.
25947 @memberof PersianCalendar */
25948 hasYearZero: false,
25949 /** The minimum month number.
25950 @memberof PersianCalendar */
25951 minMonth: 1,
25952 /** The first month in the year.
25953 @memberof PersianCalendar */
25954 firstMonth: 1,
25955 /** The minimum day number.
25956 @memberof PersianCalendar */
25957 minDay: 1,
25958
25959 /** Localisations for the plugin.
25960 Entries are objects indexed by the language code ('' being the default US/English).
25961 Each object has the following attributes.
25962 @memberof PersianCalendar
25963 @property name {string} The calendar name.
25964 @property epochs {string[]} The epoch names.
25965 @property monthNames {string[]} The long names of the months of the year.
25966 @property monthNamesShort {string[]} The short names of the months of the year.
25967 @property dayNames {string[]} The long names of the days of the week.
25968 @property dayNamesShort {string[]} The short names of the days of the week.
25969 @property dayNamesMin {string[]} The minimal names of the days of the week.
25970 @property dateFormat {string} The date format for this calendar.
25971 See the options on <a href="BaseCalendar.html#formatDate"><code>formatDate</code></a> for details.
25972 @property firstDay {number} The number of the first day of the week, starting at 0.
25973 @property isRTL {number} <code>true</code> if this localisation reads right-to-left. */
25974 regionalOptions: { // Localisations
25975 '': {
25976 name: 'Persian',
25977 epochs: ['BP', 'AP'],
25978 monthNames: ['Farvardin', 'Ordibehesht', 'Khordad', 'Tir', 'Mordad', 'Shahrivar',
25979 'Mehr', 'Aban', 'Azar', 'Day', 'Bahman', 'Esfand'],
25980 monthNamesShort: ['Far', 'Ord', 'Kho', 'Tir', 'Mor', 'Sha', 'Meh', 'Aba', 'Aza', 'Day', 'Bah', 'Esf'],
25981 dayNames: ['Yekshambe', 'Doshambe', 'Seshambe', 'Chæharshambe', 'Panjshambe', 'Jom\'e', 'Shambe'],
25982 dayNamesShort: ['Yek', 'Do', 'Se', 'Chæ', 'Panj', 'Jom', 'Sha'],
25983 dayNamesMin: ['Ye','Do','Se','Ch','Pa','Jo','Sh'],
25984 digits: null,
25985 dateFormat: 'yyyy/mm/dd',
25986 firstDay: 6,
25987 isRTL: false
25988 }
25989 },
25990
25991 /** Determine whether this date is in a leap year.
25992 @memberof PersianCalendar
25993 @param year {CDate|number} The date to examine or the year to examine.
25994 @return {boolean} <code>true</code> if this is a leap year, <code>false</code> if not.
25995 @throws Error if an invalid year or a different calendar used. */
25996 leapYear: function(year) {
25997 var date = this._validate(year, this.minMonth, this.minDay, main.local.invalidYear);
25998 return (((((date.year() - (date.year() > 0 ? 474 : 473)) % 2820) +
25999 474 + 38) * 682) % 2816) < 682;
26000 },
26001
26002 /** Determine the week of the year for a date.
26003 @memberof PersianCalendar
26004 @param year {CDate|number} The date to examine or the year to examine.
26005 @param [month] {number} The month to examine.
26006 @param [day] {number} The day to examine.
26007 @return {number} The week of the year.
26008 @throws Error if an invalid date or a different calendar used. */
26009 weekOfYear: function(year, month, day) {
26010 // Find Saturday of this week starting on Saturday
26011 var checkDate = this.newDate(year, month, day);
26012 checkDate.add(-((checkDate.dayOfWeek() + 1) % 7), 'd');
26013 return Math.floor((checkDate.dayOfYear() - 1) / 7) + 1;
26014 },
26015
26016 /** Retrieve the number of days in a month.
26017 @memberof PersianCalendar
26018 @param year {CDate|number} The date to examine or the year of the month.
26019 @param [month] {number} The month.
26020 @return {number} The number of days in this month.
26021 @throws Error if an invalid month/year or a different calendar used. */
26022 daysInMonth: function(year, month) {
26023 var date = this._validate(year, month, this.minDay, main.local.invalidMonth);
26024 return this.daysPerMonth[date.month() - 1] +
26025 (date.month() === 12 && this.leapYear(date.year()) ? 1 : 0);
26026 },
26027
26028 /** Determine whether this date is a week day.
26029 @memberof PersianCalendar
26030 @param year {CDate|number} The date to examine or the year to examine.
26031 @param [month] {number} The month to examine.
26032 @param [day] {number} The day to examine.
26033 @return {boolean} <code>true</code> if a week day, <code>false</code> if not.
26034 @throws Error if an invalid date or a different calendar used. */
26035 weekDay: function(year, month, day) {
26036 return this.dayOfWeek(year, month, day) !== 5;
26037 },
26038
26039 /** Retrieve the Julian date equivalent for this date,
26040 i.e. days since January 1, 4713 BCE Greenwich noon.
26041 @memberof PersianCalendar
26042 @param year {CDate|number} The date to convert or the year to convert.
26043 @param [month] {number} The month to convert.
26044 @param [day] {number} The day to convert.
26045 @return {number} The equivalent Julian date.
26046 @throws Error if an invalid date or a different calendar used. */
26047 toJD: function(year, month, day) {
26048 var date = this._validate(year, month, day, main.local.invalidDate);
26049 year = date.year();
26050 month = date.month();
26051 day = date.day();
26052 var epBase = year - (year >= 0 ? 474 : 473);
26053 var epYear = 474 + mod(epBase, 2820);
26054 return day + (month <= 7 ? (month - 1) * 31 : (month - 1) * 30 + 6) +
26055 Math.floor((epYear * 682 - 110) / 2816) + (epYear - 1) * 365 +
26056 Math.floor(epBase / 2820) * 1029983 + this.jdEpoch - 1;
26057 },
26058
26059 /** Create a new date from a Julian date.
26060 @memberof PersianCalendar
26061 @param jd {number} The Julian date to convert.
26062 @return {CDate} The equivalent date. */
26063 fromJD: function(jd) {
26064 jd = Math.floor(jd) + 0.5;
26065 var depoch = jd - this.toJD(475, 1, 1);
26066 var cycle = Math.floor(depoch / 1029983);
26067 var cyear = mod(depoch, 1029983);
26068 var ycycle = 2820;
26069 if (cyear !== 1029982) {
26070 var aux1 = Math.floor(cyear / 366);
26071 var aux2 = mod(cyear, 366);
26072 ycycle = Math.floor(((2134 * aux1) + (2816 * aux2) + 2815) / 1028522) + aux1 + 1;
26073 }
26074 var year = ycycle + (2820 * cycle) + 474;
26075 year = (year <= 0 ? year - 1 : year);
26076 var yday = jd - this.toJD(year, 1, 1) + 1;
26077 var month = (yday <= 186 ? Math.ceil(yday / 31) : Math.ceil((yday - 6) / 30));
26078 var day = jd - this.toJD(year, month, 1) + 1;
26079 return this.newDate(year, month, day);
26080 }
26081});
26082
26083// Modulus function which works for non-integers.
26084function mod(a, b) {
26085 return a - (b * Math.floor(a / b));
26086}
26087
26088// Persian (Jalali) calendar implementation
26089main.calendars.persian = PersianCalendar;
26090main.calendars.jalali = PersianCalendar;
26091
26092
26093},{"../main":137,"object-assign":73}],134:[function(_dereq_,module,exports){
26094/*
26095 * World Calendars
26096 * https://github.com/alexcjohnson/world-calendars
26097 *
26098 * Batch-converted from kbwood/calendars
26099 * Many thanks to Keith Wood and all of the contributors to the original project!
26100 *
26101 * This source code is licensed under the MIT license found in the
26102 * LICENSE file in the root directory of this source tree.
26103 */
26104
26105/* http://keith-wood.name/calendars.html
26106 Taiwanese (Minguo) calendar for jQuery v2.0.2.
26107 Written by Keith Wood (wood.keith{at}optusnet.com.au) February 2010.
26108 Available under the MIT (http://keith-wood.name/licence.html) license.
26109 Please attribute the author if you use it. */
26110
26111var main = _dereq_('../main');
26112var assign = _dereq_('object-assign');
26113
26114
26115var gregorianCalendar = main.instance();
26116
26117/** Implementation of the Taiwanese calendar.
26118 See http://en.wikipedia.org/wiki/Minguo_calendar.
26119 @class TaiwanCalendar
26120 @param [language=''] {string} The language code (default English) for localisation. */
26121function TaiwanCalendar(language) {
26122 this.local = this.regionalOptions[language || ''] || this.regionalOptions[''];
26123}
26124
26125TaiwanCalendar.prototype = new main.baseCalendar;
26126
26127assign(TaiwanCalendar.prototype, {
26128 /** The calendar name.
26129 @memberof TaiwanCalendar */
26130 name: 'Taiwan',
26131 /** Julian date of start of Taiwan epoch: 1 January 1912 CE (Gregorian).
26132 @memberof TaiwanCalendar */
26133 jdEpoch: 2419402.5,
26134 /** Difference in years between Taiwan and Gregorian calendars.
26135 @memberof TaiwanCalendar */
26136 yearsOffset: 1911,
26137 /** Days per month in a common year.
26138 @memberof TaiwanCalendar */
26139 daysPerMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
26140 /** <code>true</code> if has a year zero, <code>false</code> if not.
26141 @memberof TaiwanCalendar */
26142 hasYearZero: false,
26143 /** The minimum month number.
26144 @memberof TaiwanCalendar */
26145 minMonth: 1,
26146 /** The first month in the year.
26147 @memberof TaiwanCalendar */
26148 firstMonth: 1,
26149 /** The minimum day number.
26150 @memberof TaiwanCalendar */
26151 minDay: 1,
26152
26153 /** Localisations for the plugin.
26154 Entries are objects indexed by the language code ('' being the default US/English).
26155 Each object has the following attributes.
26156 @memberof TaiwanCalendar
26157 @property name {string} The calendar name.
26158 @property epochs {string[]} The epoch names.
26159 @property monthNames {string[]} The long names of the months of the year.
26160 @property monthNamesShort {string[]} The short names of the months of the year.
26161 @property dayNames {string[]} The long names of the days of the week.
26162 @property dayNamesShort {string[]} The short names of the days of the week.
26163 @property dayNamesMin {string[]} The minimal names of the days of the week.
26164 @property dateFormat {string} The date format for this calendar.
26165 See the options on <a href="BaseCalendar.html#formatDate"><code>formatDate</code></a> for details.
26166 @property firstDay {number} The number of the first day of the week, starting at 0.
26167 @property isRTL {number} <code>true</code> if this localisation reads right-to-left. */
26168 regionalOptions: { // Localisations
26169 '': {
26170 name: 'Taiwan',
26171 epochs: ['BROC', 'ROC'],
26172 monthNames: ['January', 'February', 'March', 'April', 'May', 'June',
26173 'July', 'August', 'September', 'October', 'November', 'December'],
26174 monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
26175 dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
26176 dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
26177 dayNamesMin: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
26178 digits: null,
26179 dateFormat: 'yyyy/mm/dd',
26180 firstDay: 1,
26181 isRTL: false
26182 }
26183 },
26184
26185 /** Determine whether this date is in a leap year.
26186 @memberof TaiwanCalendar
26187 @param year {CDate|number} The date to examine or the year to examine.
26188 @return {boolean} <code>true</code> if this is a leap year, <code>false</code> if not.
26189 @throws Error if an invalid year or a different calendar used. */
26190 leapYear: function(year) {
26191 var date = this._validate(year, this.minMonth, this.minDay, main.local.invalidYear);
26192 var year = this._t2gYear(date.year());
26193 return gregorianCalendar.leapYear(year);
26194 },
26195
26196 /** Determine the week of the year for a date - ISO 8601.
26197 @memberof TaiwanCalendar
26198 @param year {CDate|number} The date to examine or the year to examine.
26199 @param [month] {number} The month to examine.
26200 @param [day] {number} The day to examine.
26201 @return {number} The week of the year.
26202 @throws Error if an invalid date or a different calendar used. */
26203 weekOfYear: function(year, month, day) {
26204 var date = this._validate(year, this.minMonth, this.minDay, main.local.invalidYear);
26205 var year = this._t2gYear(date.year());
26206 return gregorianCalendar.weekOfYear(year, date.month(), date.day());
26207 },
26208
26209 /** Retrieve the number of days in a month.
26210 @memberof TaiwanCalendar
26211 @param year {CDate|number} The date to examine or the year of the month.
26212 @param [month] {number} The month.
26213 @return {number} The number of days in this month.
26214 @throws Error if an invalid month/year or a different calendar used. */
26215 daysInMonth: function(year, month) {
26216 var date = this._validate(year, month, this.minDay, main.local.invalidMonth);
26217 return this.daysPerMonth[date.month() - 1] +
26218 (date.month() === 2 && this.leapYear(date.year()) ? 1 : 0);
26219 },
26220
26221 /** Determine whether this date is a week day.
26222 @memberof TaiwanCalendar
26223 @param year {CDate|number} The date to examine or the year to examine.
26224 @param [month] {number} The month to examine.
26225 @param [day] {number} The day to examine.
26226 @return {boolean} <code>true</code> if a week day, <code>false</code> if not.
26227 @throws Error if an invalid date or a different calendar used. */
26228 weekDay: function(year, month, day) {
26229 return (this.dayOfWeek(year, month, day) || 7) < 6;
26230 },
26231
26232 /** Retrieve the Julian date equivalent for this date,
26233 i.e. days since January 1, 4713 BCE Greenwich noon.
26234 @memberof TaiwanCalendar
26235 @param year {CDate|number} The date to convert or the year to convert.
26236 @param [month] {number} The month to convert.
26237 @param [day] {number} The day to convert.
26238 @return {number} The equivalent Julian date.
26239 @throws Error if an invalid date or a different calendar used. */
26240 toJD: function(year, month, day) {
26241 var date = this._validate(year, month, day, main.local.invalidDate);
26242 var year = this._t2gYear(date.year());
26243 return gregorianCalendar.toJD(year, date.month(), date.day());
26244 },
26245
26246 /** Create a new date from a Julian date.
26247 @memberof TaiwanCalendar
26248 @param jd {number} The Julian date to convert.
26249 @return {CDate} The equivalent date. */
26250 fromJD: function(jd) {
26251 var date = gregorianCalendar.fromJD(jd);
26252 var year = this._g2tYear(date.year());
26253 return this.newDate(year, date.month(), date.day());
26254 },
26255
26256 /** Convert Taiwanese to Gregorian year.
26257 @memberof TaiwanCalendar
26258 @private
26259 @param year {number} The Taiwanese year.
26260 @return {number} The corresponding Gregorian year. */
26261 _t2gYear: function(year) {
26262 return year + this.yearsOffset + (year >= -this.yearsOffset && year <= -1 ? 1 : 0);
26263 },
26264
26265 /** Convert Gregorian to Taiwanese year.
26266 @memberof TaiwanCalendar
26267 @private
26268 @param year {number} The Gregorian year.
26269 @return {number} The corresponding Taiwanese year. */
26270 _g2tYear: function(year) {
26271 return year - this.yearsOffset - (year >= 1 && year <= this.yearsOffset ? 1 : 0);
26272 }
26273});
26274
26275// Taiwan calendar implementation
26276main.calendars.taiwan = TaiwanCalendar;
26277
26278
26279},{"../main":137,"object-assign":73}],135:[function(_dereq_,module,exports){
26280/*
26281 * World Calendars
26282 * https://github.com/alexcjohnson/world-calendars
26283 *
26284 * Batch-converted from kbwood/calendars
26285 * Many thanks to Keith Wood and all of the contributors to the original project!
26286 *
26287 * This source code is licensed under the MIT license found in the
26288 * LICENSE file in the root directory of this source tree.
26289 */
26290
26291/* http://keith-wood.name/calendars.html
26292 Thai calendar for jQuery v2.0.2.
26293 Written by Keith Wood (wood.keith{at}optusnet.com.au) February 2010.
26294 Available under the MIT (http://keith-wood.name/licence.html) license.
26295 Please attribute the author if you use it. */
26296
26297var main = _dereq_('../main');
26298var assign = _dereq_('object-assign');
26299
26300
26301var gregorianCalendar = main.instance();
26302
26303/** Implementation of the Thai calendar.
26304 See http://en.wikipedia.org/wiki/Thai_calendar.
26305 @class ThaiCalendar
26306 @param [language=''] {string} The language code (default English) for localisation. */
26307function ThaiCalendar(language) {
26308 this.local = this.regionalOptions[language || ''] || this.regionalOptions[''];
26309}
26310
26311ThaiCalendar.prototype = new main.baseCalendar;
26312
26313assign(ThaiCalendar.prototype, {
26314 /** The calendar name.
26315 @memberof ThaiCalendar */
26316 name: 'Thai',
26317 /** Julian date of start of Thai epoch: 1 January 543 BCE (Gregorian).
26318 @memberof ThaiCalendar */
26319 jdEpoch: 1523098.5,
26320 /** Difference in years between Thai and Gregorian calendars.
26321 @memberof ThaiCalendar */
26322 yearsOffset: 543,
26323 /** Days per month in a common year.
26324 @memberof ThaiCalendar */
26325 daysPerMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
26326 /** <code>true</code> if has a year zero, <code>false</code> if not.
26327 @memberof ThaiCalendar */
26328 hasYearZero: false,
26329 /** The minimum month number.
26330 @memberof ThaiCalendar */
26331 minMonth: 1,
26332 /** The first month in the year.
26333 @memberof ThaiCalendar */
26334 firstMonth: 1,
26335 /** The minimum day number.
26336 @memberof ThaiCalendar */
26337 minDay: 1,
26338
26339 /** Localisations for the plugin.
26340 Entries are objects indexed by the language code ('' being the default US/English).
26341 Each object has the following attributes.
26342 @memberof ThaiCalendar
26343 @property name {string} The calendar name.
26344 @property epochs {string[]} The epoch names.
26345 @property monthNames {string[]} The long names of the months of the year.
26346 @property monthNamesShort {string[]} The short names of the months of the year.
26347 @property dayNames {string[]} The long names of the days of the week.
26348 @property dayNamesShort {string[]} The short names of the days of the week.
26349 @property dayNamesMin {string[]} The minimal names of the days of the week.
26350 @property dateFormat {string} The date format for this calendar.
26351 See the options on <a href="BaseCalendar.html#formatDate"><code>formatDate</code></a> for details.
26352 @property firstDay {number} The number of the first day of the week, starting at 0.
26353 @property isRTL {number} <code>true</code> if this localisation reads right-to-left. */
26354 regionalOptions: { // Localisations
26355 '': {
26356 name: 'Thai',
26357 epochs: ['BBE', 'BE'],
26358 monthNames: ['January', 'February', 'March', 'April', 'May', 'June',
26359 'July', 'August', 'September', 'October', 'November', 'December'],
26360 monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
26361 dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
26362 dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
26363 dayNamesMin: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
26364 digits: null,
26365 dateFormat: 'dd/mm/yyyy',
26366 firstDay: 0,
26367 isRTL: false
26368 }
26369 },
26370
26371 /** Determine whether this date is in a leap year.
26372 @memberof ThaiCalendar
26373 @param year {CDate|number} The date to examine or the year to examine.
26374 @return {boolean} <code>true</code> if this is a leap year, <code>false</code> if not.
26375 @throws Error if an invalid year or a different calendar used. */
26376 leapYear: function(year) {
26377 var date = this._validate(year, this.minMonth, this.minDay, main.local.invalidYear);
26378 var year = this._t2gYear(date.year());
26379 return gregorianCalendar.leapYear(year);
26380 },
26381
26382 /** Determine the week of the year for a date - ISO 8601.
26383 @memberof ThaiCalendar
26384 @param year {CDate|number} The date to examine or the year to examine.
26385 @param [month] {number} The month to examine.
26386 @param [day] {number} The day to examine.
26387 @return {number} The week of the year.
26388 @throws Error if an invalid date or a different calendar used. */
26389 weekOfYear: function(year, month, day) {
26390 var date = this._validate(year, this.minMonth, this.minDay, main.local.invalidYear);
26391 var year = this._t2gYear(date.year());
26392 return gregorianCalendar.weekOfYear(year, date.month(), date.day());
26393 },
26394
26395 /** Retrieve the number of days in a month.
26396 @memberof ThaiCalendar
26397 @param year {CDate|number} The date to examine or the year of the month.
26398 @param [month] {number} The month.
26399 @return {number} The number of days in this month.
26400 @throws Error if an invalid month/year or a different calendar used. */
26401 daysInMonth: function(year, month) {
26402 var date = this._validate(year, month, this.minDay, main.local.invalidMonth);
26403 return this.daysPerMonth[date.month() - 1] +
26404 (date.month() === 2 && this.leapYear(date.year()) ? 1 : 0);
26405 },
26406
26407 /** Determine whether this date is a week day.
26408 @memberof ThaiCalendar
26409 @param year {CDate|number} The date to examine or the year to examine.
26410 @param [month] {number} The month to examine.
26411 @param [day] {number} The day to examine.
26412 @return {boolean} <code>true</code> if a week day, <code>false</code> if not.
26413 @throws Error if an invalid date or a different calendar used. */
26414 weekDay: function(year, month, day) {
26415 return (this.dayOfWeek(year, month, day) || 7) < 6;
26416 },
26417
26418 /** Retrieve the Julian date equivalent for this date,
26419 i.e. days since January 1, 4713 BCE Greenwich noon.
26420 @memberof ThaiCalendar
26421 @param year {CDate|number} The date to convert or the year to convert.
26422 @param [month] {number} The month to convert.
26423 @param [day] {number} The day to convert.
26424 @return {number} The equivalent Julian date.
26425 @throws Error if an invalid date or a different calendar used. */
26426 toJD: function(year, month, day) {
26427 var date = this._validate(year, month, day, main.local.invalidDate);
26428 var year = this._t2gYear(date.year());
26429 return gregorianCalendar.toJD(year, date.month(), date.day());
26430 },
26431
26432 /** Create a new date from a Julian date.
26433 @memberof ThaiCalendar
26434 @param jd {number} The Julian date to convert.
26435 @return {CDate} The equivalent date. */
26436 fromJD: function(jd) {
26437 var date = gregorianCalendar.fromJD(jd);
26438 var year = this._g2tYear(date.year());
26439 return this.newDate(year, date.month(), date.day());
26440 },
26441
26442 /** Convert Thai to Gregorian year.
26443 @memberof ThaiCalendar
26444 @private
26445 @param year {number} The Thai year.
26446 @return {number} The corresponding Gregorian year. */
26447 _t2gYear: function(year) {
26448 return year - this.yearsOffset - (year >= 1 && year <= this.yearsOffset ? 1 : 0);
26449 },
26450
26451 /** Convert Gregorian to Thai year.
26452 @memberof ThaiCalendar
26453 @private
26454 @param year {number} The Gregorian year.
26455 @return {number} The corresponding Thai year. */
26456 _g2tYear: function(year) {
26457 return year + this.yearsOffset + (year >= -this.yearsOffset && year <= -1 ? 1 : 0);
26458 }
26459});
26460
26461// Thai calendar implementation
26462main.calendars.thai = ThaiCalendar;
26463
26464
26465},{"../main":137,"object-assign":73}],136:[function(_dereq_,module,exports){
26466/*
26467 * World Calendars
26468 * https://github.com/alexcjohnson/world-calendars
26469 *
26470 * Batch-converted from kbwood/calendars
26471 * Many thanks to Keith Wood and all of the contributors to the original project!
26472 *
26473 * This source code is licensed under the MIT license found in the
26474 * LICENSE file in the root directory of this source tree.
26475 */
26476
26477/* http://keith-wood.name/calendars.html
26478 UmmAlQura calendar for jQuery v2.0.2.
26479 Written by Amro Osama March 2013.
26480 Modified by Binnooh.com & www.elm.sa - 2014 - Added dates back to 1276 Hijri year.
26481 Available under the MIT (http://keith-wood.name/licence.html) license.
26482 Please attribute the author if you use it. */
26483
26484var main = _dereq_('../main');
26485var assign = _dereq_('object-assign');
26486
26487
26488/** Implementation of the UmmAlQura or 'saudi' calendar.
26489 See also <a href="http://en.wikipedia.org/wiki/Islamic_calendar#Saudi_Arabia.27s_Umm_al-Qura_calendar">http://en.wikipedia.org/wiki/Islamic_calendar#Saudi_Arabia.27s_Umm_al-Qura_calendar</a>.
26490 <a href="http://www.ummulqura.org.sa/About.aspx">http://www.ummulqura.org.sa/About.aspx</a>
26491 <a href="http://www.staff.science.uu.nl/~gent0113/islam/ummalqura.htm">http://www.staff.science.uu.nl/~gent0113/islam/ummalqura.htm</a>
26492 @class UmmAlQuraCalendar
26493 @param [language=''] {string} The language code (default English) for localisation. */
26494function UmmAlQuraCalendar(language) {
26495 this.local = this.regionalOptions[language || ''] || this.regionalOptions[''];
26496}
26497
26498UmmAlQuraCalendar.prototype = new main.baseCalendar;
26499
26500assign(UmmAlQuraCalendar.prototype, {
26501 /** The calendar name.
26502 @memberof UmmAlQuraCalendar */
26503 name: 'UmmAlQura',
26504 //jdEpoch: 1948440, // Julian date of start of UmmAlQura epoch: 14 March 1937 CE
26505 //daysPerMonth: // Days per month in a common year, replaced by a method.
26506 /** <code>true</code> if has a year zero, <code>false</code> if not.
26507 @memberof UmmAlQuraCalendar */
26508 hasYearZero: false,
26509 /** The minimum month number.
26510 @memberof UmmAlQuraCalendar */
26511 minMonth: 1,
26512 /** The first month in the year.
26513 @memberof UmmAlQuraCalendar */
26514 firstMonth: 1,
26515 /** The minimum day number.
26516 @memberof UmmAlQuraCalendar */
26517 minDay: 1,
26518
26519 /** Localisations for the plugin.
26520 Entries are objects indexed by the language code ('' being the default US/English).
26521 Each object has the following attributes.
26522 @memberof UmmAlQuraCalendar
26523 @property name {string} The calendar name.
26524 @property epochs {string[]} The epoch names.
26525 @property monthNames {string[]} The long names of the months of the year.
26526 @property monthNamesShort {string[]} The short names of the months of the year.
26527 @property dayNames {string[]} The long names of the days of the week.
26528 @property dayNamesShort {string[]} The short names of the days of the week.
26529 @property dayNamesMin {string[]} The minimal names of the days of the week.
26530 @property dateFormat {string} The date format for this calendar.
26531 See the options on <a href="BaseCalendar.html#formatDate"><code>formatDate</code></a> for details.
26532 @property firstDay {number} The number of the first day of the week, starting at 0.
26533 @property isRTL {number} <code>true</code> if this localisation reads right-to-left. */
26534 regionalOptions: { // Localisations
26535 '': {
26536 name: 'Umm al-Qura',
26537 epochs: ['BH', 'AH'],
26538 monthNames: ['Al-Muharram', 'Safar', 'Rabi\' al-awwal', 'Rabi\' Al-Thani', 'Jumada Al-Awwal', 'Jumada Al-Thani',
26539 'Rajab', 'Sha\'aban', 'Ramadan', 'Shawwal', 'Dhu al-Qi\'dah', 'Dhu al-Hijjah'],
26540 monthNamesShort: ['Muh', 'Saf', 'Rab1', 'Rab2', 'Jum1', 'Jum2', 'Raj', 'Sha\'', 'Ram', 'Shaw', 'DhuQ', 'DhuH'],
26541 dayNames: ['Yawm al-Ahad', 'Yawm al-Ithnain', 'Yawm al-Thalāthā’', 'Yawm al-Arba‘ā’', 'Yawm al-Khamīs', 'Yawm al-Jum‘a', 'Yawm al-Sabt'],
26542 dayNamesMin: ['Ah', 'Ith', 'Th', 'Ar', 'Kh', 'Ju', 'Sa'],
26543 digits: null,
26544 dateFormat: 'yyyy/mm/dd',
26545 firstDay: 6,
26546 isRTL: true
26547 }
26548 },
26549
26550 /** Determine whether this date is in a leap year.
26551 @memberof UmmAlQuraCalendar
26552 @param year {CDate|number} The date to examine or the year to examine.
26553 @return {boolean} <code>true</code> if this is a leap year, <code>false</code> if not.
26554 @throws Error if an invalid year or a different calendar used. */
26555 leapYear: function (year) {
26556 var date = this._validate(year, this.minMonth, this.minDay, main.local.invalidYear);
26557 return (this.daysInYear(date.year()) === 355);
26558 },
26559
26560 /** Determine the week of the year for a date.
26561 @memberof UmmAlQuraCalendar
26562 @param year {CDate|number} The date to examine or the year to examine.
26563 @param [month] {number} The month to examine.
26564 @param [day] {number} The day to examine.
26565 @return {number} The week of the year.
26566 @throws Error if an invalid date or a different calendar used. */
26567 weekOfYear: function (year, month, day) {
26568 // Find Sunday of this week starting on Sunday
26569 var checkDate = this.newDate(year, month, day);
26570 checkDate.add(-checkDate.dayOfWeek(), 'd');
26571 return Math.floor((checkDate.dayOfYear() - 1) / 7) + 1;
26572 },
26573
26574 /** Retrieve the number of days in a year.
26575 @memberof UmmAlQuraCalendar
26576 @param year {CDate|number} The date to examine or the year to examine.
26577 @return {number} The number of days.
26578 @throws Error if an invalid year or a different calendar used. */
26579 daysInYear: function (year) {
26580 var daysCount = 0;
26581 for (var i = 1; i <= 12; i++) {
26582 daysCount += this.daysInMonth(year, i);
26583 }
26584 return daysCount;
26585 },
26586
26587 /** Retrieve the number of days in a month.
26588 @memberof UmmAlQuraCalendar
26589 @param year {CDate|number} The date to examine or the year of the month.
26590 @param [month] {number} The month.
26591 @return {number} The number of days in this month.
26592 @throws Error if an invalid month/year or a different calendar used. */
26593 daysInMonth: function (year, month) {
26594 var date = this._validate(year, month, this.minDay, main.local.invalidMonth);
26595 var mcjdn = date.toJD() - 2400000 + 0.5; // Modified Chronological Julian Day Number (MCJDN)
26596 // the MCJDN's of the start of the lunations in the Umm al-Qura calendar are stored in the 'ummalqura_dat' array
26597 var index = 0;
26598 for (var i = 0; i < ummalqura_dat.length; i++) {
26599 if (ummalqura_dat[i] > mcjdn) {
26600 return (ummalqura_dat[index] - ummalqura_dat[index - 1]);
26601 }
26602 index++;
26603 }
26604 return 30; // Unknown outside
26605 },
26606
26607 /** Determine whether this date is a week day.
26608 @memberof UmmAlQuraCalendar
26609 @param year {CDate|number} The date to examine or the year to examine.
26610 @param [month] {number} The month to examine.
26611 @param [day] {number} The day to examine.
26612 @return {boolean} <code>true</code> if a week day, <code>false</code> if not.
26613 @throws Error if an invalid date or a different calendar used. */
26614 weekDay: function (year, month, day) {
26615 return this.dayOfWeek(year, month, day) !== 5;
26616 },
26617
26618 /** Retrieve the Julian date equivalent for this date,
26619 i.e. days since January 1, 4713 BCE Greenwich noon.
26620 @memberof UmmAlQuraCalendar
26621 @param year {CDate|number} The date to convert or the year to convert.
26622 @param [month] {number} The month to convert.
26623 @param [day] {number} The day to convert.
26624 @return {number} The equivalent Julian date.
26625 @throws Error if an invalid date or a different calendar used. */
26626 toJD: function (year, month, day) {
26627 var date = this._validate(year, month, day, main.local.invalidDate);
26628 var index = (12 * (date.year() - 1)) + date.month() - 15292;
26629 var mcjdn = date.day() + ummalqura_dat[index - 1] - 1;
26630 return mcjdn + 2400000 - 0.5; // Modified Chronological Julian Day Number (MCJDN)
26631 },
26632
26633 /** Create a new date from a Julian date.
26634 @memberof UmmAlQuraCalendar
26635 @param jd {number} The Julian date to convert.
26636 @return {CDate} The equivalent date. */
26637 fromJD: function (jd) {
26638 var mcjdn = jd - 2400000 + 0.5; // Modified Chronological Julian Day Number (MCJDN)
26639 // the MCJDN's of the start of the lunations in the Umm al-Qura calendar
26640 // are stored in the 'ummalqura_dat' array
26641 var index = 0;
26642 for (var i = 0; i < ummalqura_dat.length; i++) {
26643 if (ummalqura_dat[i] > mcjdn) break;
26644 index++;
26645 }
26646 var lunation = index + 15292; //UmmAlQura Lunation Number
26647 var ii = Math.floor((lunation - 1) / 12);
26648 var year = ii + 1;
26649 var month = lunation - 12 * ii;
26650 var day = mcjdn - ummalqura_dat[index - 1] + 1;
26651 return this.newDate(year, month, day);
26652 },
26653
26654 /** Determine whether a date is valid for this calendar.
26655 @memberof UmmAlQuraCalendar
26656 @param year {number} The year to examine.
26657 @param month {number} The month to examine.
26658 @param day {number} The day to examine.
26659 @return {boolean} <code>true</code> if a valid date, <code>false</code> if not. */
26660 isValid: function(year, month, day) {
26661 var valid = main.baseCalendar.prototype.isValid.apply(this, arguments);
26662 if (valid) {
26663 year = (year.year != null ? year.year : year);
26664 valid = (year >= 1276 && year <= 1500);
26665 }
26666 return valid;
26667 },
26668
26669 /** Check that a candidate date is from the same calendar and is valid.
26670 @memberof UmmAlQuraCalendar
26671 @private
26672 @param year {CDate|number} The date to validate or the year to validate.
26673 @param month {number} The month to validate.
26674 @param day {number} The day to validate.
26675 @param error {string} Error message if invalid.
26676 @throws Error if different calendars used or invalid date. */
26677 _validate: function(year, month, day, error) {
26678 var date = main.baseCalendar.prototype._validate.apply(this, arguments);
26679 if (date.year < 1276 || date.year > 1500) {
26680 throw error.replace(/\{0\}/, this.local.name);
26681 }
26682 return date;
26683 }
26684});
26685
26686// UmmAlQura calendar implementation
26687main.calendars.ummalqura = UmmAlQuraCalendar;
26688
26689var ummalqura_dat = [
26690 20, 50, 79, 109, 138, 168, 197, 227, 256, 286, 315, 345, 374, 404, 433, 463, 492, 522, 551, 581,
26691 611, 641, 670, 700, 729, 759, 788, 818, 847, 877, 906, 936, 965, 995, 1024, 1054, 1083, 1113, 1142, 1172,
26692 1201, 1231, 1260, 1290, 1320, 1350, 1379, 1409, 1438, 1468, 1497, 1527, 1556, 1586, 1615, 1645, 1674, 1704, 1733, 1763,
26693 1792, 1822, 1851, 1881, 1910, 1940, 1969, 1999, 2028, 2058, 2087, 2117, 2146, 2176, 2205, 2235, 2264, 2294, 2323, 2353,
26694 2383, 2413, 2442, 2472, 2501, 2531, 2560, 2590, 2619, 2649, 2678, 2708, 2737, 2767, 2796, 2826, 2855, 2885, 2914, 2944,
26695 2973, 3003, 3032, 3062, 3091, 3121, 3150, 3180, 3209, 3239, 3268, 3298, 3327, 3357, 3386, 3416, 3446, 3476, 3505, 3535,
26696 3564, 3594, 3623, 3653, 3682, 3712, 3741, 3771, 3800, 3830, 3859, 3889, 3918, 3948, 3977, 4007, 4036, 4066, 4095, 4125,
26697 4155, 4185, 4214, 4244, 4273, 4303, 4332, 4362, 4391, 4421, 4450, 4480, 4509, 4539, 4568, 4598, 4627, 4657, 4686, 4716,
26698 4745, 4775, 4804, 4834, 4863, 4893, 4922, 4952, 4981, 5011, 5040, 5070, 5099, 5129, 5158, 5188, 5218, 5248, 5277, 5307,
26699 5336, 5366, 5395, 5425, 5454, 5484, 5513, 5543, 5572, 5602, 5631, 5661, 5690, 5720, 5749, 5779, 5808, 5838, 5867, 5897,
26700 5926, 5956, 5985, 6015, 6044, 6074, 6103, 6133, 6162, 6192, 6221, 6251, 6281, 6311, 6340, 6370, 6399, 6429, 6458, 6488,
26701 6517, 6547, 6576, 6606, 6635, 6665, 6694, 6724, 6753, 6783, 6812, 6842, 6871, 6901, 6930, 6960, 6989, 7019, 7048, 7078,
26702 7107, 7137, 7166, 7196, 7225, 7255, 7284, 7314, 7344, 7374, 7403, 7433, 7462, 7492, 7521, 7551, 7580, 7610, 7639, 7669,
26703 7698, 7728, 7757, 7787, 7816, 7846, 7875, 7905, 7934, 7964, 7993, 8023, 8053, 8083, 8112, 8142, 8171, 8201, 8230, 8260,
26704 8289, 8319, 8348, 8378, 8407, 8437, 8466, 8496, 8525, 8555, 8584, 8614, 8643, 8673, 8702, 8732, 8761, 8791, 8821, 8850,
26705 8880, 8909, 8938, 8968, 8997, 9027, 9056, 9086, 9115, 9145, 9175, 9205, 9234, 9264, 9293, 9322, 9352, 9381, 9410, 9440,
26706 9470, 9499, 9529, 9559, 9589, 9618, 9648, 9677, 9706, 9736, 9765, 9794, 9824, 9853, 9883, 9913, 9943, 9972, 10002, 10032,
26707 10061, 10090, 10120, 10149, 10178, 10208, 10237, 10267, 10297, 10326, 10356, 10386, 10415, 10445, 10474, 10504, 10533, 10562, 10592, 10621,
26708 10651, 10680, 10710, 10740, 10770, 10799, 10829, 10858, 10888, 10917, 10947, 10976, 11005, 11035, 11064, 11094, 11124, 11153, 11183, 11213,
26709 11242, 11272, 11301, 11331, 11360, 11389, 11419, 11448, 11478, 11507, 11537, 11567, 11596, 11626, 11655, 11685, 11715, 11744, 11774, 11803,
26710 11832, 11862, 11891, 11921, 11950, 11980, 12010, 12039, 12069, 12099, 12128, 12158, 12187, 12216, 12246, 12275, 12304, 12334, 12364, 12393,
26711 12423, 12453, 12483, 12512, 12542, 12571, 12600, 12630, 12659, 12688, 12718, 12747, 12777, 12807, 12837, 12866, 12896, 12926, 12955, 12984,
26712 13014, 13043, 13072, 13102, 13131, 13161, 13191, 13220, 13250, 13280, 13310, 13339, 13368, 13398, 13427, 13456, 13486, 13515, 13545, 13574,
26713 13604, 13634, 13664, 13693, 13723, 13752, 13782, 13811, 13840, 13870, 13899, 13929, 13958, 13988, 14018, 14047, 14077, 14107, 14136, 14166,
26714 14195, 14224, 14254, 14283, 14313, 14342, 14372, 14401, 14431, 14461, 14490, 14520, 14550, 14579, 14609, 14638, 14667, 14697, 14726, 14756,
26715 14785, 14815, 14844, 14874, 14904, 14933, 14963, 14993, 15021, 15051, 15081, 15110, 15140, 15169, 15199, 15228, 15258, 15287, 15317, 15347,
26716 15377, 15406, 15436, 15465, 15494, 15524, 15553, 15582, 15612, 15641, 15671, 15701, 15731, 15760, 15790, 15820, 15849, 15878, 15908, 15937,
26717 15966, 15996, 16025, 16055, 16085, 16114, 16144, 16174, 16204, 16233, 16262, 16292, 16321, 16350, 16380, 16409, 16439, 16468, 16498, 16528,
26718 16558, 16587, 16617, 16646, 16676, 16705, 16734, 16764, 16793, 16823, 16852, 16882, 16912, 16941, 16971, 17001, 17030, 17060, 17089, 17118,
26719 17148, 17177, 17207, 17236, 17266, 17295, 17325, 17355, 17384, 17414, 17444, 17473, 17502, 17532, 17561, 17591, 17620, 17650, 17679, 17709,
26720 17738, 17768, 17798, 17827, 17857, 17886, 17916, 17945, 17975, 18004, 18034, 18063, 18093, 18122, 18152, 18181, 18211, 18241, 18270, 18300,
26721 18330, 18359, 18388, 18418, 18447, 18476, 18506, 18535, 18565, 18595, 18625, 18654, 18684, 18714, 18743, 18772, 18802, 18831, 18860, 18890,
26722 18919, 18949, 18979, 19008, 19038, 19068, 19098, 19127, 19156, 19186, 19215, 19244, 19274, 19303, 19333, 19362, 19392, 19422, 19452, 19481,
26723 19511, 19540, 19570, 19599, 19628, 19658, 19687, 19717, 19746, 19776, 19806, 19836, 19865, 19895, 19924, 19954, 19983, 20012, 20042, 20071,
26724 20101, 20130, 20160, 20190, 20219, 20249, 20279, 20308, 20338, 20367, 20396, 20426, 20455, 20485, 20514, 20544, 20573, 20603, 20633, 20662,
26725 20692, 20721, 20751, 20780, 20810, 20839, 20869, 20898, 20928, 20957, 20987, 21016, 21046, 21076, 21105, 21135, 21164, 21194, 21223, 21253,
26726 21282, 21312, 21341, 21371, 21400, 21430, 21459, 21489, 21519, 21548, 21578, 21607, 21637, 21666, 21696, 21725, 21754, 21784, 21813, 21843,
26727 21873, 21902, 21932, 21962, 21991, 22021, 22050, 22080, 22109, 22138, 22168, 22197, 22227, 22256, 22286, 22316, 22346, 22375, 22405, 22434,
26728 22464, 22493, 22522, 22552, 22581, 22611, 22640, 22670, 22700, 22730, 22759, 22789, 22818, 22848, 22877, 22906, 22936, 22965, 22994, 23024,
26729 23054, 23083, 23113, 23143, 23173, 23202, 23232, 23261, 23290, 23320, 23349, 23379, 23408, 23438, 23467, 23497, 23527, 23556, 23586, 23616,
26730 23645, 23674, 23704, 23733, 23763, 23792, 23822, 23851, 23881, 23910, 23940, 23970, 23999, 24029, 24058, 24088, 24117, 24147, 24176, 24206,
26731 24235, 24265, 24294, 24324, 24353, 24383, 24413, 24442, 24472, 24501, 24531, 24560, 24590, 24619, 24648, 24678, 24707, 24737, 24767, 24796,
26732 24826, 24856, 24885, 24915, 24944, 24974, 25003, 25032, 25062, 25091, 25121, 25150, 25180, 25210, 25240, 25269, 25299, 25328, 25358, 25387,
26733 25416, 25446, 25475, 25505, 25534, 25564, 25594, 25624, 25653, 25683, 25712, 25742, 25771, 25800, 25830, 25859, 25888, 25918, 25948, 25977,
26734 26007, 26037, 26067, 26096, 26126, 26155, 26184, 26214, 26243, 26272, 26302, 26332, 26361, 26391, 26421, 26451, 26480, 26510, 26539, 26568,
26735 26598, 26627, 26656, 26686, 26715, 26745, 26775, 26805, 26834, 26864, 26893, 26923, 26952, 26982, 27011, 27041, 27070, 27099, 27129, 27159,
26736 27188, 27218, 27248, 27277, 27307, 27336, 27366, 27395, 27425, 27454, 27484, 27513, 27542, 27572, 27602, 27631, 27661, 27691, 27720, 27750,
26737 27779, 27809, 27838, 27868, 27897, 27926, 27956, 27985, 28015, 28045, 28074, 28104, 28134, 28163, 28193, 28222, 28252, 28281, 28310, 28340,
26738 28369, 28399, 28428, 28458, 28488, 28517, 28547, 28577,
26739 // From 1356
26740 28607, 28636, 28665, 28695, 28724, 28754, 28783, 28813, 28843, 28872, 28901, 28931, 28960, 28990, 29019, 29049, 29078, 29108, 29137, 29167,
26741 29196, 29226, 29255, 29285, 29315, 29345, 29375, 29404, 29434, 29463, 29492, 29522, 29551, 29580, 29610, 29640, 29669, 29699, 29729, 29759,
26742 29788, 29818, 29847, 29876, 29906, 29935, 29964, 29994, 30023, 30053, 30082, 30112, 30141, 30171, 30200, 30230, 30259, 30289, 30318, 30348,
26743 30378, 30408, 30437, 30467, 30496, 30526, 30555, 30585, 30614, 30644, 30673, 30703, 30732, 30762, 30791, 30821, 30850, 30880, 30909, 30939,
26744 30968, 30998, 31027, 31057, 31086, 31116, 31145, 31175, 31204, 31234, 31263, 31293, 31322, 31352, 31381, 31411, 31441, 31471, 31500, 31530,
26745 31559, 31589, 31618, 31648, 31676, 31706, 31736, 31766, 31795, 31825, 31854, 31884, 31913, 31943, 31972, 32002, 32031, 32061, 32090, 32120,
26746 32150, 32180, 32209, 32239, 32268, 32298, 32327, 32357, 32386, 32416, 32445, 32475, 32504, 32534, 32563, 32593, 32622, 32652, 32681, 32711,
26747 32740, 32770, 32799, 32829, 32858, 32888, 32917, 32947, 32976, 33006, 33035, 33065, 33094, 33124, 33153, 33183, 33213, 33243, 33272, 33302,
26748 33331, 33361, 33390, 33420, 33450, 33479, 33509, 33539, 33568, 33598, 33627, 33657, 33686, 33716, 33745, 33775, 33804, 33834, 33863, 33893,
26749 33922, 33952, 33981, 34011, 34040, 34069, 34099, 34128, 34158, 34187, 34217, 34247, 34277, 34306, 34336, 34365, 34395, 34424, 34454, 34483,
26750 34512, 34542, 34571, 34601, 34631, 34660, 34690, 34719, 34749, 34778, 34808, 34837, 34867, 34896, 34926, 34955, 34985, 35015, 35044, 35074,
26751 35103, 35133, 35162, 35192, 35222, 35251, 35280, 35310, 35340, 35370, 35399, 35429, 35458, 35488, 35517, 35547, 35576, 35605, 35635, 35665,
26752 35694, 35723, 35753, 35782, 35811, 35841, 35871, 35901, 35930, 35960, 35989, 36019, 36048, 36078, 36107, 36136, 36166, 36195, 36225, 36254,
26753 36284, 36314, 36343, 36373, 36403, 36433, 36462, 36492, 36521, 36551, 36580, 36610, 36639, 36669, 36698, 36728, 36757, 36786, 36816, 36845,
26754 36875, 36904, 36934, 36963, 36993, 37022, 37052, 37081, 37111, 37141, 37170, 37200, 37229, 37259, 37288, 37318, 37347, 37377, 37406, 37436,
26755 37465, 37495, 37524, 37554, 37584, 37613, 37643, 37672, 37701, 37731, 37760, 37790, 37819, 37849, 37878, 37908, 37938, 37967, 37997, 38027,
26756 38056, 38085, 38115, 38144, 38174, 38203, 38233, 38262, 38292, 38322, 38351, 38381, 38410, 38440, 38469, 38499, 38528, 38558, 38587, 38617,
26757 38646, 38676, 38705, 38735, 38764, 38794, 38823, 38853, 38882, 38912, 38941, 38971, 39001, 39030, 39059, 39089, 39118, 39148, 39178, 39208,
26758 39237, 39267, 39297, 39326, 39355, 39385, 39414, 39444, 39473, 39503, 39532, 39562, 39592, 39621, 39650, 39680, 39709, 39739, 39768, 39798,
26759 39827, 39857, 39886, 39916, 39946, 39975, 40005, 40035, 40064, 40094, 40123, 40153, 40182, 40212, 40241, 40271, 40300, 40330, 40359, 40389,
26760 40418, 40448, 40477, 40507, 40536, 40566, 40595, 40625, 40655, 40685, 40714, 40744, 40773, 40803, 40832, 40862, 40892, 40921, 40951, 40980,
26761 41009, 41039, 41068, 41098, 41127, 41157, 41186, 41216, 41245, 41275, 41304, 41334, 41364, 41393, 41422, 41452, 41481, 41511, 41540, 41570,
26762 41599, 41629, 41658, 41688, 41718, 41748, 41777, 41807, 41836, 41865, 41894, 41924, 41953, 41983, 42012, 42042, 42072, 42102, 42131, 42161,
26763 42190, 42220, 42249, 42279, 42308, 42337, 42367, 42397, 42426, 42456, 42485, 42515, 42545, 42574, 42604, 42633, 42662, 42692, 42721, 42751,
26764 42780, 42810, 42839, 42869, 42899, 42929, 42958, 42988, 43017, 43046, 43076, 43105, 43135, 43164, 43194, 43223, 43253, 43283, 43312, 43342,
26765 43371, 43401, 43430, 43460, 43489, 43519, 43548, 43578, 43607, 43637, 43666, 43696, 43726, 43755, 43785, 43814, 43844, 43873, 43903, 43932,
26766 43962, 43991, 44021, 44050, 44080, 44109, 44139, 44169, 44198, 44228, 44258, 44287, 44317, 44346, 44375, 44405, 44434, 44464, 44493, 44523,
26767 44553, 44582, 44612, 44641, 44671, 44700, 44730, 44759, 44788, 44818, 44847, 44877, 44906, 44936, 44966, 44996, 45025, 45055, 45084, 45114,
26768 45143, 45172, 45202, 45231, 45261, 45290, 45320, 45350, 45380, 45409, 45439, 45468, 45498, 45527, 45556, 45586, 45615, 45644, 45674, 45704,
26769 45733, 45763, 45793, 45823, 45852, 45882, 45911, 45940, 45970, 45999, 46028, 46058, 46088, 46117, 46147, 46177, 46206, 46236, 46265, 46295,
26770 46324, 46354, 46383, 46413, 46442, 46472, 46501, 46531, 46560, 46590, 46620, 46649, 46679, 46708, 46738, 46767, 46797, 46826, 46856, 46885,
26771 46915, 46944, 46974, 47003, 47033, 47063, 47092, 47122, 47151, 47181, 47210, 47240, 47269, 47298, 47328, 47357, 47387, 47417, 47446, 47476,
26772 47506, 47535, 47565, 47594, 47624, 47653, 47682, 47712, 47741, 47771, 47800, 47830, 47860, 47890, 47919, 47949, 47978, 48008, 48037, 48066,
26773 48096, 48125, 48155, 48184, 48214, 48244, 48273, 48303, 48333, 48362, 48392, 48421, 48450, 48480, 48509, 48538, 48568, 48598, 48627, 48657,
26774 48687, 48717, 48746, 48776, 48805, 48834, 48864, 48893, 48922, 48952, 48982, 49011, 49041, 49071, 49100, 49130, 49160, 49189, 49218, 49248,
26775 49277, 49306, 49336, 49365, 49395, 49425, 49455, 49484, 49514, 49543, 49573, 49602, 49632, 49661, 49690, 49720, 49749, 49779, 49809, 49838,
26776 49868, 49898, 49927, 49957, 49986, 50016, 50045, 50075, 50104, 50133, 50163, 50192, 50222, 50252, 50281, 50311, 50340, 50370, 50400, 50429,
26777 50459, 50488, 50518, 50547, 50576, 50606, 50635, 50665, 50694, 50724, 50754, 50784, 50813, 50843, 50872, 50902, 50931, 50960, 50990, 51019,
26778 51049, 51078, 51108, 51138, 51167, 51197, 51227, 51256, 51286, 51315, 51345, 51374, 51403, 51433, 51462, 51492, 51522, 51552, 51582, 51611,
26779 51641, 51670, 51699, 51729, 51758, 51787, 51816, 51846, 51876, 51906, 51936, 51965, 51995, 52025, 52054, 52083, 52113, 52142, 52171, 52200,
26780 52230, 52260, 52290, 52319, 52349, 52379, 52408, 52438, 52467, 52497, 52526, 52555, 52585, 52614, 52644, 52673, 52703, 52733, 52762, 52792,
26781 52822, 52851, 52881, 52910, 52939, 52969, 52998, 53028, 53057, 53087, 53116, 53146, 53176, 53205, 53235, 53264, 53294, 53324, 53353, 53383,
26782 53412, 53441, 53471, 53500, 53530, 53559, 53589, 53619, 53648, 53678, 53708, 53737, 53767, 53796, 53825, 53855, 53884, 53913, 53943, 53973,
26783 54003, 54032, 54062, 54092, 54121, 54151, 54180, 54209, 54239, 54268, 54297, 54327, 54357, 54387, 54416, 54446, 54476, 54505, 54535, 54564,
26784 54593, 54623, 54652, 54681, 54711, 54741, 54770, 54800, 54830, 54859, 54889, 54919, 54948, 54977, 55007, 55036, 55066, 55095, 55125, 55154,
26785 55184, 55213, 55243, 55273, 55302, 55332, 55361, 55391, 55420, 55450, 55479, 55508, 55538, 55567, 55597, 55627, 55657, 55686, 55716, 55745,
26786 55775, 55804, 55834, 55863, 55892, 55922, 55951, 55981, 56011, 56040, 56070, 56100, 56129, 56159, 56188, 56218, 56247, 56276, 56306, 56335,
26787 56365, 56394, 56424, 56454, 56483, 56513, 56543, 56572, 56601, 56631, 56660, 56690, 56719, 56749, 56778, 56808, 56837, 56867, 56897, 56926,
26788 56956, 56985, 57015, 57044, 57074, 57103, 57133, 57162, 57192, 57221, 57251, 57280, 57310, 57340, 57369, 57399, 57429, 57458, 57487, 57517,
26789 57546, 57576, 57605, 57634, 57664, 57694, 57723, 57753, 57783, 57813, 57842, 57871, 57901, 57930, 57959, 57989, 58018, 58048, 58077, 58107,
26790 58137, 58167, 58196, 58226, 58255, 58285, 58314, 58343, 58373, 58402, 58432, 58461, 58491, 58521, 58551, 58580, 58610, 58639, 58669, 58698,
26791 58727, 58757, 58786, 58816, 58845, 58875, 58905, 58934, 58964, 58994, 59023, 59053, 59082, 59111, 59141, 59170, 59200, 59229, 59259, 59288,
26792 59318, 59348, 59377, 59407, 59436, 59466, 59495, 59525, 59554, 59584, 59613, 59643, 59672, 59702, 59731, 59761, 59791, 59820, 59850, 59879,
26793 59909, 59939, 59968, 59997, 60027, 60056, 60086, 60115, 60145, 60174, 60204, 60234, 60264, 60293, 60323, 60352, 60381, 60411, 60440, 60469,
26794 60499, 60528, 60558, 60588, 60618, 60648, 60677, 60707, 60736, 60765, 60795, 60824, 60853, 60883, 60912, 60942, 60972, 61002, 61031, 61061,
26795 61090, 61120, 61149, 61179, 61208, 61237, 61267, 61296, 61326, 61356, 61385, 61415, 61445, 61474, 61504, 61533, 61563, 61592, 61621, 61651,
26796 61680, 61710, 61739, 61769, 61799, 61828, 61858, 61888, 61917, 61947, 61976, 62006, 62035, 62064, 62094, 62123, 62153, 62182, 62212, 62242,
26797 62271, 62301, 62331, 62360, 62390, 62419, 62448, 62478, 62507, 62537, 62566, 62596, 62625, 62655, 62685, 62715, 62744, 62774, 62803, 62832,
26798 62862, 62891, 62921, 62950, 62980, 63009, 63039, 63069, 63099, 63128, 63157, 63187, 63216, 63246, 63275, 63305, 63334, 63363, 63393, 63423,
26799 63453, 63482, 63512, 63541, 63571, 63600, 63630, 63659, 63689, 63718, 63747, 63777, 63807, 63836, 63866, 63895, 63925, 63955, 63984, 64014,
26800 64043, 64073, 64102, 64131, 64161, 64190, 64220, 64249, 64279, 64309, 64339, 64368, 64398, 64427, 64457, 64486, 64515, 64545, 64574, 64603,
26801 64633, 64663, 64692, 64722, 64752, 64782, 64811, 64841, 64870, 64899, 64929, 64958, 64987, 65017, 65047, 65076, 65106, 65136, 65166, 65195,
26802 65225, 65254, 65283, 65313, 65342, 65371, 65401, 65431, 65460, 65490, 65520, 65549, 65579, 65608, 65638, 65667, 65697, 65726, 65755, 65785,
26803 65815, 65844, 65874, 65903, 65933, 65963, 65992, 66022, 66051, 66081, 66110, 66140, 66169, 66199, 66228, 66258, 66287, 66317, 66346, 66376,
26804 66405, 66435, 66465, 66494, 66524, 66553, 66583, 66612, 66641, 66671, 66700, 66730, 66760, 66789, 66819, 66849, 66878, 66908, 66937, 66967,
26805 66996, 67025, 67055, 67084, 67114, 67143, 67173, 67203, 67233, 67262, 67292, 67321, 67351, 67380, 67409, 67439, 67468, 67497, 67527, 67557,
26806 67587, 67617, 67646, 67676, 67705, 67735, 67764, 67793, 67823, 67852, 67882, 67911, 67941, 67971, 68000, 68030, 68060, 68089, 68119, 68148,
26807 68177, 68207, 68236, 68266, 68295, 68325, 68354, 68384, 68414, 68443, 68473, 68502, 68532, 68561, 68591, 68620, 68650, 68679, 68708, 68738,
26808 68768, 68797, 68827, 68857, 68886, 68916, 68946, 68975, 69004, 69034, 69063, 69092, 69122, 69152, 69181, 69211, 69240, 69270, 69300, 69330,
26809 69359, 69388, 69418, 69447, 69476, 69506, 69535, 69565, 69595, 69624, 69654, 69684, 69713, 69743, 69772, 69802, 69831, 69861, 69890, 69919,
26810 69949, 69978, 70008, 70038, 70067, 70097, 70126, 70156, 70186, 70215, 70245, 70274, 70303, 70333, 70362, 70392, 70421, 70451, 70481, 70510,
26811 70540, 70570, 70599, 70629, 70658, 70687, 70717, 70746, 70776, 70805, 70835, 70864, 70894, 70924, 70954, 70983, 71013, 71042, 71071, 71101,
26812 71130, 71159, 71189, 71218, 71248, 71278, 71308, 71337, 71367, 71397, 71426, 71455, 71485, 71514, 71543, 71573, 71602, 71632, 71662, 71691,
26813 71721, 71751, 71781, 71810, 71839, 71869, 71898, 71927, 71957, 71986, 72016, 72046, 72075, 72105, 72135, 72164, 72194, 72223, 72253, 72282,
26814 72311, 72341, 72370, 72400, 72429, 72459, 72489, 72518, 72548, 72577, 72607, 72637, 72666, 72695, 72725, 72754, 72784, 72813, 72843, 72872,
26815 72902, 72931, 72961, 72991, 73020, 73050, 73080, 73109, 73139, 73168, 73197, 73227, 73256, 73286, 73315, 73345, 73375, 73404, 73434, 73464,
26816 73493, 73523, 73552, 73581, 73611, 73640, 73669, 73699, 73729, 73758, 73788, 73818, 73848, 73877, 73907, 73936, 73965, 73995, 74024, 74053,
26817 74083, 74113, 74142, 74172, 74202, 74231, 74261, 74291, 74320, 74349, 74379, 74408, 74437, 74467, 74497, 74526, 74556, 74586, 74615, 74645,
26818 74675, 74704, 74733, 74763, 74792, 74822, 74851, 74881, 74910, 74940, 74969, 74999, 75029, 75058, 75088, 75117, 75147, 75176, 75206, 75235,
26819 75264, 75294, 75323, 75353, 75383, 75412, 75442, 75472, 75501, 75531, 75560, 75590, 75619, 75648, 75678, 75707, 75737, 75766, 75796, 75826,
26820 75856, 75885, 75915, 75944, 75974, 76003, 76032, 76062, 76091, 76121, 76150, 76180, 76210, 76239, 76269, 76299, 76328, 76358, 76387, 76416,
26821 76446, 76475, 76505, 76534, 76564, 76593, 76623, 76653, 76682, 76712, 76741, 76771, 76801, 76830, 76859, 76889, 76918, 76948, 76977, 77007,
26822 77036, 77066, 77096, 77125, 77155, 77185, 77214, 77243, 77273, 77302, 77332, 77361, 77390, 77420, 77450, 77479, 77509, 77539, 77569, 77598,
26823 77627, 77657, 77686, 77715, 77745, 77774, 77804, 77833, 77863, 77893, 77923, 77952, 77982, 78011, 78041, 78070, 78099, 78129, 78158, 78188,
26824 78217, 78247, 78277, 78307, 78336, 78366, 78395, 78425, 78454, 78483, 78513, 78542, 78572, 78601, 78631, 78661, 78690, 78720, 78750, 78779,
26825 78808, 78838, 78867, 78897, 78926, 78956, 78985, 79015, 79044, 79074, 79104, 79133, 79163, 79192, 79222, 79251, 79281, 79310, 79340, 79369,
26826 79399, 79428, 79458, 79487, 79517, 79546, 79576, 79606, 79635, 79665, 79695, 79724, 79753, 79783, 79812, 79841, 79871, 79900, 79930, 79960,
26827 79990];
26828
26829
26830},{"../main":137,"object-assign":73}],137:[function(_dereq_,module,exports){
26831/*
26832 * World Calendars
26833 * https://github.com/alexcjohnson/world-calendars
26834 *
26835 * Batch-converted from kbwood/calendars
26836 * Many thanks to Keith Wood and all of the contributors to the original project!
26837 *
26838 * This source code is licensed under the MIT license found in the
26839 * LICENSE file in the root directory of this source tree.
26840 */
26841
26842/* http://keith-wood.name/calendars.html
26843 Calendars for jQuery v2.0.2.
26844 Written by Keith Wood (wood.keith{at}optusnet.com.au) August 2009.
26845 Available under the MIT (http://keith-wood.name/licence.html) license.
26846 Please attribute the author if you use it. */
26847
26848var assign = _dereq_('object-assign');
26849
26850
26851function Calendars() {
26852 this.regionalOptions = [];
26853 this.regionalOptions[''] = {
26854 invalidCalendar: 'Calendar {0} not found',
26855 invalidDate: 'Invalid {0} date',
26856 invalidMonth: 'Invalid {0} month',
26857 invalidYear: 'Invalid {0} year',
26858 differentCalendars: 'Cannot mix {0} and {1} dates'
26859 };
26860 this.local = this.regionalOptions[''];
26861 this.calendars = {};
26862 this._localCals = {};
26863}
26864
26865/** Create the calendars plugin.
26866 <p>Provides support for various world calendars in a consistent manner.</p>
26867 @class Calendars
26868 @example _exports.instance('julian').newDate(2014, 12, 25) */
26869assign(Calendars.prototype, {
26870
26871 /** Obtain a calendar implementation and localisation.
26872 @memberof Calendars
26873 @param [name='gregorian'] {string} The name of the calendar, e.g. 'gregorian', 'persian', 'islamic'.
26874 @param [language=''] {string} The language code to use for localisation (default is English).
26875 @return {Calendar} The calendar and localisation.
26876 @throws Error if calendar not found. */
26877 instance: function(name, language) {
26878 name = (name || 'gregorian').toLowerCase();
26879 language = language || '';
26880 var cal = this._localCals[name + '-' + language];
26881 if (!cal && this.calendars[name]) {
26882 cal = new this.calendars[name](language);
26883 this._localCals[name + '-' + language] = cal;
26884 }
26885 if (!cal) {
26886 throw (this.local.invalidCalendar || this.regionalOptions[''].invalidCalendar).
26887 replace(/\{0\}/, name);
26888 }
26889 return cal;
26890 },
26891
26892 /** Create a new date - for today if no other parameters given.
26893 @memberof Calendars
26894 @param year {CDate|number} The date to copy or the year for the date.
26895 @param [month] {number} The month for the date.
26896 @param [day] {number} The day for the date.
26897 @param [calendar='gregorian'] {BaseCalendar|string} The underlying calendar or the name of the calendar.
26898 @param [language=''] {string} The language to use for localisation (default English).
26899 @return {CDate} The new date.
26900 @throws Error if an invalid date. */
26901 newDate: function(year, month, day, calendar, language) {
26902 calendar = (year != null && year.year ? year.calendar() : (typeof calendar === 'string' ?
26903 this.instance(calendar, language) : calendar)) || this.instance();
26904 return calendar.newDate(year, month, day);
26905 },
26906
26907 /** A simple digit substitution function for localising numbers via the Calendar digits option.
26908 @member Calendars
26909 @param digits {string[]} The substitute digits, for 0 through 9.
26910 @return {function} The substitution function. */
26911 substituteDigits: function(digits) {
26912 return function(value) {
26913 return (value + '').replace(/[0-9]/g, function(digit) {
26914 return digits[digit];
26915 });
26916 }
26917 },
26918
26919 /** Digit substitution function for localising Chinese style numbers via the Calendar digits option.
26920 @member Calendars
26921 @param digits {string[]} The substitute digits, for 0 through 9.
26922 @param powers {string[]} The characters denoting powers of 10, i.e. 1, 10, 100, 1000.
26923 @return {function} The substitution function. */
26924 substituteChineseDigits: function(digits, powers) {
26925 return function(value) {
26926 var localNumber = '';
26927 var power = 0;
26928 while (value > 0) {
26929 var units = value % 10;
26930 localNumber = (units === 0 ? '' : digits[units] + powers[power]) + localNumber;
26931 power++;
26932 value = Math.floor(value / 10);
26933 }
26934 if (localNumber.indexOf(digits[1] + powers[1]) === 0) {
26935 localNumber = localNumber.substr(1);
26936 }
26937 return localNumber || digits[0];
26938 }
26939 }
26940});
26941
26942/** Generic date, based on a particular calendar.
26943 @class CDate
26944 @param calendar {BaseCalendar} The underlying calendar implementation.
26945 @param year {number} The year for this date.
26946 @param month {number} The month for this date.
26947 @param day {number} The day for this date.
26948 @return {CDate} The date object.
26949 @throws Error if an invalid date. */
26950function CDate(calendar, year, month, day) {
26951 this._calendar = calendar;
26952 this._year = year;
26953 this._month = month;
26954 this._day = day;
26955 if (this._calendar._validateLevel === 0 &&
26956 !this._calendar.isValid(this._year, this._month, this._day)) {
26957 throw (_exports.local.invalidDate || _exports.regionalOptions[''].invalidDate).
26958 replace(/\{0\}/, this._calendar.local.name);
26959 }
26960}
26961
26962/** Pad a numeric value with leading zeroes.
26963 @private
26964 @param value {number} The number to format.
26965 @param length {number} The minimum length.
26966 @return {string} The formatted number. */
26967function pad(value, length) {
26968 value = '' + value;
26969 return '000000'.substring(0, length - value.length) + value;
26970}
26971
26972assign(CDate.prototype, {
26973
26974 /** Create a new date.
26975 @memberof CDate
26976 @param [year] {CDate|number} The date to copy or the year for the date (default this date).
26977 @param [month] {number} The month for the date.
26978 @param [day] {number} The day for the date.
26979 @return {CDate} The new date.
26980 @throws Error if an invalid date. */
26981 newDate: function(year, month, day) {
26982 return this._calendar.newDate((year == null ? this : year), month, day);
26983 },
26984
26985 /** Set or retrieve the year for this date.
26986 @memberof CDate
26987 @param [year] {number} The year for the date.
26988 @return {number|CDate} The date's year (if no parameter) or the updated date.
26989 @throws Error if an invalid date. */
26990 year: function(year) {
26991 return (arguments.length === 0 ? this._year : this.set(year, 'y'));
26992 },
26993
26994 /** Set or retrieve the month for this date.
26995 @memberof CDate
26996 @param [month] {number} The month for the date.
26997 @return {number|CDate} The date's month (if no parameter) or the updated date.
26998 @throws Error if an invalid date. */
26999 month: function(month) {
27000 return (arguments.length === 0 ? this._month : this.set(month, 'm'));
27001 },
27002
27003 /** Set or retrieve the day for this date.
27004 @memberof CDate
27005 @param [day] {number} The day for the date.
27006 @return {number|CData} The date's day (if no parameter) or the updated date.
27007 @throws Error if an invalid date. */
27008 day: function(day) {
27009 return (arguments.length === 0 ? this._day : this.set(day, 'd'));
27010 },
27011
27012 /** Set new values for this date.
27013 @memberof CDate
27014 @param year {number} The year for the date.
27015 @param month {number} The month for the date.
27016 @param day {number} The day for the date.
27017 @return {CDate} The updated date.
27018 @throws Error if an invalid date. */
27019 date: function(year, month, day) {
27020 if (!this._calendar.isValid(year, month, day)) {
27021 throw (_exports.local.invalidDate || _exports.regionalOptions[''].invalidDate).
27022 replace(/\{0\}/, this._calendar.local.name);
27023 }
27024 this._year = year;
27025 this._month = month;
27026 this._day = day;
27027 return this;
27028 },
27029
27030 /** Determine whether this date is in a leap year.
27031 @memberof CDate
27032 @return {boolean} <code>true</code> if this is a leap year, <code>false</code> if not. */
27033 leapYear: function() {
27034 return this._calendar.leapYear(this);
27035 },
27036
27037 /** Retrieve the epoch designator for this date, e.g. BCE or CE.
27038 @memberof CDate
27039 @return {string} The current epoch. */
27040 epoch: function() {
27041 return this._calendar.epoch(this);
27042 },
27043
27044 /** Format the year, if not a simple sequential number.
27045 @memberof CDate
27046 @return {string} The formatted year. */
27047 formatYear: function() {
27048 return this._calendar.formatYear(this);
27049 },
27050
27051 /** Retrieve the month of the year for this date,
27052 i.e. the month's position within a numbered year.
27053 @memberof CDate
27054 @return {number} The month of the year: <code>minMonth</code> to months per year. */
27055 monthOfYear: function() {
27056 return this._calendar.monthOfYear(this);
27057 },
27058
27059 /** Retrieve the week of the year for this date.
27060 @memberof CDate
27061 @return {number} The week of the year: 1 to weeks per year. */
27062 weekOfYear: function() {
27063 return this._calendar.weekOfYear(this);
27064 },
27065
27066 /** Retrieve the number of days in the year for this date.
27067 @memberof CDate
27068 @return {number} The number of days in this year. */
27069 daysInYear: function() {
27070 return this._calendar.daysInYear(this);
27071 },
27072
27073 /** Retrieve the day of the year for this date.
27074 @memberof CDate
27075 @return {number} The day of the year: 1 to days per year. */
27076 dayOfYear: function() {
27077 return this._calendar.dayOfYear(this);
27078 },
27079
27080 /** Retrieve the number of days in the month for this date.
27081 @memberof CDate
27082 @return {number} The number of days. */
27083 daysInMonth: function() {
27084 return this._calendar.daysInMonth(this);
27085 },
27086
27087 /** Retrieve the day of the week for this date.
27088 @memberof CDate
27089 @return {number} The day of the week: 0 to number of days - 1. */
27090 dayOfWeek: function() {
27091 return this._calendar.dayOfWeek(this);
27092 },
27093
27094 /** Determine whether this date is a week day.
27095 @memberof CDate
27096 @return {boolean} <code>true</code> if a week day, <code>false</code> if not. */
27097 weekDay: function() {
27098 return this._calendar.weekDay(this);
27099 },
27100
27101 /** Retrieve additional information about this date.
27102 @memberof CDate
27103 @return {object} Additional information - contents depends on calendar. */
27104 extraInfo: function() {
27105 return this._calendar.extraInfo(this);
27106 },
27107
27108 /** Add period(s) to a date.
27109 @memberof CDate
27110 @param offset {number} The number of periods to adjust by.
27111 @param period {string} One of 'y' for year, 'm' for month, 'w' for week, 'd' for day.
27112 @return {CDate} The updated date. */
27113 add: function(offset, period) {
27114 return this._calendar.add(this, offset, period);
27115 },
27116
27117 /** Set a portion of the date.
27118 @memberof CDate
27119 @param value {number} The new value for the period.
27120 @param period {string} One of 'y' for year, 'm' for month, 'd' for day.
27121 @return {CDate} The updated date.
27122 @throws Error if not a valid date. */
27123 set: function(value, period) {
27124 return this._calendar.set(this, value, period);
27125 },
27126
27127 /** Compare this date to another date.
27128 @memberof CDate
27129 @param date {CDate} The other date.
27130 @return {number} -1 if this date is before the other date,
27131 0 if they are equal, or +1 if this date is after the other date. */
27132 compareTo: function(date) {
27133 if (this._calendar.name !== date._calendar.name) {
27134 throw (_exports.local.differentCalendars || _exports.regionalOptions[''].differentCalendars).
27135 replace(/\{0\}/, this._calendar.local.name).replace(/\{1\}/, date._calendar.local.name);
27136 }
27137 var c = (this._year !== date._year ? this._year - date._year :
27138 this._month !== date._month ? this.monthOfYear() - date.monthOfYear() :
27139 this._day - date._day);
27140 return (c === 0 ? 0 : (c < 0 ? -1 : +1));
27141 },
27142
27143 /** Retrieve the calendar backing this date.
27144 @memberof CDate
27145 @return {BaseCalendar} The calendar implementation. */
27146 calendar: function() {
27147 return this._calendar;
27148 },
27149
27150 /** Retrieve the Julian date equivalent for this date,
27151 i.e. days since January 1, 4713 BCE Greenwich noon.
27152 @memberof CDate
27153 @return {number} The equivalent Julian date. */
27154 toJD: function() {
27155 return this._calendar.toJD(this);
27156 },
27157
27158 /** Create a new date from a Julian date.
27159 @memberof CDate
27160 @param jd {number} The Julian date to convert.
27161 @return {CDate} The equivalent date. */
27162 fromJD: function(jd) {
27163 return this._calendar.fromJD(jd);
27164 },
27165
27166 /** Convert this date to a standard (Gregorian) JavaScript Date.
27167 @memberof CDate
27168 @return {Date} The equivalent JavaScript date. */
27169 toJSDate: function() {
27170 return this._calendar.toJSDate(this);
27171 },
27172
27173 /** Create a new date from a standard (Gregorian) JavaScript Date.
27174 @memberof CDate
27175 @param jsd {Date} The JavaScript date to convert.
27176 @return {CDate} The equivalent date. */
27177 fromJSDate: function(jsd) {
27178 return this._calendar.fromJSDate(jsd);
27179 },
27180
27181 /** Convert to a string for display.
27182 @memberof CDate
27183 @return {string} This date as a string. */
27184 toString: function() {
27185 return (this.year() < 0 ? '-' : '') + pad(Math.abs(this.year()), 4) +
27186 '-' + pad(this.month(), 2) + '-' + pad(this.day(), 2);
27187 }
27188});
27189
27190/** Basic functionality for all calendars.
27191 Other calendars should extend this:
27192 <pre>OtherCalendar.prototype = new BaseCalendar;</pre>
27193 @class BaseCalendar */
27194function BaseCalendar() {
27195 this.shortYearCutoff = '+10';
27196}
27197
27198assign(BaseCalendar.prototype, {
27199 _validateLevel: 0, // "Stack" to turn validation on/off
27200
27201 /** Create a new date within this calendar - today if no parameters given.
27202 @memberof BaseCalendar
27203 @param year {CDate|number} The date to duplicate or the year for the date.
27204 @param [month] {number} The month for the date.
27205 @param [day] {number} The day for the date.
27206 @return {CDate} The new date.
27207 @throws Error if not a valid date or a different calendar used. */
27208 newDate: function(year, month, day) {
27209 if (year == null) {
27210 return this.today();
27211 }
27212 if (year.year) {
27213 this._validate(year, month, day,
27214 _exports.local.invalidDate || _exports.regionalOptions[''].invalidDate);
27215 day = year.day();
27216 month = year.month();
27217 year = year.year();
27218 }
27219 return new CDate(this, year, month, day);
27220 },
27221
27222 /** Create a new date for today.
27223 @memberof BaseCalendar
27224 @return {CDate} Today's date. */
27225 today: function() {
27226 return this.fromJSDate(new Date());
27227 },
27228
27229 /** Retrieve the epoch designator for this date.
27230 @memberof BaseCalendar
27231 @param year {CDate|number} The date to examine or the year to examine.
27232 @return {string} The current epoch.
27233 @throws Error if an invalid year or a different calendar used. */
27234 epoch: function(year) {
27235 var date = this._validate(year, this.minMonth, this.minDay,
27236 _exports.local.invalidYear || _exports.regionalOptions[''].invalidYear);
27237 return (date.year() < 0 ? this.local.epochs[0] : this.local.epochs[1]);
27238 },
27239
27240 /** Format the year, if not a simple sequential number
27241 @memberof BaseCalendar
27242 @param year {CDate|number} The date to format or the year to format.
27243 @return {string} The formatted year.
27244 @throws Error if an invalid year or a different calendar used. */
27245 formatYear: function(year) {
27246 var date = this._validate(year, this.minMonth, this.minDay,
27247 _exports.local.invalidYear || _exports.regionalOptions[''].invalidYear);
27248 return (date.year() < 0 ? '-' : '') + pad(Math.abs(date.year()), 4)
27249 },
27250
27251 /** Retrieve the number of months in a year.
27252 @memberof BaseCalendar
27253 @param year {CDate|number} The date to examine or the year to examine.
27254 @return {number} The number of months.
27255 @throws Error if an invalid year or a different calendar used. */
27256 monthsInYear: function(year) {
27257 this._validate(year, this.minMonth, this.minDay,
27258 _exports.local.invalidYear || _exports.regionalOptions[''].invalidYear);
27259 return 12;
27260 },
27261
27262 /** Calculate the month's ordinal position within the year -
27263 for those calendars that don't start at month 1!
27264 @memberof BaseCalendar
27265 @param year {CDate|number} The date to examine or the year to examine.
27266 @param month {number} The month to examine.
27267 @return {number} The ordinal position, starting from <code>minMonth</code>.
27268 @throws Error if an invalid year/month or a different calendar used. */
27269 monthOfYear: function(year, month) {
27270 var date = this._validate(year, month, this.minDay,
27271 _exports.local.invalidMonth || _exports.regionalOptions[''].invalidMonth);
27272 return (date.month() + this.monthsInYear(date) - this.firstMonth) %
27273 this.monthsInYear(date) + this.minMonth;
27274 },
27275
27276 /** Calculate actual month from ordinal position, starting from minMonth.
27277 @memberof BaseCalendar
27278 @param year {number} The year to examine.
27279 @param ord {number} The month's ordinal position.
27280 @return {number} The month's number.
27281 @throws Error if an invalid year/month. */
27282 fromMonthOfYear: function(year, ord) {
27283 var m = (ord + this.firstMonth - 2 * this.minMonth) %
27284 this.monthsInYear(year) + this.minMonth;
27285 this._validate(year, m, this.minDay,
27286 _exports.local.invalidMonth || _exports.regionalOptions[''].invalidMonth);
27287 return m;
27288 },
27289
27290 /** Retrieve the number of days in a year.
27291 @memberof BaseCalendar
27292 @param year {CDate|number} The date to examine or the year to examine.
27293 @return {number} The number of days.
27294 @throws Error if an invalid year or a different calendar used. */
27295 daysInYear: function(year) {
27296 var date = this._validate(year, this.minMonth, this.minDay,
27297 _exports.local.invalidYear || _exports.regionalOptions[''].invalidYear);
27298 return (this.leapYear(date) ? 366 : 365);
27299 },
27300
27301 /** Retrieve the day of the year for a date.
27302 @memberof BaseCalendar
27303 @param year {CDate|number} The date to convert or the year to convert.
27304 @param [month] {number} The month to convert.
27305 @param [day] {number} The day to convert.
27306 @return {number} The day of the year.
27307 @throws Error if an invalid date or a different calendar used. */
27308 dayOfYear: function(year, month, day) {
27309 var date = this._validate(year, month, day,
27310 _exports.local.invalidDate || _exports.regionalOptions[''].invalidDate);
27311 return date.toJD() - this.newDate(date.year(),
27312 this.fromMonthOfYear(date.year(), this.minMonth), this.minDay).toJD() + 1;
27313 },
27314
27315 /** Retrieve the number of days in a week.
27316 @memberof BaseCalendar
27317 @return {number} The number of days. */
27318 daysInWeek: function() {
27319 return 7;
27320 },
27321
27322 /** Retrieve the day of the week for a date.
27323 @memberof BaseCalendar
27324 @param year {CDate|number} The date to examine or the year to examine.
27325 @param [month] {number} The month to examine.
27326 @param [day] {number} The day to examine.
27327 @return {number} The day of the week: 0 to number of days - 1.
27328 @throws Error if an invalid date or a different calendar used. */
27329 dayOfWeek: function(year, month, day) {
27330 var date = this._validate(year, month, day,
27331 _exports.local.invalidDate || _exports.regionalOptions[''].invalidDate);
27332 return (Math.floor(this.toJD(date)) + 2) % this.daysInWeek();
27333 },
27334
27335 /** Retrieve additional information about a date.
27336 @memberof BaseCalendar
27337 @param year {CDate|number} The date to examine or the year to examine.
27338 @param [month] {number} The month to examine.
27339 @param [day] {number} The day to examine.
27340 @return {object} Additional information - contents depends on calendar.
27341 @throws Error if an invalid date or a different calendar used. */
27342 extraInfo: function(year, month, day) {
27343 this._validate(year, month, day,
27344 _exports.local.invalidDate || _exports.regionalOptions[''].invalidDate);
27345 return {};
27346 },
27347
27348 /** Add period(s) to a date.
27349 Cater for no year zero.
27350 @memberof BaseCalendar
27351 @param date {CDate} The starting date.
27352 @param offset {number} The number of periods to adjust by.
27353 @param period {string} One of 'y' for year, 'm' for month, 'w' for week, 'd' for day.
27354 @return {CDate} The updated date.
27355 @throws Error if a different calendar used. */
27356 add: function(date, offset, period) {
27357 this._validate(date, this.minMonth, this.minDay,
27358 _exports.local.invalidDate || _exports.regionalOptions[''].invalidDate);
27359 return this._correctAdd(date, this._add(date, offset, period), offset, period);
27360 },
27361
27362 /** Add period(s) to a date.
27363 @memberof BaseCalendar
27364 @private
27365 @param date {CDate} The starting date.
27366 @param offset {number} The number of periods to adjust by.
27367 @param period {string} One of 'y' for year, 'm' for month, 'w' for week, 'd' for day.
27368 @return {CDate} The updated date. */
27369 _add: function(date, offset, period) {
27370 this._validateLevel++;
27371 if (period === 'd' || period === 'w') {
27372 var jd = date.toJD() + offset * (period === 'w' ? this.daysInWeek() : 1);
27373 var d = date.calendar().fromJD(jd);
27374 this._validateLevel--;
27375 return [d.year(), d.month(), d.day()];
27376 }
27377 try {
27378 var y = date.year() + (period === 'y' ? offset : 0);
27379 var m = date.monthOfYear() + (period === 'm' ? offset : 0);
27380 var d = date.day();// + (period === 'd' ? offset : 0) +
27381 //(period === 'w' ? offset * this.daysInWeek() : 0);
27382 var resyncYearMonth = function(calendar) {
27383 while (m < calendar.minMonth) {
27384 y--;
27385 m += calendar.monthsInYear(y);
27386 }
27387 var yearMonths = calendar.monthsInYear(y);
27388 while (m > yearMonths - 1 + calendar.minMonth) {
27389 y++;
27390 m -= yearMonths;
27391 yearMonths = calendar.monthsInYear(y);
27392 }
27393 };
27394 if (period === 'y') {
27395 if (date.month() !== this.fromMonthOfYear(y, m)) { // Hebrew
27396 m = this.newDate(y, date.month(), this.minDay).monthOfYear();
27397 }
27398 m = Math.min(m, this.monthsInYear(y));
27399 d = Math.min(d, this.daysInMonth(y, this.fromMonthOfYear(y, m)));
27400 }
27401 else if (period === 'm') {
27402 resyncYearMonth(this);
27403 d = Math.min(d, this.daysInMonth(y, this.fromMonthOfYear(y, m)));
27404 }
27405 var ymd = [y, this.fromMonthOfYear(y, m), d];
27406 this._validateLevel--;
27407 return ymd;
27408 }
27409 catch (e) {
27410 this._validateLevel--;
27411 throw e;
27412 }
27413 },
27414
27415 /** Correct a candidate date after adding period(s) to a date.
27416 Handle no year zero if necessary.
27417 @memberof BaseCalendar
27418 @private
27419 @param date {CDate} The starting date.
27420 @param ymd {number[]} The added date.
27421 @param offset {number} The number of periods to adjust by.
27422 @param period {string} One of 'y' for year, 'm' for month, 'w' for week, 'd' for day.
27423 @return {CDate} The updated date. */
27424 _correctAdd: function(date, ymd, offset, period) {
27425 if (!this.hasYearZero && (period === 'y' || period === 'm')) {
27426 if (ymd[0] === 0 || // In year zero
27427 (date.year() > 0) !== (ymd[0] > 0)) { // Crossed year zero
27428 var adj = {y: [1, 1, 'y'], m: [1, this.monthsInYear(-1), 'm'],
27429 w: [this.daysInWeek(), this.daysInYear(-1), 'd'],
27430 d: [1, this.daysInYear(-1), 'd']}[period];
27431 var dir = (offset < 0 ? -1 : +1);
27432 ymd = this._add(date, offset * adj[0] + dir * adj[1], adj[2]);
27433 }
27434 }
27435 return date.date(ymd[0], ymd[1], ymd[2]);
27436 },
27437
27438 /** Set a portion of the date.
27439 @memberof BaseCalendar
27440 @param date {CDate} The starting date.
27441 @param value {number} The new value for the period.
27442 @param period {string} One of 'y' for year, 'm' for month, 'd' for day.
27443 @return {CDate} The updated date.
27444 @throws Error if an invalid date or a different calendar used. */
27445 set: function(date, value, period) {
27446 this._validate(date, this.minMonth, this.minDay,
27447 _exports.local.invalidDate || _exports.regionalOptions[''].invalidDate);
27448 var y = (period === 'y' ? value : date.year());
27449 var m = (period === 'm' ? value : date.month());
27450 var d = (period === 'd' ? value : date.day());
27451 if (period === 'y' || period === 'm') {
27452 d = Math.min(d, this.daysInMonth(y, m));
27453 }
27454 return date.date(y, m, d);
27455 },
27456
27457 /** Determine whether a date is valid for this calendar.
27458 @memberof BaseCalendar
27459 @param year {number} The year to examine.
27460 @param month {number} The month to examine.
27461 @param day {number} The day to examine.
27462 @return {boolean} <code>true</code> if a valid date, <code>false</code> if not. */
27463 isValid: function(year, month, day) {
27464 this._validateLevel++;
27465 var valid = (this.hasYearZero || year !== 0);
27466 if (valid) {
27467 var date = this.newDate(year, month, this.minDay);
27468 valid = (month >= this.minMonth && month - this.minMonth < this.monthsInYear(date)) &&
27469 (day >= this.minDay && day - this.minDay < this.daysInMonth(date));
27470 }
27471 this._validateLevel--;
27472 return valid;
27473 },
27474
27475 /** Convert the date to a standard (Gregorian) JavaScript Date.
27476 @memberof BaseCalendar
27477 @param year {CDate|number} The date to convert or the year to convert.
27478 @param [month] {number} The month to convert.
27479 @param [day] {number} The day to convert.
27480 @return {Date} The equivalent JavaScript date.
27481 @throws Error if an invalid date or a different calendar used. */
27482 toJSDate: function(year, month, day) {
27483 var date = this._validate(year, month, day,
27484 _exports.local.invalidDate || _exports.regionalOptions[''].invalidDate);
27485 return _exports.instance().fromJD(this.toJD(date)).toJSDate();
27486 },
27487
27488 /** Convert the date from a standard (Gregorian) JavaScript Date.
27489 @memberof BaseCalendar
27490 @param jsd {Date} The JavaScript date.
27491 @return {CDate} The equivalent calendar date. */
27492 fromJSDate: function(jsd) {
27493 return this.fromJD(_exports.instance().fromJSDate(jsd).toJD());
27494 },
27495
27496 /** Check that a candidate date is from the same calendar and is valid.
27497 @memberof BaseCalendar
27498 @private
27499 @param year {CDate|number} The date to validate or the year to validate.
27500 @param [month] {number} The month to validate.
27501 @param [day] {number} The day to validate.
27502 @param error {string} Rrror message if invalid.
27503 @throws Error if different calendars used or invalid date. */
27504 _validate: function(year, month, day, error) {
27505 if (year.year) {
27506 if (this._validateLevel === 0 && this.name !== year.calendar().name) {
27507 throw (_exports.local.differentCalendars || _exports.regionalOptions[''].differentCalendars).
27508 replace(/\{0\}/, this.local.name).replace(/\{1\}/, year.calendar().local.name);
27509 }
27510 return year;
27511 }
27512 try {
27513 this._validateLevel++;
27514 if (this._validateLevel === 1 && !this.isValid(year, month, day)) {
27515 throw error.replace(/\{0\}/, this.local.name);
27516 }
27517 var date = this.newDate(year, month, day);
27518 this._validateLevel--;
27519 return date;
27520 }
27521 catch (e) {
27522 this._validateLevel--;
27523 throw e;
27524 }
27525 }
27526});
27527
27528/** Implementation of the Proleptic Gregorian Calendar.
27529 See <a href=":http://en.wikipedia.org/wiki/Gregorian_calendar">http://en.wikipedia.org/wiki/Gregorian_calendar</a>
27530 and <a href="http://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar">http://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar</a>.
27531 @class GregorianCalendar
27532 @augments BaseCalendar
27533 @param [language=''] {string} The language code (default English) for localisation. */
27534function GregorianCalendar(language) {
27535 this.local = this.regionalOptions[language] || this.regionalOptions[''];
27536}
27537
27538GregorianCalendar.prototype = new BaseCalendar;
27539
27540assign(GregorianCalendar.prototype, {
27541 /** The calendar name.
27542 @memberof GregorianCalendar */
27543 name: 'Gregorian',
27544 /** Julian date of start of Gregorian epoch: 1 January 0001 CE.
27545 @memberof GregorianCalendar */
27546 jdEpoch: 1721425.5,
27547 /** Days per month in a common year.
27548 @memberof GregorianCalendar */
27549 daysPerMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
27550 /** <code>true</code> if has a year zero, <code>false</code> if not.
27551 @memberof GregorianCalendar */
27552 hasYearZero: false,
27553 /** The minimum month number.
27554 @memberof GregorianCalendar */
27555 minMonth: 1,
27556 /** The first month in the year.
27557 @memberof GregorianCalendar */
27558 firstMonth: 1,
27559 /** The minimum day number.
27560 @memberof GregorianCalendar */
27561 minDay: 1,
27562
27563 /** Localisations for the plugin.
27564 Entries are objects indexed by the language code ('' being the default US/English).
27565 Each object has the following attributes.
27566 @memberof GregorianCalendar
27567 @property name {string} The calendar name.
27568 @property epochs {string[]} The epoch names.
27569 @property monthNames {string[]} The long names of the months of the year.
27570 @property monthNamesShort {string[]} The short names of the months of the year.
27571 @property dayNames {string[]} The long names of the days of the week.
27572 @property dayNamesShort {string[]} The short names of the days of the week.
27573 @property dayNamesMin {string[]} The minimal names of the days of the week.
27574 @property dateFormat {string} The date format for this calendar.
27575 See the options on <a href="BaseCalendar.html#formatDate"><code>formatDate</code></a> for details.
27576 @property firstDay {number} The number of the first day of the week, starting at 0.
27577 @property isRTL {number} <code>true</code> if this localisation reads right-to-left. */
27578 regionalOptions: { // Localisations
27579 '': {
27580 name: 'Gregorian',
27581 epochs: ['BCE', 'CE'],
27582 monthNames: ['January', 'February', 'March', 'April', 'May', 'June',
27583 'July', 'August', 'September', 'October', 'November', 'December'],
27584 monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
27585 dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
27586 dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
27587 dayNamesMin: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
27588 digits: null,
27589 dateFormat: 'mm/dd/yyyy',
27590 firstDay: 0,
27591 isRTL: false
27592 }
27593 },
27594
27595 /** Determine whether this date is in a leap year.
27596 @memberof GregorianCalendar
27597 @param year {CDate|number} The date to examine or the year to examine.
27598 @return {boolean} <code>true</code> if this is a leap year, <code>false</code> if not.
27599 @throws Error if an invalid year or a different calendar used. */
27600 leapYear: function(year) {
27601 var date = this._validate(year, this.minMonth, this.minDay,
27602 _exports.local.invalidYear || _exports.regionalOptions[''].invalidYear);
27603 var year = date.year() + (date.year() < 0 ? 1 : 0); // No year zero
27604 return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
27605 },
27606
27607 /** Determine the week of the year for a date - ISO 8601.
27608 @memberof GregorianCalendar
27609 @param year {CDate|number} The date to examine or the year to examine.
27610 @param [month] {number} The month to examine.
27611 @param [day] {number} The day to examine.
27612 @return {number} The week of the year, starting from 1.
27613 @throws Error if an invalid date or a different calendar used. */
27614 weekOfYear: function(year, month, day) {
27615 // Find Thursday of this week starting on Monday
27616 var checkDate = this.newDate(year, month, day);
27617 checkDate.add(4 - (checkDate.dayOfWeek() || 7), 'd');
27618 return Math.floor((checkDate.dayOfYear() - 1) / 7) + 1;
27619 },
27620
27621 /** Retrieve the number of days in a month.
27622 @memberof GregorianCalendar
27623 @param year {CDate|number} The date to examine or the year of the month.
27624 @param [month] {number} The month.
27625 @return {number} The number of days in this month.
27626 @throws Error if an invalid month/year or a different calendar used. */
27627 daysInMonth: function(year, month) {
27628 var date = this._validate(year, month, this.minDay,
27629 _exports.local.invalidMonth || _exports.regionalOptions[''].invalidMonth);
27630 return this.daysPerMonth[date.month() - 1] +
27631 (date.month() === 2 && this.leapYear(date.year()) ? 1 : 0);
27632 },
27633
27634 /** Determine whether this date is a week day.
27635 @memberof GregorianCalendar
27636 @param year {CDate|number} The date to examine or the year to examine.
27637 @param [month] {number} The month to examine.
27638 @param [day] {number} The day to examine.
27639 @return {boolean} <code>true</code> if a week day, <code>false</code> if not.
27640 @throws Error if an invalid date or a different calendar used. */
27641 weekDay: function(year, month, day) {
27642 return (this.dayOfWeek(year, month, day) || 7) < 6;
27643 },
27644
27645 /** Retrieve the Julian date equivalent for this date,
27646 i.e. days since January 1, 4713 BCE Greenwich noon.
27647 @memberof GregorianCalendar
27648 @param year {CDate|number} The date to convert or the year to convert.
27649 @param [month] {number} The month to convert.
27650 @param [day] {number} The day to convert.
27651 @return {number} The equivalent Julian date.
27652 @throws Error if an invalid date or a different calendar used. */
27653 toJD: function(year, month, day) {
27654 var date = this._validate(year, month, day,
27655 _exports.local.invalidDate || _exports.regionalOptions[''].invalidDate);
27656 year = date.year();
27657 month = date.month();
27658 day = date.day();
27659 if (year < 0) { year++; } // No year zero
27660 // Jean Meeus algorithm, "Astronomical Algorithms", 1991
27661 if (month < 3) {
27662 month += 12;
27663 year--;
27664 }
27665 var a = Math.floor(year / 100);
27666 var b = 2 - a + Math.floor(a / 4);
27667 return Math.floor(365.25 * (year + 4716)) +
27668 Math.floor(30.6001 * (month + 1)) + day + b - 1524.5;
27669 },
27670
27671 /** Create a new date from a Julian date.
27672 @memberof GregorianCalendar
27673 @param jd {number} The Julian date to convert.
27674 @return {CDate} The equivalent date. */
27675 fromJD: function(jd) {
27676 // Jean Meeus algorithm, "Astronomical Algorithms", 1991
27677 var z = Math.floor(jd + 0.5);
27678 var a = Math.floor((z - 1867216.25) / 36524.25);
27679 a = z + 1 + a - Math.floor(a / 4);
27680 var b = a + 1524;
27681 var c = Math.floor((b - 122.1) / 365.25);
27682 var d = Math.floor(365.25 * c);
27683 var e = Math.floor((b - d) / 30.6001);
27684 var day = b - d - Math.floor(e * 30.6001);
27685 var month = e - (e > 13.5 ? 13 : 1);
27686 var year = c - (month > 2.5 ? 4716 : 4715);
27687 if (year <= 0) { year--; } // No year zero
27688 return this.newDate(year, month, day);
27689 },
27690
27691 /** Convert this date to a standard (Gregorian) JavaScript Date.
27692 @memberof GregorianCalendar
27693 @param year {CDate|number} The date to convert or the year to convert.
27694 @param [month] {number} The month to convert.
27695 @param [day] {number} The day to convert.
27696 @return {Date} The equivalent JavaScript date.
27697 @throws Error if an invalid date or a different calendar used. */
27698 toJSDate: function(year, month, day) {
27699 var date = this._validate(year, month, day,
27700 _exports.local.invalidDate || _exports.regionalOptions[''].invalidDate);
27701 var jsd = new Date(date.year(), date.month() - 1, date.day());
27702 jsd.setHours(0);
27703 jsd.setMinutes(0);
27704 jsd.setSeconds(0);
27705 jsd.setMilliseconds(0);
27706 // Hours may be non-zero on daylight saving cut-over:
27707 // > 12 when midnight changeover, but then cannot generate
27708 // midnight datetime, so jump to 1AM, otherwise reset.
27709 jsd.setHours(jsd.getHours() > 12 ? jsd.getHours() + 2 : 0);
27710 return jsd;
27711 },
27712
27713 /** Create a new date from a standard (Gregorian) JavaScript Date.
27714 @memberof GregorianCalendar
27715 @param jsd {Date} The JavaScript date to convert.
27716 @return {CDate} The equivalent date. */
27717 fromJSDate: function(jsd) {
27718 return this.newDate(jsd.getFullYear(), jsd.getMonth() + 1, jsd.getDate());
27719 }
27720});
27721
27722// Singleton manager
27723var _exports = module.exports = new Calendars();
27724
27725// Date template
27726_exports.cdate = CDate;
27727
27728// Base calendar template
27729_exports.baseCalendar = BaseCalendar;
27730
27731// Gregorian calendar implementation
27732_exports.calendars.gregorian = GregorianCalendar;
27733
27734
27735},{"object-assign":73}],138:[function(_dereq_,module,exports){
27736/*
27737 * World Calendars
27738 * https://github.com/alexcjohnson/world-calendars
27739 *
27740 * Batch-converted from kbwood/calendars
27741 * Many thanks to Keith Wood and all of the contributors to the original project!
27742 *
27743 * This source code is licensed under the MIT license found in the
27744 * LICENSE file in the root directory of this source tree.
27745 */
27746
27747/* http://keith-wood.name/calendars.html
27748 Calendars extras for jQuery v2.0.2.
27749 Written by Keith Wood (wood.keith{at}optusnet.com.au) August 2009.
27750 Available under the MIT (http://keith-wood.name/licence.html) license.
27751 Please attribute the author if you use it. */
27752
27753var assign = _dereq_('object-assign');
27754var main = _dereq_('./main');
27755
27756
27757assign(main.regionalOptions[''], {
27758 invalidArguments: 'Invalid arguments',
27759 invalidFormat: 'Cannot format a date from another calendar',
27760 missingNumberAt: 'Missing number at position {0}',
27761 unknownNameAt: 'Unknown name at position {0}',
27762 unexpectedLiteralAt: 'Unexpected literal at position {0}',
27763 unexpectedText: 'Additional text found at end'
27764});
27765main.local = main.regionalOptions[''];
27766
27767assign(main.cdate.prototype, {
27768
27769 /** Format this date.
27770 Found in the <code>jquery.calendars.plus.js</code> module.
27771 @memberof CDate
27772 @param [format] {string} The date format to use (see <a href="BaseCalendar.html#formatDate"><code>formatDate</code></a>).
27773 @param [settings] {object} Options for the <code>formatDate</code> function.
27774 @return {string} The formatted date. */
27775 formatDate: function(format, settings) {
27776 if (typeof format !== 'string') {
27777 settings = format;
27778 format = '';
27779 }
27780 return this._calendar.formatDate(format || '', this, settings);
27781 }
27782});
27783
27784assign(main.baseCalendar.prototype, {
27785
27786 UNIX_EPOCH: main.instance().newDate(1970, 1, 1).toJD(),
27787 SECS_PER_DAY: 24 * 60 * 60,
27788 TICKS_EPOCH: main.instance().jdEpoch, // 1 January 0001 CE
27789 TICKS_PER_DAY: 24 * 60 * 60 * 10000000,
27790
27791 /** Date form for ATOM (RFC 3339/ISO 8601).
27792 Found in the <code>jquery.calendars.plus.js</code> module.
27793 @memberof BaseCalendar */
27794 ATOM: 'yyyy-mm-dd',
27795 /** Date form for cookies.
27796 Found in the <code>jquery.calendars.plus.js</code> module.
27797 @memberof BaseCalendar */
27798 COOKIE: 'D, dd M yyyy',
27799 /** Date form for full date.
27800 Found in the <code>jquery.calendars.plus.js</code> module.
27801 @memberof BaseCalendar */
27802 FULL: 'DD, MM d, yyyy',
27803 /** Date form for ISO 8601.
27804 Found in the <code>jquery.calendars.plus.js</code> module.
27805 @memberof BaseCalendar */
27806 ISO_8601: 'yyyy-mm-dd',
27807 /** Date form for Julian date.
27808 Found in the <code>jquery.calendars.plus.js</code> module.
27809 @memberof BaseCalendar */
27810 JULIAN: 'J',
27811 /** Date form for RFC 822.
27812 Found in the <code>jquery.calendars.plus.js</code> module.
27813 @memberof BaseCalendar */
27814 RFC_822: 'D, d M yy',
27815 /** Date form for RFC 850.
27816 Found in the <code>jquery.calendars.plus.js</code> module.
27817 @memberof BaseCalendar */
27818 RFC_850: 'DD, dd-M-yy',
27819 /** Date form for RFC 1036.
27820 Found in the <code>jquery.calendars.plus.js</code> module.
27821 @memberof BaseCalendar */
27822 RFC_1036: 'D, d M yy',
27823 /** Date form for RFC 1123.
27824 Found in the <code>jquery.calendars.plus.js</code> module.
27825 @memberof BaseCalendar */
27826 RFC_1123: 'D, d M yyyy',
27827 /** Date form for RFC 2822.
27828 Found in the <code>jquery.calendars.plus.js</code> module.
27829 @memberof BaseCalendar */
27830 RFC_2822: 'D, d M yyyy',
27831 /** Date form for RSS (RFC 822).
27832 Found in the <code>jquery.calendars.plus.js</code> module.
27833 @memberof BaseCalendar */
27834 RSS: 'D, d M yy',
27835 /** Date form for Windows ticks.
27836 Found in the <code>jquery.calendars.plus.js</code> module.
27837 @memberof BaseCalendar */
27838 TICKS: '!',
27839 /** Date form for Unix timestamp.
27840 Found in the <code>jquery.calendars.plus.js</code> module.
27841 @memberof BaseCalendar */
27842 TIMESTAMP: '@',
27843 /** Date form for W3c (ISO 8601).
27844 Found in the <code>jquery.calendars.plus.js</code> module.
27845 @memberof BaseCalendar */
27846 W3C: 'yyyy-mm-dd',
27847
27848 /** Format a date object into a string value.
27849 The format can be combinations of the following:
27850 <ul>
27851 <li>d - day of month (no leading zero)</li>
27852 <li>dd - day of month (two digit)</li>
27853 <li>o - day of year (no leading zeros)</li>
27854 <li>oo - day of year (three digit)</li>
27855 <li>D - day name short</li>
27856 <li>DD - day name long</li>
27857 <li>w - week of year (no leading zero)</li>
27858 <li>ww - week of year (two digit)</li>
27859 <li>m - month of year (no leading zero)</li>
27860 <li>mm - month of year (two digit)</li>
27861 <li>M - month name short</li>
27862 <li>MM - month name long</li>
27863 <li>yy - year (two digit)</li>
27864 <li>yyyy - year (four digit)</li>
27865 <li>YYYY - formatted year</li>
27866 <li>J - Julian date (days since January 1, 4713 BCE Greenwich noon)</li>
27867 <li>@ - Unix timestamp (s since 01/01/1970)</li>
27868 <li>! - Windows ticks (100ns since 01/01/0001)</li>
27869 <li>'...' - literal text</li>
27870 <li>'' - single quote</li>
27871 </ul>
27872 Found in the <code>jquery.calendars.plus.js</code> module.
27873 @memberof BaseCalendar
27874 @param [format] {string} The desired format of the date (defaults to calendar format).
27875 @param date {CDate} The date value to format.
27876 @param [settings] {object} Addition options, whose attributes include:
27877 @property [dayNamesShort] {string[]} Abbreviated names of the days from Sunday.
27878 @property [dayNames] {string[]} Names of the days from Sunday.
27879 @property [monthNamesShort] {string[]} Abbreviated names of the months.
27880 @property [monthNames] {string[]} Names of the months.
27881 @property [calculateWeek] {CalendarsPickerCalculateWeek} Function that determines week of the year.
27882 @property [localNumbers=false] {boolean} <code>true</code> to localise numbers (if available),
27883 <code>false</code> to use normal Arabic numerals.
27884 @return {string} The date in the above format.
27885 @throws Errors if the date is from a different calendar. */
27886 formatDate: function(format, date, settings) {
27887 if (typeof format !== 'string') {
27888 settings = date;
27889 date = format;
27890 format = '';
27891 }
27892 if (!date) {
27893 return '';
27894 }
27895 if (date.calendar() !== this) {
27896 throw main.local.invalidFormat || main.regionalOptions[''].invalidFormat;
27897 }
27898 format = format || this.local.dateFormat;
27899 settings = settings || {};
27900 var dayNamesShort = settings.dayNamesShort || this.local.dayNamesShort;
27901 var dayNames = settings.dayNames || this.local.dayNames;
27902 var monthNumbers = settings.monthNumbers || this.local.monthNumbers;
27903 var monthNamesShort = settings.monthNamesShort || this.local.monthNamesShort;
27904 var monthNames = settings.monthNames || this.local.monthNames;
27905 var calculateWeek = settings.calculateWeek || this.local.calculateWeek;
27906 // Check whether a format character is doubled
27907 var doubled = function(match, step) {
27908 var matches = 1;
27909 while (iFormat + matches < format.length && format.charAt(iFormat + matches) === match) {
27910 matches++;
27911 }
27912 iFormat += matches - 1;
27913 return Math.floor(matches / (step || 1)) > 1;
27914 };
27915 // Format a number, with leading zeroes if necessary
27916 var formatNumber = function(match, value, len, step) {
27917 var num = '' + value;
27918 if (doubled(match, step)) {
27919 while (num.length < len) {
27920 num = '0' + num;
27921 }
27922 }
27923 return num;
27924 };
27925 // Format a name, short or long as requested
27926 var formatName = function(match, value, shortNames, longNames) {
27927 return (doubled(match) ? longNames[value] : shortNames[value]);
27928 };
27929 // Format month number
27930 // (e.g. Chinese calendar needs to account for intercalary months)
27931 var calendar = this;
27932 var formatMonth = function(date) {
27933 return (typeof monthNumbers === 'function') ?
27934 monthNumbers.call(calendar, date, doubled('m')) :
27935 localiseNumbers(formatNumber('m', date.month(), 2));
27936 };
27937 // Format a month name, short or long as requested
27938 var formatMonthName = function(date, useLongName) {
27939 if (useLongName) {
27940 return (typeof monthNames === 'function') ?
27941 monthNames.call(calendar, date) :
27942 monthNames[date.month() - calendar.minMonth];
27943 } else {
27944 return (typeof monthNamesShort === 'function') ?
27945 monthNamesShort.call(calendar, date) :
27946 monthNamesShort[date.month() - calendar.minMonth];
27947 }
27948 };
27949 // Localise numbers if requested and available
27950 var digits = this.local.digits;
27951 var localiseNumbers = function(value) {
27952 return (settings.localNumbers && digits ? digits(value) : value);
27953 };
27954 var output = '';
27955 var literal = false;
27956 for (var iFormat = 0; iFormat < format.length; iFormat++) {
27957 if (literal) {
27958 if (format.charAt(iFormat) === "'" && !doubled("'")) {
27959 literal = false;
27960 }
27961 else {
27962 output += format.charAt(iFormat);
27963 }
27964 }
27965 else {
27966 switch (format.charAt(iFormat)) {
27967 case 'd': output += localiseNumbers(formatNumber('d', date.day(), 2)); break;
27968 case 'D': output += formatName('D', date.dayOfWeek(),
27969 dayNamesShort, dayNames); break;
27970 case 'o': output += formatNumber('o', date.dayOfYear(), 3); break;
27971 case 'w': output += formatNumber('w', date.weekOfYear(), 2); break;
27972 case 'm': output += formatMonth(date); break;
27973 case 'M': output += formatMonthName(date, doubled('M')); break;
27974 case 'y':
27975 output += (doubled('y', 2) ? date.year() :
27976 (date.year() % 100 < 10 ? '0' : '') + date.year() % 100);
27977 break;
27978 case 'Y':
27979 doubled('Y', 2);
27980 output += date.formatYear();
27981 break;
27982 case 'J': output += date.toJD(); break;
27983 case '@': output += (date.toJD() - this.UNIX_EPOCH) * this.SECS_PER_DAY; break;
27984 case '!': output += (date.toJD() - this.TICKS_EPOCH) * this.TICKS_PER_DAY; break;
27985 case "'":
27986 if (doubled("'")) {
27987 output += "'";
27988 }
27989 else {
27990 literal = true;
27991 }
27992 break;
27993 default:
27994 output += format.charAt(iFormat);
27995 }
27996 }
27997 }
27998 return output;
27999 },
28000
28001 /** Parse a string value into a date object.
28002 See <a href="#formatDate"><code>formatDate</code></a> for the possible formats, plus:
28003 <ul>
28004 <li>* - ignore rest of string</li>
28005 </ul>
28006 Found in the <code>jquery.calendars.plus.js</code> module.
28007 @memberof BaseCalendar
28008 @param format {string} The expected format of the date ('' for default calendar format).
28009 @param value {string} The date in the above format.
28010 @param [settings] {object} Additional options whose attributes include:
28011 @property [shortYearCutoff] {number} The cutoff year for determining the century.
28012 @property [dayNamesShort] {string[]} Abbreviated names of the days from Sunday.
28013 @property [dayNames] {string[]} Names of the days from Sunday.
28014 @property [monthNamesShort] {string[]} Abbreviated names of the months.
28015 @property [monthNames] {string[]} Names of the months.
28016 @return {CDate} The extracted date value or <code>null</code> if value is blank.
28017 @throws Errors if the format and/or value are missing,
28018 if the value doesn't match the format, or if the date is invalid. */
28019 parseDate: function(format, value, settings) {
28020 if (value == null) {
28021 throw main.local.invalidArguments || main.regionalOptions[''].invalidArguments;
28022 }
28023 value = (typeof value === 'object' ? value.toString() : value + '');
28024 if (value === '') {
28025 return null;
28026 }
28027 format = format || this.local.dateFormat;
28028 settings = settings || {};
28029 var shortYearCutoff = settings.shortYearCutoff || this.shortYearCutoff;
28030 shortYearCutoff = (typeof shortYearCutoff !== 'string' ? shortYearCutoff :
28031 this.today().year() % 100 + parseInt(shortYearCutoff, 10));
28032 var dayNamesShort = settings.dayNamesShort || this.local.dayNamesShort;
28033 var dayNames = settings.dayNames || this.local.dayNames;
28034 var parseMonth = settings.parseMonth || this.local.parseMonth;
28035 var monthNumbers = settings.monthNumbers || this.local.monthNumbers;
28036 var monthNamesShort = settings.monthNamesShort || this.local.monthNamesShort;
28037 var monthNames = settings.monthNames || this.local.monthNames;
28038 var jd = -1;
28039 var year = -1;
28040 var month = -1;
28041 var day = -1;
28042 var doy = -1;
28043 var shortYear = false;
28044 var literal = false;
28045 // Check whether a format character is doubled
28046 var doubled = function(match, step) {
28047 var matches = 1;
28048 while (iFormat + matches < format.length && format.charAt(iFormat + matches) === match) {
28049 matches++;
28050 }
28051 iFormat += matches - 1;
28052 return Math.floor(matches / (step || 1)) > 1;
28053 };
28054 // Extract a number from the string value
28055 var getNumber = function(match, step) {
28056 var isDoubled = doubled(match, step);
28057 var size = [2, 3, isDoubled ? 4 : 2, isDoubled ? 4 : 2, 10, 11, 20]['oyYJ@!'.indexOf(match) + 1];
28058 var digits = new RegExp('^-?\\d{1,' + size + '}');
28059 var num = value.substring(iValue).match(digits);
28060 if (!num) {
28061 throw (main.local.missingNumberAt || main.regionalOptions[''].missingNumberAt).
28062 replace(/\{0\}/, iValue);
28063 }
28064 iValue += num[0].length;
28065 return parseInt(num[0], 10);
28066 };
28067 // Extract a month number from the string value
28068 var calendar = this;
28069 var getMonthNumber = function() {
28070 if (typeof monthNumbers === 'function') {
28071 doubled('m'); // update iFormat
28072 var month = monthNumbers.call(calendar, value.substring(iValue));
28073 iValue += month.length;
28074 return month;
28075 }
28076
28077 return getNumber('m');
28078 };
28079 // Extract a name from the string value and convert to an index
28080 var getName = function(match, shortNames, longNames, step) {
28081 var names = (doubled(match, step) ? longNames : shortNames);
28082 for (var i = 0; i < names.length; i++) {
28083 if (value.substr(iValue, names[i].length).toLowerCase() === names[i].toLowerCase()) {
28084 iValue += names[i].length;
28085 return i + calendar.minMonth;
28086 }
28087 }
28088 throw (main.local.unknownNameAt || main.regionalOptions[''].unknownNameAt).
28089 replace(/\{0\}/, iValue);
28090 };
28091 // Extract a month number from the string value
28092 var getMonthName = function() {
28093 if (typeof monthNames === 'function') {
28094 var month = doubled('M') ?
28095 monthNames.call(calendar, value.substring(iValue)) :
28096 monthNamesShort.call(calendar, value.substring(iValue));
28097 iValue += month.length;
28098 return month;
28099 }
28100
28101 return getName('M', monthNamesShort, monthNames);
28102 };
28103 // Confirm that a literal character matches the string value
28104 var checkLiteral = function() {
28105 if (value.charAt(iValue) !== format.charAt(iFormat)) {
28106 throw (main.local.unexpectedLiteralAt ||
28107 main.regionalOptions[''].unexpectedLiteralAt).replace(/\{0\}/, iValue);
28108 }
28109 iValue++;
28110 };
28111 var iValue = 0;
28112 for (var iFormat = 0; iFormat < format.length; iFormat++) {
28113 if (literal) {
28114 if (format.charAt(iFormat) === "'" && !doubled("'")) {
28115 literal = false;
28116 }
28117 else {
28118 checkLiteral();
28119 }
28120 }
28121 else {
28122 switch (format.charAt(iFormat)) {
28123 case 'd': day = getNumber('d'); break;
28124 case 'D': getName('D', dayNamesShort, dayNames); break;
28125 case 'o': doy = getNumber('o'); break;
28126 case 'w': getNumber('w'); break;
28127 case 'm': month = getMonthNumber(); break;
28128 case 'M': month = getMonthName(); break;
28129 case 'y':
28130 var iSave = iFormat;
28131 shortYear = !doubled('y', 2);
28132 iFormat = iSave;
28133 year = getNumber('y', 2);
28134 break;
28135 case 'Y': year = getNumber('Y', 2); break;
28136 case 'J':
28137 jd = getNumber('J') + 0.5;
28138 if (value.charAt(iValue) === '.') {
28139 iValue++;
28140 getNumber('J');
28141 }
28142 break;
28143 case '@': jd = getNumber('@') / this.SECS_PER_DAY + this.UNIX_EPOCH; break;
28144 case '!': jd = getNumber('!') / this.TICKS_PER_DAY + this.TICKS_EPOCH; break;
28145 case '*': iValue = value.length; break;
28146 case "'":
28147 if (doubled("'")) {
28148 checkLiteral();
28149 }
28150 else {
28151 literal = true;
28152 }
28153 break;
28154 default: checkLiteral();
28155 }
28156 }
28157 }
28158 if (iValue < value.length) {
28159 throw main.local.unexpectedText || main.regionalOptions[''].unexpectedText;
28160 }
28161 if (year === -1) {
28162 year = this.today().year();
28163 }
28164 else if (year < 100 && shortYear) {
28165 year += (shortYearCutoff === -1 ? 1900 : this.today().year() -
28166 this.today().year() % 100 - (year <= shortYearCutoff ? 0 : 100));
28167 }
28168 if (typeof month === 'string') {
28169 month = parseMonth.call(this, year, month);
28170 }
28171 if (doy > -1) {
28172 month = 1;
28173 day = doy;
28174 for (var dim = this.daysInMonth(year, month); day > dim; dim = this.daysInMonth(year, month)) {
28175 month++;
28176 day -= dim;
28177 }
28178 }
28179 return (jd > -1 ? this.fromJD(jd) : this.newDate(year, month, day));
28180 },
28181
28182 /** A date may be specified as an exact value or a relative one.
28183 Found in the <code>jquery.calendars.plus.js</code> module.
28184 @memberof BaseCalendar
28185 @param dateSpec {CDate|number|string} The date as an object or string in the given format or
28186 an offset - numeric days from today, or string amounts and periods, e.g. '+1m +2w'.
28187 @param defaultDate {CDate} The date to use if no other supplied, may be <code>null</code>.
28188 @param currentDate {CDate} The current date as a possible basis for relative dates,
28189 if <code>null</code> today is used (optional)
28190 @param [dateFormat] {string} The expected date format - see <a href="#formatDate"><code>formatDate</code></a>.
28191 @param [settings] {object} Additional options whose attributes include:
28192 @property [shortYearCutoff] {number} The cutoff year for determining the century.
28193 @property [dayNamesShort] {string[]} Abbreviated names of the days from Sunday.
28194 @property [dayNames] {string[]} Names of the days from Sunday.
28195 @property [monthNamesShort] {string[]} Abbreviated names of the months.
28196 @property [monthNames] {string[]} Names of the months.
28197 @return {CDate} The decoded date. */
28198 determineDate: function(dateSpec, defaultDate, currentDate, dateFormat, settings) {
28199 if (currentDate && typeof currentDate !== 'object') {
28200 settings = dateFormat;
28201 dateFormat = currentDate;
28202 currentDate = null;
28203 }
28204 if (typeof dateFormat !== 'string') {
28205 settings = dateFormat;
28206 dateFormat = '';
28207 }
28208 var calendar = this;
28209 var offsetString = function(offset) {
28210 try {
28211 return calendar.parseDate(dateFormat, offset, settings);
28212 }
28213 catch (e) {
28214 // Ignore
28215 }
28216 offset = offset.toLowerCase();
28217 var date = (offset.match(/^c/) && currentDate ?
28218 currentDate.newDate() : null) || calendar.today();
28219 var pattern = /([+-]?[0-9]+)\s*(d|w|m|y)?/g;
28220 var matches = pattern.exec(offset);
28221 while (matches) {
28222 date.add(parseInt(matches[1], 10), matches[2] || 'd');
28223 matches = pattern.exec(offset);
28224 }
28225 return date;
28226 };
28227 defaultDate = (defaultDate ? defaultDate.newDate() : null);
28228 dateSpec = (dateSpec == null ? defaultDate :
28229 (typeof dateSpec === 'string' ? offsetString(dateSpec) : (typeof dateSpec === 'number' ?
28230 (isNaN(dateSpec) || dateSpec === Infinity || dateSpec === -Infinity ? defaultDate :
28231 calendar.today().add(dateSpec, 'd')) : calendar.newDate(dateSpec))));
28232 return dateSpec;
28233 }
28234});
28235
28236
28237},{"./main":137,"object-assign":73}],139:[function(_dereq_,module,exports){
28238'use strict';
28239
28240/**
28241 * All paths are tuned for maximum scalability of the arrowhead,
28242 * ie throughout arrowwidth=0.3..3 the head is joined smoothly
28243 * to the line, with the line coming from the left and ending at (0, 0).
28244 *
28245 * `backoff` is the distance to move the arrowhead and the end of the line,
28246 * in order that the arrowhead points to the desired place, either at
28247 * the tip of the arrow or (in the case of circle or square)
28248 * the center of the symbol.
28249 *
28250 * `noRotate`, if truthy, says that this arrowhead should not rotate with the
28251 * arrow. That's the case for squares, which should always be straight, and
28252 * circles, for which it's irrelevant.
28253 */
28254
28255module.exports = [
28256 // no arrow
28257 {
28258 path: '',
28259 backoff: 0
28260 },
28261 // wide with flat back
28262 {
28263 path: 'M-2.4,-3V3L0.6,0Z',
28264 backoff: 0.6
28265 },
28266 // narrower with flat back
28267 {
28268 path: 'M-3.7,-2.5V2.5L1.3,0Z',
28269 backoff: 1.3
28270 },
28271 // barbed
28272 {
28273 path: 'M-4.45,-3L-1.65,-0.2V0.2L-4.45,3L1.55,0Z',
28274 backoff: 1.55
28275 },
28276 // wide line-drawn
28277 {
28278 path: 'M-2.2,-2.2L-0.2,-0.2V0.2L-2.2,2.2L-1.4,3L1.6,0L-1.4,-3Z',
28279 backoff: 1.6
28280 },
28281 // narrower line-drawn
28282 {
28283 path: 'M-4.4,-2.1L-0.6,-0.2V0.2L-4.4,2.1L-4,3L2,0L-4,-3Z',
28284 backoff: 2
28285 },
28286 // circle
28287 {
28288 path: 'M2,0A2,2 0 1,1 0,-2A2,2 0 0,1 2,0Z',
28289 backoff: 0,
28290 noRotate: true
28291 },
28292 // square
28293 {
28294 path: 'M2,2V-2H-2V2Z',
28295 backoff: 0,
28296 noRotate: true
28297 }
28298];
28299
28300},{}],140:[function(_dereq_,module,exports){
28301'use strict';
28302
28303var ARROWPATHS = _dereq_('./arrow_paths');
28304var fontAttrs = _dereq_('../../plots/font_attributes');
28305var cartesianConstants = _dereq_('../../plots/cartesian/constants');
28306var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray;
28307var axisPlaceableObjs = _dereq_('../../constants/axis_placeable_objects');
28308
28309function arrowAxisRefDescription(axis) {
28310 return [
28311 'In order for absolute positioning of the arrow to work, *a' + axis +
28312 'ref* must be exactly the same as *' + axis + 'ref*, otherwise *a' + axis +
28313 'ref* will revert to *pixel* (explained next).',
28314 'For relative positioning, *a' + axis + 'ref* can be set to *pixel*,',
28315 'in which case the *a' + axis + '* value is specified in pixels',
28316 'relative to *' + axis + '*.',
28317 'Absolute positioning is useful',
28318 'for trendline annotations which should continue to indicate',
28319 'the correct trend when zoomed. Relative positioning is useful',
28320 'for specifying the text offset for an annotated point.'
28321 ].join(' ');
28322}
28323
28324function arrowCoordinateDescription(axis, lower, upper) {
28325 return [
28326 'Sets the', axis, 'component of the arrow tail about the arrow head.',
28327 'If `a' + axis + 'ref` is `pixel`, a positive (negative)',
28328 'component corresponds to an arrow pointing',
28329 'from', upper, 'to', lower, '(' + lower, 'to', upper + ').',
28330 'If `a' + axis + 'ref` is not `pixel` and is exactly the same as `' + axis + 'ref`,',
28331 'this is an absolute value on that axis,',
28332 'like `' + axis + '`, specified in the same coordinates as `' + axis + 'ref`.'
28333 ].join(' ');
28334}
28335
28336module.exports = templatedArray('annotation', {
28337 visible: {
28338 valType: 'boolean',
28339 dflt: true,
28340 editType: 'calc+arraydraw',
28341 },
28342
28343 text: {
28344 valType: 'string',
28345 editType: 'calc+arraydraw',
28346 },
28347 textangle: {
28348 valType: 'angle',
28349 dflt: 0,
28350 editType: 'calc+arraydraw',
28351 },
28352 font: fontAttrs({
28353 editType: 'calc+arraydraw',
28354 colorEditType: 'arraydraw',
28355 }),
28356 width: {
28357 valType: 'number',
28358 min: 1,
28359 dflt: null,
28360 editType: 'calc+arraydraw',
28361 },
28362 height: {
28363 valType: 'number',
28364 min: 1,
28365 dflt: null,
28366 editType: 'calc+arraydraw',
28367 },
28368 opacity: {
28369 valType: 'number',
28370 min: 0,
28371 max: 1,
28372 dflt: 1,
28373 editType: 'arraydraw',
28374 },
28375 align: {
28376 valType: 'enumerated',
28377 values: ['left', 'center', 'right'],
28378 dflt: 'center',
28379 editType: 'arraydraw',
28380 },
28381 valign: {
28382 valType: 'enumerated',
28383 values: ['top', 'middle', 'bottom'],
28384 dflt: 'middle',
28385 editType: 'arraydraw',
28386 },
28387 bgcolor: {
28388 valType: 'color',
28389 dflt: 'rgba(0,0,0,0)',
28390 editType: 'arraydraw',
28391 },
28392 bordercolor: {
28393 valType: 'color',
28394 dflt: 'rgba(0,0,0,0)',
28395 editType: 'arraydraw',
28396 },
28397 borderpad: {
28398 valType: 'number',
28399 min: 0,
28400 dflt: 1,
28401 editType: 'calc+arraydraw',
28402 },
28403 borderwidth: {
28404 valType: 'number',
28405 min: 0,
28406 dflt: 1,
28407 editType: 'calc+arraydraw',
28408 },
28409 // arrow
28410 showarrow: {
28411 valType: 'boolean',
28412 dflt: true,
28413 editType: 'calc+arraydraw',
28414 },
28415 arrowcolor: {
28416 valType: 'color',
28417 editType: 'arraydraw',
28418 },
28419 arrowhead: {
28420 valType: 'integer',
28421 min: 0,
28422 max: ARROWPATHS.length,
28423 dflt: 1,
28424 editType: 'arraydraw',
28425 },
28426 startarrowhead: {
28427 valType: 'integer',
28428 min: 0,
28429 max: ARROWPATHS.length,
28430 dflt: 1,
28431 editType: 'arraydraw',
28432 },
28433 arrowside: {
28434 valType: 'flaglist',
28435 flags: ['end', 'start'],
28436 extras: ['none'],
28437 dflt: 'end',
28438 editType: 'arraydraw',
28439 },
28440 arrowsize: {
28441 valType: 'number',
28442 min: 0.3,
28443 dflt: 1,
28444 editType: 'calc+arraydraw',
28445 },
28446 startarrowsize: {
28447 valType: 'number',
28448 min: 0.3,
28449 dflt: 1,
28450 editType: 'calc+arraydraw',
28451 },
28452 arrowwidth: {
28453 valType: 'number',
28454 min: 0.1,
28455 editType: 'calc+arraydraw',
28456 },
28457 standoff: {
28458 valType: 'number',
28459 min: 0,
28460 dflt: 0,
28461 editType: 'calc+arraydraw',
28462 },
28463 startstandoff: {
28464 valType: 'number',
28465 min: 0,
28466 dflt: 0,
28467 editType: 'calc+arraydraw',
28468 },
28469 ax: {
28470 valType: 'any',
28471 editType: 'calc+arraydraw',
28472 },
28473 ay: {
28474 valType: 'any',
28475 editType: 'calc+arraydraw',
28476 },
28477 axref: {
28478 valType: 'enumerated',
28479 dflt: 'pixel',
28480 values: [
28481 'pixel',
28482 cartesianConstants.idRegex.x.toString()
28483 ],
28484 editType: 'calc',
28485 },
28486 ayref: {
28487 valType: 'enumerated',
28488 dflt: 'pixel',
28489 values: [
28490 'pixel',
28491 cartesianConstants.idRegex.y.toString()
28492 ],
28493 editType: 'calc',
28494 },
28495 // positioning
28496 xref: {
28497 valType: 'enumerated',
28498 values: [
28499 'paper',
28500 cartesianConstants.idRegex.x.toString()
28501 ],
28502 editType: 'calc',
28503 },
28504 x: {
28505 valType: 'any',
28506 editType: 'calc+arraydraw',
28507 },
28508 xanchor: {
28509 valType: 'enumerated',
28510 values: ['auto', 'left', 'center', 'right'],
28511 dflt: 'auto',
28512 editType: 'calc+arraydraw',
28513 },
28514 xshift: {
28515 valType: 'number',
28516 dflt: 0,
28517 editType: 'calc+arraydraw',
28518 },
28519 yref: {
28520 valType: 'enumerated',
28521 values: [
28522 'paper',
28523 cartesianConstants.idRegex.y.toString()
28524 ],
28525 editType: 'calc',
28526 },
28527 y: {
28528 valType: 'any',
28529 editType: 'calc+arraydraw',
28530 },
28531 yanchor: {
28532 valType: 'enumerated',
28533 values: ['auto', 'top', 'middle', 'bottom'],
28534 dflt: 'auto',
28535 editType: 'calc+arraydraw',
28536 },
28537 yshift: {
28538 valType: 'number',
28539 dflt: 0,
28540 editType: 'calc+arraydraw',
28541 },
28542 clicktoshow: {
28543 valType: 'enumerated',
28544 values: [false, 'onoff', 'onout'],
28545 dflt: false,
28546 editType: 'arraydraw',
28547 },
28548 xclick: {
28549 valType: 'any',
28550 editType: 'arraydraw',
28551 },
28552 yclick: {
28553 valType: 'any',
28554 editType: 'arraydraw',
28555 },
28556 hovertext: {
28557 valType: 'string',
28558 editType: 'arraydraw',
28559 },
28560 hoverlabel: {
28561 bgcolor: {
28562 valType: 'color',
28563 editType: 'arraydraw',
28564 },
28565 bordercolor: {
28566 valType: 'color',
28567 editType: 'arraydraw',
28568 },
28569 font: fontAttrs({
28570 editType: 'arraydraw',
28571 }),
28572 editType: 'arraydraw'
28573 },
28574 captureevents: {
28575 valType: 'boolean',
28576 editType: 'arraydraw',
28577 },
28578 editType: 'calc',
28579
28580 _deprecated: {
28581 ref: {
28582 valType: 'string',
28583 editType: 'calc',
28584 }
28585 }
28586});
28587
28588},{"../../constants/axis_placeable_objects":263,"../../plot_api/plot_template":323,"../../plots/cartesian/constants":341,"../../plots/font_attributes":363,"./arrow_paths":139}],141:[function(_dereq_,module,exports){
28589'use strict';
28590
28591var Lib = _dereq_('../../lib');
28592var Axes = _dereq_('../../plots/cartesian/axes');
28593
28594var draw = _dereq_('./draw').draw;
28595
28596
28597module.exports = function calcAutorange(gd) {
28598 var fullLayout = gd._fullLayout;
28599 var annotationList = Lib.filterVisible(fullLayout.annotations);
28600
28601 if(annotationList.length && gd._fullData.length) {
28602 return Lib.syncOrAsync([draw, annAutorange], gd);
28603 }
28604};
28605
28606function annAutorange(gd) {
28607 var fullLayout = gd._fullLayout;
28608
28609 // find the bounding boxes for each of these annotations'
28610 // relative to their anchor points
28611 // use the arrow and the text bg rectangle,
28612 // as the whole anno may include hidden text in its bbox
28613 Lib.filterVisible(fullLayout.annotations).forEach(function(ann) {
28614 var xa = Axes.getFromId(gd, ann.xref);
28615 var ya = Axes.getFromId(gd, ann.yref);
28616 var xRefType = Axes.getRefType(ann.xref);
28617 var yRefType = Axes.getRefType(ann.yref);
28618
28619 ann._extremes = {};
28620 if(xRefType === 'range') calcAxisExpansion(ann, xa);
28621 if(yRefType === 'range') calcAxisExpansion(ann, ya);
28622 });
28623}
28624
28625function calcAxisExpansion(ann, ax) {
28626 var axId = ax._id;
28627 var letter = axId.charAt(0);
28628 var pos = ann[letter];
28629 var apos = ann['a' + letter];
28630 var ref = ann[letter + 'ref'];
28631 var aref = ann['a' + letter + 'ref'];
28632 var padplus = ann['_' + letter + 'padplus'];
28633 var padminus = ann['_' + letter + 'padminus'];
28634 var shift = {x: 1, y: -1}[letter] * ann[letter + 'shift'];
28635 var headSize = 3 * ann.arrowsize * ann.arrowwidth || 0;
28636 var headPlus = headSize + shift;
28637 var headMinus = headSize - shift;
28638 var startHeadSize = 3 * ann.startarrowsize * ann.arrowwidth || 0;
28639 var startHeadPlus = startHeadSize + shift;
28640 var startHeadMinus = startHeadSize - shift;
28641 var extremes;
28642
28643 if(aref === ref) {
28644 // expand for the arrowhead (padded by arrowhead)
28645 var extremeArrowHead = Axes.findExtremes(ax, [ax.r2c(pos)], {
28646 ppadplus: headPlus,
28647 ppadminus: headMinus
28648 });
28649 // again for the textbox (padded by textbox)
28650 var extremeText = Axes.findExtremes(ax, [ax.r2c(apos)], {
28651 ppadplus: Math.max(padplus, startHeadPlus),
28652 ppadminus: Math.max(padminus, startHeadMinus)
28653 });
28654 extremes = {
28655 min: [extremeArrowHead.min[0], extremeText.min[0]],
28656 max: [extremeArrowHead.max[0], extremeText.max[0]]
28657 };
28658 } else {
28659 startHeadPlus = apos ? startHeadPlus + apos : startHeadPlus;
28660 startHeadMinus = apos ? startHeadMinus - apos : startHeadMinus;
28661 extremes = Axes.findExtremes(ax, [ax.r2c(pos)], {
28662 ppadplus: Math.max(padplus, headPlus, startHeadPlus),
28663 ppadminus: Math.max(padminus, headMinus, startHeadMinus)
28664 });
28665 }
28666
28667 ann._extremes[axId] = extremes;
28668}
28669
28670},{"../../lib":287,"../../plots/cartesian/axes":334,"./draw":146}],142:[function(_dereq_,module,exports){
28671'use strict';
28672
28673var Lib = _dereq_('../../lib');
28674var Registry = _dereq_('../../registry');
28675var arrayEditor = _dereq_('../../plot_api/plot_template').arrayEditor;
28676
28677module.exports = {
28678 hasClickToShow: hasClickToShow,
28679 onClick: onClick
28680};
28681
28682/*
28683 * hasClickToShow: does the given hoverData have ANY annotations which will
28684 * turn ON if we click here? (used by hover events to set cursor)
28685 *
28686 * gd: graphDiv
28687 * hoverData: a hoverData array, as included with the *plotly_hover* or
28688 * *plotly_click* events in the `points` attribute
28689 *
28690 * returns: boolean
28691 */
28692function hasClickToShow(gd, hoverData) {
28693 var sets = getToggleSets(gd, hoverData);
28694 return sets.on.length > 0 || sets.explicitOff.length > 0;
28695}
28696
28697/*
28698 * onClick: perform the toggling (via Plotly.update) implied by clicking
28699 * at this hoverData
28700 *
28701 * gd: graphDiv
28702 * hoverData: a hoverData array, as included with the *plotly_hover* or
28703 * *plotly_click* events in the `points` attribute
28704 *
28705 * returns: Promise that the update is complete
28706 */
28707function onClick(gd, hoverData) {
28708 var toggleSets = getToggleSets(gd, hoverData);
28709 var onSet = toggleSets.on;
28710 var offSet = toggleSets.off.concat(toggleSets.explicitOff);
28711 var update = {};
28712 var annotationsOut = gd._fullLayout.annotations;
28713 var i, editHelpers;
28714
28715 if(!(onSet.length || offSet.length)) return;
28716
28717 for(i = 0; i < onSet.length; i++) {
28718 editHelpers = arrayEditor(gd.layout, 'annotations', annotationsOut[onSet[i]]);
28719 editHelpers.modifyItem('visible', true);
28720 Lib.extendFlat(update, editHelpers.getUpdateObj());
28721 }
28722
28723 for(i = 0; i < offSet.length; i++) {
28724 editHelpers = arrayEditor(gd.layout, 'annotations', annotationsOut[offSet[i]]);
28725 editHelpers.modifyItem('visible', false);
28726 Lib.extendFlat(update, editHelpers.getUpdateObj());
28727 }
28728
28729 return Registry.call('update', gd, {}, update);
28730}
28731
28732/*
28733 * getToggleSets: find the annotations which will turn on or off at this
28734 * hoverData
28735 *
28736 * gd: graphDiv
28737 * hoverData: a hoverData array, as included with the *plotly_hover* or
28738 * *plotly_click* events in the `points` attribute
28739 *
28740 * returns: {
28741 * on: Array (indices of annotations to turn on),
28742 * off: Array (indices to turn off because you're not hovering on them),
28743 * explicitOff: Array (indices to turn off because you *are* hovering on them)
28744 * }
28745 */
28746function getToggleSets(gd, hoverData) {
28747 var annotations = gd._fullLayout.annotations;
28748 var onSet = [];
28749 var offSet = [];
28750 var explicitOffSet = [];
28751 var hoverLen = (hoverData || []).length;
28752
28753 var i, j, anni, showMode, pointj, xa, ya, toggleType;
28754
28755 for(i = 0; i < annotations.length; i++) {
28756 anni = annotations[i];
28757 showMode = anni.clicktoshow;
28758
28759 if(showMode) {
28760 for(j = 0; j < hoverLen; j++) {
28761 pointj = hoverData[j];
28762 xa = pointj.xaxis;
28763 ya = pointj.yaxis;
28764
28765 if(xa._id === anni.xref &&
28766 ya._id === anni.yref &&
28767 xa.d2r(pointj.x) === clickData2r(anni._xclick, xa) &&
28768 ya.d2r(pointj.y) === clickData2r(anni._yclick, ya)
28769 ) {
28770 // match! toggle this annotation
28771 // regardless of its clicktoshow mode
28772 // but if it's onout mode, off is implicit
28773 if(anni.visible) {
28774 if(showMode === 'onout') toggleType = offSet;
28775 else toggleType = explicitOffSet;
28776 } else {
28777 toggleType = onSet;
28778 }
28779 toggleType.push(i);
28780 break;
28781 }
28782 }
28783
28784 if(j === hoverLen) {
28785 // no match - only turn this annotation OFF, and only if
28786 // showmode is 'onout'
28787 if(anni.visible && showMode === 'onout') offSet.push(i);
28788 }
28789 }
28790 }
28791
28792 return {on: onSet, off: offSet, explicitOff: explicitOffSet};
28793}
28794
28795// to handle log axes until v3
28796function clickData2r(d, ax) {
28797 return ax.type === 'log' ? ax.l2r(d) : ax.d2r(d);
28798}
28799
28800},{"../../lib":287,"../../plot_api/plot_template":323,"../../registry":376}],143:[function(_dereq_,module,exports){
28801'use strict';
28802
28803var Lib = _dereq_('../../lib');
28804var Color = _dereq_('../color');
28805
28806// defaults common to 'annotations' and 'annotations3d'
28807module.exports = function handleAnnotationCommonDefaults(annIn, annOut, fullLayout, coerce) {
28808 coerce('opacity');
28809 var bgColor = coerce('bgcolor');
28810
28811 var borderColor = coerce('bordercolor');
28812 var borderOpacity = Color.opacity(borderColor);
28813
28814 coerce('borderpad');
28815
28816 var borderWidth = coerce('borderwidth');
28817 var showArrow = coerce('showarrow');
28818
28819 coerce('text', showArrow ? ' ' : fullLayout._dfltTitle.annotation);
28820 coerce('textangle');
28821 Lib.coerceFont(coerce, 'font', fullLayout.font);
28822
28823 coerce('width');
28824 coerce('align');
28825
28826 var h = coerce('height');
28827 if(h) coerce('valign');
28828
28829 if(showArrow) {
28830 var arrowside = coerce('arrowside');
28831 var arrowhead;
28832 var arrowsize;
28833
28834 if(arrowside.indexOf('end') !== -1) {
28835 arrowhead = coerce('arrowhead');
28836 arrowsize = coerce('arrowsize');
28837 }
28838
28839 if(arrowside.indexOf('start') !== -1) {
28840 coerce('startarrowhead', arrowhead);
28841 coerce('startarrowsize', arrowsize);
28842 }
28843 coerce('arrowcolor', borderOpacity ? annOut.bordercolor : Color.defaultLine);
28844 coerce('arrowwidth', ((borderOpacity && borderWidth) || 1) * 2);
28845 coerce('standoff');
28846 coerce('startstandoff');
28847 }
28848
28849 var hoverText = coerce('hovertext');
28850 var globalHoverLabel = fullLayout.hoverlabel || {};
28851
28852 if(hoverText) {
28853 var hoverBG = coerce('hoverlabel.bgcolor', globalHoverLabel.bgcolor ||
28854 (Color.opacity(bgColor) ? Color.rgb(bgColor) : Color.defaultLine)
28855 );
28856
28857 var hoverBorder = coerce('hoverlabel.bordercolor', globalHoverLabel.bordercolor ||
28858 Color.contrast(hoverBG)
28859 );
28860
28861 Lib.coerceFont(coerce, 'hoverlabel.font', {
28862 family: globalHoverLabel.font.family,
28863 size: globalHoverLabel.font.size,
28864 color: globalHoverLabel.font.color || hoverBorder
28865 });
28866 }
28867
28868 coerce('captureevents', !!hoverText);
28869};
28870
28871},{"../../lib":287,"../color":157}],144:[function(_dereq_,module,exports){
28872'use strict';
28873
28874var isNumeric = _dereq_('fast-isnumeric');
28875var toLogRange = _dereq_('../../lib/to_log_range');
28876
28877/*
28878 * convertCoords: when converting an axis between log and linear
28879 * you need to alter any annotations on that axis to keep them
28880 * pointing at the same data point.
28881 * In v3.0 this will become obsolete
28882 *
28883 * gd: the plot div
28884 * ax: the axis being changed
28885 * newType: the type it's getting
28886 * doExtra: function(attr, val) from inside relayout that sets the attribute.
28887 * Use this to make the changes as it's aware if any other changes in the
28888 * same relayout call should override this conversion.
28889 */
28890module.exports = function convertCoords(gd, ax, newType, doExtra) {
28891 ax = ax || {};
28892
28893 var toLog = (newType === 'log') && (ax.type === 'linear');
28894 var fromLog = (newType === 'linear') && (ax.type === 'log');
28895
28896 if(!(toLog || fromLog)) return;
28897
28898 var annotations = gd._fullLayout.annotations;
28899 var axLetter = ax._id.charAt(0);
28900 var ann;
28901 var attrPrefix;
28902
28903 function convert(attr) {
28904 var currentVal = ann[attr];
28905 var newVal = null;
28906
28907 if(toLog) newVal = toLogRange(currentVal, ax.range);
28908 else newVal = Math.pow(10, currentVal);
28909
28910 // if conversion failed, delete the value so it gets a default value
28911 if(!isNumeric(newVal)) newVal = null;
28912
28913 doExtra(attrPrefix + attr, newVal);
28914 }
28915
28916 for(var i = 0; i < annotations.length; i++) {
28917 ann = annotations[i];
28918 attrPrefix = 'annotations[' + i + '].';
28919
28920 if(ann[axLetter + 'ref'] === ax._id) convert(axLetter);
28921 if(ann['a' + axLetter + 'ref'] === ax._id) convert('a' + axLetter);
28922 }
28923};
28924
28925},{"../../lib/to_log_range":312,"fast-isnumeric":33}],145:[function(_dereq_,module,exports){
28926'use strict';
28927
28928var Lib = _dereq_('../../lib');
28929var Axes = _dereq_('../../plots/cartesian/axes');
28930var handleArrayContainerDefaults = _dereq_('../../plots/array_container_defaults');
28931
28932var handleAnnotationCommonDefaults = _dereq_('./common_defaults');
28933var attributes = _dereq_('./attributes');
28934
28935
28936module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) {
28937 handleArrayContainerDefaults(layoutIn, layoutOut, {
28938 name: 'annotations',
28939 handleItemDefaults: handleAnnotationDefaults
28940 });
28941};
28942
28943function handleAnnotationDefaults(annIn, annOut, fullLayout) {
28944 function coerce(attr, dflt) {
28945 return Lib.coerce(annIn, annOut, attributes, attr, dflt);
28946 }
28947
28948 var visible = coerce('visible');
28949 var clickToShow = coerce('clicktoshow');
28950
28951 if(!(visible || clickToShow)) return;
28952
28953 handleAnnotationCommonDefaults(annIn, annOut, fullLayout, coerce);
28954
28955 var showArrow = annOut.showarrow;
28956
28957 // positioning
28958 var axLetters = ['x', 'y'];
28959 var arrowPosDflt = [-10, -30];
28960 var gdMock = {_fullLayout: fullLayout};
28961
28962 for(var i = 0; i < 2; i++) {
28963 var axLetter = axLetters[i];
28964
28965 // xref, yref
28966 var axRef = Axes.coerceRef(annIn, annOut, gdMock, axLetter, '', 'paper');
28967
28968 if(axRef !== 'paper') {
28969 var ax = Axes.getFromId(gdMock, axRef);
28970 ax._annIndices.push(annOut._index);
28971 }
28972
28973 // x, y
28974 Axes.coercePosition(annOut, gdMock, coerce, axRef, axLetter, 0.5);
28975
28976 if(showArrow) {
28977 var arrowPosAttr = 'a' + axLetter;
28978 // axref, ayref
28979 var aaxRef = Axes.coerceRef(annIn, annOut, gdMock, arrowPosAttr, 'pixel',
28980 ['pixel', 'paper']);
28981
28982 // for now the arrow can only be on the same axis or specified as pixels
28983 // TODO: sometime it might be interesting to allow it to be on *any* axis
28984 // but that would require updates to drawing & autorange code and maybe more
28985 if(aaxRef !== 'pixel' && aaxRef !== axRef) {
28986 aaxRef = annOut[arrowPosAttr] = 'pixel';
28987 }
28988
28989 // ax, ay
28990 var aDflt = (aaxRef === 'pixel') ? arrowPosDflt[i] : 0.4;
28991 Axes.coercePosition(annOut, gdMock, coerce, aaxRef, arrowPosAttr, aDflt);
28992 }
28993
28994 // xanchor, yanchor
28995 coerce(axLetter + 'anchor');
28996
28997 // xshift, yshift
28998 coerce(axLetter + 'shift');
28999 }
29000
29001 // if you have one coordinate you should have both
29002 Lib.noneOrAll(annIn, annOut, ['x', 'y']);
29003
29004 // if you have one part of arrow length you should have both
29005 if(showArrow) {
29006 Lib.noneOrAll(annIn, annOut, ['ax', 'ay']);
29007 }
29008
29009 if(clickToShow) {
29010 var xClick = coerce('xclick');
29011 var yClick = coerce('yclick');
29012
29013 // put the actual click data to bind to into private attributes
29014 // so we don't have to do this little bit of logic on every hover event
29015 annOut._xclick = (xClick === undefined) ?
29016 annOut.x :
29017 Axes.cleanPosition(xClick, gdMock, annOut.xref);
29018 annOut._yclick = (yClick === undefined) ?
29019 annOut.y :
29020 Axes.cleanPosition(yClick, gdMock, annOut.yref);
29021 }
29022}
29023
29024},{"../../lib":287,"../../plots/array_container_defaults":329,"../../plots/cartesian/axes":334,"./attributes":140,"./common_defaults":143}],146:[function(_dereq_,module,exports){
29025'use strict';
29026
29027var d3 = _dereq_('@plotly/d3');
29028
29029var Registry = _dereq_('../../registry');
29030var Plots = _dereq_('../../plots/plots');
29031var Lib = _dereq_('../../lib');
29032var strTranslate = Lib.strTranslate;
29033var Axes = _dereq_('../../plots/cartesian/axes');
29034var Color = _dereq_('../color');
29035var Drawing = _dereq_('../drawing');
29036var Fx = _dereq_('../fx');
29037var svgTextUtils = _dereq_('../../lib/svg_text_utils');
29038var setCursor = _dereq_('../../lib/setcursor');
29039var dragElement = _dereq_('../dragelement');
29040var arrayEditor = _dereq_('../../plot_api/plot_template').arrayEditor;
29041
29042var drawArrowHead = _dereq_('./draw_arrow_head');
29043
29044// Annotations are stored in gd.layout.annotations, an array of objects
29045// index can point to one item in this array,
29046// or non-numeric to simply add a new one
29047// or -1 to modify all existing
29048// opt can be the full options object, or one key (to be set to value)
29049// or undefined to simply redraw
29050// if opt is blank, val can be 'add' or a full options object to add a new
29051// annotation at that point in the array, or 'remove' to delete this one
29052
29053module.exports = {
29054 draw: draw,
29055 drawOne: drawOne,
29056 drawRaw: drawRaw
29057};
29058
29059/*
29060 * draw: draw all annotations without any new modifications
29061 */
29062function draw(gd) {
29063 var fullLayout = gd._fullLayout;
29064
29065 fullLayout._infolayer.selectAll('.annotation').remove();
29066
29067 for(var i = 0; i < fullLayout.annotations.length; i++) {
29068 if(fullLayout.annotations[i].visible) {
29069 drawOne(gd, i);
29070 }
29071 }
29072
29073 return Plots.previousPromises(gd);
29074}
29075
29076/*
29077 * drawOne: draw a single cartesian or paper-ref annotation, potentially with modifications
29078 *
29079 * index (int): the annotation to draw
29080 */
29081function drawOne(gd, index) {
29082 var fullLayout = gd._fullLayout;
29083 var options = fullLayout.annotations[index] || {};
29084 var xa = Axes.getFromId(gd, options.xref);
29085 var ya = Axes.getFromId(gd, options.yref);
29086
29087 if(xa) xa.setScale();
29088 if(ya) ya.setScale();
29089
29090 drawRaw(gd, options, index, false, xa, ya);
29091}
29092
29093// Convert pixels to the coordinates relevant for the axis referred to. For
29094// example, for paper it would convert to a value normalized by the dimension of
29095// the plot.
29096// axDomainRef: if true and axa defined, draws relative to axis domain,
29097// otherwise draws relative to data (if axa defined) or paper (if not).
29098function shiftPosition(axa, dAx, axLetter, gs, options) {
29099 var optAx = options[axLetter];
29100 var axRef = options[axLetter + 'ref'];
29101 var vertical = axLetter.indexOf('y') !== -1;
29102 var axDomainRef = Axes.getRefType(axRef) === 'domain';
29103 var gsDim = vertical ? gs.h : gs.w;
29104 if(axa) {
29105 if(axDomainRef) {
29106 // here optAx normalized to length of axis (e.g., normally in range
29107 // 0 to 1). But dAx is in pixels. So we normalize dAx to length of
29108 // axis before doing the math.
29109 return optAx + (vertical ? -dAx : dAx) / axa._length;
29110 } else {
29111 return axa.p2r(axa.r2p(optAx) + dAx);
29112 }
29113 } else {
29114 return optAx + (vertical ? -dAx : dAx) / gsDim;
29115 }
29116}
29117
29118/**
29119 * drawRaw: draw a single annotation, potentially with modifications
29120 *
29121 * @param {DOM element} gd
29122 * @param {object} options : this annotation's fullLayout options
29123 * @param {integer} index : index in 'annotations' container of the annotation to draw
29124 * @param {string} subplotId : id of the annotation's subplot
29125 * - use false for 2d (i.e. cartesian or paper-ref) annotations
29126 * @param {object | undefined} xa : full x-axis object to compute subplot pos-to-px
29127 * @param {object | undefined} ya : ... y-axis
29128 */
29129function drawRaw(gd, options, index, subplotId, xa, ya) {
29130 var fullLayout = gd._fullLayout;
29131 var gs = gd._fullLayout._size;
29132 var edits = gd._context.edits;
29133
29134 var className, containerStr;
29135
29136 if(subplotId) {
29137 className = 'annotation-' + subplotId;
29138 containerStr = subplotId + '.annotations';
29139 } else {
29140 className = 'annotation';
29141 containerStr = 'annotations';
29142 }
29143
29144 var editHelpers = arrayEditor(gd.layout, containerStr, options);
29145 var modifyBase = editHelpers.modifyBase;
29146 var modifyItem = editHelpers.modifyItem;
29147 var getUpdateObj = editHelpers.getUpdateObj;
29148
29149 // remove the existing annotation if there is one
29150 fullLayout._infolayer
29151 .selectAll('.' + className + '[data-index="' + index + '"]')
29152 .remove();
29153
29154 var annClipID = 'clip' + fullLayout._uid + '_ann' + index;
29155
29156 // this annotation is gone - quit now after deleting it
29157 // TODO: use d3 idioms instead of deleting and redrawing every time
29158 if(!options._input || options.visible === false) {
29159 d3.selectAll('#' + annClipID).remove();
29160 return;
29161 }
29162
29163 // calculated pixel positions
29164 // x & y each will get text, head, and tail as appropriate
29165 var annPosPx = {x: {}, y: {}};
29166 var textangle = +options.textangle || 0;
29167
29168 // create the components
29169 // made a single group to contain all, so opacity can work right
29170 // with border/arrow together this could handle a whole bunch of
29171 // cleanup at this point, but works for now
29172 var annGroup = fullLayout._infolayer.append('g')
29173 .classed(className, true)
29174 .attr('data-index', String(index))
29175 .style('opacity', options.opacity);
29176
29177 // another group for text+background so that they can rotate together
29178 var annTextGroup = annGroup.append('g')
29179 .classed('annotation-text-g', true);
29180
29181 var editTextPosition = edits[options.showarrow ? 'annotationTail' : 'annotationPosition'];
29182 var textEvents = options.captureevents || edits.annotationText || editTextPosition;
29183
29184 function makeEventData(initialEvent) {
29185 var eventData = {
29186 index: index,
29187 annotation: options._input,
29188 fullAnnotation: options,
29189 event: initialEvent
29190 };
29191 if(subplotId) {
29192 eventData.subplotId = subplotId;
29193 }
29194 return eventData;
29195 }
29196
29197 var annTextGroupInner = annTextGroup.append('g')
29198 .style('pointer-events', textEvents ? 'all' : null)
29199 .call(setCursor, 'pointer')
29200 .on('click', function() {
29201 gd._dragging = false;
29202 gd.emit('plotly_clickannotation', makeEventData(d3.event));
29203 });
29204
29205 if(options.hovertext) {
29206 annTextGroupInner
29207 .on('mouseover', function() {
29208 var hoverOptions = options.hoverlabel;
29209 var hoverFont = hoverOptions.font;
29210 var bBox = this.getBoundingClientRect();
29211 var bBoxRef = gd.getBoundingClientRect();
29212
29213 Fx.loneHover({
29214 x0: bBox.left - bBoxRef.left,
29215 x1: bBox.right - bBoxRef.left,
29216 y: (bBox.top + bBox.bottom) / 2 - bBoxRef.top,
29217 text: options.hovertext,
29218 color: hoverOptions.bgcolor,
29219 borderColor: hoverOptions.bordercolor,
29220 fontFamily: hoverFont.family,
29221 fontSize: hoverFont.size,
29222 fontColor: hoverFont.color
29223 }, {
29224 container: fullLayout._hoverlayer.node(),
29225 outerContainer: fullLayout._paper.node(),
29226 gd: gd
29227 });
29228 })
29229 .on('mouseout', function() {
29230 Fx.loneUnhover(fullLayout._hoverlayer.node());
29231 });
29232 }
29233
29234 var borderwidth = options.borderwidth;
29235 var borderpad = options.borderpad;
29236 var borderfull = borderwidth + borderpad;
29237
29238 var annTextBG = annTextGroupInner.append('rect')
29239 .attr('class', 'bg')
29240 .style('stroke-width', borderwidth + 'px')
29241 .call(Color.stroke, options.bordercolor)
29242 .call(Color.fill, options.bgcolor);
29243
29244 var isSizeConstrained = options.width || options.height;
29245
29246 var annTextClip = fullLayout._topclips
29247 .selectAll('#' + annClipID)
29248 .data(isSizeConstrained ? [0] : []);
29249
29250 annTextClip.enter().append('clipPath')
29251 .classed('annclip', true)
29252 .attr('id', annClipID)
29253 .append('rect');
29254 annTextClip.exit().remove();
29255
29256 var font = options.font;
29257
29258 var text = fullLayout._meta ?
29259 Lib.templateString(options.text, fullLayout._meta) :
29260 options.text;
29261
29262 var annText = annTextGroupInner.append('text')
29263 .classed('annotation-text', true)
29264 .text(text);
29265
29266 function textLayout(s) {
29267 s.call(Drawing.font, font)
29268 .attr({
29269 'text-anchor': {
29270 left: 'start',
29271 right: 'end'
29272 }[options.align] || 'middle'
29273 });
29274
29275 svgTextUtils.convertToTspans(s, gd, drawGraphicalElements);
29276 return s;
29277 }
29278
29279 function drawGraphicalElements() {
29280 // if the text has *only* a link, make the whole box into a link
29281 var anchor3 = annText.selectAll('a');
29282 if(anchor3.size() === 1 && anchor3.text() === annText.text()) {
29283 var wholeLink = annTextGroupInner.insert('a', ':first-child').attr({
29284 'xlink:xlink:href': anchor3.attr('xlink:href'),
29285 'xlink:xlink:show': anchor3.attr('xlink:show')
29286 })
29287 .style({cursor: 'pointer'});
29288
29289 wholeLink.node().appendChild(annTextBG.node());
29290 }
29291
29292 var mathjaxGroup = annTextGroupInner.select('.annotation-text-math-group');
29293 var hasMathjax = !mathjaxGroup.empty();
29294 var anntextBB = Drawing.bBox(
29295 (hasMathjax ? mathjaxGroup : annText).node());
29296 var textWidth = anntextBB.width;
29297 var textHeight = anntextBB.height;
29298 var annWidth = options.width || textWidth;
29299 var annHeight = options.height || textHeight;
29300 var outerWidth = Math.round(annWidth + 2 * borderfull);
29301 var outerHeight = Math.round(annHeight + 2 * borderfull);
29302
29303 function shiftFraction(v, anchor) {
29304 if(anchor === 'auto') {
29305 if(v < 1 / 3) anchor = 'left';
29306 else if(v > 2 / 3) anchor = 'right';
29307 else anchor = 'center';
29308 }
29309 return {
29310 center: 0,
29311 middle: 0,
29312 left: 0.5,
29313 bottom: -0.5,
29314 right: -0.5,
29315 top: 0.5
29316 }[anchor];
29317 }
29318
29319 var annotationIsOffscreen = false;
29320 var letters = ['x', 'y'];
29321
29322 for(var i = 0; i < letters.length; i++) {
29323 var axLetter = letters[i];
29324 var axRef = options[axLetter + 'ref'] || axLetter;
29325 var tailRef = options['a' + axLetter + 'ref'];
29326 var ax = {x: xa, y: ya}[axLetter];
29327 var dimAngle = (textangle + (axLetter === 'x' ? 0 : -90)) * Math.PI / 180;
29328 // note that these two can be either positive or negative
29329 var annSizeFromWidth = outerWidth * Math.cos(dimAngle);
29330 var annSizeFromHeight = outerHeight * Math.sin(dimAngle);
29331 // but this one is the positive total size
29332 var annSize = Math.abs(annSizeFromWidth) + Math.abs(annSizeFromHeight);
29333 var anchor = options[axLetter + 'anchor'];
29334 var overallShift = options[axLetter + 'shift'] * (axLetter === 'x' ? 1 : -1);
29335 var posPx = annPosPx[axLetter];
29336 var basePx;
29337 var textPadShift;
29338 var alignPosition;
29339 var autoAlignFraction;
29340 var textShift;
29341 var axRefType = Axes.getRefType(axRef);
29342
29343 /*
29344 * calculate the *primary* pixel position
29345 * which is the arrowhead if there is one,
29346 * otherwise the text anchor point
29347 */
29348 if(ax && (axRefType !== 'domain')) {
29349 // check if annotation is off screen, to bypass DOM manipulations
29350 var posFraction = ax.r2fraction(options[axLetter]);
29351 if(posFraction < 0 || posFraction > 1) {
29352 if(tailRef === axRef) {
29353 posFraction = ax.r2fraction(options['a' + axLetter]);
29354 if(posFraction < 0 || posFraction > 1) {
29355 annotationIsOffscreen = true;
29356 }
29357 } else {
29358 annotationIsOffscreen = true;
29359 }
29360 }
29361 basePx = ax._offset + ax.r2p(options[axLetter]);
29362 autoAlignFraction = 0.5;
29363 } else {
29364 var axRefTypeEqDomain = axRefType === 'domain';
29365 if(axLetter === 'x') {
29366 alignPosition = options[axLetter];
29367 basePx = axRefTypeEqDomain ?
29368 ax._offset + ax._length * alignPosition :
29369 basePx = gs.l + gs.w * alignPosition;
29370 } else {
29371 alignPosition = 1 - options[axLetter];
29372 basePx = axRefTypeEqDomain ?
29373 ax._offset + ax._length * alignPosition :
29374 basePx = gs.t + gs.h * alignPosition;
29375 }
29376 autoAlignFraction = options.showarrow ? 0.5 : alignPosition;
29377 }
29378
29379 // now translate this into pixel positions of head, tail, and text
29380 // as well as paddings for autorange
29381 if(options.showarrow) {
29382 posPx.head = basePx;
29383
29384 var arrowLength = options['a' + axLetter];
29385
29386 // with an arrow, the text rotates around the anchor point
29387 textShift = annSizeFromWidth * shiftFraction(0.5, options.xanchor) -
29388 annSizeFromHeight * shiftFraction(0.5, options.yanchor);
29389
29390 if(tailRef === axRef) {
29391 // In the case tailRefType is 'domain' or 'paper', the arrow's
29392 // position is set absolutely, which is consistent with how
29393 // it behaves when its position is set in data ('range')
29394 // coordinates.
29395 var tailRefType = Axes.getRefType(tailRef);
29396 if(tailRefType === 'domain') {
29397 if(axLetter === 'y') {
29398 arrowLength = 1 - arrowLength;
29399 }
29400 posPx.tail = ax._offset + ax._length * arrowLength;
29401 } else if(tailRefType === 'paper') {
29402 if(axLetter === 'y') {
29403 arrowLength = 1 - arrowLength;
29404 posPx.tail = gs.t + gs.h * arrowLength;
29405 } else {
29406 posPx.tail = gs.l + gs.w * arrowLength;
29407 }
29408 } else {
29409 // assumed tailRef is range or paper referenced
29410 posPx.tail = ax._offset + ax.r2p(arrowLength);
29411 }
29412 // tail is range- or domain-referenced: autorange pads the
29413 // text in px from the tail
29414 textPadShift = textShift;
29415 } else {
29416 posPx.tail = basePx + arrowLength;
29417 // tail is specified in px from head, so autorange also pads vs head
29418 textPadShift = textShift + arrowLength;
29419 }
29420
29421 posPx.text = posPx.tail + textShift;
29422
29423 // constrain pixel/paper referenced so the draggers are at least
29424 // partially visible
29425 var maxPx = fullLayout[(axLetter === 'x') ? 'width' : 'height'];
29426 if(axRef === 'paper') {
29427 posPx.head = Lib.constrain(posPx.head, 1, maxPx - 1);
29428 }
29429 if(tailRef === 'pixel') {
29430 var shiftPlus = -Math.max(posPx.tail - 3, posPx.text);
29431 var shiftMinus = Math.min(posPx.tail + 3, posPx.text) - maxPx;
29432 if(shiftPlus > 0) {
29433 posPx.tail += shiftPlus;
29434 posPx.text += shiftPlus;
29435 } else if(shiftMinus > 0) {
29436 posPx.tail -= shiftMinus;
29437 posPx.text -= shiftMinus;
29438 }
29439 }
29440
29441 posPx.tail += overallShift;
29442 posPx.head += overallShift;
29443 } else {
29444 // with no arrow, the text rotates and *then* we put the anchor
29445 // relative to the new bounding box
29446 textShift = annSize * shiftFraction(autoAlignFraction, anchor);
29447 textPadShift = textShift;
29448 posPx.text = basePx + textShift;
29449 }
29450
29451 posPx.text += overallShift;
29452 textShift += overallShift;
29453 textPadShift += overallShift;
29454
29455 // padplus/minus are used by autorange
29456 options['_' + axLetter + 'padplus'] = (annSize / 2) + textPadShift;
29457 options['_' + axLetter + 'padminus'] = (annSize / 2) - textPadShift;
29458
29459 // size/shift are used during dragging
29460 options['_' + axLetter + 'size'] = annSize;
29461 options['_' + axLetter + 'shift'] = textShift;
29462 }
29463
29464 if(annotationIsOffscreen) {
29465 annTextGroupInner.remove();
29466 return;
29467 }
29468
29469 var xShift = 0;
29470 var yShift = 0;
29471
29472 if(options.align !== 'left') {
29473 xShift = (annWidth - textWidth) * (options.align === 'center' ? 0.5 : 1);
29474 }
29475 if(options.valign !== 'top') {
29476 yShift = (annHeight - textHeight) * (options.valign === 'middle' ? 0.5 : 1);
29477 }
29478
29479 if(hasMathjax) {
29480 mathjaxGroup.select('svg').attr({
29481 x: borderfull + xShift - 1,
29482 y: borderfull + yShift
29483 })
29484 .call(Drawing.setClipUrl, isSizeConstrained ? annClipID : null, gd);
29485 } else {
29486 var texty = borderfull + yShift - anntextBB.top;
29487 var textx = borderfull + xShift - anntextBB.left;
29488
29489 annText.call(svgTextUtils.positionText, textx, texty)
29490 .call(Drawing.setClipUrl, isSizeConstrained ? annClipID : null, gd);
29491 }
29492
29493 annTextClip.select('rect').call(Drawing.setRect, borderfull, borderfull,
29494 annWidth, annHeight);
29495
29496 annTextBG.call(Drawing.setRect, borderwidth / 2, borderwidth / 2,
29497 outerWidth - borderwidth, outerHeight - borderwidth);
29498
29499 annTextGroupInner.call(Drawing.setTranslate,
29500 Math.round(annPosPx.x.text - outerWidth / 2),
29501 Math.round(annPosPx.y.text - outerHeight / 2));
29502
29503 /*
29504 * rotate text and background
29505 * we already calculated the text center position *as rotated*
29506 * because we needed that for autoranging anyway, so now whether
29507 * we have an arrow or not, we rotate about the text center.
29508 */
29509 annTextGroup.attr({transform: 'rotate(' + textangle + ',' +
29510 annPosPx.x.text + ',' + annPosPx.y.text + ')'});
29511
29512 /*
29513 * add the arrow
29514 * uses options[arrowwidth,arrowcolor,arrowhead] for styling
29515 * dx and dy are normally zero, but when you are dragging the textbox
29516 * while the head stays put, dx and dy are the pixel offsets
29517 */
29518 var drawArrow = function(dx, dy) {
29519 annGroup
29520 .selectAll('.annotation-arrow-g')
29521 .remove();
29522
29523 var headX = annPosPx.x.head;
29524 var headY = annPosPx.y.head;
29525 var tailX = annPosPx.x.tail + dx;
29526 var tailY = annPosPx.y.tail + dy;
29527 var textX = annPosPx.x.text + dx;
29528 var textY = annPosPx.y.text + dy;
29529
29530 // find the edge of the text box, where we'll start the arrow:
29531 // create transform matrix to rotate the text box corners
29532 var transform = Lib.rotationXYMatrix(textangle, textX, textY);
29533 var applyTransform = Lib.apply2DTransform(transform);
29534 var applyTransform2 = Lib.apply2DTransform2(transform);
29535
29536 // calculate and transform bounding box
29537 var width = +annTextBG.attr('width');
29538 var height = +annTextBG.attr('height');
29539 var xLeft = textX - 0.5 * width;
29540 var xRight = xLeft + width;
29541 var yTop = textY - 0.5 * height;
29542 var yBottom = yTop + height;
29543 var edges = [
29544 [xLeft, yTop, xLeft, yBottom],
29545 [xLeft, yBottom, xRight, yBottom],
29546 [xRight, yBottom, xRight, yTop],
29547 [xRight, yTop, xLeft, yTop]
29548 ].map(applyTransform2);
29549
29550 // Remove the line if it ends inside the box. Use ray
29551 // casting for rotated boxes: see which edges intersect a
29552 // line from the arrowhead to far away and reduce with xor
29553 // to get the parity of the number of intersections.
29554 if(edges.reduce(function(a, x) {
29555 return a ^
29556 !!Lib.segmentsIntersect(headX, headY, headX + 1e6, headY + 1e6,
29557 x[0], x[1], x[2], x[3]);
29558 }, false)) {
29559 // no line or arrow - so quit drawArrow now
29560 return;
29561 }
29562
29563 edges.forEach(function(x) {
29564 var p = Lib.segmentsIntersect(tailX, tailY, headX, headY,
29565 x[0], x[1], x[2], x[3]);
29566 if(p) {
29567 tailX = p.x;
29568 tailY = p.y;
29569 }
29570 });
29571
29572 var strokewidth = options.arrowwidth;
29573 var arrowColor = options.arrowcolor;
29574 var arrowSide = options.arrowside;
29575
29576 var arrowGroup = annGroup.append('g')
29577 .style({opacity: Color.opacity(arrowColor)})
29578 .classed('annotation-arrow-g', true);
29579
29580 var arrow = arrowGroup.append('path')
29581 .attr('d', 'M' + tailX + ',' + tailY + 'L' + headX + ',' + headY)
29582 .style('stroke-width', strokewidth + 'px')
29583 .call(Color.stroke, Color.rgb(arrowColor));
29584
29585 drawArrowHead(arrow, arrowSide, options);
29586
29587 // the arrow dragger is a small square right at the head, then a line to the tail,
29588 // all expanded by a stroke width of 6px plus the arrow line width
29589 if(edits.annotationPosition && arrow.node().parentNode && !subplotId) {
29590 var arrowDragHeadX = headX;
29591 var arrowDragHeadY = headY;
29592 if(options.standoff) {
29593 var arrowLength = Math.sqrt(Math.pow(headX - tailX, 2) + Math.pow(headY - tailY, 2));
29594 arrowDragHeadX += options.standoff * (tailX - headX) / arrowLength;
29595 arrowDragHeadY += options.standoff * (tailY - headY) / arrowLength;
29596 }
29597 var arrowDrag = arrowGroup.append('path')
29598 .classed('annotation-arrow', true)
29599 .classed('anndrag', true)
29600 .classed('cursor-move', true)
29601 .attr({
29602 d: 'M3,3H-3V-3H3ZM0,0L' + (tailX - arrowDragHeadX) + ',' + (tailY - arrowDragHeadY),
29603 transform: strTranslate(arrowDragHeadX, arrowDragHeadY)
29604 })
29605 .style('stroke-width', (strokewidth + 6) + 'px')
29606 .call(Color.stroke, 'rgba(0,0,0,0)')
29607 .call(Color.fill, 'rgba(0,0,0,0)');
29608
29609 var annx0, anny0;
29610
29611 // dragger for the arrow & head: translates the whole thing
29612 // (head/tail/text) all together
29613 dragElement.init({
29614 element: arrowDrag.node(),
29615 gd: gd,
29616 prepFn: function() {
29617 var pos = Drawing.getTranslate(annTextGroupInner);
29618
29619 annx0 = pos.x;
29620 anny0 = pos.y;
29621 if(xa && xa.autorange) {
29622 modifyBase(xa._name + '.autorange', true);
29623 }
29624 if(ya && ya.autorange) {
29625 modifyBase(ya._name + '.autorange', true);
29626 }
29627 },
29628 moveFn: function(dx, dy) {
29629 var annxy0 = applyTransform(annx0, anny0);
29630 var xcenter = annxy0[0] + dx;
29631 var ycenter = annxy0[1] + dy;
29632 annTextGroupInner.call(Drawing.setTranslate, xcenter, ycenter);
29633
29634 modifyItem('x',
29635 shiftPosition(xa, dx, 'x', gs, options));
29636 modifyItem('y',
29637 shiftPosition(ya, dy, 'y', gs, options));
29638
29639 // for these 2 calls to shiftPosition, it is assumed xa, ya are
29640 // defined, so gsDim will not be used, but we put it in
29641 // anyways for consistency
29642 if(options.axref === options.xref) {
29643 modifyItem('ax', shiftPosition(xa, dx, 'ax', gs, options));
29644 }
29645
29646 if(options.ayref === options.yref) {
29647 modifyItem('ay', shiftPosition(ya, dy, 'ay', gs, options));
29648 }
29649
29650 arrowGroup.attr('transform', strTranslate(dx, dy));
29651 annTextGroup.attr({
29652 transform: 'rotate(' + textangle + ',' +
29653 xcenter + ',' + ycenter + ')'
29654 });
29655 },
29656 doneFn: function() {
29657 Registry.call('_guiRelayout', gd, getUpdateObj());
29658 var notesBox = document.querySelector('.js-notes-box-panel');
29659 if(notesBox) notesBox.redraw(notesBox.selectedObj);
29660 }
29661 });
29662 }
29663 };
29664
29665 if(options.showarrow) drawArrow(0, 0);
29666
29667 // user dragging the annotation (text, not arrow)
29668 if(editTextPosition) {
29669 var baseTextTransform;
29670
29671 // dragger for the textbox: if there's an arrow, just drag the
29672 // textbox and tail, leave the head untouched
29673 dragElement.init({
29674 element: annTextGroupInner.node(),
29675 gd: gd,
29676 prepFn: function() {
29677 baseTextTransform = annTextGroup.attr('transform');
29678 },
29679 moveFn: function(dx, dy) {
29680 var csr = 'pointer';
29681 if(options.showarrow) {
29682 // for these 2 calls to shiftPosition, it is assumed xa, ya are
29683 // defined, so gsDim will not be used, but we put it in
29684 // anyways for consistency
29685 if(options.axref === options.xref) {
29686 modifyItem('ax', shiftPosition(xa, dx, 'ax', gs, options));
29687 } else {
29688 modifyItem('ax', options.ax + dx);
29689 }
29690
29691 if(options.ayref === options.yref) {
29692 modifyItem('ay', shiftPosition(ya, dy, 'ay', gs.w, options));
29693 } else {
29694 modifyItem('ay', options.ay + dy);
29695 }
29696
29697 drawArrow(dx, dy);
29698 } else if(!subplotId) {
29699 var xUpdate, yUpdate;
29700 if(xa) {
29701 // shiftPosition will not execute code where xa was
29702 // undefined, so we use to calculate xUpdate too
29703 xUpdate = shiftPosition(xa, dx, 'x', gs, options);
29704 } else {
29705 var widthFraction = options._xsize / gs.w;
29706 var xLeft = options.x + (options._xshift - options.xshift) / gs.w - widthFraction / 2;
29707
29708 xUpdate = dragElement.align(xLeft + dx / gs.w,
29709 widthFraction, 0, 1, options.xanchor);
29710 }
29711
29712 if(ya) {
29713 // shiftPosition will not execute code where ya was
29714 // undefined, so we use to calculate yUpdate too
29715 yUpdate = shiftPosition(ya, dy, 'y', gs, options);
29716 } else {
29717 var heightFraction = options._ysize / gs.h;
29718 var yBottom = options.y - (options._yshift + options.yshift) / gs.h - heightFraction / 2;
29719
29720 yUpdate = dragElement.align(yBottom - dy / gs.h,
29721 heightFraction, 0, 1, options.yanchor);
29722 }
29723 modifyItem('x', xUpdate);
29724 modifyItem('y', yUpdate);
29725 if(!xa || !ya) {
29726 csr = dragElement.getCursor(
29727 xa ? 0.5 : xUpdate,
29728 ya ? 0.5 : yUpdate,
29729 options.xanchor, options.yanchor
29730 );
29731 }
29732 } else return;
29733
29734 annTextGroup.attr({
29735 transform: strTranslate(dx, dy) + baseTextTransform
29736 });
29737
29738 setCursor(annTextGroupInner, csr);
29739 },
29740 clickFn: function(_, initialEvent) {
29741 if(options.captureevents) {
29742 gd.emit('plotly_clickannotation', makeEventData(initialEvent));
29743 }
29744 },
29745 doneFn: function() {
29746 setCursor(annTextGroupInner);
29747 Registry.call('_guiRelayout', gd, getUpdateObj());
29748 var notesBox = document.querySelector('.js-notes-box-panel');
29749 if(notesBox) notesBox.redraw(notesBox.selectedObj);
29750 }
29751 });
29752 }
29753 }
29754
29755 if(edits.annotationText) {
29756 annText.call(svgTextUtils.makeEditable, {delegate: annTextGroupInner, gd: gd})
29757 .call(textLayout)
29758 .on('edit', function(_text) {
29759 options.text = _text;
29760
29761 this.call(textLayout);
29762
29763 modifyItem('text', _text);
29764
29765 if(xa && xa.autorange) {
29766 modifyBase(xa._name + '.autorange', true);
29767 }
29768 if(ya && ya.autorange) {
29769 modifyBase(ya._name + '.autorange', true);
29770 }
29771
29772 Registry.call('_guiRelayout', gd, getUpdateObj());
29773 });
29774 } else annText.call(textLayout);
29775}
29776
29777},{"../../lib":287,"../../lib/setcursor":307,"../../lib/svg_text_utils":310,"../../plot_api/plot_template":323,"../../plots/cartesian/axes":334,"../../plots/plots":369,"../../registry":376,"../color":157,"../dragelement":176,"../drawing":179,"../fx":197,"./draw_arrow_head":147,"@plotly/d3":20}],147:[function(_dereq_,module,exports){
29778'use strict';
29779
29780var d3 = _dereq_('@plotly/d3');
29781
29782var Color = _dereq_('../color');
29783
29784var ARROWPATHS = _dereq_('./arrow_paths');
29785
29786var Lib = _dereq_('../../lib');
29787var strScale = Lib.strScale;
29788var strRotate = Lib.strRotate;
29789var strTranslate = Lib.strTranslate;
29790
29791/**
29792 * Add arrowhead(s) to a path or line element
29793 *
29794 * @param {d3.selection} el3: a d3-selected line or path element
29795 *
29796 * @param {string} ends: 'none', 'start', 'end', or 'start+end' for which ends get arrowheads
29797 *
29798 * @param {object} options: style information. Must have all the following:
29799 * @param {number} options.arrowhead: end head style - see ./arrow_paths
29800 * @param {number} options.startarrowhead: start head style - see ./arrow_paths
29801 * @param {number} options.arrowsize: relative size of the end head vs line width
29802 * @param {number} options.startarrowsize: relative size of the start head vs line width
29803 * @param {number} options.standoff: distance in px to move the end arrow point from its target
29804 * @param {number} options.startstandoff: distance in px to move the start arrow point from its target
29805 * @param {number} options.arrowwidth: width of the arrow line
29806 * @param {string} options.arrowcolor: color of the arrow line, for the head to match
29807 * Note that the opacity of this color is ignored, as it's assumed the container
29808 * of both the line and head has opacity applied to it so there isn't greater opacity
29809 * where they overlap.
29810 */
29811module.exports = function drawArrowHead(el3, ends, options) {
29812 var el = el3.node();
29813 var headStyle = ARROWPATHS[options.arrowhead || 0];
29814 var startHeadStyle = ARROWPATHS[options.startarrowhead || 0];
29815 var scale = (options.arrowwidth || 1) * (options.arrowsize || 1);
29816 var startScale = (options.arrowwidth || 1) * (options.startarrowsize || 1);
29817 var doStart = ends.indexOf('start') >= 0;
29818 var doEnd = ends.indexOf('end') >= 0;
29819 var backOff = headStyle.backoff * scale + options.standoff;
29820 var startBackOff = startHeadStyle.backoff * startScale + options.startstandoff;
29821
29822 var start, end, startRot, endRot;
29823
29824 if(el.nodeName === 'line') {
29825 start = {x: +el3.attr('x1'), y: +el3.attr('y1')};
29826 end = {x: +el3.attr('x2'), y: +el3.attr('y2')};
29827
29828 var dx = start.x - end.x;
29829 var dy = start.y - end.y;
29830
29831 startRot = Math.atan2(dy, dx);
29832 endRot = startRot + Math.PI;
29833 if(backOff && startBackOff) {
29834 if(backOff + startBackOff > Math.sqrt(dx * dx + dy * dy)) {
29835 hideLine();
29836 return;
29837 }
29838 }
29839
29840 if(backOff) {
29841 if(backOff * backOff > dx * dx + dy * dy) {
29842 hideLine();
29843 return;
29844 }
29845 var backOffX = backOff * Math.cos(startRot);
29846 var backOffY = backOff * Math.sin(startRot);
29847
29848 end.x += backOffX;
29849 end.y += backOffY;
29850 el3.attr({x2: end.x, y2: end.y});
29851 }
29852
29853 if(startBackOff) {
29854 if(startBackOff * startBackOff > dx * dx + dy * dy) {
29855 hideLine();
29856 return;
29857 }
29858 var startBackOffX = startBackOff * Math.cos(startRot);
29859 var startbackOffY = startBackOff * Math.sin(startRot);
29860
29861 start.x -= startBackOffX;
29862 start.y -= startbackOffY;
29863 el3.attr({x1: start.x, y1: start.y});
29864 }
29865 } else if(el.nodeName === 'path') {
29866 var pathlen = el.getTotalLength();
29867 // using dash to hide the backOff region of the path.
29868 // if we ever allow dash for the arrow we'll have to
29869 // do better than this hack... maybe just manually
29870 // combine the two
29871 var dashArray = '';
29872
29873 if(pathlen < backOff + startBackOff) {
29874 hideLine();
29875 return;
29876 }
29877
29878
29879 var start0 = el.getPointAtLength(0);
29880 var dstart = el.getPointAtLength(0.1);
29881
29882 startRot = Math.atan2(start0.y - dstart.y, start0.x - dstart.x);
29883 start = el.getPointAtLength(Math.min(startBackOff, pathlen));
29884
29885 dashArray = '0px,' + startBackOff + 'px,';
29886
29887 var end0 = el.getPointAtLength(pathlen);
29888 var dend = el.getPointAtLength(pathlen - 0.1);
29889
29890 endRot = Math.atan2(end0.y - dend.y, end0.x - dend.x);
29891 end = el.getPointAtLength(Math.max(0, pathlen - backOff));
29892
29893 var shortening = dashArray ? startBackOff + backOff : backOff;
29894 dashArray += (pathlen - shortening) + 'px,' + pathlen + 'px';
29895
29896 el3.style('stroke-dasharray', dashArray);
29897 }
29898
29899 function hideLine() { el3.style('stroke-dasharray', '0px,100px'); }
29900
29901 function drawhead(arrowHeadStyle, p, rot, arrowScale) {
29902 if(!arrowHeadStyle.path) return;
29903 if(arrowHeadStyle.noRotate) rot = 0;
29904
29905 d3.select(el.parentNode).append('path')
29906 .attr({
29907 'class': el3.attr('class'),
29908 d: arrowHeadStyle.path,
29909 transform:
29910 strTranslate(p.x, p.y) +
29911 strRotate(rot * 180 / Math.PI) +
29912 strScale(arrowScale)
29913 })
29914 .style({
29915 fill: Color.rgb(options.arrowcolor),
29916 'stroke-width': 0
29917 });
29918 }
29919
29920 if(doStart) drawhead(startHeadStyle, start, startRot, startScale);
29921 if(doEnd) drawhead(headStyle, end, endRot, scale);
29922};
29923
29924},{"../../lib":287,"../color":157,"./arrow_paths":139,"@plotly/d3":20}],148:[function(_dereq_,module,exports){
29925'use strict';
29926
29927var drawModule = _dereq_('./draw');
29928var clickModule = _dereq_('./click');
29929
29930module.exports = {
29931 moduleType: 'component',
29932 name: 'annotations',
29933
29934 layoutAttributes: _dereq_('./attributes'),
29935 supplyLayoutDefaults: _dereq_('./defaults'),
29936 includeBasePlot: _dereq_('../../plots/cartesian/include_components')('annotations'),
29937
29938 calcAutorange: _dereq_('./calc_autorange'),
29939 draw: drawModule.draw,
29940 drawOne: drawModule.drawOne,
29941 drawRaw: drawModule.drawRaw,
29942
29943 hasClickToShow: clickModule.hasClickToShow,
29944 onClick: clickModule.onClick,
29945
29946 convertCoords: _dereq_('./convert_coords')
29947};
29948
29949},{"../../plots/cartesian/include_components":347,"./attributes":140,"./calc_autorange":141,"./click":142,"./convert_coords":144,"./defaults":145,"./draw":146}],149:[function(_dereq_,module,exports){
29950'use strict';
29951
29952var annAttrs = _dereq_('../annotations/attributes');
29953var overrideAll = _dereq_('../../plot_api/edit_types').overrideAll;
29954var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray;
29955
29956module.exports = overrideAll(templatedArray('annotation', {
29957 visible: annAttrs.visible,
29958 x: {
29959 valType: 'any',
29960 },
29961 y: {
29962 valType: 'any',
29963 },
29964 z: {
29965 valType: 'any',
29966 },
29967 ax: {
29968 valType: 'number',
29969 },
29970 ay: {
29971 valType: 'number',
29972 },
29973
29974 xanchor: annAttrs.xanchor,
29975 xshift: annAttrs.xshift,
29976 yanchor: annAttrs.yanchor,
29977 yshift: annAttrs.yshift,
29978
29979 text: annAttrs.text,
29980 textangle: annAttrs.textangle,
29981 font: annAttrs.font,
29982 width: annAttrs.width,
29983 height: annAttrs.height,
29984 opacity: annAttrs.opacity,
29985 align: annAttrs.align,
29986 valign: annAttrs.valign,
29987 bgcolor: annAttrs.bgcolor,
29988 bordercolor: annAttrs.bordercolor,
29989 borderpad: annAttrs.borderpad,
29990 borderwidth: annAttrs.borderwidth,
29991 showarrow: annAttrs.showarrow,
29992 arrowcolor: annAttrs.arrowcolor,
29993 arrowhead: annAttrs.arrowhead,
29994 startarrowhead: annAttrs.startarrowhead,
29995 arrowside: annAttrs.arrowside,
29996 arrowsize: annAttrs.arrowsize,
29997 startarrowsize: annAttrs.startarrowsize,
29998 arrowwidth: annAttrs.arrowwidth,
29999 standoff: annAttrs.standoff,
30000 startstandoff: annAttrs.startstandoff,
30001 hovertext: annAttrs.hovertext,
30002 hoverlabel: annAttrs.hoverlabel,
30003 captureevents: annAttrs.captureevents,
30004
30005 // maybes later?
30006 // clicktoshow: annAttrs.clicktoshow,
30007 // xclick: annAttrs.xclick,
30008 // yclick: annAttrs.yclick,
30009
30010 // not needed!
30011 // axref: 'pixel'
30012 // ayref: 'pixel'
30013 // xref: 'x'
30014 // yref: 'y
30015 // zref: 'z'
30016}), 'calc', 'from-root');
30017
30018},{"../../plot_api/edit_types":316,"../../plot_api/plot_template":323,"../annotations/attributes":140}],150:[function(_dereq_,module,exports){
30019'use strict';
30020
30021var Lib = _dereq_('../../lib');
30022var Axes = _dereq_('../../plots/cartesian/axes');
30023
30024module.exports = function convert(scene) {
30025 var fullSceneLayout = scene.fullSceneLayout;
30026 var anns = fullSceneLayout.annotations;
30027
30028 for(var i = 0; i < anns.length; i++) {
30029 mockAnnAxes(anns[i], scene);
30030 }
30031
30032 scene.fullLayout._infolayer
30033 .selectAll('.annotation-' + scene.id)
30034 .remove();
30035};
30036
30037function mockAnnAxes(ann, scene) {
30038 var fullSceneLayout = scene.fullSceneLayout;
30039 var domain = fullSceneLayout.domain;
30040 var size = scene.fullLayout._size;
30041
30042 var base = {
30043 // this gets fill in on render
30044 pdata: null,
30045
30046 // to get setConvert to not execute cleanly
30047 type: 'linear',
30048
30049 // don't try to update them on `editable: true`
30050 autorange: false,
30051
30052 // set infinite range so that annotation draw routine
30053 // does not try to remove 'outside-range' annotations,
30054 // this case is handled in the render loop
30055 range: [-Infinity, Infinity]
30056 };
30057
30058 ann._xa = {};
30059 Lib.extendFlat(ann._xa, base);
30060 Axes.setConvert(ann._xa);
30061 ann._xa._offset = size.l + domain.x[0] * size.w;
30062 ann._xa.l2p = function() {
30063 return 0.5 * (1 + ann._pdata[0] / ann._pdata[3]) * size.w * (domain.x[1] - domain.x[0]);
30064 };
30065
30066 ann._ya = {};
30067 Lib.extendFlat(ann._ya, base);
30068 Axes.setConvert(ann._ya);
30069 ann._ya._offset = size.t + (1 - domain.y[1]) * size.h;
30070 ann._ya.l2p = function() {
30071 return 0.5 * (1 - ann._pdata[1] / ann._pdata[3]) * size.h * (domain.y[1] - domain.y[0]);
30072 };
30073}
30074
30075},{"../../lib":287,"../../plots/cartesian/axes":334}],151:[function(_dereq_,module,exports){
30076'use strict';
30077
30078var Lib = _dereq_('../../lib');
30079var Axes = _dereq_('../../plots/cartesian/axes');
30080var handleArrayContainerDefaults = _dereq_('../../plots/array_container_defaults');
30081var handleAnnotationCommonDefaults = _dereq_('../annotations/common_defaults');
30082var attributes = _dereq_('./attributes');
30083
30084module.exports = function handleDefaults(sceneLayoutIn, sceneLayoutOut, opts) {
30085 handleArrayContainerDefaults(sceneLayoutIn, sceneLayoutOut, {
30086 name: 'annotations',
30087 handleItemDefaults: handleAnnotationDefaults,
30088 fullLayout: opts.fullLayout
30089 });
30090};
30091
30092function handleAnnotationDefaults(annIn, annOut, sceneLayout, opts) {
30093 function coerce(attr, dflt) {
30094 return Lib.coerce(annIn, annOut, attributes, attr, dflt);
30095 }
30096
30097 function coercePosition(axLetter) {
30098 var axName = axLetter + 'axis';
30099
30100 // mock in such way that getFromId grabs correct 3D axis
30101 var gdMock = { _fullLayout: {} };
30102 gdMock._fullLayout[axName] = sceneLayout[axName];
30103
30104 return Axes.coercePosition(annOut, gdMock, coerce, axLetter, axLetter, 0.5);
30105 }
30106
30107
30108 var visible = coerce('visible');
30109 if(!visible) return;
30110
30111 handleAnnotationCommonDefaults(annIn, annOut, opts.fullLayout, coerce);
30112
30113 coercePosition('x');
30114 coercePosition('y');
30115 coercePosition('z');
30116
30117 // if you have one coordinate you should all three
30118 Lib.noneOrAll(annIn, annOut, ['x', 'y', 'z']);
30119
30120 // hard-set here for completeness
30121 annOut.xref = 'x';
30122 annOut.yref = 'y';
30123 annOut.zref = 'z';
30124
30125 coerce('xanchor');
30126 coerce('yanchor');
30127 coerce('xshift');
30128 coerce('yshift');
30129
30130 if(annOut.showarrow) {
30131 annOut.axref = 'pixel';
30132 annOut.ayref = 'pixel';
30133
30134 // TODO maybe default values should be bigger than the 2D case?
30135 coerce('ax', -10);
30136 coerce('ay', -30);
30137
30138 // if you have one part of arrow length you should have both
30139 Lib.noneOrAll(annIn, annOut, ['ax', 'ay']);
30140 }
30141}
30142
30143},{"../../lib":287,"../../plots/array_container_defaults":329,"../../plots/cartesian/axes":334,"../annotations/common_defaults":143,"./attributes":149}],152:[function(_dereq_,module,exports){
30144'use strict';
30145
30146var drawRaw = _dereq_('../annotations/draw').drawRaw;
30147var project = _dereq_('../../plots/gl3d/project');
30148var axLetters = ['x', 'y', 'z'];
30149
30150module.exports = function draw(scene) {
30151 var fullSceneLayout = scene.fullSceneLayout;
30152 var dataScale = scene.dataScale;
30153 var anns = fullSceneLayout.annotations;
30154
30155 for(var i = 0; i < anns.length; i++) {
30156 var ann = anns[i];
30157 var annotationIsOffscreen = false;
30158
30159 for(var j = 0; j < 3; j++) {
30160 var axLetter = axLetters[j];
30161 var pos = ann[axLetter];
30162 var ax = fullSceneLayout[axLetter + 'axis'];
30163 var posFraction = ax.r2fraction(pos);
30164
30165 if(posFraction < 0 || posFraction > 1) {
30166 annotationIsOffscreen = true;
30167 break;
30168 }
30169 }
30170
30171 if(annotationIsOffscreen) {
30172 scene.fullLayout._infolayer
30173 .select('.annotation-' + scene.id + '[data-index="' + i + '"]')
30174 .remove();
30175 } else {
30176 ann._pdata = project(scene.glplot.cameraParams, [
30177 fullSceneLayout.xaxis.r2l(ann.x) * dataScale[0],
30178 fullSceneLayout.yaxis.r2l(ann.y) * dataScale[1],
30179 fullSceneLayout.zaxis.r2l(ann.z) * dataScale[2]
30180 ]);
30181
30182 drawRaw(scene.graphDiv, ann, i, scene.id, ann._xa, ann._ya);
30183 }
30184 }
30185};
30186
30187},{"../../plots/gl3d/project":366,"../annotations/draw":146}],153:[function(_dereq_,module,exports){
30188'use strict';
30189
30190var Registry = _dereq_('../../registry');
30191var Lib = _dereq_('../../lib');
30192
30193module.exports = {
30194 moduleType: 'component',
30195 name: 'annotations3d',
30196
30197 schema: {
30198 subplots: {
30199 scene: {annotations: _dereq_('./attributes')}
30200 }
30201 },
30202
30203 layoutAttributes: _dereq_('./attributes'),
30204 handleDefaults: _dereq_('./defaults'),
30205 includeBasePlot: includeGL3D,
30206
30207 convert: _dereq_('./convert'),
30208 draw: _dereq_('./draw')
30209};
30210
30211function includeGL3D(layoutIn, layoutOut) {
30212 var GL3D = Registry.subplotsRegistry.gl3d;
30213 if(!GL3D) return;
30214
30215 var attrRegex = GL3D.attrRegex;
30216
30217 var keys = Object.keys(layoutIn);
30218 for(var i = 0; i < keys.length; i++) {
30219 var k = keys[i];
30220 if(attrRegex.test(k) && (layoutIn[k].annotations || []).length) {
30221 Lib.pushUnique(layoutOut._basePlotModules, GL3D);
30222 Lib.pushUnique(layoutOut._subplots.gl3d, k);
30223 }
30224 }
30225}
30226
30227},{"../../lib":287,"../../registry":376,"./attributes":149,"./convert":150,"./defaults":151,"./draw":152}],154:[function(_dereq_,module,exports){
30228'use strict';
30229
30230// a trimmed down version of:
30231// https://github.com/alexcjohnson/world-calendars/blob/master/dist/index.js
30232
30233module.exports = _dereq_('world-calendars/dist/main');
30234
30235_dereq_('world-calendars/dist/plus');
30236
30237_dereq_('world-calendars/dist/calendars/chinese');
30238_dereq_('world-calendars/dist/calendars/coptic');
30239_dereq_('world-calendars/dist/calendars/discworld');
30240_dereq_('world-calendars/dist/calendars/ethiopian');
30241_dereq_('world-calendars/dist/calendars/hebrew');
30242_dereq_('world-calendars/dist/calendars/islamic');
30243_dereq_('world-calendars/dist/calendars/julian');
30244_dereq_('world-calendars/dist/calendars/mayan');
30245_dereq_('world-calendars/dist/calendars/nanakshahi');
30246_dereq_('world-calendars/dist/calendars/nepali');
30247_dereq_('world-calendars/dist/calendars/persian');
30248_dereq_('world-calendars/dist/calendars/taiwan');
30249_dereq_('world-calendars/dist/calendars/thai');
30250_dereq_('world-calendars/dist/calendars/ummalqura');
30251
30252},{"world-calendars/dist/calendars/chinese":123,"world-calendars/dist/calendars/coptic":124,"world-calendars/dist/calendars/discworld":125,"world-calendars/dist/calendars/ethiopian":126,"world-calendars/dist/calendars/hebrew":127,"world-calendars/dist/calendars/islamic":128,"world-calendars/dist/calendars/julian":129,"world-calendars/dist/calendars/mayan":130,"world-calendars/dist/calendars/nanakshahi":131,"world-calendars/dist/calendars/nepali":132,"world-calendars/dist/calendars/persian":133,"world-calendars/dist/calendars/taiwan":134,"world-calendars/dist/calendars/thai":135,"world-calendars/dist/calendars/ummalqura":136,"world-calendars/dist/main":137,"world-calendars/dist/plus":138}],155:[function(_dereq_,module,exports){
30253'use strict';
30254
30255var calendars = _dereq_('./calendars');
30256
30257var Lib = _dereq_('../../lib');
30258var constants = _dereq_('../../constants/numerical');
30259
30260var EPOCHJD = constants.EPOCHJD;
30261var ONEDAY = constants.ONEDAY;
30262
30263var attributes = {
30264 valType: 'enumerated',
30265 values: Lib.sortObjectKeys(calendars.calendars),
30266 editType: 'calc',
30267 dflt: 'gregorian'
30268};
30269
30270var handleDefaults = function(contIn, contOut, attr, dflt) {
30271 var attrs = {};
30272 attrs[attr] = attributes;
30273
30274 return Lib.coerce(contIn, contOut, attrs, attr, dflt);
30275};
30276
30277var handleTraceDefaults = function(traceIn, traceOut, coords, layout) {
30278 for(var i = 0; i < coords.length; i++) {
30279 handleDefaults(traceIn, traceOut, coords[i] + 'calendar', layout.calendar);
30280 }
30281};
30282
30283// each calendar needs its own default canonical tick. I would love to use
30284// 2000-01-01 (or even 0000-01-01) for them all but they don't necessarily
30285// all support either of those dates. Instead I'll use the most significant
30286// number they *do* support, biased toward the present day.
30287var CANONICAL_TICK = {
30288 chinese: '2000-01-01',
30289 coptic: '2000-01-01',
30290 discworld: '2000-01-01',
30291 ethiopian: '2000-01-01',
30292 hebrew: '5000-01-01',
30293 islamic: '1000-01-01',
30294 julian: '2000-01-01',
30295 mayan: '5000-01-01',
30296 nanakshahi: '1000-01-01',
30297 nepali: '2000-01-01',
30298 persian: '1000-01-01',
30299 jalali: '1000-01-01',
30300 taiwan: '1000-01-01',
30301 thai: '2000-01-01',
30302 ummalqura: '1400-01-01'
30303};
30304
30305// Start on a Sunday - for week ticks
30306// Discworld and Mayan calendars don't have 7-day weeks but we're going to give them
30307// 7-day week ticks so start on our Sundays.
30308// If anyone really cares we can customize the auto tick spacings for these calendars.
30309var CANONICAL_SUNDAY = {
30310 chinese: '2000-01-02',
30311 coptic: '2000-01-03',
30312 discworld: '2000-01-03',
30313 ethiopian: '2000-01-05',
30314 hebrew: '5000-01-01',
30315 islamic: '1000-01-02',
30316 julian: '2000-01-03',
30317 mayan: '5000-01-01',
30318 nanakshahi: '1000-01-05',
30319 nepali: '2000-01-05',
30320 persian: '1000-01-01',
30321 jalali: '1000-01-01',
30322 taiwan: '1000-01-04',
30323 thai: '2000-01-04',
30324 ummalqura: '1400-01-06'
30325};
30326
30327var DFLTRANGE = {
30328 chinese: ['2000-01-01', '2001-01-01'],
30329 coptic: ['1700-01-01', '1701-01-01'],
30330 discworld: ['1800-01-01', '1801-01-01'],
30331 ethiopian: ['2000-01-01', '2001-01-01'],
30332 hebrew: ['5700-01-01', '5701-01-01'],
30333 islamic: ['1400-01-01', '1401-01-01'],
30334 julian: ['2000-01-01', '2001-01-01'],
30335 mayan: ['5200-01-01', '5201-01-01'],
30336 nanakshahi: ['0500-01-01', '0501-01-01'],
30337 nepali: ['2000-01-01', '2001-01-01'],
30338 persian: ['1400-01-01', '1401-01-01'],
30339 jalali: ['1400-01-01', '1401-01-01'],
30340 taiwan: ['0100-01-01', '0101-01-01'],
30341 thai: ['2500-01-01', '2501-01-01'],
30342 ummalqura: ['1400-01-01', '1401-01-01']
30343};
30344
30345/*
30346 * convert d3 templates to world-calendars templates, so our users only need
30347 * to know d3's specifiers. Map space padding to no padding, and unknown fields
30348 * to an ugly placeholder
30349 */
30350var UNKNOWN = '##';
30351var d3ToWorldCalendars = {
30352 'd': {'0': 'dd', '-': 'd'}, // 2-digit or unpadded day of month
30353 'e': {'0': 'd', '-': 'd'}, // alternate, always unpadded day of month
30354 'a': {'0': 'D', '-': 'D'}, // short weekday name
30355 'A': {'0': 'DD', '-': 'DD'}, // full weekday name
30356 'j': {'0': 'oo', '-': 'o'}, // 3-digit or unpadded day of the year
30357 'W': {'0': 'ww', '-': 'w'}, // 2-digit or unpadded week of the year (Monday first)
30358 'm': {'0': 'mm', '-': 'm'}, // 2-digit or unpadded month number
30359 'b': {'0': 'M', '-': 'M'}, // short month name
30360 'B': {'0': 'MM', '-': 'MM'}, // full month name
30361 'y': {'0': 'yy', '-': 'yy'}, // 2-digit year (map unpadded to zero-padded)
30362 'Y': {'0': 'yyyy', '-': 'yyyy'}, // 4-digit year (map unpadded to zero-padded)
30363 'U': UNKNOWN, // Sunday-first week of the year
30364 'w': UNKNOWN, // day of the week [0(sunday),6]
30365 // combined format, we replace the date part with the world-calendar version
30366 // and the %X stays there for d3 to handle with time parts
30367 'c': {'0': 'D M d %X yyyy', '-': 'D M d %X yyyy'},
30368 'x': {'0': 'mm/dd/yyyy', '-': 'mm/dd/yyyy'}
30369};
30370
30371function worldCalFmt(fmt, x, calendar) {
30372 var dateJD = Math.floor((x + 0.05) / ONEDAY) + EPOCHJD;
30373 var cDate = getCal(calendar).fromJD(dateJD);
30374 var i = 0;
30375 var modifier, directive, directiveLen, directiveObj, replacementPart;
30376
30377 while((i = fmt.indexOf('%', i)) !== -1) {
30378 modifier = fmt.charAt(i + 1);
30379 if(modifier === '0' || modifier === '-' || modifier === '_') {
30380 directiveLen = 3;
30381 directive = fmt.charAt(i + 2);
30382 if(modifier === '_') modifier = '-';
30383 } else {
30384 directive = modifier;
30385 modifier = '0';
30386 directiveLen = 2;
30387 }
30388 directiveObj = d3ToWorldCalendars[directive];
30389 if(!directiveObj) {
30390 i += directiveLen;
30391 } else {
30392 // code is recognized as a date part but world-calendars doesn't support it
30393 if(directiveObj === UNKNOWN) replacementPart = UNKNOWN;
30394
30395 // format the cDate according to the translated directive
30396 else replacementPart = cDate.formatDate(directiveObj[modifier]);
30397
30398 fmt = fmt.substr(0, i) + replacementPart + fmt.substr(i + directiveLen);
30399 i += replacementPart.length;
30400 }
30401 }
30402 return fmt;
30403}
30404
30405// cache world calendars, so we don't have to reinstantiate
30406// during each date-time conversion
30407var allCals = {};
30408function getCal(calendar) {
30409 var calendarObj = allCals[calendar];
30410 if(calendarObj) return calendarObj;
30411
30412 calendarObj = allCals[calendar] = calendars.instance(calendar);
30413 return calendarObj;
30414}
30415
30416function makeAttrs(description) {
30417 return Lib.extendFlat({}, attributes, { description: description });
30418}
30419
30420function makeTraceAttrsDescription(coord) {
30421 return 'Sets the calendar system to use with `' + coord + '` date data.';
30422}
30423
30424var xAttrs = {
30425 xcalendar: makeAttrs(makeTraceAttrsDescription('x'))
30426};
30427
30428var xyAttrs = Lib.extendFlat({}, xAttrs, {
30429 ycalendar: makeAttrs(makeTraceAttrsDescription('y'))
30430});
30431
30432var xyzAttrs = Lib.extendFlat({}, xyAttrs, {
30433 zcalendar: makeAttrs(makeTraceAttrsDescription('z'))
30434});
30435
30436var axisAttrs = makeAttrs([
30437 'Sets the calendar system to use for `range` and `tick0`',
30438 'if this is a date axis. This does not set the calendar for',
30439 'interpreting data on this axis, that\'s specified in the trace',
30440 'or via the global `layout.calendar`'
30441].join(' '));
30442
30443module.exports = {
30444 moduleType: 'component',
30445 name: 'calendars',
30446
30447 schema: {
30448 traces: {
30449 scatter: xyAttrs,
30450 bar: xyAttrs,
30451 box: xyAttrs,
30452 heatmap: xyAttrs,
30453 contour: xyAttrs,
30454 histogram: xyAttrs,
30455 histogram2d: xyAttrs,
30456 histogram2dcontour: xyAttrs,
30457 scatter3d: xyzAttrs,
30458 surface: xyzAttrs,
30459 mesh3d: xyzAttrs,
30460 scattergl: xyAttrs,
30461 ohlc: xAttrs,
30462 candlestick: xAttrs
30463 },
30464 layout: {
30465 calendar: makeAttrs([
30466 'Sets the default calendar system to use for interpreting and',
30467 'displaying dates throughout the plot.'
30468 ].join(' '))
30469 },
30470 subplots: {
30471 xaxis: {calendar: axisAttrs},
30472 yaxis: {calendar: axisAttrs},
30473 scene: {
30474 xaxis: {calendar: axisAttrs},
30475 // TODO: it's actually redundant to include yaxis and zaxis here
30476 // because in the scene attributes these are the same object so merging
30477 // into one merges into them all. However, I left them in for parity with
30478 // cartesian, where yaxis is unused until we Plotschema.get() when we
30479 // use its presence or absence to determine whether to delete attributes
30480 // from yaxis if they only apply to x (rangeselector/rangeslider)
30481 yaxis: {calendar: axisAttrs},
30482 zaxis: {calendar: axisAttrs}
30483 },
30484 polar: {
30485 radialaxis: {calendar: axisAttrs}
30486 }
30487 },
30488 transforms: {
30489 filter: {
30490 valuecalendar: makeAttrs([
30491 'WARNING: All transforms are deprecated and may be removed from the API in next major version.',
30492 'Sets the calendar system to use for `value`, if it is a date.'
30493 ].join(' ')),
30494 targetcalendar: makeAttrs([
30495 'WARNING: All transforms are deprecated and may be removed from the API in next major version.',
30496 'Sets the calendar system to use for `target`, if it is an',
30497 'array of dates. If `target` is a string (eg *x*) we use the',
30498 'corresponding trace attribute (eg `xcalendar`) if it exists,',
30499 'even if `targetcalendar` is provided.'
30500 ].join(' '))
30501 }
30502 }
30503 },
30504
30505 layoutAttributes: attributes,
30506
30507 handleDefaults: handleDefaults,
30508 handleTraceDefaults: handleTraceDefaults,
30509
30510 CANONICAL_SUNDAY: CANONICAL_SUNDAY,
30511 CANONICAL_TICK: CANONICAL_TICK,
30512 DFLTRANGE: DFLTRANGE,
30513
30514 getCal: getCal,
30515 worldCalFmt: worldCalFmt
30516};
30517
30518},{"../../constants/numerical":267,"../../lib":287,"./calendars":154}],156:[function(_dereq_,module,exports){
30519'use strict';
30520
30521
30522// IMPORTANT - default colors should be in hex for compatibility
30523exports.defaults = [
30524 '#1f77b4', // muted blue
30525 '#ff7f0e', // safety orange
30526 '#2ca02c', // cooked asparagus green
30527 '#d62728', // brick red
30528 '#9467bd', // muted purple
30529 '#8c564b', // chestnut brown
30530 '#e377c2', // raspberry yogurt pink
30531 '#7f7f7f', // middle gray
30532 '#bcbd22', // curry yellow-green
30533 '#17becf' // blue-teal
30534];
30535
30536exports.defaultLine = '#444';
30537
30538exports.lightLine = '#eee';
30539
30540exports.background = '#fff';
30541
30542exports.borderLine = '#BEC8D9';
30543
30544// with axis.color and Color.interp we aren't using lightLine
30545// itself anymore, instead interpolating between axis.color
30546// and the background color using tinycolor.mix. lightFraction
30547// gives back exactly lightLine if the other colors are defaults.
30548exports.lightFraction = 100 * (0xe - 0x4) / (0xf - 0x4);
30549
30550},{}],157:[function(_dereq_,module,exports){
30551'use strict';
30552
30553var tinycolor = _dereq_('tinycolor2');
30554var isNumeric = _dereq_('fast-isnumeric');
30555var isTypedArray = _dereq_('../../lib/array').isTypedArray;
30556
30557var color = module.exports = {};
30558
30559var colorAttrs = _dereq_('./attributes');
30560color.defaults = colorAttrs.defaults;
30561var defaultLine = color.defaultLine = colorAttrs.defaultLine;
30562color.lightLine = colorAttrs.lightLine;
30563var background = color.background = colorAttrs.background;
30564
30565/*
30566 * tinyRGB: turn a tinycolor into an rgb string, but
30567 * unlike the built-in tinycolor.toRgbString this never includes alpha
30568 */
30569color.tinyRGB = function(tc) {
30570 var c = tc.toRgb();
30571 return 'rgb(' + Math.round(c.r) + ', ' +
30572 Math.round(c.g) + ', ' + Math.round(c.b) + ')';
30573};
30574
30575color.rgb = function(cstr) { return color.tinyRGB(tinycolor(cstr)); };
30576
30577color.opacity = function(cstr) { return cstr ? tinycolor(cstr).getAlpha() : 0; };
30578
30579color.addOpacity = function(cstr, op) {
30580 var c = tinycolor(cstr).toRgb();
30581 return 'rgba(' + Math.round(c.r) + ', ' +
30582 Math.round(c.g) + ', ' + Math.round(c.b) + ', ' + op + ')';
30583};
30584
30585// combine two colors into one apparent color
30586// if back has transparency or is missing,
30587// color.background is assumed behind it
30588color.combine = function(front, back) {
30589 var fc = tinycolor(front).toRgb();
30590 if(fc.a === 1) return tinycolor(front).toRgbString();
30591
30592 var bc = tinycolor(back || background).toRgb();
30593 var bcflat = bc.a === 1 ? bc : {
30594 r: 255 * (1 - bc.a) + bc.r * bc.a,
30595 g: 255 * (1 - bc.a) + bc.g * bc.a,
30596 b: 255 * (1 - bc.a) + bc.b * bc.a
30597 };
30598 var fcflat = {
30599 r: bcflat.r * (1 - fc.a) + fc.r * fc.a,
30600 g: bcflat.g * (1 - fc.a) + fc.g * fc.a,
30601 b: bcflat.b * (1 - fc.a) + fc.b * fc.a
30602 };
30603 return tinycolor(fcflat).toRgbString();
30604};
30605
30606/*
30607 * Create a color that contrasts with cstr.
30608 *
30609 * If cstr is a dark color, we lighten it; if it's light, we darken.
30610 *
30611 * If lightAmount / darkAmount are used, we adjust by these percentages,
30612 * otherwise we go all the way to white or black.
30613 */
30614color.contrast = function(cstr, lightAmount, darkAmount) {
30615 var tc = tinycolor(cstr);
30616
30617 if(tc.getAlpha() !== 1) tc = tinycolor(color.combine(cstr, background));
30618
30619 var newColor = tc.isDark() ?
30620 (lightAmount ? tc.lighten(lightAmount) : background) :
30621 (darkAmount ? tc.darken(darkAmount) : defaultLine);
30622
30623 return newColor.toString();
30624};
30625
30626color.stroke = function(s, c) {
30627 var tc = tinycolor(c);
30628 s.style({'stroke': color.tinyRGB(tc), 'stroke-opacity': tc.getAlpha()});
30629};
30630
30631color.fill = function(s, c) {
30632 var tc = tinycolor(c);
30633 s.style({
30634 'fill': color.tinyRGB(tc),
30635 'fill-opacity': tc.getAlpha()
30636 });
30637};
30638
30639// search container for colors with the deprecated rgb(fractions) format
30640// and convert them to rgb(0-255 values)
30641color.clean = function(container) {
30642 if(!container || typeof container !== 'object') return;
30643
30644 var keys = Object.keys(container);
30645 var i, j, key, val;
30646
30647 for(i = 0; i < keys.length; i++) {
30648 key = keys[i];
30649 val = container[key];
30650
30651 if(key.substr(key.length - 5) === 'color') {
30652 // only sanitize keys that end in "color" or "colorscale"
30653
30654 if(Array.isArray(val)) {
30655 for(j = 0; j < val.length; j++) val[j] = cleanOne(val[j]);
30656 } else container[key] = cleanOne(val);
30657 } else if(key.substr(key.length - 10) === 'colorscale' && Array.isArray(val)) {
30658 // colorscales have the format [[0, color1], [frac, color2], ... [1, colorN]]
30659
30660 for(j = 0; j < val.length; j++) {
30661 if(Array.isArray(val[j])) val[j][1] = cleanOne(val[j][1]);
30662 }
30663 } else if(Array.isArray(val)) {
30664 // recurse into arrays of objects, and plain objects
30665
30666 var el0 = val[0];
30667 if(!Array.isArray(el0) && el0 && typeof el0 === 'object') {
30668 for(j = 0; j < val.length; j++) color.clean(val[j]);
30669 }
30670 } else if(val && typeof val === 'object' && !isTypedArray(val)) color.clean(val);
30671 }
30672};
30673
30674function cleanOne(val) {
30675 if(isNumeric(val) || typeof val !== 'string') return val;
30676
30677 var valTrim = val.trim();
30678 if(valTrim.substr(0, 3) !== 'rgb') return val;
30679
30680 var match = valTrim.match(/^rgba?\s*\(([^()]*)\)$/);
30681 if(!match) return val;
30682
30683 var parts = match[1].trim().split(/\s*[\s,]\s*/);
30684 var rgba = valTrim.charAt(3) === 'a' && parts.length === 4;
30685 if(!rgba && parts.length !== 3) return val;
30686
30687 for(var i = 0; i < parts.length; i++) {
30688 if(!parts[i].length) return val;
30689 parts[i] = Number(parts[i]);
30690
30691 if(!(parts[i] >= 0)) {
30692 // all parts must be non-negative numbers
30693
30694 return val;
30695 }
30696
30697 if(i === 3) {
30698 // alpha>1 gets clipped to 1
30699
30700 if(parts[i] > 1) parts[i] = 1;
30701 } else if(parts[i] >= 1) {
30702 // r, g, b must be < 1 (ie 1 itself is not allowed)
30703
30704 return val;
30705 }
30706 }
30707
30708 var rgbStr = Math.round(parts[0] * 255) + ', ' +
30709 Math.round(parts[1] * 255) + ', ' +
30710 Math.round(parts[2] * 255);
30711
30712 if(rgba) return 'rgba(' + rgbStr + ', ' + parts[3] + ')';
30713 return 'rgb(' + rgbStr + ')';
30714}
30715
30716},{"../../lib/array":273,"./attributes":156,"fast-isnumeric":33,"tinycolor2":121}],158:[function(_dereq_,module,exports){
30717'use strict';
30718
30719var axesAttrs = _dereq_('../../plots/cartesian/layout_attributes');
30720var fontAttrs = _dereq_('../../plots/font_attributes');
30721var extendFlat = _dereq_('../../lib/extend').extendFlat;
30722var overrideAll = _dereq_('../../plot_api/edit_types').overrideAll;
30723
30724
30725module.exports = overrideAll({
30726// TODO: only right is supported currently
30727// orient: {
30728// valType: 'enumerated',
30729// values: ['left', 'right', 'top', 'bottom'],
30730// dflt: 'right',
30731//
30732// },
30733 thicknessmode: {
30734 valType: 'enumerated',
30735 values: ['fraction', 'pixels'],
30736 dflt: 'pixels',
30737 },
30738 thickness: {
30739 valType: 'number',
30740 min: 0,
30741 dflt: 30,
30742 },
30743 lenmode: {
30744 valType: 'enumerated',
30745 values: ['fraction', 'pixels'],
30746 dflt: 'fraction',
30747 },
30748 len: {
30749 valType: 'number',
30750 min: 0,
30751 dflt: 1,
30752 },
30753 x: {
30754 valType: 'number',
30755 dflt: 1.02,
30756 min: -2,
30757 max: 3,
30758 },
30759 xanchor: {
30760 valType: 'enumerated',
30761 values: ['left', 'center', 'right'],
30762 dflt: 'left',
30763 },
30764 xpad: {
30765 valType: 'number',
30766 min: 0,
30767 dflt: 10,
30768 },
30769 y: {
30770 valType: 'number',
30771 dflt: 0.5,
30772 min: -2,
30773 max: 3,
30774 },
30775 yanchor: {
30776 valType: 'enumerated',
30777 values: ['top', 'middle', 'bottom'],
30778 dflt: 'middle',
30779 },
30780 ypad: {
30781 valType: 'number',
30782 min: 0,
30783 dflt: 10,
30784 },
30785 // a possible line around the bar itself
30786 outlinecolor: axesAttrs.linecolor,
30787 outlinewidth: axesAttrs.linewidth,
30788 // Should outlinewidth have {dflt: 0} ?
30789 // another possible line outside the padding and tick labels
30790 bordercolor: axesAttrs.linecolor,
30791 borderwidth: {
30792 valType: 'number',
30793 min: 0,
30794 dflt: 0,
30795 },
30796 bgcolor: {
30797 valType: 'color',
30798 dflt: 'rgba(0,0,0,0)',
30799 },
30800 // tick and title properties named and function exactly as in axes
30801 tickmode: axesAttrs.tickmode,
30802 nticks: axesAttrs.nticks,
30803 tick0: axesAttrs.tick0,
30804 dtick: axesAttrs.dtick,
30805 tickvals: axesAttrs.tickvals,
30806 ticktext: axesAttrs.ticktext,
30807 ticks: extendFlat({}, axesAttrs.ticks, {dflt: ''}),
30808 ticklabeloverflow: extendFlat({}, axesAttrs.ticklabeloverflow, {
30809 }),
30810 ticklabelposition: {
30811 valType: 'enumerated',
30812 values: [
30813 'outside', 'inside',
30814 'outside top', 'inside top',
30815 'outside bottom', 'inside bottom'
30816 ],
30817 dflt: 'outside',
30818 },
30819 ticklen: axesAttrs.ticklen,
30820 tickwidth: axesAttrs.tickwidth,
30821 tickcolor: axesAttrs.tickcolor,
30822 showticklabels: axesAttrs.showticklabels,
30823 tickfont: fontAttrs({
30824 }),
30825 tickangle: axesAttrs.tickangle,
30826 tickformat: axesAttrs.tickformat,
30827 tickformatstops: axesAttrs.tickformatstops,
30828 tickprefix: axesAttrs.tickprefix,
30829 showtickprefix: axesAttrs.showtickprefix,
30830 ticksuffix: axesAttrs.ticksuffix,
30831 showticksuffix: axesAttrs.showticksuffix,
30832 separatethousands: axesAttrs.separatethousands,
30833 exponentformat: axesAttrs.exponentformat,
30834 minexponent: axesAttrs.minexponent,
30835 showexponent: axesAttrs.showexponent,
30836 title: {
30837 text: {
30838 valType: 'string',
30839 },
30840 font: fontAttrs({
30841 }),
30842 side: {
30843 valType: 'enumerated',
30844 values: ['right', 'top', 'bottom'],
30845 dflt: 'top',
30846 }
30847 },
30848
30849 _deprecated: {
30850 title: {
30851 valType: 'string',
30852 },
30853 titlefont: fontAttrs({
30854 }),
30855 titleside: {
30856 valType: 'enumerated',
30857 values: ['right', 'top', 'bottom'],
30858 dflt: 'top',
30859 }
30860 }
30861}, 'colorbars', 'from-root');
30862
30863},{"../../lib/extend":281,"../../plot_api/edit_types":316,"../../plots/cartesian/layout_attributes":349,"../../plots/font_attributes":363}],159:[function(_dereq_,module,exports){
30864'use strict';
30865
30866module.exports = {
30867 cn: {
30868 colorbar: 'colorbar',
30869 cbbg: 'cbbg',
30870 cbfill: 'cbfill',
30871 cbfills: 'cbfills',
30872 cbline: 'cbline',
30873 cblines: 'cblines',
30874 cbaxis: 'cbaxis',
30875 cbtitleunshift: 'cbtitleunshift',
30876 cbtitle: 'cbtitle',
30877 cboutline: 'cboutline',
30878 crisp: 'crisp',
30879 jsPlaceholder: 'js-placeholder'
30880 }
30881};
30882
30883},{}],160:[function(_dereq_,module,exports){
30884'use strict';
30885
30886var Lib = _dereq_('../../lib');
30887var Template = _dereq_('../../plot_api/plot_template');
30888
30889var handleTickValueDefaults = _dereq_('../../plots/cartesian/tick_value_defaults');
30890var handleTickMarkDefaults = _dereq_('../../plots/cartesian/tick_mark_defaults');
30891var handleTickLabelDefaults = _dereq_('../../plots/cartesian/tick_label_defaults');
30892
30893var attributes = _dereq_('./attributes');
30894
30895module.exports = function colorbarDefaults(containerIn, containerOut, layout) {
30896 var colorbarOut = Template.newContainer(containerOut, 'colorbar');
30897 var colorbarIn = containerIn.colorbar || {};
30898
30899 function coerce(attr, dflt) {
30900 return Lib.coerce(colorbarIn, colorbarOut, attributes, attr, dflt);
30901 }
30902
30903 var thicknessmode = coerce('thicknessmode');
30904 coerce('thickness', (thicknessmode === 'fraction') ?
30905 30 / (layout.width - layout.margin.l - layout.margin.r) :
30906 30
30907 );
30908
30909 var lenmode = coerce('lenmode');
30910 coerce('len', (lenmode === 'fraction') ?
30911 1 :
30912 layout.height - layout.margin.t - layout.margin.b
30913 );
30914
30915 coerce('x');
30916 coerce('xanchor');
30917 coerce('xpad');
30918 coerce('y');
30919 coerce('yanchor');
30920 coerce('ypad');
30921 Lib.noneOrAll(colorbarIn, colorbarOut, ['x', 'y']);
30922
30923 coerce('outlinecolor');
30924 coerce('outlinewidth');
30925 coerce('bordercolor');
30926 coerce('borderwidth');
30927 coerce('bgcolor');
30928
30929 var ticklabelposition = coerce('ticklabelposition');
30930 coerce('ticklabeloverflow', ticklabelposition.indexOf('inside') !== -1 ? 'hide past domain' : 'hide past div');
30931
30932 handleTickValueDefaults(colorbarIn, colorbarOut, coerce, 'linear');
30933
30934 var font = layout.font;
30935 var opts = {outerTicks: false, font: font};
30936 if(ticklabelposition.indexOf('inside') !== -1) {
30937 opts.bgColor = 'black'; // could we instead use the average of colors in the scale?
30938 }
30939 handleTickLabelDefaults(colorbarIn, colorbarOut, coerce, 'linear', opts);
30940 handleTickMarkDefaults(colorbarIn, colorbarOut, coerce, 'linear', opts);
30941
30942 coerce('title.text', layout._dfltTitle.colorbar);
30943
30944 var tickFont = colorbarOut.tickfont;
30945 var dfltTitleFont = Lib.extendFlat({}, tickFont, {
30946 color: font.color,
30947 size: Lib.bigFont(tickFont.size)
30948 });
30949 Lib.coerceFont(coerce, 'title.font', dfltTitleFont);
30950 coerce('title.side');
30951};
30952
30953},{"../../lib":287,"../../plot_api/plot_template":323,"../../plots/cartesian/tick_label_defaults":356,"../../plots/cartesian/tick_mark_defaults":357,"../../plots/cartesian/tick_value_defaults":358,"./attributes":158}],161:[function(_dereq_,module,exports){
30954'use strict';
30955
30956var d3 = _dereq_('@plotly/d3');
30957var tinycolor = _dereq_('tinycolor2');
30958
30959var Plots = _dereq_('../../plots/plots');
30960var Registry = _dereq_('../../registry');
30961var Axes = _dereq_('../../plots/cartesian/axes');
30962var dragElement = _dereq_('../dragelement');
30963var Lib = _dereq_('../../lib');
30964var strTranslate = Lib.strTranslate;
30965var extendFlat = _dereq_('../../lib/extend').extendFlat;
30966var setCursor = _dereq_('../../lib/setcursor');
30967var Drawing = _dereq_('../drawing');
30968var Color = _dereq_('../color');
30969var Titles = _dereq_('../titles');
30970var svgTextUtils = _dereq_('../../lib/svg_text_utils');
30971var flipScale = _dereq_('../colorscale/helpers').flipScale;
30972
30973var handleAxisDefaults = _dereq_('../../plots/cartesian/axis_defaults');
30974var handleAxisPositionDefaults = _dereq_('../../plots/cartesian/position_defaults');
30975var axisLayoutAttrs = _dereq_('../../plots/cartesian/layout_attributes');
30976
30977var alignmentConstants = _dereq_('../../constants/alignment');
30978var LINE_SPACING = alignmentConstants.LINE_SPACING;
30979var FROM_TL = alignmentConstants.FROM_TL;
30980var FROM_BR = alignmentConstants.FROM_BR;
30981
30982var cn = _dereq_('./constants').cn;
30983
30984function draw(gd) {
30985 var fullLayout = gd._fullLayout;
30986
30987 var colorBars = fullLayout._infolayer
30988 .selectAll('g.' + cn.colorbar)
30989 .data(makeColorBarData(gd), function(opts) { return opts._id; });
30990
30991 colorBars.enter().append('g')
30992 .attr('class', function(opts) { return opts._id; })
30993 .classed(cn.colorbar, true);
30994
30995 colorBars.each(function(opts) {
30996 var g = d3.select(this);
30997
30998 Lib.ensureSingle(g, 'rect', cn.cbbg);
30999 Lib.ensureSingle(g, 'g', cn.cbfills);
31000 Lib.ensureSingle(g, 'g', cn.cblines);
31001 Lib.ensureSingle(g, 'g', cn.cbaxis, function(s) { s.classed(cn.crisp, true); });
31002 Lib.ensureSingle(g, 'g', cn.cbtitleunshift, function(s) { s.append('g').classed(cn.cbtitle, true); });
31003 Lib.ensureSingle(g, 'rect', cn.cboutline);
31004
31005 var done = drawColorBar(g, opts, gd);
31006 if(done && done.then) (gd._promises || []).push(done);
31007
31008 if(gd._context.edits.colorbarPosition) {
31009 makeEditable(g, opts, gd);
31010 }
31011 });
31012
31013 colorBars.exit()
31014 .each(function(opts) { Plots.autoMargin(gd, opts._id); })
31015 .remove();
31016
31017 colorBars.order();
31018}
31019
31020function makeColorBarData(gd) {
31021 var fullLayout = gd._fullLayout;
31022 var calcdata = gd.calcdata;
31023 var out = [];
31024
31025 // single out item
31026 var opts;
31027 // colorbar attr parent container
31028 var cont;
31029 // trace attr container
31030 var trace;
31031 // colorbar options
31032 var cbOpt;
31033
31034 function initOpts(opts) {
31035 return extendFlat(opts, {
31036 // fillcolor can be a d3 scale, domain is z values, range is colors
31037 // or leave it out for no fill,
31038 // or set to a string constant for single-color fill
31039 _fillcolor: null,
31040 // line.color has the same options as fillcolor
31041 _line: {color: null, width: null, dash: null},
31042 // levels of lines to draw.
31043 // note that this DOES NOT determine the extent of the bar
31044 // that's given by the domain of fillcolor
31045 // (or line.color if no fillcolor domain)
31046 _levels: {start: null, end: null, size: null},
31047 // separate fill levels (for example, heatmap coloring of a
31048 // contour map) if this is omitted, fillcolors will be
31049 // evaluated halfway between levels
31050 _filllevels: null,
31051 // for continuous colorscales: fill with a gradient instead of explicit levels
31052 // value should be the colorscale [[0, c0], [v1, c1], ..., [1, cEnd]]
31053 _fillgradient: null,
31054 // when using a gradient, we need the data range specified separately
31055 _zrange: null
31056 });
31057 }
31058
31059 function calcOpts() {
31060 if(typeof cbOpt.calc === 'function') {
31061 cbOpt.calc(gd, trace, opts);
31062 } else {
31063 opts._fillgradient = cont.reversescale ?
31064 flipScale(cont.colorscale) :
31065 cont.colorscale;
31066 opts._zrange = [cont[cbOpt.min], cont[cbOpt.max]];
31067 }
31068 }
31069
31070 for(var i = 0; i < calcdata.length; i++) {
31071 var cd = calcdata[i];
31072 trace = cd[0].trace;
31073 var moduleOpts = trace._module.colorbar;
31074
31075 if(trace.visible === true && moduleOpts) {
31076 var allowsMultiplotCbs = Array.isArray(moduleOpts);
31077 var cbOpts = allowsMultiplotCbs ? moduleOpts : [moduleOpts];
31078
31079 for(var j = 0; j < cbOpts.length; j++) {
31080 cbOpt = cbOpts[j];
31081 var contName = cbOpt.container;
31082 cont = contName ? trace[contName] : trace;
31083
31084 if(cont && cont.showscale) {
31085 opts = initOpts(cont.colorbar);
31086 opts._id = 'cb' + trace.uid + (allowsMultiplotCbs && contName ? '-' + contName : '');
31087 opts._traceIndex = trace.index;
31088 opts._propPrefix = (contName ? contName + '.' : '') + 'colorbar.';
31089 opts._meta = trace._meta;
31090 calcOpts();
31091 out.push(opts);
31092 }
31093 }
31094 }
31095 }
31096
31097 for(var k in fullLayout._colorAxes) {
31098 cont = fullLayout[k];
31099
31100 if(cont.showscale) {
31101 var colorAxOpts = fullLayout._colorAxes[k];
31102
31103 opts = initOpts(cont.colorbar);
31104 opts._id = 'cb' + k;
31105 opts._propPrefix = k + '.colorbar.';
31106 opts._meta = fullLayout._meta;
31107
31108 cbOpt = {min: 'cmin', max: 'cmax'};
31109 if(colorAxOpts[0] !== 'heatmap') {
31110 trace = colorAxOpts[1];
31111 cbOpt.calc = trace._module.colorbar.calc;
31112 }
31113
31114 calcOpts();
31115 out.push(opts);
31116 }
31117 }
31118
31119 return out;
31120}
31121
31122function drawColorBar(g, opts, gd) {
31123 var fullLayout = gd._fullLayout;
31124 var gs = fullLayout._size;
31125
31126 var fillColor = opts._fillcolor;
31127 var line = opts._line;
31128 var title = opts.title;
31129 var titleSide = title.side;
31130
31131 var zrange = opts._zrange ||
31132 d3.extent((typeof fillColor === 'function' ? fillColor : line.color).domain());
31133
31134 var lineColormap = typeof line.color === 'function' ?
31135 line.color :
31136 function() { return line.color; };
31137 var fillColormap = typeof fillColor === 'function' ?
31138 fillColor :
31139 function() { return fillColor; };
31140
31141 var levelsIn = opts._levels;
31142 var levelsOut = calcLevels(gd, opts, zrange);
31143 var fillLevels = levelsOut.fill;
31144 var lineLevels = levelsOut.line;
31145
31146 // we calculate pixel sizes based on the specified graph size,
31147 // not the actual (in case something pushed the margins around)
31148 // which is a little odd but avoids an odd iterative effect
31149 // when the colorbar itself is pushing the margins.
31150 // but then the fractional size is calculated based on the
31151 // actual graph size, so that the axes will size correctly.
31152 var thickPx = Math.round(opts.thickness * (opts.thicknessmode === 'fraction' ? gs.w : 1));
31153 var thickFrac = thickPx / gs.w;
31154 var lenPx = Math.round(opts.len * (opts.lenmode === 'fraction' ? gs.h : 1));
31155 var lenFrac = lenPx / gs.h;
31156 var xpadFrac = opts.xpad / gs.w;
31157 var yExtraPx = (opts.borderwidth + opts.outlinewidth) / 2;
31158 var ypadFrac = opts.ypad / gs.h;
31159
31160 // x positioning: do it initially just for left anchor,
31161 // then fix at the end (since we don't know the width yet)
31162 var xLeft = Math.round(opts.x * gs.w + opts.xpad);
31163 // for dragging... this is getting a little muddled...
31164 var xLeftFrac = opts.x - thickFrac * ({middle: 0.5, right: 1}[opts.xanchor] || 0);
31165
31166 // y positioning we can do correctly from the start
31167 var yBottomFrac = opts.y + lenFrac * (({top: -0.5, bottom: 0.5}[opts.yanchor] || 0) - 0.5);
31168 var yBottomPx = Math.round(gs.h * (1 - yBottomFrac));
31169 var yTopPx = yBottomPx - lenPx;
31170
31171 // stash a few things for makeEditable
31172 opts._lenFrac = lenFrac;
31173 opts._thickFrac = thickFrac;
31174 opts._xLeftFrac = xLeftFrac;
31175 opts._yBottomFrac = yBottomFrac;
31176
31177 // stash mocked axis for contour label formatting
31178 var ax = opts._axis = mockColorBarAxis(gd, opts, zrange);
31179
31180 // position can't go in through supplyDefaults
31181 // because that restricts it to [0,1]
31182 ax.position = opts.x + xpadFrac + thickFrac;
31183
31184 if(['top', 'bottom'].indexOf(titleSide) !== -1) {
31185 ax.title.side = titleSide;
31186 ax.titlex = opts.x + xpadFrac;
31187 ax.titley = yBottomFrac + (title.side === 'top' ? lenFrac - ypadFrac : ypadFrac);
31188 }
31189
31190 if(line.color && opts.tickmode === 'auto') {
31191 ax.tickmode = 'linear';
31192 ax.tick0 = levelsIn.start;
31193 var dtick = levelsIn.size;
31194 // expand if too many contours, so we don't get too many ticks
31195 var autoNtick = Lib.constrain((yBottomPx - yTopPx) / 50, 4, 15) + 1;
31196 var dtFactor = (zrange[1] - zrange[0]) / ((opts.nticks || autoNtick) * dtick);
31197 if(dtFactor > 1) {
31198 var dtexp = Math.pow(10, Math.floor(Math.log(dtFactor) / Math.LN10));
31199 dtick *= dtexp * Lib.roundUp(dtFactor / dtexp, [2, 5, 10]);
31200 // if the contours are at round multiples, reset tick0
31201 // so they're still at round multiples. Otherwise,
31202 // keep the first label on the first contour level
31203 if((Math.abs(levelsIn.start) / levelsIn.size + 1e-6) % 1 < 2e-6) {
31204 ax.tick0 = 0;
31205 }
31206 }
31207 ax.dtick = dtick;
31208 }
31209
31210 // set domain after init, because we may want to
31211 // allow it outside [0,1]
31212 ax.domain = [
31213 yBottomFrac + ypadFrac,
31214 yBottomFrac + lenFrac - ypadFrac
31215 ];
31216
31217 ax.setScale();
31218
31219 g.attr('transform', strTranslate(Math.round(gs.l), Math.round(gs.t)));
31220
31221 var titleCont = g.select('.' + cn.cbtitleunshift)
31222 .attr('transform', strTranslate(-Math.round(gs.l), -Math.round(gs.t)));
31223
31224 var axLayer = g.select('.' + cn.cbaxis);
31225 var titleEl;
31226 var titleHeight = 0;
31227
31228 function drawTitle(titleClass, titleOpts) {
31229 var dfltTitleOpts = {
31230 propContainer: ax,
31231 propName: opts._propPrefix + 'title',
31232 traceIndex: opts._traceIndex,
31233 _meta: opts._meta,
31234 placeholder: fullLayout._dfltTitle.colorbar,
31235 containerGroup: g.select('.' + cn.cbtitle)
31236 };
31237
31238 // this class-to-rotate thing with convertToTspans is
31239 // getting hackier and hackier... delete groups with the
31240 // wrong class (in case earlier the colorbar was drawn on
31241 // a different side, I think?)
31242 var otherClass = titleClass.charAt(0) === 'h' ?
31243 titleClass.substr(1) :
31244 'h' + titleClass;
31245 g.selectAll('.' + otherClass + ',.' + otherClass + '-math-group').remove();
31246
31247 Titles.draw(gd, titleClass, extendFlat(dfltTitleOpts, titleOpts || {}));
31248 }
31249
31250 function drawDummyTitle() {
31251 if(['top', 'bottom'].indexOf(titleSide) !== -1) {
31252 // draw the title so we know how much room it needs
31253 // when we squish the axis. This one only applies to
31254 // top or bottom titles, not right side.
31255 var x = gs.l + (opts.x + xpadFrac) * gs.w;
31256 var fontSize = ax.title.font.size;
31257 var y;
31258
31259 if(titleSide === 'top') {
31260 y = (1 - (yBottomFrac + lenFrac - ypadFrac)) * gs.h +
31261 gs.t + 3 + fontSize * 0.75;
31262 } else {
31263 y = (1 - (yBottomFrac + ypadFrac)) * gs.h +
31264 gs.t - 3 - fontSize * 0.25;
31265 }
31266 drawTitle(ax._id + 'title', {
31267 attributes: {x: x, y: y, 'text-anchor': 'start'}
31268 });
31269 }
31270 }
31271
31272 function drawCbTitle() {
31273 if(['top', 'bottom'].indexOf(titleSide) === -1) {
31274 var fontSize = ax.title.font.size;
31275 var y = ax._offset + ax._length / 2;
31276 var x = gs.l + (ax.position || 0) * gs.w + ((ax.side === 'right') ?
31277 10 + fontSize * ((ax.showticklabels ? 1 : 0.5)) :
31278 -10 - fontSize * ((ax.showticklabels ? 0.5 : 0)));
31279
31280 // the 'h' + is a hack to get around the fact that
31281 // convertToTspans rotates any 'y...' class by 90 degrees.
31282 // TODO: find a better way to control this.
31283 drawTitle('h' + ax._id + 'title', {
31284 avoid: {
31285 selection: d3.select(gd).selectAll('g.' + ax._id + 'tick'),
31286 side: titleSide,
31287 offsetLeft: gs.l,
31288 offsetTop: 0,
31289 maxShift: fullLayout.width
31290 },
31291 attributes: {x: x, y: y, 'text-anchor': 'middle'},
31292 transform: {rotate: '-90', offset: 0}
31293 });
31294 }
31295 }
31296
31297 function drawAxis() {
31298 if(['top', 'bottom'].indexOf(titleSide) !== -1) {
31299 // squish the axis top to make room for the title
31300 var titleGroup = g.select('.' + cn.cbtitle);
31301 var titleText = titleGroup.select('text');
31302 var titleTrans = [-opts.outlinewidth / 2, opts.outlinewidth / 2];
31303 var mathJaxNode = titleGroup
31304 .select('.h' + ax._id + 'title-math-group')
31305 .node();
31306 var lineSize = 15.6;
31307 if(titleText.node()) {
31308 lineSize = parseInt(titleText.node().style.fontSize, 10) * LINE_SPACING;
31309 }
31310 if(mathJaxNode) {
31311 titleHeight = Drawing.bBox(mathJaxNode).height;
31312 if(titleHeight > lineSize) {
31313 // not entirely sure how mathjax is doing
31314 // vertical alignment, but this seems to work.
31315 titleTrans[1] -= (titleHeight - lineSize) / 2;
31316 }
31317 } else if(titleText.node() && !titleText.classed(cn.jsPlaceholder)) {
31318 titleHeight = Drawing.bBox(titleText.node()).height;
31319 }
31320 if(titleHeight) {
31321 // buffer btwn colorbar and title
31322 // TODO: configurable
31323 titleHeight += 5;
31324
31325 if(titleSide === 'top') {
31326 ax.domain[1] -= titleHeight / gs.h;
31327 titleTrans[1] *= -1;
31328 } else {
31329 ax.domain[0] += titleHeight / gs.h;
31330 var nlines = svgTextUtils.lineCount(titleText);
31331 titleTrans[1] += (1 - nlines) * lineSize;
31332 }
31333
31334 titleGroup.attr('transform', strTranslate(titleTrans[0], titleTrans[1]));
31335 ax.setScale();
31336 }
31337 }
31338
31339 g.selectAll('.' + cn.cbfills + ',.' + cn.cblines)
31340 .attr('transform', strTranslate(0, Math.round(gs.h * (1 - ax.domain[1]))));
31341
31342 axLayer.attr('transform', strTranslate(0, Math.round(-gs.t)));
31343
31344 var fills = g.select('.' + cn.cbfills)
31345 .selectAll('rect.' + cn.cbfill)
31346 .attr('style', '')
31347 .data(fillLevels);
31348 fills.enter().append('rect')
31349 .classed(cn.cbfill, true)
31350 .style('stroke', 'none');
31351 fills.exit().remove();
31352
31353 var zBounds = zrange
31354 .map(ax.c2p)
31355 .map(Math.round)
31356 .sort(function(a, b) { return a - b; });
31357
31358 fills.each(function(d, i) {
31359 var z = [
31360 (i === 0) ? zrange[0] : (fillLevels[i] + fillLevels[i - 1]) / 2,
31361 (i === fillLevels.length - 1) ? zrange[1] : (fillLevels[i] + fillLevels[i + 1]) / 2
31362 ]
31363 .map(ax.c2p)
31364 .map(Math.round);
31365
31366 // offset the side adjoining the next rectangle so they
31367 // overlap, to prevent antialiasing gaps
31368 z[1] = Lib.constrain(z[1] + (z[1] > z[0]) ? 1 : -1, zBounds[0], zBounds[1]);
31369
31370
31371 // Colorbar cannot currently support opacities so we
31372 // use an opaque fill even when alpha channels present
31373 var fillEl = d3.select(this).attr({
31374 x: xLeft,
31375 width: Math.max(thickPx, 2),
31376 y: d3.min(z),
31377 height: Math.max(d3.max(z) - d3.min(z), 2),
31378 });
31379
31380 if(opts._fillgradient) {
31381 Drawing.gradient(fillEl, gd, opts._id, 'vertical', opts._fillgradient, 'fill');
31382 } else {
31383 // tinycolor can't handle exponents and
31384 // at this scale, removing it makes no difference.
31385 var colorString = fillColormap(d).replace('e-', '');
31386 fillEl.attr('fill', tinycolor(colorString).toHexString());
31387 }
31388 });
31389
31390 var lines = g.select('.' + cn.cblines)
31391 .selectAll('path.' + cn.cbline)
31392 .data(line.color && line.width ? lineLevels : []);
31393 lines.enter().append('path')
31394 .classed(cn.cbline, true);
31395 lines.exit().remove();
31396 lines.each(function(d) {
31397 d3.select(this)
31398 .attr('d', 'M' + xLeft + ',' +
31399 (Math.round(ax.c2p(d)) + (line.width / 2) % 1) + 'h' + thickPx)
31400 .call(Drawing.lineGroupStyle, line.width, lineColormap(d), line.dash);
31401 });
31402
31403 // force full redraw of labels and ticks
31404 axLayer.selectAll('g.' + ax._id + 'tick,path').remove();
31405
31406 var shift = xLeft + thickPx +
31407 (opts.outlinewidth || 0) / 2 - (opts.ticks === 'outside' ? 1 : 0);
31408
31409 var vals = Axes.calcTicks(ax);
31410 var tickSign = Axes.getTickSigns(ax)[2];
31411
31412 Axes.drawTicks(gd, ax, {
31413 vals: ax.ticks === 'inside' ? Axes.clipEnds(ax, vals) : vals,
31414 layer: axLayer,
31415 path: Axes.makeTickPath(ax, shift, tickSign),
31416 transFn: Axes.makeTransTickFn(ax)
31417 });
31418
31419 return Axes.drawLabels(gd, ax, {
31420 vals: vals,
31421 layer: axLayer,
31422 transFn: Axes.makeTransTickLabelFn(ax),
31423 labelFns: Axes.makeLabelFns(ax, shift)
31424 });
31425 }
31426
31427 // wait for the axis & title to finish rendering before
31428 // continuing positioning
31429 // TODO: why are we redrawing multiple times now with this?
31430 // I guess autoMargin doesn't like being post-promise?
31431 function positionCB() {
31432 var innerWidth = thickPx + opts.outlinewidth / 2;
31433 if(ax.ticklabelposition.indexOf('inside') === -1) {
31434 innerWidth += Drawing.bBox(axLayer.node()).width;
31435 }
31436
31437 titleEl = titleCont.select('text');
31438
31439 if(titleEl.node() && !titleEl.classed(cn.jsPlaceholder)) {
31440 var mathJaxNode = titleCont.select('.h' + ax._id + 'title-math-group').node();
31441 var titleWidth;
31442 if(mathJaxNode && ['top', 'bottom'].indexOf(titleSide) !== -1) {
31443 titleWidth = Drawing.bBox(mathJaxNode).width;
31444 } else {
31445 // note: the formula below works for all title sides,
31446 // (except for top/bottom mathjax, above)
31447 // but the weird gs.l is because the titleunshift
31448 // transform gets removed by Drawing.bBox
31449 titleWidth = Drawing.bBox(titleCont.node()).right - xLeft - gs.l;
31450 }
31451 innerWidth = Math.max(innerWidth, titleWidth);
31452 }
31453
31454 var outerwidth = 2 * opts.xpad + innerWidth + opts.borderwidth + opts.outlinewidth / 2;
31455 var outerheight = yBottomPx - yTopPx;
31456
31457 g.select('.' + cn.cbbg).attr({
31458 x: xLeft - opts.xpad - (opts.borderwidth + opts.outlinewidth) / 2,
31459 y: yTopPx - yExtraPx,
31460 width: Math.max(outerwidth, 2),
31461 height: Math.max(outerheight + 2 * yExtraPx, 2)
31462 })
31463 .call(Color.fill, opts.bgcolor)
31464 .call(Color.stroke, opts.bordercolor)
31465 .style('stroke-width', opts.borderwidth);
31466
31467 g.selectAll('.' + cn.cboutline).attr({
31468 x: xLeft,
31469 y: yTopPx + opts.ypad + (titleSide === 'top' ? titleHeight : 0),
31470 width: Math.max(thickPx, 2),
31471 height: Math.max(outerheight - 2 * opts.ypad - titleHeight, 2)
31472 })
31473 .call(Color.stroke, opts.outlinecolor)
31474 .style({
31475 fill: 'none',
31476 'stroke-width': opts.outlinewidth
31477 });
31478
31479 // fix positioning for xanchor!='left'
31480 var xoffset = ({center: 0.5, right: 1}[opts.xanchor] || 0) * outerwidth;
31481 g.attr('transform', strTranslate(gs.l - xoffset, gs.t));
31482
31483 // auto margin adjustment
31484 var marginOpts = {};
31485 var tFrac = FROM_TL[opts.yanchor];
31486 var bFrac = FROM_BR[opts.yanchor];
31487 if(opts.lenmode === 'pixels') {
31488 marginOpts.y = opts.y;
31489 marginOpts.t = outerheight * tFrac;
31490 marginOpts.b = outerheight * bFrac;
31491 } else {
31492 marginOpts.t = marginOpts.b = 0;
31493 marginOpts.yt = opts.y + opts.len * tFrac;
31494 marginOpts.yb = opts.y - opts.len * bFrac;
31495 }
31496
31497 var lFrac = FROM_TL[opts.xanchor];
31498 var rFrac = FROM_BR[opts.xanchor];
31499 if(opts.thicknessmode === 'pixels') {
31500 marginOpts.x = opts.x;
31501 marginOpts.l = outerwidth * lFrac;
31502 marginOpts.r = outerwidth * rFrac;
31503 } else {
31504 var extraThickness = outerwidth - thickPx;
31505 marginOpts.l = extraThickness * lFrac;
31506 marginOpts.r = extraThickness * rFrac;
31507 marginOpts.xl = opts.x - opts.thickness * lFrac;
31508 marginOpts.xr = opts.x + opts.thickness * rFrac;
31509 }
31510
31511 Plots.autoMargin(gd, opts._id, marginOpts);
31512 }
31513
31514 return Lib.syncOrAsync([
31515 Plots.previousPromises,
31516 drawDummyTitle,
31517 drawAxis,
31518 drawCbTitle,
31519 Plots.previousPromises,
31520 positionCB
31521 ], gd);
31522}
31523
31524function makeEditable(g, opts, gd) {
31525 var fullLayout = gd._fullLayout;
31526 var gs = fullLayout._size;
31527 var t0, xf, yf;
31528
31529 dragElement.init({
31530 element: g.node(),
31531 gd: gd,
31532 prepFn: function() {
31533 t0 = g.attr('transform');
31534 setCursor(g);
31535 },
31536 moveFn: function(dx, dy) {
31537 g.attr('transform', t0 + strTranslate(dx, dy));
31538
31539 xf = dragElement.align(opts._xLeftFrac + (dx / gs.w), opts._thickFrac,
31540 0, 1, opts.xanchor);
31541 yf = dragElement.align(opts._yBottomFrac - (dy / gs.h), opts._lenFrac,
31542 0, 1, opts.yanchor);
31543
31544 var csr = dragElement.getCursor(xf, yf, opts.xanchor, opts.yanchor);
31545 setCursor(g, csr);
31546 },
31547 doneFn: function() {
31548 setCursor(g);
31549
31550 if(xf !== undefined && yf !== undefined) {
31551 var update = {};
31552 update[opts._propPrefix + 'x'] = xf;
31553 update[opts._propPrefix + 'y'] = yf;
31554 if(opts._traceIndex !== undefined) {
31555 Registry.call('_guiRestyle', gd, update, opts._traceIndex);
31556 } else {
31557 Registry.call('_guiRelayout', gd, update);
31558 }
31559 }
31560 }
31561 });
31562}
31563
31564function calcLevels(gd, opts, zrange) {
31565 var levelsIn = opts._levels;
31566 var lineLevels = [];
31567 var fillLevels = [];
31568 var l;
31569 var i;
31570
31571 var l0 = levelsIn.end + levelsIn.size / 100;
31572 var ls = levelsIn.size;
31573 var zr0 = (1.001 * zrange[0] - 0.001 * zrange[1]);
31574 var zr1 = (1.001 * zrange[1] - 0.001 * zrange[0]);
31575
31576 for(i = 0; i < 1e5; i++) {
31577 l = levelsIn.start + i * ls;
31578 if(ls > 0 ? (l >= l0) : (l <= l0)) break;
31579 if(l > zr0 && l < zr1) lineLevels.push(l);
31580 }
31581
31582 if(opts._fillgradient) {
31583 fillLevels = [0];
31584 } else if(typeof opts._fillcolor === 'function') {
31585 var fillLevelsIn = opts._filllevels;
31586
31587 if(fillLevelsIn) {
31588 l0 = fillLevelsIn.end + fillLevelsIn.size / 100;
31589 ls = fillLevelsIn.size;
31590 for(i = 0; i < 1e5; i++) {
31591 l = fillLevelsIn.start + i * ls;
31592 if(ls > 0 ? (l >= l0) : (l <= l0)) break;
31593 if(l > zrange[0] && l < zrange[1]) fillLevels.push(l);
31594 }
31595 } else {
31596 fillLevels = lineLevels.map(function(v) {
31597 return v - levelsIn.size / 2;
31598 });
31599 fillLevels.push(fillLevels[fillLevels.length - 1] + levelsIn.size);
31600 }
31601 } else if(opts._fillcolor && typeof opts._fillcolor === 'string') {
31602 // doesn't matter what this value is, with a single value
31603 // we'll make a single fill rect covering the whole bar
31604 fillLevels = [0];
31605 }
31606
31607 if(levelsIn.size < 0) {
31608 lineLevels.reverse();
31609 fillLevels.reverse();
31610 }
31611
31612 return {line: lineLevels, fill: fillLevels};
31613}
31614
31615function mockColorBarAxis(gd, opts, zrange) {
31616 var fullLayout = gd._fullLayout;
31617
31618 var cbAxisIn = {
31619 type: 'linear',
31620 range: zrange,
31621 tickmode: opts.tickmode,
31622 nticks: opts.nticks,
31623 tick0: opts.tick0,
31624 dtick: opts.dtick,
31625 tickvals: opts.tickvals,
31626 ticktext: opts.ticktext,
31627 ticks: opts.ticks,
31628 ticklen: opts.ticklen,
31629 tickwidth: opts.tickwidth,
31630 tickcolor: opts.tickcolor,
31631 showticklabels: opts.showticklabels,
31632 ticklabelposition: opts.ticklabelposition,
31633 ticklabeloverflow: opts.ticklabeloverflow,
31634 tickfont: opts.tickfont,
31635 tickangle: opts.tickangle,
31636 tickformat: opts.tickformat,
31637 exponentformat: opts.exponentformat,
31638 minexponent: opts.minexponent,
31639 separatethousands: opts.separatethousands,
31640 showexponent: opts.showexponent,
31641 showtickprefix: opts.showtickprefix,
31642 tickprefix: opts.tickprefix,
31643 showticksuffix: opts.showticksuffix,
31644 ticksuffix: opts.ticksuffix,
31645 title: opts.title,
31646 showline: true,
31647 anchor: 'free',
31648 side: 'right',
31649 position: 1
31650 };
31651
31652 var cbAxisOut = {
31653 type: 'linear',
31654 _id: 'y' + opts._id
31655 };
31656
31657 var axisOptions = {
31658 letter: 'y',
31659 font: fullLayout.font,
31660 noHover: true,
31661 noTickson: true,
31662 noTicklabelmode: true,
31663 calendar: fullLayout.calendar // not really necessary (yet?)
31664 };
31665
31666 function coerce(attr, dflt) {
31667 return Lib.coerce(cbAxisIn, cbAxisOut, axisLayoutAttrs, attr, dflt);
31668 }
31669
31670 handleAxisDefaults(cbAxisIn, cbAxisOut, coerce, axisOptions, fullLayout);
31671 handleAxisPositionDefaults(cbAxisIn, cbAxisOut, coerce, axisOptions);
31672
31673 return cbAxisOut;
31674}
31675
31676module.exports = {
31677 draw: draw
31678};
31679
31680},{"../../constants/alignment":262,"../../lib":287,"../../lib/extend":281,"../../lib/setcursor":307,"../../lib/svg_text_utils":310,"../../plots/cartesian/axes":334,"../../plots/cartesian/axis_defaults":336,"../../plots/cartesian/layout_attributes":349,"../../plots/cartesian/position_defaults":352,"../../plots/plots":369,"../../registry":376,"../color":157,"../colorscale/helpers":168,"../dragelement":176,"../drawing":179,"../titles":255,"./constants":159,"@plotly/d3":20,"tinycolor2":121}],162:[function(_dereq_,module,exports){
31681'use strict';
31682
31683var Lib = _dereq_('../../lib');
31684
31685
31686module.exports = function hasColorbar(container) {
31687 return Lib.isPlainObject(container.colorbar);
31688};
31689
31690},{"../../lib":287}],163:[function(_dereq_,module,exports){
31691'use strict';
31692
31693module.exports = {
31694 moduleType: 'component',
31695 name: 'colorbar',
31696
31697 attributes: _dereq_('./attributes'),
31698 supplyDefaults: _dereq_('./defaults'),
31699
31700 draw: _dereq_('./draw').draw,
31701 hasColorbar: _dereq_('./has_colorbar')
31702};
31703
31704},{"./attributes":158,"./defaults":160,"./draw":161,"./has_colorbar":162}],164:[function(_dereq_,module,exports){
31705'use strict';
31706
31707var colorbarAttrs = _dereq_('../colorbar/attributes');
31708var counterRegex = _dereq_('../../lib/regex').counter;
31709var sortObjectKeys = _dereq_('../../lib/sort_object_keys');
31710
31711var palettes = _dereq_('./scales.js').scales;
31712var paletteStr = sortObjectKeys(palettes);
31713
31714function code(s) {
31715 return '`' + s + '`';
31716}
31717
31718/**
31719 * Make colorscale attribute declarations for
31720 *
31721 * - colorscale,
31722 * - (c|z)auto, (c|z)min, (c|z)max,
31723 * - autocolorscale, reversescale,
31724 * - showscale (optionally)
31725 * - color (optionally)
31726 *
31727 * @param {string} context (dflt: '', i.e. from trace root):
31728 * the container this is in ('', *marker*, *marker.line* etc)
31729 *
31730 * @param {object} opts:
31731 * - cLetter {string} (dflt: 'c'):
31732 * leading letter for 'min', 'max and 'auto' attribute (either 'z' or 'c')
31733 *
31734 * - colorAttr {string} (dflt: 'z' if `cLetter: 'z'`, 'color' if `cLetter: 'c'`):
31735 * (for descriptions) sets the name of the color attribute that maps to the colorscale.
31736 *
31737 * N.B. if `colorAttr: 'color'`, we include the `color` declaration here.
31738 *
31739 * - onlyIfNumerical {string} (dflt: false' if `cLetter: 'z'`, true if `cLetter: 'c'`):
31740 * (for descriptions) set to true if colorscale attribute only
31741 *
31742 * - colorscaleDflt {string}:
31743 * overrides the colorscale dflt
31744 *
31745 * - autoColorDflt {boolean} (dflt true):
31746 * normally autocolorscale.dflt is `true`, but pass `false` to override
31747 *
31748 * - noScale {boolean} (dflt: true if `context: 'marker.line'`, false otherwise):
31749 * set to `false` to not include showscale attribute (e.g. for 'marker.line')
31750 *
31751 * - showScaleDflt {boolean} (dflt: true if `cLetter: 'z'`, false otherwise)
31752 *
31753 * - editTypeOverride {boolean} (dflt: ''):
31754 * most of these attributes already require a recalc, but the ones that do not
31755 * have editType *style* or *plot* unless you override (presumably with *calc*)
31756 *
31757 * - anim {boolean) (dflt: undefined): is 'color' animatable?
31758 *
31759 * @return {object}
31760 */
31761module.exports = function colorScaleAttrs(context, opts) {
31762 context = context || '';
31763 opts = opts || {};
31764
31765 var cLetter = opts.cLetter || 'c';
31766 var onlyIfNumerical = ('onlyIfNumerical' in opts) ? opts.onlyIfNumerical : Boolean(context);
31767 var noScale = ('noScale' in opts) ? opts.noScale : context === 'marker.line';
31768 var showScaleDflt = ('showScaleDflt' in opts) ? opts.showScaleDflt : cLetter === 'z';
31769 var colorscaleDflt = typeof opts.colorscaleDflt === 'string' ? palettes[opts.colorscaleDflt] : null;
31770 var editTypeOverride = opts.editTypeOverride || '';
31771 var contextHead = context ? (context + '.') : '';
31772
31773 var colorAttr, colorAttrFull;
31774
31775 if('colorAttr' in opts) {
31776 colorAttr = opts.colorAttr;
31777 colorAttrFull = opts.colorAttr;
31778 } else {
31779 colorAttr = {z: 'z', c: 'color'}[cLetter];
31780 colorAttrFull = 'in ' + code(contextHead + colorAttr);
31781 }
31782
31783 var effectDesc = onlyIfNumerical ?
31784 ' Has an effect only if ' + colorAttrFull + 'is set to a numerical array.' :
31785 '';
31786
31787 var auto = cLetter + 'auto';
31788 var min = cLetter + 'min';
31789 var max = cLetter + 'max';
31790 var mid = cLetter + 'mid';
31791 var autoFull = code(contextHead + auto);
31792 var minFull = code(contextHead + min);
31793 var maxFull = code(contextHead + max);
31794 var minmaxFull = minFull + ' and ' + maxFull;
31795 var autoImpliedEdits = {};
31796 autoImpliedEdits[min] = autoImpliedEdits[max] = undefined;
31797 var minmaxImpliedEdits = {};
31798 minmaxImpliedEdits[auto] = false;
31799
31800 var attrs = {};
31801
31802 if(colorAttr === 'color') {
31803 attrs.color = {
31804 valType: 'color',
31805 arrayOk: true,
31806 editType: editTypeOverride || 'style',
31807 };
31808
31809 if(opts.anim) {
31810 attrs.color.anim = true;
31811 }
31812 }
31813
31814 attrs[auto] = {
31815 valType: 'boolean',
31816 dflt: true,
31817 editType: 'calc',
31818 impliedEdits: autoImpliedEdits,
31819 };
31820
31821 attrs[min] = {
31822 valType: 'number',
31823 dflt: null,
31824 editType: editTypeOverride || 'plot',
31825 impliedEdits: minmaxImpliedEdits,
31826 };
31827
31828 attrs[max] = {
31829 valType: 'number',
31830 dflt: null,
31831 editType: editTypeOverride || 'plot',
31832 impliedEdits: minmaxImpliedEdits,
31833 };
31834
31835 attrs[mid] = {
31836 valType: 'number',
31837 dflt: null,
31838 editType: 'calc',
31839 impliedEdits: autoImpliedEdits,
31840 };
31841
31842 attrs.colorscale = {
31843 valType: 'colorscale',
31844 editType: 'calc',
31845 dflt: colorscaleDflt,
31846 impliedEdits: {autocolorscale: false},
31847 };
31848
31849 attrs.autocolorscale = {
31850 valType: 'boolean',
31851 // gets overrode in 'heatmap' & 'surface' for backwards comp.
31852 dflt: opts.autoColorDflt === false ? false : true,
31853 editType: 'calc',
31854 impliedEdits: {colorscale: undefined},
31855 };
31856
31857 attrs.reversescale = {
31858 valType: 'boolean',
31859 dflt: false,
31860 editType: 'plot',
31861 };
31862
31863 if(!noScale) {
31864 attrs.showscale = {
31865 valType: 'boolean',
31866 dflt: showScaleDflt,
31867 editType: 'calc',
31868 };
31869
31870 attrs.colorbar = colorbarAttrs;
31871 }
31872
31873 if(!opts.noColorAxis) {
31874 attrs.coloraxis = {
31875 valType: 'subplotid',
31876 regex: counterRegex('coloraxis'),
31877 dflt: null,
31878 editType: 'calc',
31879 };
31880 }
31881
31882 return attrs;
31883};
31884
31885},{"../../lib/regex":303,"../../lib/sort_object_keys":308,"../colorbar/attributes":158,"./scales.js":172}],165:[function(_dereq_,module,exports){
31886'use strict';
31887
31888var isNumeric = _dereq_('fast-isnumeric');
31889
31890var Lib = _dereq_('../../lib');
31891var extractOpts = _dereq_('./helpers').extractOpts;
31892
31893module.exports = function calc(gd, trace, opts) {
31894 var fullLayout = gd._fullLayout;
31895 var vals = opts.vals;
31896 var containerStr = opts.containerStr;
31897
31898 var container = containerStr ?
31899 Lib.nestedProperty(trace, containerStr).get() :
31900 trace;
31901
31902 var cOpts = extractOpts(container);
31903 var auto = cOpts.auto !== false;
31904 var min = cOpts.min;
31905 var max = cOpts.max;
31906 var mid = cOpts.mid;
31907
31908 var minVal = function() { return Lib.aggNums(Math.min, null, vals); };
31909 var maxVal = function() { return Lib.aggNums(Math.max, null, vals); };
31910
31911 if(min === undefined) {
31912 min = minVal();
31913 } else if(auto) {
31914 if(container._colorAx && isNumeric(min)) {
31915 min = Math.min(min, minVal());
31916 } else {
31917 min = minVal();
31918 }
31919 }
31920
31921 if(max === undefined) {
31922 max = maxVal();
31923 } else if(auto) {
31924 if(container._colorAx && isNumeric(max)) {
31925 max = Math.max(max, maxVal());
31926 } else {
31927 max = maxVal();
31928 }
31929 }
31930
31931 if(auto && mid !== undefined) {
31932 if(max - mid > mid - min) {
31933 min = mid - (max - mid);
31934 } else if(max - mid < mid - min) {
31935 max = mid + (mid - min);
31936 }
31937 }
31938
31939 if(min === max) {
31940 min -= 0.5;
31941 max += 0.5;
31942 }
31943
31944 cOpts._sync('min', min);
31945 cOpts._sync('max', max);
31946
31947 if(cOpts.autocolorscale) {
31948 var scl;
31949 if(min * max < 0) scl = fullLayout.colorscale.diverging;
31950 else if(min >= 0) scl = fullLayout.colorscale.sequential;
31951 else scl = fullLayout.colorscale.sequentialminus;
31952 cOpts._sync('colorscale', scl);
31953 }
31954};
31955
31956},{"../../lib":287,"./helpers":168,"fast-isnumeric":33}],166:[function(_dereq_,module,exports){
31957'use strict';
31958
31959var Lib = _dereq_('../../lib');
31960var hasColorscale = _dereq_('./helpers').hasColorscale;
31961var extractOpts = _dereq_('./helpers').extractOpts;
31962
31963module.exports = function crossTraceDefaults(fullData, fullLayout) {
31964 function replace(cont, k) {
31965 var val = cont['_' + k];
31966 if(val !== undefined) {
31967 cont[k] = val;
31968 }
31969 }
31970
31971 function relinkColorAttrs(outerCont, cbOpt) {
31972 var cont = cbOpt.container ?
31973 Lib.nestedProperty(outerCont, cbOpt.container).get() :
31974 outerCont;
31975
31976 if(cont) {
31977 if(cont.coloraxis) {
31978 // stash ref to color axis
31979 cont._colorAx = fullLayout[cont.coloraxis];
31980 } else {
31981 var cOpts = extractOpts(cont);
31982 var isAuto = cOpts.auto;
31983
31984 if(isAuto || cOpts.min === undefined) {
31985 replace(cont, cbOpt.min);
31986 }
31987 if(isAuto || cOpts.max === undefined) {
31988 replace(cont, cbOpt.max);
31989 }
31990 if(cOpts.autocolorscale) {
31991 replace(cont, 'colorscale');
31992 }
31993 }
31994 }
31995 }
31996
31997 for(var i = 0; i < fullData.length; i++) {
31998 var trace = fullData[i];
31999 var cbOpts = trace._module.colorbar;
32000
32001 if(cbOpts) {
32002 if(Array.isArray(cbOpts)) {
32003 for(var j = 0; j < cbOpts.length; j++) {
32004 relinkColorAttrs(trace, cbOpts[j]);
32005 }
32006 } else {
32007 relinkColorAttrs(trace, cbOpts);
32008 }
32009 }
32010
32011 if(hasColorscale(trace, 'marker.line')) {
32012 relinkColorAttrs(trace, {
32013 container: 'marker.line',
32014 min: 'cmin',
32015 max: 'cmax'
32016 });
32017 }
32018 }
32019
32020 for(var k in fullLayout._colorAxes) {
32021 relinkColorAttrs(fullLayout[k], {min: 'cmin', max: 'cmax'});
32022 }
32023};
32024
32025},{"../../lib":287,"./helpers":168}],167:[function(_dereq_,module,exports){
32026'use strict';
32027
32028var isNumeric = _dereq_('fast-isnumeric');
32029
32030var Lib = _dereq_('../../lib');
32031var hasColorbar = _dereq_('../colorbar/has_colorbar');
32032var colorbarDefaults = _dereq_('../colorbar/defaults');
32033
32034var isValidScale = _dereq_('./scales').isValid;
32035var traceIs = _dereq_('../../registry').traceIs;
32036
32037function npMaybe(parentCont, prefix) {
32038 var containerStr = prefix.slice(0, prefix.length - 1);
32039 return prefix ?
32040 Lib.nestedProperty(parentCont, containerStr).get() || {} :
32041 parentCont;
32042}
32043
32044/**
32045 * Colorscale / colorbar default handler
32046 *
32047 * @param {object} parentContIn : user (input) parent container (e.g. trace or layout coloraxis object)
32048 * @param {object} parentContOut : full parent container
32049 * @param {object} layout : (full) layout object
32050 * @param {fn} coerce : Lib.coerce wrapper
32051 * @param {object} opts :
32052 * - prefix {string} : attr string prefix to colorscale container from parent root
32053 * - cLetter {string} : 'c or 'z' color letter
32054 */
32055module.exports = function colorScaleDefaults(parentContIn, parentContOut, layout, coerce, opts) {
32056 var prefix = opts.prefix;
32057 var cLetter = opts.cLetter;
32058 var inTrace = '_module' in parentContOut;
32059 var containerIn = npMaybe(parentContIn, prefix);
32060 var containerOut = npMaybe(parentContOut, prefix);
32061 var template = npMaybe(parentContOut._template || {}, prefix) || {};
32062
32063 // colorScaleDefaults wrapper called if-ever we need to reset the colorscale
32064 // attributes for containers that were linked to invalid color axes
32065 var thisFn = function() {
32066 delete parentContIn.coloraxis;
32067 delete parentContOut.coloraxis;
32068 return colorScaleDefaults(parentContIn, parentContOut, layout, coerce, opts);
32069 };
32070
32071 if(inTrace) {
32072 var colorAxes = layout._colorAxes || {};
32073 var colorAx = coerce(prefix + 'coloraxis');
32074
32075 if(colorAx) {
32076 var colorbarVisuals = (
32077 traceIs(parentContOut, 'contour') &&
32078 Lib.nestedProperty(parentContOut, 'contours.coloring').get()
32079 ) || 'heatmap';
32080
32081 var stash = colorAxes[colorAx];
32082
32083 if(stash) {
32084 stash[2].push(thisFn);
32085
32086 if(stash[0] !== colorbarVisuals) {
32087 stash[0] = false;
32088 Lib.warn([
32089 'Ignoring coloraxis:', colorAx, 'setting',
32090 'as it is linked to incompatible colorscales.'
32091 ].join(' '));
32092 }
32093 } else {
32094 // stash:
32095 // - colorbar visual 'type'
32096 // - colorbar options to help in Colorbar.draw
32097 // - list of colorScaleDefaults wrapper functions
32098 colorAxes[colorAx] = [colorbarVisuals, parentContOut, [thisFn]];
32099 }
32100 return;
32101 }
32102 }
32103
32104 var minIn = containerIn[cLetter + 'min'];
32105 var maxIn = containerIn[cLetter + 'max'];
32106 var validMinMax = isNumeric(minIn) && isNumeric(maxIn) && (minIn < maxIn);
32107 var auto = coerce(prefix + cLetter + 'auto', !validMinMax);
32108
32109 if(auto) {
32110 coerce(prefix + cLetter + 'mid');
32111 } else {
32112 coerce(prefix + cLetter + 'min');
32113 coerce(prefix + cLetter + 'max');
32114 }
32115
32116 // handles both the trace case (autocolorscale is false by default) and
32117 // the marker and marker.line case (autocolorscale is true by default)
32118 var sclIn = containerIn.colorscale;
32119 var sclTemplate = template.colorscale;
32120 var autoColorscaleDflt;
32121 if(sclIn !== undefined) autoColorscaleDflt = !isValidScale(sclIn);
32122 if(sclTemplate !== undefined) autoColorscaleDflt = !isValidScale(sclTemplate);
32123 coerce(prefix + 'autocolorscale', autoColorscaleDflt);
32124
32125 coerce(prefix + 'colorscale');
32126 coerce(prefix + 'reversescale');
32127
32128 if(prefix !== 'marker.line.') {
32129 // handles both the trace case where the dflt is listed in attributes and
32130 // the marker case where the dflt is determined by hasColorbar
32131 var showScaleDflt;
32132 if(prefix && inTrace) showScaleDflt = hasColorbar(containerIn);
32133
32134 var showScale = coerce(prefix + 'showscale', showScaleDflt);
32135 if(showScale) {
32136 if(prefix && template) containerOut._template = template;
32137 colorbarDefaults(containerIn, containerOut, layout);
32138 }
32139 }
32140};
32141
32142},{"../../lib":287,"../../registry":376,"../colorbar/defaults":160,"../colorbar/has_colorbar":162,"./scales":172,"fast-isnumeric":33}],168:[function(_dereq_,module,exports){
32143'use strict';
32144
32145var d3 = _dereq_('@plotly/d3');
32146var tinycolor = _dereq_('tinycolor2');
32147var isNumeric = _dereq_('fast-isnumeric');
32148
32149var Lib = _dereq_('../../lib');
32150var Color = _dereq_('../color');
32151
32152var isValidScale = _dereq_('./scales').isValid;
32153
32154function hasColorscale(trace, containerStr, colorKey) {
32155 var container = containerStr ?
32156 Lib.nestedProperty(trace, containerStr).get() || {} :
32157 trace;
32158 var color = container[colorKey || 'color'];
32159
32160 var isArrayWithOneNumber = false;
32161 if(Lib.isArrayOrTypedArray(color)) {
32162 for(var i = 0; i < color.length; i++) {
32163 if(isNumeric(color[i])) {
32164 isArrayWithOneNumber = true;
32165 break;
32166 }
32167 }
32168 }
32169
32170 return (
32171 Lib.isPlainObject(container) && (
32172 isArrayWithOneNumber ||
32173 container.showscale === true ||
32174 (isNumeric(container.cmin) && isNumeric(container.cmax)) ||
32175 isValidScale(container.colorscale) ||
32176 Lib.isPlainObject(container.colorbar)
32177 )
32178 );
32179}
32180
32181var constantAttrs = ['showscale', 'autocolorscale', 'colorscale', 'reversescale', 'colorbar'];
32182var letterAttrs = ['min', 'max', 'mid', 'auto'];
32183
32184/**
32185 * Extract 'c' / 'z', trace / color axis colorscale options
32186 *
32187 * Note that it would be nice to replace all z* with c* equivalents in v3
32188 *
32189 * @param {object} cont : attribute container
32190 * @return {object}:
32191 * - min: cmin or zmin
32192 * - max: cmax or zmax
32193 * - mid: cmid or zmid
32194 * - auto: cauto or zauto
32195 * - *scale: *scale attrs
32196 * - colorbar: colorbar
32197 * - _sync: function syncing attr and underscore dual (useful when calc'ing min/max)
32198 */
32199function extractOpts(cont) {
32200 var colorAx = cont._colorAx;
32201 var cont2 = colorAx ? colorAx : cont;
32202 var out = {};
32203 var cLetter;
32204 var i, k;
32205
32206 for(i = 0; i < constantAttrs.length; i++) {
32207 k = constantAttrs[i];
32208 out[k] = cont2[k];
32209 }
32210
32211 if(colorAx) {
32212 cLetter = 'c';
32213 for(i = 0; i < letterAttrs.length; i++) {
32214 k = letterAttrs[i];
32215 out[k] = cont2['c' + k];
32216 }
32217 } else {
32218 var k2;
32219 for(i = 0; i < letterAttrs.length; i++) {
32220 k = letterAttrs[i];
32221 k2 = 'c' + k;
32222 if(k2 in cont2) {
32223 out[k] = cont2[k2];
32224 continue;
32225 }
32226 k2 = 'z' + k;
32227 if(k2 in cont2) {
32228 out[k] = cont2[k2];
32229 }
32230 }
32231 cLetter = k2.charAt(0);
32232 }
32233
32234 out._sync = function(k, v) {
32235 var k2 = letterAttrs.indexOf(k) !== -1 ? cLetter + k : k;
32236 cont2[k2] = cont2['_' + k2] = v;
32237 };
32238
32239 return out;
32240}
32241
32242/**
32243 * Extract colorscale into numeric domain and color range.
32244 *
32245 * @param {object} cont colorscale container (e.g. trace, marker)
32246 * - colorscale {array of arrays}
32247 * - cmin/zmin {number}
32248 * - cmax/zmax {number}
32249 * - reversescale {boolean}
32250 *
32251 * @return {object}
32252 * - domain {array}
32253 * - range {array}
32254 */
32255function extractScale(cont) {
32256 var cOpts = extractOpts(cont);
32257 var cmin = cOpts.min;
32258 var cmax = cOpts.max;
32259
32260 var scl = cOpts.reversescale ?
32261 flipScale(cOpts.colorscale) :
32262 cOpts.colorscale;
32263
32264 var N = scl.length;
32265 var domain = new Array(N);
32266 var range = new Array(N);
32267
32268 for(var i = 0; i < N; i++) {
32269 var si = scl[i];
32270 domain[i] = cmin + si[0] * (cmax - cmin);
32271 range[i] = si[1];
32272 }
32273
32274 return {domain: domain, range: range};
32275}
32276
32277function flipScale(scl) {
32278 var N = scl.length;
32279 var sclNew = new Array(N);
32280
32281 for(var i = N - 1, j = 0; i >= 0; i--, j++) {
32282 var si = scl[i];
32283 sclNew[j] = [1 - si[0], si[1]];
32284 }
32285 return sclNew;
32286}
32287
32288/**
32289 * General colorscale function generator.
32290 *
32291 * @param {object} specs output of Colorscale.extractScale or precomputed domain, range.
32292 * - domain {array}
32293 * - range {array}
32294 *
32295 * @param {object} opts
32296 * - noNumericCheck {boolean} if true, scale func bypasses numeric checks
32297 * - returnArray {boolean} if true, scale func return 4-item array instead of color strings
32298 *
32299 * @return {function}
32300 */
32301function makeColorScaleFunc(specs, opts) {
32302 opts = opts || {};
32303
32304 var domain = specs.domain;
32305 var range = specs.range;
32306 var N = range.length;
32307 var _range = new Array(N);
32308
32309 for(var i = 0; i < N; i++) {
32310 var rgba = tinycolor(range[i]).toRgb();
32311 _range[i] = [rgba.r, rgba.g, rgba.b, rgba.a];
32312 }
32313
32314 var _sclFunc = d3.scale.linear()
32315 .domain(domain)
32316 .range(_range)
32317 .clamp(true);
32318
32319 var noNumericCheck = opts.noNumericCheck;
32320 var returnArray = opts.returnArray;
32321 var sclFunc;
32322
32323 if(noNumericCheck && returnArray) {
32324 sclFunc = _sclFunc;
32325 } else if(noNumericCheck) {
32326 sclFunc = function(v) {
32327 return colorArray2rbga(_sclFunc(v));
32328 };
32329 } else if(returnArray) {
32330 sclFunc = function(v) {
32331 if(isNumeric(v)) return _sclFunc(v);
32332 else if(tinycolor(v).isValid()) return v;
32333 else return Color.defaultLine;
32334 };
32335 } else {
32336 sclFunc = function(v) {
32337 if(isNumeric(v)) return colorArray2rbga(_sclFunc(v));
32338 else if(tinycolor(v).isValid()) return v;
32339 else return Color.defaultLine;
32340 };
32341 }
32342
32343 // colorbar draw looks into the d3 scale closure for domain and range
32344 sclFunc.domain = _sclFunc.domain;
32345 sclFunc.range = function() { return range; };
32346
32347 return sclFunc;
32348}
32349
32350function makeColorScaleFuncFromTrace(trace, opts) {
32351 return makeColorScaleFunc(extractScale(trace), opts);
32352}
32353
32354function colorArray2rbga(colorArray) {
32355 var colorObj = {
32356 r: colorArray[0],
32357 g: colorArray[1],
32358 b: colorArray[2],
32359 a: colorArray[3]
32360 };
32361
32362 return tinycolor(colorObj).toRgbString();
32363}
32364
32365module.exports = {
32366 hasColorscale: hasColorscale,
32367 extractOpts: extractOpts,
32368 extractScale: extractScale,
32369 flipScale: flipScale,
32370 makeColorScaleFunc: makeColorScaleFunc,
32371 makeColorScaleFuncFromTrace: makeColorScaleFuncFromTrace
32372};
32373
32374},{"../../lib":287,"../color":157,"./scales":172,"@plotly/d3":20,"fast-isnumeric":33,"tinycolor2":121}],169:[function(_dereq_,module,exports){
32375'use strict';
32376
32377var scales = _dereq_('./scales');
32378var helpers = _dereq_('./helpers');
32379
32380module.exports = {
32381 moduleType: 'component',
32382 name: 'colorscale',
32383
32384 attributes: _dereq_('./attributes'),
32385 layoutAttributes: _dereq_('./layout_attributes'),
32386
32387 supplyLayoutDefaults: _dereq_('./layout_defaults'),
32388 handleDefaults: _dereq_('./defaults'),
32389 crossTraceDefaults: _dereq_('./cross_trace_defaults'),
32390
32391 calc: _dereq_('./calc'),
32392
32393 // ./scales.js is required in lib/coerce.js ;
32394 // it needs to be a separate module to avoid circular a dependency
32395 scales: scales.scales,
32396 defaultScale: scales.defaultScale,
32397 getScale: scales.get,
32398 isValidScale: scales.isValid,
32399
32400 hasColorscale: helpers.hasColorscale,
32401 extractOpts: helpers.extractOpts,
32402 extractScale: helpers.extractScale,
32403 flipScale: helpers.flipScale,
32404 makeColorScaleFunc: helpers.makeColorScaleFunc,
32405 makeColorScaleFuncFromTrace: helpers.makeColorScaleFuncFromTrace
32406};
32407
32408},{"./attributes":164,"./calc":165,"./cross_trace_defaults":166,"./defaults":167,"./helpers":168,"./layout_attributes":170,"./layout_defaults":171,"./scales":172}],170:[function(_dereq_,module,exports){
32409'use strict';
32410
32411var extendFlat = _dereq_('../../lib/extend').extendFlat;
32412
32413var colorScaleAttrs = _dereq_('./attributes');
32414var scales = _dereq_('./scales').scales;
32415
32416var msg = 'Note that `autocolorscale` must be true for this attribute to work.';
32417
32418module.exports = {
32419 editType: 'calc',
32420
32421 colorscale: {
32422 editType: 'calc',
32423
32424 sequential: {
32425 valType: 'colorscale',
32426 dflt: scales.Reds,
32427 editType: 'calc',
32428 },
32429 sequentialminus: {
32430 valType: 'colorscale',
32431 dflt: scales.Blues,
32432 editType: 'calc',
32433 },
32434 diverging: {
32435 valType: 'colorscale',
32436 dflt: scales.RdBu,
32437 editType: 'calc',
32438 }
32439 },
32440
32441 coloraxis: extendFlat({
32442 // not really a 'subplot' attribute container,
32443 // but this is the flag we use to denote attributes that
32444 // support yaxis, yaxis2, yaxis3, ... counters
32445 _isSubplotObj: true,
32446 editType: 'calc',
32447 }, colorScaleAttrs('', {
32448 colorAttr: 'corresponding trace color array(s)',
32449 noColorAxis: true,
32450 showScaleDflt: true
32451 }))
32452};
32453
32454},{"../../lib/extend":281,"./attributes":164,"./scales":172}],171:[function(_dereq_,module,exports){
32455'use strict';
32456
32457var Lib = _dereq_('../../lib');
32458var Template = _dereq_('../../plot_api/plot_template');
32459
32460var colorScaleAttrs = _dereq_('./layout_attributes');
32461var colorScaleDefaults = _dereq_('./defaults');
32462
32463module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) {
32464 function coerce(attr, dflt) {
32465 return Lib.coerce(layoutIn, layoutOut, colorScaleAttrs, attr, dflt);
32466 }
32467
32468 coerce('colorscale.sequential');
32469 coerce('colorscale.sequentialminus');
32470 coerce('colorscale.diverging');
32471
32472 var colorAxes = layoutOut._colorAxes;
32473 var colorAxIn, colorAxOut;
32474
32475 function coerceAx(attr, dflt) {
32476 return Lib.coerce(colorAxIn, colorAxOut, colorScaleAttrs.coloraxis, attr, dflt);
32477 }
32478
32479 for(var k in colorAxes) {
32480 var stash = colorAxes[k];
32481
32482 if(stash[0]) {
32483 colorAxIn = layoutIn[k] || {};
32484 colorAxOut = Template.newContainer(layoutOut, k, 'coloraxis');
32485 colorAxOut._name = k;
32486 colorScaleDefaults(colorAxIn, colorAxOut, layoutOut, coerceAx, {prefix: '', cLetter: 'c'});
32487 } else {
32488 // re-coerce colorscale attributes w/o coloraxis
32489 for(var i = 0; i < stash[2].length; i++) {
32490 stash[2][i]();
32491 }
32492 delete layoutOut._colorAxes[k];
32493 }
32494 }
32495};
32496
32497},{"../../lib":287,"../../plot_api/plot_template":323,"./defaults":167,"./layout_attributes":170}],172:[function(_dereq_,module,exports){
32498'use strict';
32499
32500var tinycolor = _dereq_('tinycolor2');
32501
32502var scales = {
32503 'Greys': [
32504 [0, 'rgb(0,0,0)'], [1, 'rgb(255,255,255)']
32505 ],
32506
32507 'YlGnBu': [
32508 [0, 'rgb(8,29,88)'], [0.125, 'rgb(37,52,148)'],
32509 [0.25, 'rgb(34,94,168)'], [0.375, 'rgb(29,145,192)'],
32510 [0.5, 'rgb(65,182,196)'], [0.625, 'rgb(127,205,187)'],
32511 [0.75, 'rgb(199,233,180)'], [0.875, 'rgb(237,248,217)'],
32512 [1, 'rgb(255,255,217)']
32513 ],
32514
32515 'Greens': [
32516 [0, 'rgb(0,68,27)'], [0.125, 'rgb(0,109,44)'],
32517 [0.25, 'rgb(35,139,69)'], [0.375, 'rgb(65,171,93)'],
32518 [0.5, 'rgb(116,196,118)'], [0.625, 'rgb(161,217,155)'],
32519 [0.75, 'rgb(199,233,192)'], [0.875, 'rgb(229,245,224)'],
32520 [1, 'rgb(247,252,245)']
32521 ],
32522
32523 'YlOrRd': [
32524 [0, 'rgb(128,0,38)'], [0.125, 'rgb(189,0,38)'],
32525 [0.25, 'rgb(227,26,28)'], [0.375, 'rgb(252,78,42)'],
32526 [0.5, 'rgb(253,141,60)'], [0.625, 'rgb(254,178,76)'],
32527 [0.75, 'rgb(254,217,118)'], [0.875, 'rgb(255,237,160)'],
32528 [1, 'rgb(255,255,204)']
32529 ],
32530
32531 'Bluered': [
32532 [0, 'rgb(0,0,255)'], [1, 'rgb(255,0,0)']
32533 ],
32534
32535 // modified RdBu based on
32536 // http://www.kennethmoreland.com/color-maps/
32537 'RdBu': [
32538 [0, 'rgb(5,10,172)'], [0.35, 'rgb(106,137,247)'],
32539 [0.5, 'rgb(190,190,190)'], [0.6, 'rgb(220,170,132)'],
32540 [0.7, 'rgb(230,145,90)'], [1, 'rgb(178,10,28)']
32541 ],
32542
32543 // Scale for non-negative numeric values
32544 'Reds': [
32545 [0, 'rgb(220,220,220)'], [0.2, 'rgb(245,195,157)'],
32546 [0.4, 'rgb(245,160,105)'], [1, 'rgb(178,10,28)']
32547 ],
32548
32549 // Scale for non-positive numeric values
32550 'Blues': [
32551 [0, 'rgb(5,10,172)'], [0.35, 'rgb(40,60,190)'],
32552 [0.5, 'rgb(70,100,245)'], [0.6, 'rgb(90,120,245)'],
32553 [0.7, 'rgb(106,137,247)'], [1, 'rgb(220,220,220)']
32554 ],
32555
32556 'Picnic': [
32557 [0, 'rgb(0,0,255)'], [0.1, 'rgb(51,153,255)'],
32558 [0.2, 'rgb(102,204,255)'], [0.3, 'rgb(153,204,255)'],
32559 [0.4, 'rgb(204,204,255)'], [0.5, 'rgb(255,255,255)'],
32560 [0.6, 'rgb(255,204,255)'], [0.7, 'rgb(255,153,255)'],
32561 [0.8, 'rgb(255,102,204)'], [0.9, 'rgb(255,102,102)'],
32562 [1, 'rgb(255,0,0)']
32563 ],
32564
32565 'Rainbow': [
32566 [0, 'rgb(150,0,90)'], [0.125, 'rgb(0,0,200)'],
32567 [0.25, 'rgb(0,25,255)'], [0.375, 'rgb(0,152,255)'],
32568 [0.5, 'rgb(44,255,150)'], [0.625, 'rgb(151,255,0)'],
32569 [0.75, 'rgb(255,234,0)'], [0.875, 'rgb(255,111,0)'],
32570 [1, 'rgb(255,0,0)']
32571 ],
32572
32573 'Portland': [
32574 [0, 'rgb(12,51,131)'], [0.25, 'rgb(10,136,186)'],
32575 [0.5, 'rgb(242,211,56)'], [0.75, 'rgb(242,143,56)'],
32576 [1, 'rgb(217,30,30)']
32577 ],
32578
32579 'Jet': [
32580 [0, 'rgb(0,0,131)'], [0.125, 'rgb(0,60,170)'],
32581 [0.375, 'rgb(5,255,255)'], [0.625, 'rgb(255,255,0)'],
32582 [0.875, 'rgb(250,0,0)'], [1, 'rgb(128,0,0)']
32583 ],
32584
32585 'Hot': [
32586 [0, 'rgb(0,0,0)'], [0.3, 'rgb(230,0,0)'],
32587 [0.6, 'rgb(255,210,0)'], [1, 'rgb(255,255,255)']
32588 ],
32589
32590 'Blackbody': [
32591 [0, 'rgb(0,0,0)'], [0.2, 'rgb(230,0,0)'],
32592 [0.4, 'rgb(230,210,0)'], [0.7, 'rgb(255,255,255)'],
32593 [1, 'rgb(160,200,255)']
32594 ],
32595
32596 'Earth': [
32597 [0, 'rgb(0,0,130)'], [0.1, 'rgb(0,180,180)'],
32598 [0.2, 'rgb(40,210,40)'], [0.4, 'rgb(230,230,50)'],
32599 [0.6, 'rgb(120,70,20)'], [1, 'rgb(255,255,255)']
32600 ],
32601
32602 'Electric': [
32603 [0, 'rgb(0,0,0)'], [0.15, 'rgb(30,0,100)'],
32604 [0.4, 'rgb(120,0,100)'], [0.6, 'rgb(160,90,0)'],
32605 [0.8, 'rgb(230,200,0)'], [1, 'rgb(255,250,220)']
32606 ],
32607
32608 'Viridis': [
32609 [0, '#440154'], [0.06274509803921569, '#48186a'],
32610 [0.12549019607843137, '#472d7b'], [0.18823529411764706, '#424086'],
32611 [0.25098039215686274, '#3b528b'], [0.3137254901960784, '#33638d'],
32612 [0.3764705882352941, '#2c728e'], [0.4392156862745098, '#26828e'],
32613 [0.5019607843137255, '#21918c'], [0.5647058823529412, '#1fa088'],
32614 [0.6274509803921569, '#28ae80'], [0.6901960784313725, '#3fbc73'],
32615 [0.7529411764705882, '#5ec962'], [0.8156862745098039, '#84d44b'],
32616 [0.8784313725490196, '#addc30'], [0.9411764705882353, '#d8e219'],
32617 [1, '#fde725']
32618 ],
32619
32620 'Cividis': [
32621 [0.000000, 'rgb(0,32,76)'], [0.058824, 'rgb(0,42,102)'],
32622 [0.117647, 'rgb(0,52,110)'], [0.176471, 'rgb(39,63,108)'],
32623 [0.235294, 'rgb(60,74,107)'], [0.294118, 'rgb(76,85,107)'],
32624 [0.352941, 'rgb(91,95,109)'], [0.411765, 'rgb(104,106,112)'],
32625 [0.470588, 'rgb(117,117,117)'], [0.529412, 'rgb(131,129,120)'],
32626 [0.588235, 'rgb(146,140,120)'], [0.647059, 'rgb(161,152,118)'],
32627 [0.705882, 'rgb(176,165,114)'], [0.764706, 'rgb(192,177,109)'],
32628 [0.823529, 'rgb(209,191,102)'], [0.882353, 'rgb(225,204,92)'],
32629 [0.941176, 'rgb(243,219,79)'], [1.000000, 'rgb(255,233,69)']
32630 ]
32631};
32632
32633var defaultScale = scales.RdBu;
32634
32635function getScale(scl, dflt) {
32636 if(!dflt) dflt = defaultScale;
32637 if(!scl) return dflt;
32638
32639 function parseScale() {
32640 try {
32641 scl = scales[scl] || JSON.parse(scl);
32642 } catch(e) {
32643 scl = dflt;
32644 }
32645 }
32646
32647 if(typeof scl === 'string') {
32648 parseScale();
32649 // occasionally scl is double-JSON encoded...
32650 if(typeof scl === 'string') parseScale();
32651 }
32652
32653 if(!isValidScaleArray(scl)) return dflt;
32654 return scl;
32655}
32656
32657
32658function isValidScaleArray(scl) {
32659 var highestVal = 0;
32660
32661 if(!Array.isArray(scl) || scl.length < 2) return false;
32662
32663 if(!scl[0] || !scl[scl.length - 1]) return false;
32664
32665 if(+scl[0][0] !== 0 || +scl[scl.length - 1][0] !== 1) return false;
32666
32667 for(var i = 0; i < scl.length; i++) {
32668 var si = scl[i];
32669
32670 if(si.length !== 2 || +si[0] < highestVal || !tinycolor(si[1]).isValid()) {
32671 return false;
32672 }
32673
32674 highestVal = +si[0];
32675 }
32676
32677 return true;
32678}
32679
32680function isValidScale(scl) {
32681 if(scales[scl] !== undefined) return true;
32682 else return isValidScaleArray(scl);
32683}
32684
32685module.exports = {
32686 scales: scales,
32687 defaultScale: defaultScale,
32688
32689 get: getScale,
32690 isValid: isValidScale
32691};
32692
32693},{"tinycolor2":121}],173:[function(_dereq_,module,exports){
32694'use strict';
32695
32696
32697// for automatic alignment on dragging, <1/3 means left align,
32698// >2/3 means right, and between is center. Pick the right fraction
32699// based on where you are, and return the fraction corresponding to
32700// that position on the object
32701module.exports = function align(v, dv, v0, v1, anchor) {
32702 var vmin = (v - v0) / (v1 - v0);
32703 var vmax = vmin + dv / (v1 - v0);
32704 var vc = (vmin + vmax) / 2;
32705
32706 // explicitly specified anchor
32707 if(anchor === 'left' || anchor === 'bottom') return vmin;
32708 if(anchor === 'center' || anchor === 'middle') return vc;
32709 if(anchor === 'right' || anchor === 'top') return vmax;
32710
32711 // automatic based on position
32712 if(vmin < (2 / 3) - vc) return vmin;
32713 if(vmax > (4 / 3) - vc) return vmax;
32714 return vc;
32715};
32716
32717},{}],174:[function(_dereq_,module,exports){
32718'use strict';
32719
32720var Lib = _dereq_('../../lib');
32721
32722
32723// set cursors pointing toward the closest corner/side,
32724// to indicate alignment
32725// x and y are 0-1, fractions of the plot area
32726var cursorset = [
32727 ['sw-resize', 's-resize', 'se-resize'],
32728 ['w-resize', 'move', 'e-resize'],
32729 ['nw-resize', 'n-resize', 'ne-resize']
32730];
32731
32732module.exports = function getCursor(x, y, xanchor, yanchor) {
32733 if(xanchor === 'left') x = 0;
32734 else if(xanchor === 'center') x = 1;
32735 else if(xanchor === 'right') x = 2;
32736 else x = Lib.constrain(Math.floor(x * 3), 0, 2);
32737
32738 if(yanchor === 'bottom') y = 0;
32739 else if(yanchor === 'middle') y = 1;
32740 else if(yanchor === 'top') y = 2;
32741 else y = Lib.constrain(Math.floor(y * 3), 0, 2);
32742
32743 return cursorset[y][x];
32744};
32745
32746},{"../../lib":287}],175:[function(_dereq_,module,exports){
32747'use strict';
32748
32749exports.selectMode = function(dragmode) {
32750 return (
32751 dragmode === 'lasso' ||
32752 dragmode === 'select'
32753 );
32754};
32755
32756exports.drawMode = function(dragmode) {
32757 return (
32758 dragmode === 'drawclosedpath' ||
32759 dragmode === 'drawopenpath' ||
32760 dragmode === 'drawline' ||
32761 dragmode === 'drawrect' ||
32762 dragmode === 'drawcircle'
32763 );
32764};
32765
32766exports.openMode = function(dragmode) {
32767 return (
32768 dragmode === 'drawline' ||
32769 dragmode === 'drawopenpath'
32770 );
32771};
32772
32773exports.rectMode = function(dragmode) {
32774 return (
32775 dragmode === 'select' ||
32776 dragmode === 'drawline' ||
32777 dragmode === 'drawrect' ||
32778 dragmode === 'drawcircle'
32779 );
32780};
32781
32782exports.freeMode = function(dragmode) {
32783 return (
32784 dragmode === 'lasso' ||
32785 dragmode === 'drawclosedpath' ||
32786 dragmode === 'drawopenpath'
32787 );
32788};
32789
32790exports.selectingOrDrawing = function(dragmode) {
32791 return (
32792 exports.freeMode(dragmode) ||
32793 exports.rectMode(dragmode)
32794 );
32795};
32796
32797},{}],176:[function(_dereq_,module,exports){
32798'use strict';
32799
32800var mouseOffset = _dereq_('mouse-event-offset');
32801var hasHover = _dereq_('has-hover');
32802var supportsPassive = _dereq_('has-passive-events');
32803
32804var removeElement = _dereq_('../../lib').removeElement;
32805var constants = _dereq_('../../plots/cartesian/constants');
32806
32807var dragElement = module.exports = {};
32808
32809dragElement.align = _dereq_('./align');
32810dragElement.getCursor = _dereq_('./cursor');
32811
32812var unhover = _dereq_('./unhover');
32813dragElement.unhover = unhover.wrapped;
32814dragElement.unhoverRaw = unhover.raw;
32815
32816/**
32817 * Abstracts click & drag interactions
32818 *
32819 * During the interaction, a "coverSlip" element - a transparent
32820 * div covering the whole page - is created, which has two key effects:
32821 * - Lets you drag beyond the boundaries of the plot itself without
32822 * dropping (but if you drag all the way out of the browser window the
32823 * interaction will end)
32824 * - Freezes the cursor: whatever mouse cursor the drag element had when the
32825 * interaction started gets copied to the coverSlip for use until mouseup
32826 *
32827 * If the user executes a drag bigger than MINDRAG, callbacks will fire as:
32828 * prepFn, moveFn (1 or more times), doneFn
32829 * If the user does not drag enough, prepFn and clickFn will fire.
32830 *
32831 * Note: If you cancel contextmenu, clickFn will fire even with a right click
32832 * (unlike native events) so you'll get a `plotly_click` event. Cancel context eg:
32833 * gd.addEventListener('contextmenu', function(e) { e.preventDefault(); });
32834 * TODO: we should probably turn this into a `config` parameter, so we can fix it
32835 * such that if you *don't* cancel contextmenu, we can prevent partial drags, which
32836 * put you in a weird state.
32837 *
32838 * If the user clicks multiple times quickly, clickFn will fire each time
32839 * but numClicks will increase to help you recognize doubleclicks.
32840 *
32841 * @param {object} options with keys:
32842 * element (required) the DOM element to drag
32843 * prepFn (optional) function(event, startX, startY)
32844 * executed on mousedown
32845 * startX and startY are the clientX and clientY pixel position
32846 * of the mousedown event
32847 * moveFn (optional) function(dx, dy)
32848 * executed on move, ONLY after we've exceeded MINDRAG
32849 * (we keep executing moveFn if you move back to where you started)
32850 * dx and dy are the net pixel offset of the drag,
32851 * dragged is true/false, has the mouse moved enough to
32852 * constitute a drag
32853 * doneFn (optional) function(e)
32854 * executed on mouseup, ONLY if we exceeded MINDRAG (so you can be
32855 * sure that moveFn has been called at least once)
32856 * numClicks is how many clicks we've registered within
32857 * a doubleclick time
32858 * e is the original mouseup event
32859 * clickFn (optional) function(numClicks, e)
32860 * executed on mouseup if we have NOT exceeded MINDRAG (ie moveFn
32861 * has not been called at all)
32862 * numClicks is how many clicks we've registered within
32863 * a doubleclick time
32864 * e is the original mousedown event
32865 * clampFn (optional, function(dx, dy) return [dx2, dy2])
32866 * Provide custom clamping function for small displacements.
32867 * By default, clamping is done using `minDrag` to x and y displacements
32868 * independently.
32869 */
32870dragElement.init = function init(options) {
32871 var gd = options.gd;
32872 var numClicks = 1;
32873 var doubleClickDelay = gd._context.doubleClickDelay;
32874 var element = options.element;
32875
32876 var startX,
32877 startY,
32878 newMouseDownTime,
32879 cursor,
32880 dragCover,
32881 initialEvent,
32882 initialTarget,
32883 rightClick;
32884
32885 if(!gd._mouseDownTime) gd._mouseDownTime = 0;
32886
32887 element.style.pointerEvents = 'all';
32888
32889 element.onmousedown = onStart;
32890
32891 if(!supportsPassive) {
32892 element.ontouchstart = onStart;
32893 } else {
32894 if(element._ontouchstart) {
32895 element.removeEventListener('touchstart', element._ontouchstart);
32896 }
32897 element._ontouchstart = onStart;
32898 element.addEventListener('touchstart', onStart, {passive: false});
32899 }
32900
32901 function _clampFn(dx, dy, minDrag) {
32902 if(Math.abs(dx) < minDrag) dx = 0;
32903 if(Math.abs(dy) < minDrag) dy = 0;
32904 return [dx, dy];
32905 }
32906
32907 var clampFn = options.clampFn || _clampFn;
32908
32909 function onStart(e) {
32910 // make dragging and dragged into properties of gd
32911 // so that others can look at and modify them
32912 gd._dragged = false;
32913 gd._dragging = true;
32914 var offset = pointerOffset(e);
32915 startX = offset[0];
32916 startY = offset[1];
32917 initialTarget = e.target;
32918 initialEvent = e;
32919 rightClick = e.buttons === 2 || e.ctrlKey;
32920
32921 // fix Fx.hover for touch events
32922 if(typeof e.clientX === 'undefined' && typeof e.clientY === 'undefined') {
32923 e.clientX = startX;
32924 e.clientY = startY;
32925 }
32926
32927 newMouseDownTime = (new Date()).getTime();
32928 if(newMouseDownTime - gd._mouseDownTime < doubleClickDelay) {
32929 // in a click train
32930 numClicks += 1;
32931 } else {
32932 // new click train
32933 numClicks = 1;
32934 gd._mouseDownTime = newMouseDownTime;
32935 }
32936
32937 if(options.prepFn) options.prepFn(e, startX, startY);
32938
32939 if(hasHover && !rightClick) {
32940 dragCover = coverSlip();
32941 dragCover.style.cursor = window.getComputedStyle(element).cursor;
32942 } else if(!hasHover) {
32943 // document acts as a dragcover for mobile, bc we can't create dragcover dynamically
32944 dragCover = document;
32945 cursor = window.getComputedStyle(document.documentElement).cursor;
32946 document.documentElement.style.cursor = window.getComputedStyle(element).cursor;
32947 }
32948
32949 document.addEventListener('mouseup', onDone);
32950 document.addEventListener('touchend', onDone);
32951
32952 if(options.dragmode !== false) {
32953 e.preventDefault();
32954 document.addEventListener('mousemove', onMove);
32955 document.addEventListener('touchmove', onMove, {passive: false});
32956 }
32957
32958 return;
32959 }
32960
32961 function onMove(e) {
32962 e.preventDefault();
32963
32964 var offset = pointerOffset(e);
32965 var minDrag = options.minDrag || constants.MINDRAG;
32966 var dxdy = clampFn(offset[0] - startX, offset[1] - startY, minDrag);
32967 var dx = dxdy[0];
32968 var dy = dxdy[1];
32969
32970 if(dx || dy) {
32971 gd._dragged = true;
32972 dragElement.unhover(gd, e);
32973 }
32974
32975 if(gd._dragged && options.moveFn && !rightClick) {
32976 gd._dragdata = {
32977 element: element,
32978 dx: dx,
32979 dy: dy
32980 };
32981 options.moveFn(dx, dy);
32982 }
32983
32984 return;
32985 }
32986
32987 function onDone(e) {
32988 delete gd._dragdata;
32989
32990 if(options.dragmode !== false) {
32991 e.preventDefault();
32992 document.removeEventListener('mousemove', onMove);
32993 document.removeEventListener('touchmove', onMove);
32994 }
32995
32996 document.removeEventListener('mouseup', onDone);
32997 document.removeEventListener('touchend', onDone);
32998
32999 if(hasHover) {
33000 removeElement(dragCover);
33001 } else if(cursor) {
33002 dragCover.documentElement.style.cursor = cursor;
33003 cursor = null;
33004 }
33005
33006 if(!gd._dragging) {
33007 gd._dragged = false;
33008 return;
33009 }
33010 gd._dragging = false;
33011
33012 // don't count as a dblClick unless the mouseUp is also within
33013 // the dblclick delay
33014 if((new Date()).getTime() - gd._mouseDownTime > doubleClickDelay) {
33015 numClicks = Math.max(numClicks - 1, 1);
33016 }
33017
33018 if(gd._dragged) {
33019 if(options.doneFn) options.doneFn();
33020 } else {
33021 if(options.clickFn) options.clickFn(numClicks, initialEvent);
33022
33023 // If we haven't dragged, this should be a click. But because of the
33024 // coverSlip changing the element, the natural system might not generate one,
33025 // so we need to make our own. But right clicks don't normally generate
33026 // click events, only contextmenu events, which happen on mousedown.
33027 if(!rightClick) {
33028 var e2;
33029
33030 try {
33031 e2 = new MouseEvent('click', e);
33032 } catch(err) {
33033 var offset = pointerOffset(e);
33034 e2 = document.createEvent('MouseEvents');
33035 e2.initMouseEvent('click',
33036 e.bubbles, e.cancelable,
33037 e.view, e.detail,
33038 e.screenX, e.screenY,
33039 offset[0], offset[1],
33040 e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
33041 e.button, e.relatedTarget);
33042 }
33043
33044 initialTarget.dispatchEvent(e2);
33045 }
33046 }
33047
33048 gd._dragging = false;
33049 gd._dragged = false;
33050 return;
33051 }
33052};
33053
33054function coverSlip() {
33055 var cover = document.createElement('div');
33056
33057 cover.className = 'dragcover';
33058 var cStyle = cover.style;
33059 cStyle.position = 'fixed';
33060 cStyle.left = 0;
33061 cStyle.right = 0;
33062 cStyle.top = 0;
33063 cStyle.bottom = 0;
33064 cStyle.zIndex = 999999999;
33065 cStyle.background = 'none';
33066
33067 document.body.appendChild(cover);
33068
33069 return cover;
33070}
33071
33072dragElement.coverSlip = coverSlip;
33073
33074function pointerOffset(e) {
33075 return mouseOffset(
33076 e.changedTouches ? e.changedTouches[0] : e,
33077 document.body
33078 );
33079}
33080
33081},{"../../lib":287,"../../plots/cartesian/constants":341,"./align":173,"./cursor":174,"./unhover":177,"has-hover":64,"has-passive-events":65,"mouse-event-offset":71}],177:[function(_dereq_,module,exports){
33082'use strict';
33083
33084var Events = _dereq_('../../lib/events');
33085var throttle = _dereq_('../../lib/throttle');
33086var getGraphDiv = _dereq_('../../lib/dom').getGraphDiv;
33087
33088var hoverConstants = _dereq_('../fx/constants');
33089
33090var unhover = module.exports = {};
33091
33092unhover.wrapped = function(gd, evt, subplot) {
33093 gd = getGraphDiv(gd);
33094
33095 // Important, clear any queued hovers
33096 if(gd._fullLayout) {
33097 throttle.clear(gd._fullLayout._uid + hoverConstants.HOVERID);
33098 }
33099
33100 unhover.raw(gd, evt, subplot);
33101};
33102
33103
33104// remove hover effects on mouse out, and emit unhover event
33105unhover.raw = function raw(gd, evt) {
33106 var fullLayout = gd._fullLayout;
33107 var oldhoverdata = gd._hoverdata;
33108
33109 if(!evt) evt = {};
33110 if(evt.target && !gd._dragged &&
33111 Events.triggerHandler(gd, 'plotly_beforehover', evt) === false) {
33112 return;
33113 }
33114
33115 fullLayout._hoverlayer.selectAll('g').remove();
33116 fullLayout._hoverlayer.selectAll('line').remove();
33117 fullLayout._hoverlayer.selectAll('circle').remove();
33118 gd._hoverdata = undefined;
33119
33120 if(evt.target && oldhoverdata) {
33121 gd.emit('plotly_unhover', {
33122 event: evt,
33123 points: oldhoverdata
33124 });
33125 }
33126};
33127
33128},{"../../lib/dom":279,"../../lib/events":280,"../../lib/throttle":311,"../fx/constants":191}],178:[function(_dereq_,module,exports){
33129'use strict';
33130
33131exports.dash = {
33132 valType: 'string',
33133 // string type usually doesn't take values... this one should really be
33134 // a special type or at least a special coercion function, from the GUI
33135 // you only get these values but elsewhere the user can supply a list of
33136 // dash lengths in px, and it will be honored
33137 values: ['solid', 'dot', 'dash', 'longdash', 'dashdot', 'longdashdot'],
33138 dflt: 'solid',
33139 editType: 'style',
33140};
33141
33142exports.pattern = {
33143 shape: {
33144 valType: 'enumerated',
33145 values: ['', '/', '\\', 'x', '-', '|', '+', '.'],
33146 dflt: '',
33147 arrayOk: true,
33148 editType: 'style',
33149 },
33150 fillmode: {
33151 valType: 'enumerated',
33152 values: ['replace', 'overlay'],
33153 dflt: 'replace',
33154 editType: 'style',
33155 },
33156 bgcolor: {
33157 valType: 'color',
33158 arrayOk: true,
33159 editType: 'style',
33160 },
33161 fgcolor: {
33162 valType: 'color',
33163 arrayOk: true,
33164 editType: 'style',
33165 },
33166 fgopacity: {
33167 valType: 'number',
33168 editType: 'style',
33169 min: 0,
33170 max: 1,
33171 },
33172 size: {
33173 valType: 'number',
33174 min: 0,
33175 dflt: 8,
33176 arrayOk: true,
33177 editType: 'style',
33178 },
33179 solidity: {
33180 valType: 'number',
33181 min: 0,
33182 max: 1,
33183 dflt: 0.3,
33184 arrayOk: true,
33185 editType: 'style',
33186 },
33187 editType: 'style',
33188};
33189
33190},{}],179:[function(_dereq_,module,exports){
33191'use strict';
33192
33193var d3 = _dereq_('@plotly/d3');
33194var Lib = _dereq_('../../lib');
33195var numberFormat = Lib.numberFormat;
33196var isNumeric = _dereq_('fast-isnumeric');
33197var tinycolor = _dereq_('tinycolor2');
33198
33199var Registry = _dereq_('../../registry');
33200var Color = _dereq_('../color');
33201var Colorscale = _dereq_('../colorscale');
33202var strTranslate = Lib.strTranslate;
33203var svgTextUtils = _dereq_('../../lib/svg_text_utils');
33204
33205var xmlnsNamespaces = _dereq_('../../constants/xmlns_namespaces');
33206var alignment = _dereq_('../../constants/alignment');
33207var LINE_SPACING = alignment.LINE_SPACING;
33208var DESELECTDIM = _dereq_('../../constants/interactions').DESELECTDIM;
33209
33210var subTypes = _dereq_('../../traces/scatter/subtypes');
33211var makeBubbleSizeFn = _dereq_('../../traces/scatter/make_bubble_size_func');
33212var appendArrayPointValue = _dereq_('../../components/fx/helpers').appendArrayPointValue;
33213
33214var drawing = module.exports = {};
33215
33216// -----------------------------------------------------
33217// styling functions for plot elements
33218// -----------------------------------------------------
33219
33220drawing.font = function(s, family, size, color) {
33221 // also allow the form font(s, {family, size, color})
33222 if(Lib.isPlainObject(family)) {
33223 color = family.color;
33224 size = family.size;
33225 family = family.family;
33226 }
33227 if(family) s.style('font-family', family);
33228 if(size + 1) s.style('font-size', size + 'px');
33229 if(color) s.call(Color.fill, color);
33230};
33231
33232/*
33233 * Positioning helpers
33234 * Note: do not use `setPosition` with <text> nodes modified by
33235 * `svgTextUtils.convertToTspans`. Use `svgTextUtils.positionText`
33236 * instead, so that <tspan.line> elements get updated to match.
33237 */
33238drawing.setPosition = function(s, x, y) { s.attr('x', x).attr('y', y); };
33239drawing.setSize = function(s, w, h) { s.attr('width', w).attr('height', h); };
33240drawing.setRect = function(s, x, y, w, h) {
33241 s.call(drawing.setPosition, x, y).call(drawing.setSize, w, h);
33242};
33243
33244/** Translate node
33245 *
33246 * @param {object} d : calcdata point item
33247 * @param {sel} sel : d3 selction of node to translate
33248 * @param {object} xa : corresponding full xaxis object
33249 * @param {object} ya : corresponding full yaxis object
33250 *
33251 * @return {boolean} :
33252 * true if selection got translated
33253 * false if selection could not get translated
33254 */
33255drawing.translatePoint = function(d, sel, xa, ya) {
33256 var x = xa.c2p(d.x);
33257 var y = ya.c2p(d.y);
33258
33259 if(isNumeric(x) && isNumeric(y) && sel.node()) {
33260 // for multiline text this works better
33261 if(sel.node().nodeName === 'text') {
33262 sel.attr('x', x).attr('y', y);
33263 } else {
33264 sel.attr('transform', strTranslate(x, y));
33265 }
33266 } else {
33267 return false;
33268 }
33269
33270 return true;
33271};
33272
33273drawing.translatePoints = function(s, xa, ya) {
33274 s.each(function(d) {
33275 var sel = d3.select(this);
33276 drawing.translatePoint(d, sel, xa, ya);
33277 });
33278};
33279
33280drawing.hideOutsideRangePoint = function(d, sel, xa, ya, xcalendar, ycalendar) {
33281 sel.attr(
33282 'display',
33283 (xa.isPtWithinRange(d, xcalendar) && ya.isPtWithinRange(d, ycalendar)) ? null : 'none'
33284 );
33285};
33286
33287drawing.hideOutsideRangePoints = function(traceGroups, subplot) {
33288 if(!subplot._hasClipOnAxisFalse) return;
33289
33290 var xa = subplot.xaxis;
33291 var ya = subplot.yaxis;
33292
33293 traceGroups.each(function(d) {
33294 var trace = d[0].trace;
33295 var xcalendar = trace.xcalendar;
33296 var ycalendar = trace.ycalendar;
33297 var selector = Registry.traceIs(trace, 'bar-like') ? '.bartext' : '.point,.textpoint';
33298
33299 traceGroups.selectAll(selector).each(function(d) {
33300 drawing.hideOutsideRangePoint(d, d3.select(this), xa, ya, xcalendar, ycalendar);
33301 });
33302 });
33303};
33304
33305drawing.crispRound = function(gd, lineWidth, dflt) {
33306 // for lines that disable antialiasing we want to
33307 // make sure the width is an integer, and at least 1 if it's nonzero
33308
33309 if(!lineWidth || !isNumeric(lineWidth)) return dflt || 0;
33310
33311 // but not for static plots - these don't get antialiased anyway.
33312 if(gd._context.staticPlot) return lineWidth;
33313
33314 if(lineWidth < 1) return 1;
33315 return Math.round(lineWidth);
33316};
33317
33318drawing.singleLineStyle = function(d, s, lw, lc, ld) {
33319 s.style('fill', 'none');
33320 var line = (((d || [])[0] || {}).trace || {}).line || {};
33321 var lw1 = lw || line.width || 0;
33322 var dash = ld || line.dash || '';
33323
33324 Color.stroke(s, lc || line.color);
33325 drawing.dashLine(s, dash, lw1);
33326};
33327
33328drawing.lineGroupStyle = function(s, lw, lc, ld) {
33329 s.style('fill', 'none')
33330 .each(function(d) {
33331 var line = (((d || [])[0] || {}).trace || {}).line || {};
33332 var lw1 = lw || line.width || 0;
33333 var dash = ld || line.dash || '';
33334
33335 d3.select(this)
33336 .call(Color.stroke, lc || line.color)
33337 .call(drawing.dashLine, dash, lw1);
33338 });
33339};
33340
33341drawing.dashLine = function(s, dash, lineWidth) {
33342 lineWidth = +lineWidth || 0;
33343
33344 dash = drawing.dashStyle(dash, lineWidth);
33345
33346 s.style({
33347 'stroke-dasharray': dash,
33348 'stroke-width': lineWidth + 'px'
33349 });
33350};
33351
33352drawing.dashStyle = function(dash, lineWidth) {
33353 lineWidth = +lineWidth || 1;
33354 var dlw = Math.max(lineWidth, 3);
33355
33356 if(dash === 'solid') dash = '';
33357 else if(dash === 'dot') dash = dlw + 'px,' + dlw + 'px';
33358 else if(dash === 'dash') dash = (3 * dlw) + 'px,' + (3 * dlw) + 'px';
33359 else if(dash === 'longdash') dash = (5 * dlw) + 'px,' + (5 * dlw) + 'px';
33360 else if(dash === 'dashdot') {
33361 dash = (3 * dlw) + 'px,' + dlw + 'px,' + dlw + 'px,' + dlw + 'px';
33362 } else if(dash === 'longdashdot') {
33363 dash = (5 * dlw) + 'px,' + (2 * dlw) + 'px,' + dlw + 'px,' + (2 * dlw) + 'px';
33364 }
33365 // otherwise user wrote the dasharray themselves - leave it be
33366
33367 return dash;
33368};
33369
33370// Same as fillGroupStyle, except in this case the selection may be a transition
33371drawing.singleFillStyle = function(sel) {
33372 var node = d3.select(sel.node());
33373 var data = node.data();
33374 var fillcolor = (((data[0] || [])[0] || {}).trace || {}).fillcolor;
33375 if(fillcolor) {
33376 sel.call(Color.fill, fillcolor);
33377 }
33378};
33379
33380drawing.fillGroupStyle = function(s) {
33381 s.style('stroke-width', 0)
33382 .each(function(d) {
33383 var shape = d3.select(this);
33384 // N.B. 'd' won't be a calcdata item when
33385 // fill !== 'none' on a segment-less and marker-less trace
33386 if(d[0].trace) {
33387 shape.call(Color.fill, d[0].trace.fillcolor);
33388 }
33389 });
33390};
33391
33392var SYMBOLDEFS = _dereq_('./symbol_defs');
33393
33394drawing.symbolNames = [];
33395drawing.symbolFuncs = [];
33396drawing.symbolNeedLines = {};
33397drawing.symbolNoDot = {};
33398drawing.symbolNoFill = {};
33399drawing.symbolList = [];
33400
33401Object.keys(SYMBOLDEFS).forEach(function(k) {
33402 var symDef = SYMBOLDEFS[k];
33403 var n = symDef.n;
33404 drawing.symbolList.push(
33405 n,
33406 String(n),
33407 k,
33408
33409 n + 100,
33410 String(n + 100),
33411 k + '-open'
33412 );
33413 drawing.symbolNames[n] = k;
33414 drawing.symbolFuncs[n] = symDef.f;
33415
33416 if(symDef.needLine) {
33417 drawing.symbolNeedLines[n] = true;
33418 }
33419 if(symDef.noDot) {
33420 drawing.symbolNoDot[n] = true;
33421 } else {
33422 drawing.symbolList.push(
33423 n + 200,
33424 String(n + 200),
33425 k + '-dot',
33426
33427 n + 300,
33428 String(n + 300),
33429 k + '-open-dot'
33430 );
33431 }
33432 if(symDef.noFill) {
33433 drawing.symbolNoFill[n] = true;
33434 }
33435});
33436
33437var MAXSYMBOL = drawing.symbolNames.length;
33438// add a dot in the middle of the symbol
33439var DOTPATH = 'M0,0.5L0.5,0L0,-0.5L-0.5,0Z';
33440
33441drawing.symbolNumber = function(v) {
33442 if(isNumeric(v)) {
33443 v = +v;
33444 } else if(typeof v === 'string') {
33445 var vbase = 0;
33446 if(v.indexOf('-open') > 0) {
33447 vbase = 100;
33448 v = v.replace('-open', '');
33449 }
33450 if(v.indexOf('-dot') > 0) {
33451 vbase += 200;
33452 v = v.replace('-dot', '');
33453 }
33454 v = drawing.symbolNames.indexOf(v);
33455 if(v >= 0) { v += vbase; }
33456 }
33457
33458 return (v % 100 >= MAXSYMBOL || v >= 400) ?
33459 0 : Math.floor(Math.max(v, 0));
33460};
33461
33462function makePointPath(symbolNumber, r) {
33463 var base = symbolNumber % 100;
33464 return drawing.symbolFuncs[base](r) + (symbolNumber >= 200 ? DOTPATH : '');
33465}
33466
33467var HORZGRADIENT = {x1: 1, x2: 0, y1: 0, y2: 0};
33468var VERTGRADIENT = {x1: 0, x2: 0, y1: 1, y2: 0};
33469var stopFormatter = numberFormat('~f');
33470var gradientInfo = {
33471 radial: {node: 'radialGradient'},
33472 radialreversed: {node: 'radialGradient', reversed: true},
33473 horizontal: {node: 'linearGradient', attrs: HORZGRADIENT},
33474 horizontalreversed: {node: 'linearGradient', attrs: HORZGRADIENT, reversed: true},
33475 vertical: {node: 'linearGradient', attrs: VERTGRADIENT},
33476 verticalreversed: {node: 'linearGradient', attrs: VERTGRADIENT, reversed: true}
33477};
33478
33479/**
33480 * gradient: create and apply a gradient fill
33481 *
33482 * @param {object} sel: d3 selection to apply this gradient to
33483 * You can use `selection.call(Drawing.gradient, ...)`
33484 * @param {DOM element} gd: the graph div `sel` is part of
33485 * @param {string} gradientID: a unique (within this plot) identifier
33486 * for this gradient, so that we don't create unnecessary definitions
33487 * @param {string} type: 'radial', 'horizontal', or 'vertical', optionally with
33488 * 'reversed' at the end. Normally radial goes center to edge,
33489 * horizontal goes right to left, and vertical goes bottom to top
33490 * @param {array} colorscale: as in attribute values, [[fraction, color], ...]
33491 * @param {string} prop: the property to apply to, 'fill' or 'stroke'
33492 */
33493drawing.gradient = function(sel, gd, gradientID, type, colorscale, prop) {
33494 var len = colorscale.length;
33495 var info = gradientInfo[type];
33496 var colorStops = new Array(len);
33497 for(var i = 0; i < len; i++) {
33498 if(info.reversed) {
33499 colorStops[len - 1 - i] = [stopFormatter((1 - colorscale[i][0]) * 100), colorscale[i][1]];
33500 } else {
33501 colorStops[i] = [stopFormatter(colorscale[i][0] * 100), colorscale[i][1]];
33502 }
33503 }
33504
33505 var fullLayout = gd._fullLayout;
33506 var fullID = 'g' + fullLayout._uid + '-' + gradientID;
33507
33508 var gradient = fullLayout._defs.select('.gradients')
33509 .selectAll('#' + fullID)
33510 .data([type + colorStops.join(';')], Lib.identity);
33511
33512 gradient.exit().remove();
33513
33514 gradient.enter()
33515 .append(info.node)
33516 .each(function() {
33517 var el = d3.select(this);
33518 if(info.attrs) el.attr(info.attrs);
33519
33520 el.attr('id', fullID);
33521
33522 var stops = el.selectAll('stop')
33523 .data(colorStops);
33524 stops.exit().remove();
33525 stops.enter().append('stop');
33526
33527 stops.each(function(d) {
33528 var tc = tinycolor(d[1]);
33529 d3.select(this).attr({
33530 offset: d[0] + '%',
33531 'stop-color': Color.tinyRGB(tc),
33532 'stop-opacity': tc.getAlpha()
33533 });
33534 });
33535 });
33536
33537 sel.style(prop, getFullUrl(fullID, gd))
33538 .style(prop + '-opacity', null);
33539
33540 var className2query = function(s) {
33541 return '.' + s.attr('class').replace(/\s/g, '.');
33542 };
33543 var k = className2query(d3.select(sel.node().parentNode)) +
33544 '>' + className2query(sel);
33545 fullLayout._gradientUrlQueryParts[k] = 1;
33546};
33547
33548/**
33549 * pattern: create and apply a pattern fill
33550 *
33551 * @param {object} sel: d3 selection to apply this pattern to
33552 * You can use `selection.call(Drawing.pattern, ...)`
33553 * @param {string} calledBy: option to know the caller component
33554 * @param {DOM element} gd: the graph div `sel` is part of
33555 * @param {string} patternID: a unique (within this plot) identifier
33556 * for this pattern, so that we don't create unnecessary definitions
33557 * @param {number} size: size of unit squares for repetition of this pattern
33558 * @param {number} solidity: how solid lines of this pattern are
33559 * @param {string} mcc: color when painted with colorscale
33560 * @param {string} fillmode: fillmode for this pattern
33561 * @param {string} bgcolor: background color for this pattern
33562 * @param {string} fgcolor: foreground color for this pattern
33563 * @param {number} fgopacity: foreground opacity for this pattern
33564 */
33565drawing.pattern = function(sel, calledBy, gd, patternID, shape, size, solidity, mcc, fillmode, bgcolor, fgcolor, fgopacity) {
33566 var isLegend = calledBy === 'legend';
33567
33568 if(mcc) {
33569 if(fillmode === 'overlay') {
33570 bgcolor = mcc;
33571 fgcolor = Color.contrast(bgcolor);
33572 } else {
33573 bgcolor = undefined;
33574 fgcolor = mcc;
33575 }
33576 }
33577
33578 var fullLayout = gd._fullLayout;
33579 var fullID = 'p' + fullLayout._uid + '-' + patternID;
33580 var width, height;
33581
33582 // linear interpolation
33583 var linearFn = function(x, x0, x1, y0, y1) {
33584 return y0 + (y1 - y0) * (x - x0) / (x1 - x0);
33585 };
33586
33587 var path, linewidth, radius;
33588 var patternTag;
33589 var patternAttrs = {};
33590 switch(shape) {
33591 case '/':
33592 width = size * Math.sqrt(2);
33593 height = size * Math.sqrt(2);
33594 path = 'M-' + (width / 4) + ',' + (height / 4) + 'l' + (width / 2) + ',-' + (height / 2) +
33595 'M0,' + height + 'L' + width + ',0' +
33596 'M' + (width / 4 * 3) + ',' + (height / 4 * 5) + 'l' + (width / 2) + ',-' + (height / 2);
33597 linewidth = solidity * size;
33598 patternTag = 'path';
33599 patternAttrs = {
33600 'd': path,
33601 'opacity': fgopacity,
33602 'stroke': fgcolor,
33603 'stroke-width': linewidth + 'px'
33604 };
33605 break;
33606 case '\\':
33607 width = size * Math.sqrt(2);
33608 height = size * Math.sqrt(2);
33609 path = 'M' + (width / 4 * 3) + ',-' + (height / 4) + 'l' + (width / 2) + ',' + (height / 2) +
33610 'M0,0L' + width + ',' + height +
33611 'M-' + (width / 4) + ',' + (height / 4 * 3) + 'l' + (width / 2) + ',' + (height / 2);
33612 linewidth = solidity * size;
33613 patternTag = 'path';
33614 patternAttrs = {
33615 'd': path,
33616 'opacity': fgopacity,
33617 'stroke': fgcolor,
33618 'stroke-width': linewidth + 'px'
33619 };
33620 break;
33621 case 'x':
33622 width = size * Math.sqrt(2);
33623 height = size * Math.sqrt(2);
33624 path = 'M-' + (width / 4) + ',' + (height / 4) + 'l' + (width / 2) + ',-' + (height / 2) +
33625 'M0,' + height + 'L' + width + ',0' +
33626 'M' + (width / 4 * 3) + ',' + (height / 4 * 5) + 'l' + (width / 2) + ',-' + (height / 2) +
33627 'M' + (width / 4 * 3) + ',-' + (height / 4) + 'l' + (width / 2) + ',' + (height / 2) +
33628 'M0,0L' + width + ',' + height +
33629 'M-' + (width / 4) + ',' + (height / 4 * 3) + 'l' + (width / 2) + ',' + (height / 2);
33630 linewidth = size - size * Math.sqrt(1.0 - solidity);
33631 patternTag = 'path';
33632 patternAttrs = {
33633 'd': path,
33634 'opacity': fgopacity,
33635 'stroke': fgcolor,
33636 'stroke-width': linewidth + 'px'
33637 };
33638 break;
33639 case '|':
33640 width = size;
33641 height = size;
33642 patternTag = 'path';
33643 path = 'M' + (width / 2) + ',0L' + (width / 2) + ',' + height;
33644 linewidth = solidity * size;
33645 patternTag = 'path';
33646 patternAttrs = {
33647 'd': path,
33648 'opacity': fgopacity,
33649 'stroke': fgcolor,
33650 'stroke-width': linewidth + 'px'
33651 };
33652 break;
33653 case '-':
33654 width = size;
33655 height = size;
33656 patternTag = 'path';
33657 path = 'M0,' + (height / 2) + 'L' + width + ',' + (height / 2);
33658 linewidth = solidity * size;
33659 patternTag = 'path';
33660 patternAttrs = {
33661 'd': path,
33662 'opacity': fgopacity,
33663 'stroke': fgcolor,
33664 'stroke-width': linewidth + 'px'
33665 };
33666 break;
33667 case '+':
33668 width = size;
33669 height = size;
33670 patternTag = 'path';
33671 path = 'M' + (width / 2) + ',0L' + (width / 2) + ',' + height +
33672 'M0,' + (height / 2) + 'L' + width + ',' + (height / 2);
33673 linewidth = size - size * Math.sqrt(1.0 - solidity);
33674 patternTag = 'path';
33675 patternAttrs = {
33676 'd': path,
33677 'opacity': fgopacity,
33678 'stroke': fgcolor,
33679 'stroke-width': linewidth + 'px'
33680 };
33681 break;
33682 case '.':
33683 width = size;
33684 height = size;
33685 if(solidity < Math.PI / 4) {
33686 radius = Math.sqrt(solidity * size * size / Math.PI);
33687 } else {
33688 radius = linearFn(solidity, Math.PI / 4, 1.0, size / 2, size / Math.sqrt(2));
33689 }
33690 patternTag = 'circle';
33691 patternAttrs = {
33692 'cx': width / 2,
33693 'cy': height / 2,
33694 'r': radius,
33695 'opacity': fgopacity,
33696 'fill': fgcolor
33697 };
33698 break;
33699 }
33700
33701 var str = [
33702 shape || 'noSh',
33703 bgcolor || 'noBg',
33704 fgcolor || 'noFg',
33705 size,
33706 solidity
33707 ].join(';');
33708
33709 var pattern = fullLayout._defs.select('.patterns')
33710 .selectAll('#' + fullID)
33711 .data([str], Lib.identity);
33712
33713 pattern.exit().remove();
33714
33715 pattern.enter()
33716 .append('pattern')
33717 .each(function() {
33718 var el = d3.select(this);
33719
33720 el.attr({
33721 'id': fullID,
33722 'width': width + 'px',
33723 'height': height + 'px',
33724 'patternUnits': 'userSpaceOnUse',
33725 // for legends scale down patterns just a bit so that default size (i.e 8) nicely fit in small icons
33726 'patternTransform': isLegend ? 'scale(0.8)' : ''
33727 });
33728
33729 if(bgcolor) {
33730 var rects = el.selectAll('rect').data([0]);
33731 rects.exit().remove();
33732 rects.enter()
33733 .append('rect')
33734 .attr({
33735 'width': width + 'px',
33736 'height': height + 'px',
33737 'fill': bgcolor
33738 });
33739 }
33740
33741 var patterns = el.selectAll(patternTag).data([0]);
33742 patterns.exit().remove();
33743 patterns.enter()
33744 .append(patternTag)
33745 .attr(patternAttrs);
33746 });
33747
33748 sel.style('fill', getFullUrl(fullID, gd))
33749 .style('fill-opacity', null);
33750
33751 sel.classed('pattern_filled', true);
33752 var className2query = function(s) {
33753 return '.' + s.attr('class').replace(/\s/g, '.');
33754 };
33755 var k = className2query(d3.select(sel.node().parentNode)) + '>.pattern_filled';
33756 fullLayout._patternUrlQueryParts[k] = 1;
33757};
33758
33759/*
33760 * Make the gradients container and clear out any previous gradients.
33761 * We never collect all the gradients we need in one place,
33762 * so we can't ever remove gradients that have stopped being useful,
33763 * except all at once before a full redraw.
33764 * The upside of this is arbitrary points can share gradient defs
33765 */
33766drawing.initGradients = function(gd) {
33767 var fullLayout = gd._fullLayout;
33768
33769 var gradientsGroup = Lib.ensureSingle(fullLayout._defs, 'g', 'gradients');
33770 gradientsGroup.selectAll('linearGradient,radialGradient').remove();
33771
33772 // initialize stash of query parts filled in Drawing.gradient,
33773 // used to fix URL strings during image exports
33774 fullLayout._gradientUrlQueryParts = {};
33775};
33776
33777drawing.initPatterns = function(gd) {
33778 var fullLayout = gd._fullLayout;
33779
33780 var patternsGroup = Lib.ensureSingle(fullLayout._defs, 'g', 'patterns');
33781 patternsGroup.selectAll('pattern').remove();
33782
33783 // initialize stash of query parts filled in Drawing.pattern,
33784 // used to fix URL strings during image exports
33785 fullLayout._patternUrlQueryParts = {};
33786};
33787
33788drawing.getPatternAttr = function(mp, i, dflt) {
33789 if(mp && Lib.isArrayOrTypedArray(mp)) {
33790 return i < mp.length ? mp[i] : dflt;
33791 }
33792 return mp;
33793};
33794
33795drawing.pointStyle = function(s, trace, gd) {
33796 if(!s.size()) return;
33797
33798 var fns = drawing.makePointStyleFns(trace);
33799
33800 s.each(function(d) {
33801 drawing.singlePointStyle(d, d3.select(this), trace, fns, gd);
33802 });
33803};
33804
33805drawing.singlePointStyle = function(d, sel, trace, fns, gd) {
33806 var marker = trace.marker;
33807 var markerLine = marker.line;
33808
33809 sel.style('opacity',
33810 fns.selectedOpacityFn ? fns.selectedOpacityFn(d) :
33811 (d.mo === undefined ? marker.opacity : d.mo)
33812 );
33813
33814 if(fns.ms2mrc) {
33815 var r;
33816
33817 // handle multi-trace graph edit case
33818 if(d.ms === 'various' || marker.size === 'various') {
33819 r = 3;
33820 } else {
33821 r = fns.ms2mrc(d.ms);
33822 }
33823
33824 // store the calculated size so hover can use it
33825 d.mrc = r;
33826
33827 if(fns.selectedSizeFn) {
33828 r = d.mrc = fns.selectedSizeFn(d);
33829 }
33830
33831 // turn the symbol into a sanitized number
33832 var x = drawing.symbolNumber(d.mx || marker.symbol) || 0;
33833
33834 // save if this marker is open
33835 // because that impacts how to handle colors
33836 d.om = x % 200 >= 100;
33837
33838 sel.attr('d', makePointPath(x, r));
33839 }
33840
33841 var perPointGradient = false;
33842 var fillColor, lineColor, lineWidth;
33843
33844 // 'so' is suspected outliers, for box plots
33845 if(d.so) {
33846 lineWidth = markerLine.outlierwidth;
33847 lineColor = markerLine.outliercolor;
33848 fillColor = marker.outliercolor;
33849 } else {
33850 var markerLineWidth = (markerLine || {}).width;
33851
33852 lineWidth = (
33853 d.mlw + 1 ||
33854 markerLineWidth + 1 ||
33855 // TODO: we need the latter for legends... can we get rid of it?
33856 (d.trace ? (d.trace.marker.line || {}).width : 0) + 1
33857 ) - 1 || 0;
33858
33859 if('mlc' in d) lineColor = d.mlcc = fns.lineScale(d.mlc);
33860 // weird case: array wasn't long enough to apply to every point
33861 else if(Lib.isArrayOrTypedArray(markerLine.color)) lineColor = Color.defaultLine;
33862 else lineColor = markerLine.color;
33863
33864 if(Lib.isArrayOrTypedArray(marker.color)) {
33865 fillColor = Color.defaultLine;
33866 perPointGradient = true;
33867 }
33868
33869 if('mc' in d) {
33870 fillColor = d.mcc = fns.markerScale(d.mc);
33871 } else {
33872 fillColor = marker.color || 'rgba(0,0,0,0)';
33873 }
33874
33875 if(fns.selectedColorFn) {
33876 fillColor = fns.selectedColorFn(d);
33877 }
33878 }
33879
33880 if(d.om) {
33881 // open markers can't have zero linewidth, default to 1px,
33882 // and use fill color as stroke color
33883 sel.call(Color.stroke, fillColor)
33884 .style({
33885 'stroke-width': (lineWidth || 1) + 'px',
33886 fill: 'none'
33887 });
33888 } else {
33889 sel.style('stroke-width', (d.isBlank ? 0 : lineWidth) + 'px');
33890
33891 var markerGradient = marker.gradient;
33892
33893 var gradientType = d.mgt;
33894 if(gradientType) perPointGradient = true;
33895 else gradientType = markerGradient && markerGradient.type;
33896
33897 // for legend - arrays will propagate through here, but we don't need
33898 // to treat it as per-point.
33899 if(Lib.isArrayOrTypedArray(gradientType)) {
33900 gradientType = gradientType[0];
33901 if(!gradientInfo[gradientType]) gradientType = 0;
33902 }
33903
33904 var markerPattern = marker.pattern;
33905 var patternShape = markerPattern && drawing.getPatternAttr(markerPattern.shape, d.i, '');
33906
33907 if(gradientType && gradientType !== 'none') {
33908 var gradientColor = d.mgc;
33909 if(gradientColor) perPointGradient = true;
33910 else gradientColor = markerGradient.color;
33911
33912 var gradientID = trace.uid;
33913 if(perPointGradient) gradientID += '-' + d.i;
33914
33915 drawing.gradient(sel, gd, gradientID, gradientType,
33916 [[0, gradientColor], [1, fillColor]], 'fill');
33917 } else if(patternShape) {
33918 var patternBGColor = drawing.getPatternAttr(markerPattern.bgcolor, d.i, null);
33919 var patternFGColor = drawing.getPatternAttr(markerPattern.fgcolor, d.i, null);
33920 var patternFGOpacity = markerPattern.fgopacity;
33921 var patternSize = drawing.getPatternAttr(markerPattern.size, d.i, 8);
33922 var patternSolidity = drawing.getPatternAttr(markerPattern.solidity, d.i, 0.3);
33923 var perPointPattern = d.mcc ||
33924 Lib.isArrayOrTypedArray(markerPattern.shape) ||
33925 Lib.isArrayOrTypedArray(markerPattern.bgcolor) ||
33926 Lib.isArrayOrTypedArray(markerPattern.size) ||
33927 Lib.isArrayOrTypedArray(markerPattern.solidity);
33928
33929 var patternID = trace.uid;
33930 if(perPointPattern) patternID += '-' + d.i;
33931
33932 drawing.pattern(
33933 sel, 'point', gd, patternID,
33934 patternShape, patternSize, patternSolidity,
33935 d.mcc, markerPattern.fillmode,
33936 patternBGColor, patternFGColor, patternFGOpacity
33937 );
33938 } else {
33939 Color.fill(sel, fillColor);
33940 }
33941
33942 if(lineWidth) {
33943 Color.stroke(sel, lineColor);
33944 }
33945 }
33946};
33947
33948drawing.makePointStyleFns = function(trace) {
33949 var out = {};
33950 var marker = trace.marker;
33951
33952 // allow array marker and marker line colors to be
33953 // scaled by given max and min to colorscales
33954 out.markerScale = drawing.tryColorscale(marker, '');
33955 out.lineScale = drawing.tryColorscale(marker, 'line');
33956
33957 if(Registry.traceIs(trace, 'symbols')) {
33958 out.ms2mrc = subTypes.isBubble(trace) ?
33959 makeBubbleSizeFn(trace) :
33960 function() { return (marker.size || 6) / 2; };
33961 }
33962
33963 if(trace.selectedpoints) {
33964 Lib.extendFlat(out, drawing.makeSelectedPointStyleFns(trace));
33965 }
33966
33967 return out;
33968};
33969
33970drawing.makeSelectedPointStyleFns = function(trace) {
33971 var out = {};
33972
33973 var selectedAttrs = trace.selected || {};
33974 var unselectedAttrs = trace.unselected || {};
33975
33976 var marker = trace.marker || {};
33977 var selectedMarker = selectedAttrs.marker || {};
33978 var unselectedMarker = unselectedAttrs.marker || {};
33979
33980 var mo = marker.opacity;
33981 var smo = selectedMarker.opacity;
33982 var usmo = unselectedMarker.opacity;
33983 var smoIsDefined = smo !== undefined;
33984 var usmoIsDefined = usmo !== undefined;
33985
33986 if(Lib.isArrayOrTypedArray(mo) || smoIsDefined || usmoIsDefined) {
33987 out.selectedOpacityFn = function(d) {
33988 var base = d.mo === undefined ? marker.opacity : d.mo;
33989
33990 if(d.selected) {
33991 return smoIsDefined ? smo : base;
33992 } else {
33993 return usmoIsDefined ? usmo : DESELECTDIM * base;
33994 }
33995 };
33996 }
33997
33998 var mc = marker.color;
33999 var smc = selectedMarker.color;
34000 var usmc = unselectedMarker.color;
34001
34002 if(smc || usmc) {
34003 out.selectedColorFn = function(d) {
34004 var base = d.mcc || mc;
34005
34006 if(d.selected) {
34007 return smc || base;
34008 } else {
34009 return usmc || base;
34010 }
34011 };
34012 }
34013
34014 var ms = marker.size;
34015 var sms = selectedMarker.size;
34016 var usms = unselectedMarker.size;
34017 var smsIsDefined = sms !== undefined;
34018 var usmsIsDefined = usms !== undefined;
34019
34020 if(Registry.traceIs(trace, 'symbols') && (smsIsDefined || usmsIsDefined)) {
34021 out.selectedSizeFn = function(d) {
34022 var base = d.mrc || ms / 2;
34023
34024 if(d.selected) {
34025 return smsIsDefined ? sms / 2 : base;
34026 } else {
34027 return usmsIsDefined ? usms / 2 : base;
34028 }
34029 };
34030 }
34031
34032 return out;
34033};
34034
34035drawing.makeSelectedTextStyleFns = function(trace) {
34036 var out = {};
34037
34038 var selectedAttrs = trace.selected || {};
34039 var unselectedAttrs = trace.unselected || {};
34040
34041 var textFont = trace.textfont || {};
34042 var selectedTextFont = selectedAttrs.textfont || {};
34043 var unselectedTextFont = unselectedAttrs.textfont || {};
34044
34045 var tc = textFont.color;
34046 var stc = selectedTextFont.color;
34047 var utc = unselectedTextFont.color;
34048
34049 out.selectedTextColorFn = function(d) {
34050 var base = d.tc || tc;
34051
34052 if(d.selected) {
34053 return stc || base;
34054 } else {
34055 if(utc) return utc;
34056 else return stc ? base : Color.addOpacity(base, DESELECTDIM);
34057 }
34058 };
34059
34060 return out;
34061};
34062
34063drawing.selectedPointStyle = function(s, trace) {
34064 if(!s.size() || !trace.selectedpoints) return;
34065
34066 var fns = drawing.makeSelectedPointStyleFns(trace);
34067 var marker = trace.marker || {};
34068 var seq = [];
34069
34070 if(fns.selectedOpacityFn) {
34071 seq.push(function(pt, d) {
34072 pt.style('opacity', fns.selectedOpacityFn(d));
34073 });
34074 }
34075
34076 if(fns.selectedColorFn) {
34077 seq.push(function(pt, d) {
34078 Color.fill(pt, fns.selectedColorFn(d));
34079 });
34080 }
34081
34082 if(fns.selectedSizeFn) {
34083 seq.push(function(pt, d) {
34084 var mx = d.mx || marker.symbol || 0;
34085 var mrc2 = fns.selectedSizeFn(d);
34086
34087 pt.attr('d', makePointPath(drawing.symbolNumber(mx), mrc2));
34088
34089 // save for Drawing.selectedTextStyle
34090 d.mrc2 = mrc2;
34091 });
34092 }
34093
34094 if(seq.length) {
34095 s.each(function(d) {
34096 var pt = d3.select(this);
34097 for(var i = 0; i < seq.length; i++) {
34098 seq[i](pt, d);
34099 }
34100 });
34101 }
34102};
34103
34104drawing.tryColorscale = function(marker, prefix) {
34105 var cont = prefix ? Lib.nestedProperty(marker, prefix).get() : marker;
34106
34107 if(cont) {
34108 var colorArray = cont.color;
34109 if((cont.colorscale || cont._colorAx) && Lib.isArrayOrTypedArray(colorArray)) {
34110 return Colorscale.makeColorScaleFuncFromTrace(cont);
34111 }
34112 }
34113 return Lib.identity;
34114};
34115
34116var TEXTOFFSETSIGN = {
34117 start: 1, end: -1, middle: 0, bottom: 1, top: -1
34118};
34119
34120function textPointPosition(s, textPosition, fontSize, markerRadius) {
34121 var group = d3.select(s.node().parentNode);
34122
34123 var v = textPosition.indexOf('top') !== -1 ?
34124 'top' :
34125 textPosition.indexOf('bottom') !== -1 ? 'bottom' : 'middle';
34126 var h = textPosition.indexOf('left') !== -1 ?
34127 'end' :
34128 textPosition.indexOf('right') !== -1 ? 'start' : 'middle';
34129
34130 // if markers are shown, offset a little more than
34131 // the nominal marker size
34132 // ie 2/1.6 * nominal, bcs some markers are a bit bigger
34133 var r = markerRadius ? markerRadius / 0.8 + 1 : 0;
34134
34135 var numLines = (svgTextUtils.lineCount(s) - 1) * LINE_SPACING + 1;
34136 var dx = TEXTOFFSETSIGN[h] * r;
34137 var dy = fontSize * 0.75 + TEXTOFFSETSIGN[v] * r +
34138 (TEXTOFFSETSIGN[v] - 1) * numLines * fontSize / 2;
34139
34140 // fix the overall text group position
34141 s.attr('text-anchor', h);
34142 group.attr('transform', strTranslate(dx, dy));
34143}
34144
34145function extracTextFontSize(d, trace) {
34146 var fontSize = d.ts || trace.textfont.size;
34147 return (isNumeric(fontSize) && fontSize > 0) ? fontSize : 0;
34148}
34149
34150// draw text at points
34151drawing.textPointStyle = function(s, trace, gd) {
34152 if(!s.size()) return;
34153
34154 var selectedTextColorFn;
34155 if(trace.selectedpoints) {
34156 var fns = drawing.makeSelectedTextStyleFns(trace);
34157 selectedTextColorFn = fns.selectedTextColorFn;
34158 }
34159
34160 var texttemplate = trace.texttemplate;
34161 var fullLayout = gd._fullLayout;
34162
34163 s.each(function(d) {
34164 var p = d3.select(this);
34165
34166 var text = texttemplate ?
34167 Lib.extractOption(d, trace, 'txt', 'texttemplate') :
34168 Lib.extractOption(d, trace, 'tx', 'text');
34169
34170 if(!text && text !== 0) {
34171 p.remove();
34172 return;
34173 }
34174
34175 if(texttemplate) {
34176 var fn = trace._module.formatLabels;
34177 var labels = fn ? fn(d, trace, fullLayout) : {};
34178 var pointValues = {};
34179 appendArrayPointValue(pointValues, trace, d.i);
34180 var meta = trace._meta || {};
34181 text = Lib.texttemplateString(text, labels, fullLayout._d3locale, pointValues, d, meta);
34182 }
34183
34184 var pos = d.tp || trace.textposition;
34185 var fontSize = extracTextFontSize(d, trace);
34186 var fontColor = selectedTextColorFn ?
34187 selectedTextColorFn(d) :
34188 (d.tc || trace.textfont.color);
34189
34190 p.call(drawing.font,
34191 d.tf || trace.textfont.family,
34192 fontSize,
34193 fontColor)
34194 .text(text)
34195 .call(svgTextUtils.convertToTspans, gd)
34196 .call(textPointPosition, pos, fontSize, d.mrc);
34197 });
34198};
34199
34200drawing.selectedTextStyle = function(s, trace) {
34201 if(!s.size() || !trace.selectedpoints) return;
34202
34203 var fns = drawing.makeSelectedTextStyleFns(trace);
34204
34205 s.each(function(d) {
34206 var tx = d3.select(this);
34207 var tc = fns.selectedTextColorFn(d);
34208 var tp = d.tp || trace.textposition;
34209 var fontSize = extracTextFontSize(d, trace);
34210
34211 Color.fill(tx, tc);
34212 textPointPosition(tx, tp, fontSize, d.mrc2 || d.mrc);
34213 });
34214};
34215
34216// generalized Catmull-Rom splines, per
34217// http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf
34218var CatmullRomExp = 0.5;
34219drawing.smoothopen = function(pts, smoothness) {
34220 if(pts.length < 3) { return 'M' + pts.join('L');}
34221 var path = 'M' + pts[0];
34222 var tangents = [];
34223 var i;
34224 for(i = 1; i < pts.length - 1; i++) {
34225 tangents.push(makeTangent(pts[i - 1], pts[i], pts[i + 1], smoothness));
34226 }
34227 path += 'Q' + tangents[0][0] + ' ' + pts[1];
34228 for(i = 2; i < pts.length - 1; i++) {
34229 path += 'C' + tangents[i - 2][1] + ' ' + tangents[i - 1][0] + ' ' + pts[i];
34230 }
34231 path += 'Q' + tangents[pts.length - 3][1] + ' ' + pts[pts.length - 1];
34232 return path;
34233};
34234
34235drawing.smoothclosed = function(pts, smoothness) {
34236 if(pts.length < 3) { return 'M' + pts.join('L') + 'Z'; }
34237 var path = 'M' + pts[0];
34238 var pLast = pts.length - 1;
34239 var tangents = [makeTangent(pts[pLast], pts[0], pts[1], smoothness)];
34240 var i;
34241 for(i = 1; i < pLast; i++) {
34242 tangents.push(makeTangent(pts[i - 1], pts[i], pts[i + 1], smoothness));
34243 }
34244 tangents.push(
34245 makeTangent(pts[pLast - 1], pts[pLast], pts[0], smoothness)
34246 );
34247
34248 for(i = 1; i <= pLast; i++) {
34249 path += 'C' + tangents[i - 1][1] + ' ' + tangents[i][0] + ' ' + pts[i];
34250 }
34251 path += 'C' + tangents[pLast][1] + ' ' + tangents[0][0] + ' ' + pts[0] + 'Z';
34252 return path;
34253};
34254
34255function makeTangent(prevpt, thispt, nextpt, smoothness) {
34256 var d1x = prevpt[0] - thispt[0];
34257 var d1y = prevpt[1] - thispt[1];
34258 var d2x = nextpt[0] - thispt[0];
34259 var d2y = nextpt[1] - thispt[1];
34260 var d1a = Math.pow(d1x * d1x + d1y * d1y, CatmullRomExp / 2);
34261 var d2a = Math.pow(d2x * d2x + d2y * d2y, CatmullRomExp / 2);
34262 var numx = (d2a * d2a * d1x - d1a * d1a * d2x) * smoothness;
34263 var numy = (d2a * d2a * d1y - d1a * d1a * d2y) * smoothness;
34264 var denom1 = 3 * d2a * (d1a + d2a);
34265 var denom2 = 3 * d1a * (d1a + d2a);
34266 return [
34267 [
34268 d3.round(thispt[0] + (denom1 && numx / denom1), 2),
34269 d3.round(thispt[1] + (denom1 && numy / denom1), 2)
34270 ], [
34271 d3.round(thispt[0] - (denom2 && numx / denom2), 2),
34272 d3.round(thispt[1] - (denom2 && numy / denom2), 2)
34273 ]
34274 ];
34275}
34276
34277// step paths - returns a generator function for paths
34278// with the given step shape
34279var STEPPATH = {
34280 hv: function(p0, p1) {
34281 return 'H' + d3.round(p1[0], 2) + 'V' + d3.round(p1[1], 2);
34282 },
34283 vh: function(p0, p1) {
34284 return 'V' + d3.round(p1[1], 2) + 'H' + d3.round(p1[0], 2);
34285 },
34286 hvh: function(p0, p1) {
34287 return 'H' + d3.round((p0[0] + p1[0]) / 2, 2) + 'V' +
34288 d3.round(p1[1], 2) + 'H' + d3.round(p1[0], 2);
34289 },
34290 vhv: function(p0, p1) {
34291 return 'V' + d3.round((p0[1] + p1[1]) / 2, 2) + 'H' +
34292 d3.round(p1[0], 2) + 'V' + d3.round(p1[1], 2);
34293 }
34294};
34295var STEPLINEAR = function(p0, p1) {
34296 return 'L' + d3.round(p1[0], 2) + ',' + d3.round(p1[1], 2);
34297};
34298drawing.steps = function(shape) {
34299 var onestep = STEPPATH[shape] || STEPLINEAR;
34300 return function(pts) {
34301 var path = 'M' + d3.round(pts[0][0], 2) + ',' + d3.round(pts[0][1], 2);
34302 for(var i = 1; i < pts.length; i++) {
34303 path += onestep(pts[i - 1], pts[i]);
34304 }
34305 return path;
34306 };
34307};
34308
34309// off-screen svg render testing element, shared by the whole page
34310// uses the id 'js-plotly-tester' and stores it in drawing.tester
34311drawing.makeTester = function() {
34312 var tester = Lib.ensureSingleById(d3.select('body'), 'svg', 'js-plotly-tester', function(s) {
34313 s.attr(xmlnsNamespaces.svgAttrs)
34314 .style({
34315 position: 'absolute',
34316 left: '-10000px',
34317 top: '-10000px',
34318 width: '9000px',
34319 height: '9000px',
34320 'z-index': '1'
34321 });
34322 });
34323
34324 // browsers differ on how they describe the bounding rect of
34325 // the svg if its contents spill over... so make a 1x1px
34326 // reference point we can measure off of.
34327 var testref = Lib.ensureSingle(tester, 'path', 'js-reference-point', function(s) {
34328 s.attr('d', 'M0,0H1V1H0Z')
34329 .style({
34330 'stroke-width': 0,
34331 fill: 'black'
34332 });
34333 });
34334
34335 drawing.tester = tester;
34336 drawing.testref = testref;
34337};
34338
34339/*
34340 * use our offscreen tester to get a clientRect for an element,
34341 * in a reference frame where it isn't translated (or transformed) and
34342 * its anchor point is at (0,0)
34343 * always returns a copy of the bbox, so the caller can modify it safely
34344 *
34345 * @param {SVGElement} node: the element to measure. If possible this should be
34346 * a <text> or MathJax <g> element that's already passed through
34347 * `convertToTspans` because in that case we can cache the results, but it's
34348 * possible to pass in any svg element.
34349 *
34350 * @param {boolean} inTester: is this element already in `drawing.tester`?
34351 * If you are measuring a dummy element, rather than one you really intend
34352 * to use on the plot, making it in `drawing.tester` in the first place
34353 * allows us to test faster because it cuts out cloning and appending it.
34354 *
34355 * @param {string} hash: for internal use only, if we already know the cache key
34356 * for this element beforehand.
34357 *
34358 * @return {object}: a plain object containing the width, height, left, right,
34359 * top, and bottom of `node`
34360 */
34361drawing.savedBBoxes = {};
34362var savedBBoxesCount = 0;
34363var maxSavedBBoxes = 10000;
34364
34365drawing.bBox = function(node, inTester, hash) {
34366 /*
34367 * Cache elements we've already measured so we don't have to
34368 * remeasure the same thing many times
34369 * We have a few bBox callers though who pass a node larger than
34370 * a <text> or a MathJax <g>, such as an axis group containing many labels.
34371 * These will not generate a hash (unless we figure out an appropriate
34372 * hash key for them) and thus we will not hash them.
34373 */
34374 if(!hash) hash = nodeHash(node);
34375 var out;
34376 if(hash) {
34377 out = drawing.savedBBoxes[hash];
34378 if(out) return Lib.extendFlat({}, out);
34379 } else if(node.childNodes.length === 1) {
34380 /*
34381 * If we have only one child element, which is itself hashable, make
34382 * a new hash from this element plus its x,y,transform
34383 * These bounding boxes *include* x,y,transform - mostly for use by
34384 * callers trying to avoid overlaps (ie titles)
34385 */
34386 var innerNode = node.childNodes[0];
34387
34388 hash = nodeHash(innerNode);
34389 if(hash) {
34390 var x = +innerNode.getAttribute('x') || 0;
34391 var y = +innerNode.getAttribute('y') || 0;
34392 var transform = innerNode.getAttribute('transform');
34393
34394 if(!transform) {
34395 // in this case, just varying x and y, don't bother caching
34396 // the final bBox because the alteration is quick.
34397 var innerBB = drawing.bBox(innerNode, false, hash);
34398 if(x) {
34399 innerBB.left += x;
34400 innerBB.right += x;
34401 }
34402 if(y) {
34403 innerBB.top += y;
34404 innerBB.bottom += y;
34405 }
34406 return innerBB;
34407 }
34408 /*
34409 * else we have a transform - rather than make a complicated
34410 * (and error-prone and probably slow) transform parser/calculator,
34411 * just continue on calculating the boundingClientRect of the group
34412 * and use the new composite hash to cache it.
34413 * That said, `innerNode.transform.baseVal` is an array of
34414 * `SVGTransform` objects, that *do* seem to have a nice matrix
34415 * multiplication interface that we could use to avoid making
34416 * another getBoundingClientRect call...
34417 */
34418 hash += '~' + x + '~' + y + '~' + transform;
34419
34420 out = drawing.savedBBoxes[hash];
34421 if(out) return Lib.extendFlat({}, out);
34422 }
34423 }
34424 var testNode, tester;
34425 if(inTester) {
34426 testNode = node;
34427 } else {
34428 tester = drawing.tester.node();
34429
34430 // copy the node to test into the tester
34431 testNode = node.cloneNode(true);
34432 tester.appendChild(testNode);
34433 }
34434
34435 // standardize its position (and newline tspans if any)
34436 d3.select(testNode)
34437 .attr('transform', null)
34438 .call(svgTextUtils.positionText, 0, 0);
34439
34440 var testRect = testNode.getBoundingClientRect();
34441 var refRect = drawing.testref
34442 .node()
34443 .getBoundingClientRect();
34444
34445 if(!inTester) tester.removeChild(testNode);
34446
34447 var bb = {
34448 height: testRect.height,
34449 width: testRect.width,
34450 left: testRect.left - refRect.left,
34451 top: testRect.top - refRect.top,
34452 right: testRect.right - refRect.left,
34453 bottom: testRect.bottom - refRect.top
34454 };
34455
34456 // make sure we don't have too many saved boxes,
34457 // or a long session could overload on memory
34458 // by saving boxes for long-gone elements
34459 if(savedBBoxesCount >= maxSavedBBoxes) {
34460 drawing.savedBBoxes = {};
34461 savedBBoxesCount = 0;
34462 }
34463
34464 // cache this bbox
34465 if(hash) drawing.savedBBoxes[hash] = bb;
34466 savedBBoxesCount++;
34467
34468 return Lib.extendFlat({}, bb);
34469};
34470
34471// capture everything about a node (at least in our usage) that
34472// impacts its bounding box, given that bBox clears x, y, and transform
34473function nodeHash(node) {
34474 var inputText = node.getAttribute('data-unformatted');
34475 if(inputText === null) return;
34476 return inputText +
34477 node.getAttribute('data-math') +
34478 node.getAttribute('text-anchor') +
34479 node.getAttribute('style');
34480}
34481
34482/**
34483 * Set clipPath URL in a way that work for all situations.
34484 *
34485 * In details, graphs on pages with <base> HTML tags need to prepend
34486 * the clip path ids with the page's base url EXCEPT during toImage exports.
34487 *
34488 * @param {d3 selection} s : node to add clip-path attribute
34489 * @param {string} localId : local clip-path (w/o base url) id
34490 * @param {DOM element || object} gd
34491 * - context._baseUrl {string}
34492 * - context._exportedPlot {boolean}
34493 */
34494drawing.setClipUrl = function(s, localId, gd) {
34495 s.attr('clip-path', getFullUrl(localId, gd));
34496};
34497
34498function getFullUrl(localId, gd) {
34499 if(!localId) return null;
34500
34501 var context = gd._context;
34502 var baseUrl = context._exportedPlot ? '' : (context._baseUrl || '');
34503 return baseUrl ?
34504 'url(\'' + baseUrl + '#' + localId + '\')' :
34505 'url(#' + localId + ')';
34506}
34507
34508drawing.getTranslate = function(element) {
34509 // Note the separator [^\d] between x and y in this regex
34510 // We generally use ',' but IE will convert it to ' '
34511 var re = /.*\btranslate\((-?\d*\.?\d*)[^-\d]*(-?\d*\.?\d*)[^\d].*/;
34512 var getter = element.attr ? 'attr' : 'getAttribute';
34513 var transform = element[getter]('transform') || '';
34514
34515 var translate = transform.replace(re, function(match, p1, p2) {
34516 return [p1, p2].join(' ');
34517 })
34518 .split(' ');
34519
34520 return {
34521 x: +translate[0] || 0,
34522 y: +translate[1] || 0
34523 };
34524};
34525
34526drawing.setTranslate = function(element, x, y) {
34527 var re = /(\btranslate\(.*?\);?)/;
34528 var getter = element.attr ? 'attr' : 'getAttribute';
34529 var setter = element.attr ? 'attr' : 'setAttribute';
34530 var transform = element[getter]('transform') || '';
34531
34532 x = x || 0;
34533 y = y || 0;
34534
34535 transform = transform.replace(re, '').trim();
34536 transform += strTranslate(x, y);
34537 transform = transform.trim();
34538
34539 element[setter]('transform', transform);
34540
34541 return transform;
34542};
34543
34544drawing.getScale = function(element) {
34545 var re = /.*\bscale\((\d*\.?\d*)[^\d]*(\d*\.?\d*)[^\d].*/;
34546 var getter = element.attr ? 'attr' : 'getAttribute';
34547 var transform = element[getter]('transform') || '';
34548
34549 var translate = transform.replace(re, function(match, p1, p2) {
34550 return [p1, p2].join(' ');
34551 })
34552 .split(' ');
34553
34554 return {
34555 x: +translate[0] || 1,
34556 y: +translate[1] || 1
34557 };
34558};
34559
34560drawing.setScale = function(element, x, y) {
34561 var re = /(\bscale\(.*?\);?)/;
34562 var getter = element.attr ? 'attr' : 'getAttribute';
34563 var setter = element.attr ? 'attr' : 'setAttribute';
34564 var transform = element[getter]('transform') || '';
34565
34566 x = x || 1;
34567 y = y || 1;
34568
34569 transform = transform.replace(re, '').trim();
34570 transform += 'scale(' + x + ',' + y + ')';
34571 transform = transform.trim();
34572
34573 element[setter]('transform', transform);
34574
34575 return transform;
34576};
34577
34578var SCALE_RE = /\s*sc.*/;
34579
34580drawing.setPointGroupScale = function(selection, xScale, yScale) {
34581 xScale = xScale || 1;
34582 yScale = yScale || 1;
34583
34584 if(!selection) return;
34585
34586 // The same scale transform for every point:
34587 var scale = (xScale === 1 && yScale === 1) ?
34588 '' :
34589 'scale(' + xScale + ',' + yScale + ')';
34590
34591 selection.each(function() {
34592 var t = (this.getAttribute('transform') || '').replace(SCALE_RE, '');
34593 t += scale;
34594 t = t.trim();
34595 this.setAttribute('transform', t);
34596 });
34597};
34598
34599var TEXT_POINT_LAST_TRANSLATION_RE = /translate\([^)]*\)\s*$/;
34600
34601drawing.setTextPointsScale = function(selection, xScale, yScale) {
34602 if(!selection) return;
34603
34604 selection.each(function() {
34605 var transforms;
34606 var el = d3.select(this);
34607 var text = el.select('text');
34608
34609 if(!text.node()) return;
34610
34611 var x = parseFloat(text.attr('x') || 0);
34612 var y = parseFloat(text.attr('y') || 0);
34613
34614 var existingTransform = (el.attr('transform') || '').match(TEXT_POINT_LAST_TRANSLATION_RE);
34615
34616 if(xScale === 1 && yScale === 1) {
34617 transforms = [];
34618 } else {
34619 transforms = [
34620 strTranslate(x, y),
34621 'scale(' + xScale + ',' + yScale + ')',
34622 strTranslate(-x, -y),
34623 ];
34624 }
34625
34626 if(existingTransform) {
34627 transforms.push(existingTransform);
34628 }
34629
34630 el.attr('transform', transforms.join(''));
34631 });
34632};
34633
34634},{"../../components/fx/helpers":193,"../../constants/alignment":262,"../../constants/interactions":266,"../../constants/xmlns_namespaces":268,"../../lib":287,"../../lib/svg_text_utils":310,"../../registry":376,"../../traces/scatter/make_bubble_size_func":514,"../../traces/scatter/subtypes":522,"../color":157,"../colorscale":169,"./symbol_defs":180,"@plotly/d3":20,"fast-isnumeric":33,"tinycolor2":121}],180:[function(_dereq_,module,exports){
34635'use strict';
34636
34637var d3 = _dereq_('@plotly/d3');
34638
34639/** Marker symbol definitions
34640 * users can specify markers either by number or name
34641 * add 100 (or '-open') and you get an open marker
34642 * open markers have no fill and use line color as the stroke color
34643 * add 200 (or '-dot') and you get a dot in the middle
34644 * add both and you get both
34645 */
34646
34647module.exports = {
34648 circle: {
34649 n: 0,
34650 f: function(r) {
34651 var rs = d3.round(r, 2);
34652 return 'M' + rs + ',0A' + rs + ',' + rs + ' 0 1,1 0,-' + rs +
34653 'A' + rs + ',' + rs + ' 0 0,1 ' + rs + ',0Z';
34654 }
34655 },
34656 square: {
34657 n: 1,
34658 f: function(r) {
34659 var rs = d3.round(r, 2);
34660 return 'M' + rs + ',' + rs + 'H-' + rs + 'V-' + rs + 'H' + rs + 'Z';
34661 }
34662 },
34663 diamond: {
34664 n: 2,
34665 f: function(r) {
34666 var rd = d3.round(r * 1.3, 2);
34667 return 'M' + rd + ',0L0,' + rd + 'L-' + rd + ',0L0,-' + rd + 'Z';
34668 }
34669 },
34670 cross: {
34671 n: 3,
34672 f: function(r) {
34673 var rc = d3.round(r * 0.4, 2);
34674 var rc2 = d3.round(r * 1.2, 2);
34675 return 'M' + rc2 + ',' + rc + 'H' + rc + 'V' + rc2 + 'H-' + rc +
34676 'V' + rc + 'H-' + rc2 + 'V-' + rc + 'H-' + rc + 'V-' + rc2 +
34677 'H' + rc + 'V-' + rc + 'H' + rc2 + 'Z';
34678 }
34679 },
34680 x: {
34681 n: 4,
34682 f: function(r) {
34683 var rx = d3.round(r * 0.8 / Math.sqrt(2), 2);
34684 var ne = 'l' + rx + ',' + rx;
34685 var se = 'l' + rx + ',-' + rx;
34686 var sw = 'l-' + rx + ',-' + rx;
34687 var nw = 'l-' + rx + ',' + rx;
34688 return 'M0,' + rx + ne + se + sw + se + sw + nw + sw + nw + ne + nw + ne + 'Z';
34689 }
34690 },
34691 'triangle-up': {
34692 n: 5,
34693 f: function(r) {
34694 var rt = d3.round(r * 2 / Math.sqrt(3), 2);
34695 var r2 = d3.round(r / 2, 2);
34696 var rs = d3.round(r, 2);
34697 return 'M-' + rt + ',' + r2 + 'H' + rt + 'L0,-' + rs + 'Z';
34698 }
34699 },
34700 'triangle-down': {
34701 n: 6,
34702 f: function(r) {
34703 var rt = d3.round(r * 2 / Math.sqrt(3), 2);
34704 var r2 = d3.round(r / 2, 2);
34705 var rs = d3.round(r, 2);
34706 return 'M-' + rt + ',-' + r2 + 'H' + rt + 'L0,' + rs + 'Z';
34707 }
34708 },
34709 'triangle-left': {
34710 n: 7,
34711 f: function(r) {
34712 var rt = d3.round(r * 2 / Math.sqrt(3), 2);
34713 var r2 = d3.round(r / 2, 2);
34714 var rs = d3.round(r, 2);
34715 return 'M' + r2 + ',-' + rt + 'V' + rt + 'L-' + rs + ',0Z';
34716 }
34717 },
34718 'triangle-right': {
34719 n: 8,
34720 f: function(r) {
34721 var rt = d3.round(r * 2 / Math.sqrt(3), 2);
34722 var r2 = d3.round(r / 2, 2);
34723 var rs = d3.round(r, 2);
34724 return 'M-' + r2 + ',-' + rt + 'V' + rt + 'L' + rs + ',0Z';
34725 }
34726 },
34727 'triangle-ne': {
34728 n: 9,
34729 f: function(r) {
34730 var r1 = d3.round(r * 0.6, 2);
34731 var r2 = d3.round(r * 1.2, 2);
34732 return 'M-' + r2 + ',-' + r1 + 'H' + r1 + 'V' + r2 + 'Z';
34733 }
34734 },
34735 'triangle-se': {
34736 n: 10,
34737 f: function(r) {
34738 var r1 = d3.round(r * 0.6, 2);
34739 var r2 = d3.round(r * 1.2, 2);
34740 return 'M' + r1 + ',-' + r2 + 'V' + r1 + 'H-' + r2 + 'Z';
34741 }
34742 },
34743 'triangle-sw': {
34744 n: 11,
34745 f: function(r) {
34746 var r1 = d3.round(r * 0.6, 2);
34747 var r2 = d3.round(r * 1.2, 2);
34748 return 'M' + r2 + ',' + r1 + 'H-' + r1 + 'V-' + r2 + 'Z';
34749 }
34750 },
34751 'triangle-nw': {
34752 n: 12,
34753 f: function(r) {
34754 var r1 = d3.round(r * 0.6, 2);
34755 var r2 = d3.round(r * 1.2, 2);
34756 return 'M-' + r1 + ',' + r2 + 'V-' + r1 + 'H' + r2 + 'Z';
34757 }
34758 },
34759 pentagon: {
34760 n: 13,
34761 f: function(r) {
34762 var x1 = d3.round(r * 0.951, 2);
34763 var x2 = d3.round(r * 0.588, 2);
34764 var y0 = d3.round(-r, 2);
34765 var y1 = d3.round(r * -0.309, 2);
34766 var y2 = d3.round(r * 0.809, 2);
34767 return 'M' + x1 + ',' + y1 + 'L' + x2 + ',' + y2 + 'H-' + x2 +
34768 'L-' + x1 + ',' + y1 + 'L0,' + y0 + 'Z';
34769 }
34770 },
34771 hexagon: {
34772 n: 14,
34773 f: function(r) {
34774 var y0 = d3.round(r, 2);
34775 var y1 = d3.round(r / 2, 2);
34776 var x = d3.round(r * Math.sqrt(3) / 2, 2);
34777 return 'M' + x + ',-' + y1 + 'V' + y1 + 'L0,' + y0 +
34778 'L-' + x + ',' + y1 + 'V-' + y1 + 'L0,-' + y0 + 'Z';
34779 }
34780 },
34781 hexagon2: {
34782 n: 15,
34783 f: function(r) {
34784 var x0 = d3.round(r, 2);
34785 var x1 = d3.round(r / 2, 2);
34786 var y = d3.round(r * Math.sqrt(3) / 2, 2);
34787 return 'M-' + x1 + ',' + y + 'H' + x1 + 'L' + x0 +
34788 ',0L' + x1 + ',-' + y + 'H-' + x1 + 'L-' + x0 + ',0Z';
34789 }
34790 },
34791 octagon: {
34792 n: 16,
34793 f: function(r) {
34794 var a = d3.round(r * 0.924, 2);
34795 var b = d3.round(r * 0.383, 2);
34796 return 'M-' + b + ',-' + a + 'H' + b + 'L' + a + ',-' + b + 'V' + b +
34797 'L' + b + ',' + a + 'H-' + b + 'L-' + a + ',' + b + 'V-' + b + 'Z';
34798 }
34799 },
34800 star: {
34801 n: 17,
34802 f: function(r) {
34803 var rs = r * 1.4;
34804 var x1 = d3.round(rs * 0.225, 2);
34805 var x2 = d3.round(rs * 0.951, 2);
34806 var x3 = d3.round(rs * 0.363, 2);
34807 var x4 = d3.round(rs * 0.588, 2);
34808 var y0 = d3.round(-rs, 2);
34809 var y1 = d3.round(rs * -0.309, 2);
34810 var y3 = d3.round(rs * 0.118, 2);
34811 var y4 = d3.round(rs * 0.809, 2);
34812 var y5 = d3.round(rs * 0.382, 2);
34813 return 'M' + x1 + ',' + y1 + 'H' + x2 + 'L' + x3 + ',' + y3 +
34814 'L' + x4 + ',' + y4 + 'L0,' + y5 + 'L-' + x4 + ',' + y4 +
34815 'L-' + x3 + ',' + y3 + 'L-' + x2 + ',' + y1 + 'H-' + x1 +
34816 'L0,' + y0 + 'Z';
34817 }
34818 },
34819 hexagram: {
34820 n: 18,
34821 f: function(r) {
34822 var y = d3.round(r * 0.66, 2);
34823 var x1 = d3.round(r * 0.38, 2);
34824 var x2 = d3.round(r * 0.76, 2);
34825 return 'M-' + x2 + ',0l-' + x1 + ',-' + y + 'h' + x2 +
34826 'l' + x1 + ',-' + y + 'l' + x1 + ',' + y + 'h' + x2 +
34827 'l-' + x1 + ',' + y + 'l' + x1 + ',' + y + 'h-' + x2 +
34828 'l-' + x1 + ',' + y + 'l-' + x1 + ',-' + y + 'h-' + x2 + 'Z';
34829 }
34830 },
34831 'star-triangle-up': {
34832 n: 19,
34833 f: function(r) {
34834 var x = d3.round(r * Math.sqrt(3) * 0.8, 2);
34835 var y1 = d3.round(r * 0.8, 2);
34836 var y2 = d3.round(r * 1.6, 2);
34837 var rc = d3.round(r * 4, 2);
34838 var aPart = 'A ' + rc + ',' + rc + ' 0 0 1 ';
34839 return 'M-' + x + ',' + y1 + aPart + x + ',' + y1 +
34840 aPart + '0,-' + y2 + aPart + '-' + x + ',' + y1 + 'Z';
34841 }
34842 },
34843 'star-triangle-down': {
34844 n: 20,
34845 f: function(r) {
34846 var x = d3.round(r * Math.sqrt(3) * 0.8, 2);
34847 var y1 = d3.round(r * 0.8, 2);
34848 var y2 = d3.round(r * 1.6, 2);
34849 var rc = d3.round(r * 4, 2);
34850 var aPart = 'A ' + rc + ',' + rc + ' 0 0 1 ';
34851 return 'M' + x + ',-' + y1 + aPart + '-' + x + ',-' + y1 +
34852 aPart + '0,' + y2 + aPart + x + ',-' + y1 + 'Z';
34853 }
34854 },
34855 'star-square': {
34856 n: 21,
34857 f: function(r) {
34858 var rp = d3.round(r * 1.1, 2);
34859 var rc = d3.round(r * 2, 2);
34860 var aPart = 'A ' + rc + ',' + rc + ' 0 0 1 ';
34861 return 'M-' + rp + ',-' + rp + aPart + '-' + rp + ',' + rp +
34862 aPart + rp + ',' + rp + aPart + rp + ',-' + rp +
34863 aPart + '-' + rp + ',-' + rp + 'Z';
34864 }
34865 },
34866 'star-diamond': {
34867 n: 22,
34868 f: function(r) {
34869 var rp = d3.round(r * 1.4, 2);
34870 var rc = d3.round(r * 1.9, 2);
34871 var aPart = 'A ' + rc + ',' + rc + ' 0 0 1 ';
34872 return 'M-' + rp + ',0' + aPart + '0,' + rp +
34873 aPart + rp + ',0' + aPart + '0,-' + rp +
34874 aPart + '-' + rp + ',0' + 'Z';
34875 }
34876 },
34877 'diamond-tall': {
34878 n: 23,
34879 f: function(r) {
34880 var x = d3.round(r * 0.7, 2);
34881 var y = d3.round(r * 1.4, 2);
34882 return 'M0,' + y + 'L' + x + ',0L0,-' + y + 'L-' + x + ',0Z';
34883 }
34884 },
34885 'diamond-wide': {
34886 n: 24,
34887 f: function(r) {
34888 var x = d3.round(r * 1.4, 2);
34889 var y = d3.round(r * 0.7, 2);
34890 return 'M0,' + y + 'L' + x + ',0L0,-' + y + 'L-' + x + ',0Z';
34891 }
34892 },
34893 hourglass: {
34894 n: 25,
34895 f: function(r) {
34896 var rs = d3.round(r, 2);
34897 return 'M' + rs + ',' + rs + 'H-' + rs + 'L' + rs + ',-' + rs + 'H-' + rs + 'Z';
34898 },
34899 noDot: true
34900 },
34901 bowtie: {
34902 n: 26,
34903 f: function(r) {
34904 var rs = d3.round(r, 2);
34905 return 'M' + rs + ',' + rs + 'V-' + rs + 'L-' + rs + ',' + rs + 'V-' + rs + 'Z';
34906 },
34907 noDot: true
34908 },
34909 'circle-cross': {
34910 n: 27,
34911 f: function(r) {
34912 var rs = d3.round(r, 2);
34913 return 'M0,' + rs + 'V-' + rs + 'M' + rs + ',0H-' + rs +
34914 'M' + rs + ',0A' + rs + ',' + rs + ' 0 1,1 0,-' + rs +
34915 'A' + rs + ',' + rs + ' 0 0,1 ' + rs + ',0Z';
34916 },
34917 needLine: true,
34918 noDot: true
34919 },
34920 'circle-x': {
34921 n: 28,
34922 f: function(r) {
34923 var rs = d3.round(r, 2);
34924 var rc = d3.round(r / Math.sqrt(2), 2);
34925 return 'M' + rc + ',' + rc + 'L-' + rc + ',-' + rc +
34926 'M' + rc + ',-' + rc + 'L-' + rc + ',' + rc +
34927 'M' + rs + ',0A' + rs + ',' + rs + ' 0 1,1 0,-' + rs +
34928 'A' + rs + ',' + rs + ' 0 0,1 ' + rs + ',0Z';
34929 },
34930 needLine: true,
34931 noDot: true
34932 },
34933 'square-cross': {
34934 n: 29,
34935 f: function(r) {
34936 var rs = d3.round(r, 2);
34937 return 'M0,' + rs + 'V-' + rs + 'M' + rs + ',0H-' + rs +
34938 'M' + rs + ',' + rs + 'H-' + rs + 'V-' + rs + 'H' + rs + 'Z';
34939 },
34940 needLine: true,
34941 noDot: true
34942 },
34943 'square-x': {
34944 n: 30,
34945 f: function(r) {
34946 var rs = d3.round(r, 2);
34947 return 'M' + rs + ',' + rs + 'L-' + rs + ',-' + rs +
34948 'M' + rs + ',-' + rs + 'L-' + rs + ',' + rs +
34949 'M' + rs + ',' + rs + 'H-' + rs + 'V-' + rs + 'H' + rs + 'Z';
34950 },
34951 needLine: true,
34952 noDot: true
34953 },
34954 'diamond-cross': {
34955 n: 31,
34956 f: function(r) {
34957 var rd = d3.round(r * 1.3, 2);
34958 return 'M' + rd + ',0L0,' + rd + 'L-' + rd + ',0L0,-' + rd + 'Z' +
34959 'M0,-' + rd + 'V' + rd + 'M-' + rd + ',0H' + rd;
34960 },
34961 needLine: true,
34962 noDot: true
34963 },
34964 'diamond-x': {
34965 n: 32,
34966 f: function(r) {
34967 var rd = d3.round(r * 1.3, 2);
34968 var r2 = d3.round(r * 0.65, 2);
34969 return 'M' + rd + ',0L0,' + rd + 'L-' + rd + ',0L0,-' + rd + 'Z' +
34970 'M-' + r2 + ',-' + r2 + 'L' + r2 + ',' + r2 +
34971 'M-' + r2 + ',' + r2 + 'L' + r2 + ',-' + r2;
34972 },
34973 needLine: true,
34974 noDot: true
34975 },
34976 'cross-thin': {
34977 n: 33,
34978 f: function(r) {
34979 var rc = d3.round(r * 1.4, 2);
34980 return 'M0,' + rc + 'V-' + rc + 'M' + rc + ',0H-' + rc;
34981 },
34982 needLine: true,
34983 noDot: true,
34984 noFill: true
34985 },
34986 'x-thin': {
34987 n: 34,
34988 f: function(r) {
34989 var rx = d3.round(r, 2);
34990 return 'M' + rx + ',' + rx + 'L-' + rx + ',-' + rx +
34991 'M' + rx + ',-' + rx + 'L-' + rx + ',' + rx;
34992 },
34993 needLine: true,
34994 noDot: true,
34995 noFill: true
34996 },
34997 asterisk: {
34998 n: 35,
34999 f: function(r) {
35000 var rc = d3.round(r * 1.2, 2);
35001 var rs = d3.round(r * 0.85, 2);
35002 return 'M0,' + rc + 'V-' + rc + 'M' + rc + ',0H-' + rc +
35003 'M' + rs + ',' + rs + 'L-' + rs + ',-' + rs +
35004 'M' + rs + ',-' + rs + 'L-' + rs + ',' + rs;
35005 },
35006 needLine: true,
35007 noDot: true,
35008 noFill: true
35009 },
35010 hash: {
35011 n: 36,
35012 f: function(r) {
35013 var r1 = d3.round(r / 2, 2);
35014 var r2 = d3.round(r, 2);
35015 return 'M' + r1 + ',' + r2 + 'V-' + r2 +
35016 'm-' + r2 + ',0V' + r2 +
35017 'M' + r2 + ',' + r1 + 'H-' + r2 +
35018 'm0,-' + r2 + 'H' + r2;
35019 },
35020 needLine: true,
35021 noFill: true
35022 },
35023 'y-up': {
35024 n: 37,
35025 f: function(r) {
35026 var x = d3.round(r * 1.2, 2);
35027 var y0 = d3.round(r * 1.6, 2);
35028 var y1 = d3.round(r * 0.8, 2);
35029 return 'M-' + x + ',' + y1 + 'L0,0M' + x + ',' + y1 + 'L0,0M0,-' + y0 + 'L0,0';
35030 },
35031 needLine: true,
35032 noDot: true,
35033 noFill: true
35034 },
35035 'y-down': {
35036 n: 38,
35037 f: function(r) {
35038 var x = d3.round(r * 1.2, 2);
35039 var y0 = d3.round(r * 1.6, 2);
35040 var y1 = d3.round(r * 0.8, 2);
35041 return 'M-' + x + ',-' + y1 + 'L0,0M' + x + ',-' + y1 + 'L0,0M0,' + y0 + 'L0,0';
35042 },
35043 needLine: true,
35044 noDot: true,
35045 noFill: true
35046 },
35047 'y-left': {
35048 n: 39,
35049 f: function(r) {
35050 var y = d3.round(r * 1.2, 2);
35051 var x0 = d3.round(r * 1.6, 2);
35052 var x1 = d3.round(r * 0.8, 2);
35053 return 'M' + x1 + ',' + y + 'L0,0M' + x1 + ',-' + y + 'L0,0M-' + x0 + ',0L0,0';
35054 },
35055 needLine: true,
35056 noDot: true,
35057 noFill: true
35058 },
35059 'y-right': {
35060 n: 40,
35061 f: function(r) {
35062 var y = d3.round(r * 1.2, 2);
35063 var x0 = d3.round(r * 1.6, 2);
35064 var x1 = d3.round(r * 0.8, 2);
35065 return 'M-' + x1 + ',' + y + 'L0,0M-' + x1 + ',-' + y + 'L0,0M' + x0 + ',0L0,0';
35066 },
35067 needLine: true,
35068 noDot: true,
35069 noFill: true
35070 },
35071 'line-ew': {
35072 n: 41,
35073 f: function(r) {
35074 var rc = d3.round(r * 1.4, 2);
35075 return 'M' + rc + ',0H-' + rc;
35076 },
35077 needLine: true,
35078 noDot: true,
35079 noFill: true
35080 },
35081 'line-ns': {
35082 n: 42,
35083 f: function(r) {
35084 var rc = d3.round(r * 1.4, 2);
35085 return 'M0,' + rc + 'V-' + rc;
35086 },
35087 needLine: true,
35088 noDot: true,
35089 noFill: true
35090 },
35091 'line-ne': {
35092 n: 43,
35093 f: function(r) {
35094 var rx = d3.round(r, 2);
35095 return 'M' + rx + ',-' + rx + 'L-' + rx + ',' + rx;
35096 },
35097 needLine: true,
35098 noDot: true,
35099 noFill: true
35100 },
35101 'line-nw': {
35102 n: 44,
35103 f: function(r) {
35104 var rx = d3.round(r, 2);
35105 return 'M' + rx + ',' + rx + 'L-' + rx + ',-' + rx;
35106 },
35107 needLine: true,
35108 noDot: true,
35109 noFill: true
35110 },
35111 'arrow-up': {
35112 n: 45,
35113 f: function(r) {
35114 var rx = d3.round(r, 2);
35115 var ry = d3.round(r * 2, 2);
35116 return 'M0,0L-' + rx + ',' + ry + 'H' + rx + 'Z';
35117 },
35118 noDot: true
35119 },
35120 'arrow-down': {
35121 n: 46,
35122 f: function(r) {
35123 var rx = d3.round(r, 2);
35124 var ry = d3.round(r * 2, 2);
35125 return 'M0,0L-' + rx + ',-' + ry + 'H' + rx + 'Z';
35126 },
35127 noDot: true
35128 },
35129 'arrow-left': {
35130 n: 47,
35131 f: function(r) {
35132 var rx = d3.round(r * 2, 2);
35133 var ry = d3.round(r, 2);
35134 return 'M0,0L' + rx + ',-' + ry + 'V' + ry + 'Z';
35135 },
35136 noDot: true
35137 },
35138 'arrow-right': {
35139 n: 48,
35140 f: function(r) {
35141 var rx = d3.round(r * 2, 2);
35142 var ry = d3.round(r, 2);
35143 return 'M0,0L-' + rx + ',-' + ry + 'V' + ry + 'Z';
35144 },
35145 noDot: true
35146 },
35147 'arrow-bar-up': {
35148 n: 49,
35149 f: function(r) {
35150 var rx = d3.round(r, 2);
35151 var ry = d3.round(r * 2, 2);
35152 return 'M-' + rx + ',0H' + rx + 'M0,0L-' + rx + ',' + ry + 'H' + rx + 'Z';
35153 },
35154 needLine: true,
35155 noDot: true
35156 },
35157 'arrow-bar-down': {
35158 n: 50,
35159 f: function(r) {
35160 var rx = d3.round(r, 2);
35161 var ry = d3.round(r * 2, 2);
35162 return 'M-' + rx + ',0H' + rx + 'M0,0L-' + rx + ',-' + ry + 'H' + rx + 'Z';
35163 },
35164 needLine: true,
35165 noDot: true
35166 },
35167 'arrow-bar-left': {
35168 n: 51,
35169 f: function(r) {
35170 var rx = d3.round(r * 2, 2);
35171 var ry = d3.round(r, 2);
35172 return 'M0,-' + ry + 'V' + ry + 'M0,0L' + rx + ',-' + ry + 'V' + ry + 'Z';
35173 },
35174 needLine: true,
35175 noDot: true
35176 },
35177 'arrow-bar-right': {
35178 n: 52,
35179 f: function(r) {
35180 var rx = d3.round(r * 2, 2);
35181 var ry = d3.round(r, 2);
35182 return 'M0,-' + ry + 'V' + ry + 'M0,0L-' + rx + ',-' + ry + 'V' + ry + 'Z';
35183 },
35184 needLine: true,
35185 noDot: true
35186 }
35187};
35188
35189},{"@plotly/d3":20}],181:[function(_dereq_,module,exports){
35190'use strict';
35191
35192
35193module.exports = {
35194 visible: {
35195 valType: 'boolean',
35196 editType: 'calc',
35197 },
35198 type: {
35199 valType: 'enumerated',
35200 values: ['percent', 'constant', 'sqrt', 'data'],
35201 editType: 'calc',
35202 },
35203 symmetric: {
35204 valType: 'boolean',
35205 editType: 'calc',
35206 },
35207 array: {
35208 valType: 'data_array',
35209 editType: 'calc',
35210 },
35211 arrayminus: {
35212 valType: 'data_array',
35213 editType: 'calc',
35214 },
35215 value: {
35216 valType: 'number',
35217 min: 0,
35218 dflt: 10,
35219 editType: 'calc',
35220 },
35221 valueminus: {
35222 valType: 'number',
35223 min: 0,
35224 dflt: 10,
35225 editType: 'calc',
35226 },
35227 traceref: {
35228 valType: 'integer',
35229 min: 0,
35230 dflt: 0,
35231 editType: 'style'
35232 },
35233 tracerefminus: {
35234 valType: 'integer',
35235 min: 0,
35236 dflt: 0,
35237 editType: 'style'
35238 },
35239 copy_ystyle: {
35240 valType: 'boolean',
35241 editType: 'plot'
35242 },
35243 copy_zstyle: {
35244 valType: 'boolean',
35245 editType: 'style'
35246 },
35247 color: {
35248 valType: 'color',
35249 editType: 'style',
35250 },
35251 thickness: {
35252 valType: 'number',
35253 min: 0,
35254 dflt: 2,
35255 editType: 'style',
35256 },
35257 width: {
35258 valType: 'number',
35259 min: 0,
35260 editType: 'plot',
35261 },
35262 editType: 'calc',
35263
35264 _deprecated: {
35265 opacity: {
35266 valType: 'number',
35267 editType: 'style',
35268 }
35269 }
35270};
35271
35272},{}],182:[function(_dereq_,module,exports){
35273'use strict';
35274
35275var isNumeric = _dereq_('fast-isnumeric');
35276
35277var Registry = _dereq_('../../registry');
35278var Axes = _dereq_('../../plots/cartesian/axes');
35279var Lib = _dereq_('../../lib');
35280
35281var makeComputeError = _dereq_('./compute_error');
35282
35283module.exports = function calc(gd) {
35284 var calcdata = gd.calcdata;
35285
35286 for(var i = 0; i < calcdata.length; i++) {
35287 var calcTrace = calcdata[i];
35288 var trace = calcTrace[0].trace;
35289
35290 if(trace.visible === true && Registry.traceIs(trace, 'errorBarsOK')) {
35291 var xa = Axes.getFromId(gd, trace.xaxis);
35292 var ya = Axes.getFromId(gd, trace.yaxis);
35293 calcOneAxis(calcTrace, trace, xa, 'x');
35294 calcOneAxis(calcTrace, trace, ya, 'y');
35295 }
35296 }
35297};
35298
35299function calcOneAxis(calcTrace, trace, axis, coord) {
35300 var opts = trace['error_' + coord] || {};
35301 var isVisible = (opts.visible && ['linear', 'log'].indexOf(axis.type) !== -1);
35302 var vals = [];
35303
35304 if(!isVisible) return;
35305
35306 var computeError = makeComputeError(opts);
35307
35308 for(var i = 0; i < calcTrace.length; i++) {
35309 var calcPt = calcTrace[i];
35310
35311 var iIn = calcPt.i;
35312
35313 // for types that don't include `i` in each calcdata point
35314 if(iIn === undefined) iIn = i;
35315
35316 // for stacked area inserted points
35317 // TODO: errorbars have been tested cursorily with stacked area,
35318 // but not thoroughly. It's not even really clear what you want to do:
35319 // Should it just be calculated based on that trace's size data?
35320 // Should you add errors from below in quadrature?
35321 // And what about normalization, where in principle the errors shrink
35322 // again when you get up to the top end?
35323 // One option would be to forbid errorbars with stacking until we
35324 // decide how to handle these questions.
35325 else if(iIn === null) continue;
35326
35327 var calcCoord = calcPt[coord];
35328
35329 if(!isNumeric(axis.c2l(calcCoord))) continue;
35330
35331 var errors = computeError(calcCoord, iIn);
35332 if(isNumeric(errors[0]) && isNumeric(errors[1])) {
35333 var shoe = calcPt[coord + 's'] = calcCoord - errors[0];
35334 var hat = calcPt[coord + 'h'] = calcCoord + errors[1];
35335 vals.push(shoe, hat);
35336 }
35337 }
35338
35339 var axId = axis._id;
35340 var baseExtremes = trace._extremes[axId];
35341 var extremes = Axes.findExtremes(
35342 axis,
35343 vals,
35344 Lib.extendFlat({tozero: baseExtremes.opts.tozero}, {padded: true})
35345 );
35346 baseExtremes.min = baseExtremes.min.concat(extremes.min);
35347 baseExtremes.max = baseExtremes.max.concat(extremes.max);
35348}
35349
35350},{"../../lib":287,"../../plots/cartesian/axes":334,"../../registry":376,"./compute_error":183,"fast-isnumeric":33}],183:[function(_dereq_,module,exports){
35351'use strict';
35352
35353
35354/**
35355 * Error bar computing function generator
35356 *
35357 * N.B. The generated function does not clean the dataPt entries. Non-numeric
35358 * entries result in undefined error magnitudes.
35359 *
35360 * @param {object} opts error bar attributes
35361 *
35362 * @return {function} :
35363 * @param {numeric} dataPt data point from where to compute the error magnitude
35364 * @param {number} index index of dataPt in its corresponding data array
35365 * @return {array}
35366 * - error[0] : error magnitude in the negative direction
35367 * - error[1] : " " " " positive "
35368 */
35369module.exports = function makeComputeError(opts) {
35370 var type = opts.type;
35371 var symmetric = opts.symmetric;
35372
35373 if(type === 'data') {
35374 var array = opts.array || [];
35375
35376 if(symmetric) {
35377 return function computeError(dataPt, index) {
35378 var val = +(array[index]);
35379 return [val, val];
35380 };
35381 } else {
35382 var arrayminus = opts.arrayminus || [];
35383 return function computeError(dataPt, index) {
35384 var val = +array[index];
35385 var valMinus = +arrayminus[index];
35386 // in case one is present and the other is missing, fill in 0
35387 // so we still see the present one. Mostly useful during manual
35388 // data entry.
35389 if(!isNaN(val) || !isNaN(valMinus)) {
35390 return [valMinus || 0, val || 0];
35391 }
35392 return [NaN, NaN];
35393 };
35394 }
35395 } else {
35396 var computeErrorValue = makeComputeErrorValue(type, opts.value);
35397 var computeErrorValueMinus = makeComputeErrorValue(type, opts.valueminus);
35398
35399 if(symmetric || opts.valueminus === undefined) {
35400 return function computeError(dataPt) {
35401 var val = computeErrorValue(dataPt);
35402 return [val, val];
35403 };
35404 } else {
35405 return function computeError(dataPt) {
35406 return [
35407 computeErrorValueMinus(dataPt),
35408 computeErrorValue(dataPt)
35409 ];
35410 };
35411 }
35412 }
35413};
35414
35415/**
35416 * Compute error bar magnitude (for all types except data)
35417 *
35418 * @param {string} type error bar type
35419 * @param {numeric} value error bar value
35420 *
35421 * @return {function} :
35422 * @param {numeric} dataPt
35423 */
35424function makeComputeErrorValue(type, value) {
35425 if(type === 'percent') {
35426 return function(dataPt) {
35427 return Math.abs(dataPt * value / 100);
35428 };
35429 }
35430 if(type === 'constant') {
35431 return function() {
35432 return Math.abs(value);
35433 };
35434 }
35435 if(type === 'sqrt') {
35436 return function(dataPt) {
35437 return Math.sqrt(Math.abs(dataPt));
35438 };
35439 }
35440}
35441
35442},{}],184:[function(_dereq_,module,exports){
35443'use strict';
35444
35445var isNumeric = _dereq_('fast-isnumeric');
35446
35447var Registry = _dereq_('../../registry');
35448var Lib = _dereq_('../../lib');
35449var Template = _dereq_('../../plot_api/plot_template');
35450
35451var attributes = _dereq_('./attributes');
35452
35453
35454module.exports = function(traceIn, traceOut, defaultColor, opts) {
35455 var objName = 'error_' + opts.axis;
35456 var containerOut = Template.newContainer(traceOut, objName);
35457 var containerIn = traceIn[objName] || {};
35458
35459 function coerce(attr, dflt) {
35460 return Lib.coerce(containerIn, containerOut, attributes, attr, dflt);
35461 }
35462
35463 var hasErrorBars = (
35464 containerIn.array !== undefined ||
35465 containerIn.value !== undefined ||
35466 containerIn.type === 'sqrt'
35467 );
35468
35469 var visible = coerce('visible', hasErrorBars);
35470
35471 if(visible === false) return;
35472
35473 var type = coerce('type', 'array' in containerIn ? 'data' : 'percent');
35474 var symmetric = true;
35475
35476 if(type !== 'sqrt') {
35477 symmetric = coerce('symmetric',
35478 !((type === 'data' ? 'arrayminus' : 'valueminus') in containerIn));
35479 }
35480
35481 if(type === 'data') {
35482 coerce('array');
35483 coerce('traceref');
35484 if(!symmetric) {
35485 coerce('arrayminus');
35486 coerce('tracerefminus');
35487 }
35488 } else if(type === 'percent' || type === 'constant') {
35489 coerce('value');
35490 if(!symmetric) coerce('valueminus');
35491 }
35492
35493 var copyAttr = 'copy_' + opts.inherit + 'style';
35494 if(opts.inherit) {
35495 var inheritObj = traceOut['error_' + opts.inherit];
35496 if((inheritObj || {}).visible) {
35497 coerce(copyAttr, !(containerIn.color ||
35498 isNumeric(containerIn.thickness) ||
35499 isNumeric(containerIn.width)));
35500 }
35501 }
35502 if(!opts.inherit || !containerOut[copyAttr]) {
35503 coerce('color', defaultColor);
35504 coerce('thickness');
35505 coerce('width', Registry.traceIs(traceOut, 'gl3d') ? 0 : 4);
35506 }
35507};
35508
35509},{"../../lib":287,"../../plot_api/plot_template":323,"../../registry":376,"./attributes":181,"fast-isnumeric":33}],185:[function(_dereq_,module,exports){
35510'use strict';
35511
35512var Lib = _dereq_('../../lib');
35513var overrideAll = _dereq_('../../plot_api/edit_types').overrideAll;
35514
35515var attributes = _dereq_('./attributes');
35516
35517var xyAttrs = {
35518 error_x: Lib.extendFlat({}, attributes),
35519 error_y: Lib.extendFlat({}, attributes)
35520};
35521delete xyAttrs.error_x.copy_zstyle;
35522delete xyAttrs.error_y.copy_zstyle;
35523delete xyAttrs.error_y.copy_ystyle;
35524
35525var xyzAttrs = {
35526 error_x: Lib.extendFlat({}, attributes),
35527 error_y: Lib.extendFlat({}, attributes),
35528 error_z: Lib.extendFlat({}, attributes)
35529};
35530delete xyzAttrs.error_x.copy_ystyle;
35531delete xyzAttrs.error_y.copy_ystyle;
35532delete xyzAttrs.error_z.copy_ystyle;
35533delete xyzAttrs.error_z.copy_zstyle;
35534
35535module.exports = {
35536 moduleType: 'component',
35537 name: 'errorbars',
35538
35539 schema: {
35540 traces: {
35541 scatter: xyAttrs,
35542 bar: xyAttrs,
35543 histogram: xyAttrs,
35544 scatter3d: overrideAll(xyzAttrs, 'calc', 'nested'),
35545 scattergl: overrideAll(xyAttrs, 'calc', 'nested')
35546 }
35547 },
35548
35549 supplyDefaults: _dereq_('./defaults'),
35550
35551 calc: _dereq_('./calc'),
35552 makeComputeError: _dereq_('./compute_error'),
35553
35554 plot: _dereq_('./plot'),
35555 style: _dereq_('./style'),
35556 hoverInfo: hoverInfo
35557};
35558
35559function hoverInfo(calcPoint, trace, hoverPoint) {
35560 if((trace.error_y || {}).visible) {
35561 hoverPoint.yerr = calcPoint.yh - calcPoint.y;
35562 if(!trace.error_y.symmetric) hoverPoint.yerrneg = calcPoint.y - calcPoint.ys;
35563 }
35564 if((trace.error_x || {}).visible) {
35565 hoverPoint.xerr = calcPoint.xh - calcPoint.x;
35566 if(!trace.error_x.symmetric) hoverPoint.xerrneg = calcPoint.x - calcPoint.xs;
35567 }
35568}
35569
35570},{"../../lib":287,"../../plot_api/edit_types":316,"./attributes":181,"./calc":182,"./compute_error":183,"./defaults":184,"./plot":186,"./style":187}],186:[function(_dereq_,module,exports){
35571'use strict';
35572
35573var d3 = _dereq_('@plotly/d3');
35574var isNumeric = _dereq_('fast-isnumeric');
35575
35576var Drawing = _dereq_('../drawing');
35577var subTypes = _dereq_('../../traces/scatter/subtypes');
35578
35579module.exports = function plot(gd, traces, plotinfo, transitionOpts) {
35580 var isNew;
35581
35582 var xa = plotinfo.xaxis;
35583 var ya = plotinfo.yaxis;
35584
35585 var hasAnimation = transitionOpts && transitionOpts.duration > 0;
35586
35587 traces.each(function(d) {
35588 var trace = d[0].trace;
35589 // || {} is in case the trace (specifically scatterternary)
35590 // doesn't support error bars at all, but does go through
35591 // the scatter.plot mechanics, which calls ErrorBars.plot
35592 // internally
35593 var xObj = trace.error_x || {};
35594 var yObj = trace.error_y || {};
35595
35596 var keyFunc;
35597
35598 if(trace.ids) {
35599 keyFunc = function(d) {return d.id;};
35600 }
35601
35602 var sparse = (
35603 subTypes.hasMarkers(trace) &&
35604 trace.marker.maxdisplayed > 0
35605 );
35606
35607 if(!yObj.visible && !xObj.visible) d = [];
35608
35609 var errorbars = d3.select(this).selectAll('g.errorbar')
35610 .data(d, keyFunc);
35611
35612 errorbars.exit().remove();
35613
35614 if(!d.length) return;
35615
35616 if(!xObj.visible) errorbars.selectAll('path.xerror').remove();
35617 if(!yObj.visible) errorbars.selectAll('path.yerror').remove();
35618
35619 errorbars.style('opacity', 1);
35620
35621 var enter = errorbars.enter().append('g')
35622 .classed('errorbar', true);
35623
35624 if(hasAnimation) {
35625 enter.style('opacity', 0).transition()
35626 .duration(transitionOpts.duration)
35627 .style('opacity', 1);
35628 }
35629
35630 Drawing.setClipUrl(errorbars, plotinfo.layerClipId, gd);
35631
35632 errorbars.each(function(d) {
35633 var errorbar = d3.select(this);
35634 var coords = errorCoords(d, xa, ya);
35635
35636 if(sparse && !d.vis) return;
35637
35638 var path;
35639
35640 var yerror = errorbar.select('path.yerror');
35641 if(yObj.visible && isNumeric(coords.x) &&
35642 isNumeric(coords.yh) &&
35643 isNumeric(coords.ys)) {
35644 var yw = yObj.width;
35645
35646 path = 'M' + (coords.x - yw) + ',' +
35647 coords.yh + 'h' + (2 * yw) + // hat
35648 'm-' + yw + ',0V' + coords.ys; // bar
35649
35650
35651 if(!coords.noYS) path += 'm-' + yw + ',0h' + (2 * yw); // shoe
35652
35653 isNew = !yerror.size();
35654
35655 if(isNew) {
35656 yerror = errorbar.append('path')
35657 .style('vector-effect', 'non-scaling-stroke')
35658 .classed('yerror', true);
35659 } else if(hasAnimation) {
35660 yerror = yerror
35661 .transition()
35662 .duration(transitionOpts.duration)
35663 .ease(transitionOpts.easing);
35664 }
35665
35666 yerror.attr('d', path);
35667 } else yerror.remove();
35668
35669 var xerror = errorbar.select('path.xerror');
35670 if(xObj.visible && isNumeric(coords.y) &&
35671 isNumeric(coords.xh) &&
35672 isNumeric(coords.xs)) {
35673 var xw = (xObj.copy_ystyle ? yObj : xObj).width;
35674
35675 path = 'M' + coords.xh + ',' +
35676 (coords.y - xw) + 'v' + (2 * xw) + // hat
35677 'm0,-' + xw + 'H' + coords.xs; // bar
35678
35679 if(!coords.noXS) path += 'm0,-' + xw + 'v' + (2 * xw); // shoe
35680
35681 isNew = !xerror.size();
35682
35683 if(isNew) {
35684 xerror = errorbar.append('path')
35685 .style('vector-effect', 'non-scaling-stroke')
35686 .classed('xerror', true);
35687 } else if(hasAnimation) {
35688 xerror = xerror
35689 .transition()
35690 .duration(transitionOpts.duration)
35691 .ease(transitionOpts.easing);
35692 }
35693
35694 xerror.attr('d', path);
35695 } else xerror.remove();
35696 });
35697 });
35698};
35699
35700// compute the coordinates of the error-bar objects
35701function errorCoords(d, xa, ya) {
35702 var out = {
35703 x: xa.c2p(d.x),
35704 y: ya.c2p(d.y)
35705 };
35706
35707 // calculate the error bar size and hat and shoe locations
35708 if(d.yh !== undefined) {
35709 out.yh = ya.c2p(d.yh);
35710 out.ys = ya.c2p(d.ys);
35711
35712 // if the shoes go off-scale (ie log scale, error bars past zero)
35713 // clip the bar and hide the shoes
35714 if(!isNumeric(out.ys)) {
35715 out.noYS = true;
35716 out.ys = ya.c2p(d.ys, true);
35717 }
35718 }
35719
35720 if(d.xh !== undefined) {
35721 out.xh = xa.c2p(d.xh);
35722 out.xs = xa.c2p(d.xs);
35723
35724 if(!isNumeric(out.xs)) {
35725 out.noXS = true;
35726 out.xs = xa.c2p(d.xs, true);
35727 }
35728 }
35729
35730 return out;
35731}
35732
35733},{"../../traces/scatter/subtypes":522,"../drawing":179,"@plotly/d3":20,"fast-isnumeric":33}],187:[function(_dereq_,module,exports){
35734'use strict';
35735
35736var d3 = _dereq_('@plotly/d3');
35737
35738var Color = _dereq_('../color');
35739
35740
35741module.exports = function style(traces) {
35742 traces.each(function(d) {
35743 var trace = d[0].trace;
35744 var yObj = trace.error_y || {};
35745 var xObj = trace.error_x || {};
35746
35747 var s = d3.select(this);
35748
35749 s.selectAll('path.yerror')
35750 .style('stroke-width', yObj.thickness + 'px')
35751 .call(Color.stroke, yObj.color);
35752
35753 if(xObj.copy_ystyle) xObj = yObj;
35754
35755 s.selectAll('path.xerror')
35756 .style('stroke-width', xObj.thickness + 'px')
35757 .call(Color.stroke, xObj.color);
35758 });
35759};
35760
35761},{"../color":157,"@plotly/d3":20}],188:[function(_dereq_,module,exports){
35762'use strict';
35763
35764var fontAttrs = _dereq_('../../plots/font_attributes');
35765var hoverLabelAttrs = _dereq_('./layout_attributes').hoverlabel;
35766var extendFlat = _dereq_('../../lib/extend').extendFlat;
35767
35768module.exports = {
35769 hoverlabel: {
35770 bgcolor: extendFlat({}, hoverLabelAttrs.bgcolor, {
35771 arrayOk: true,
35772 }),
35773 bordercolor: extendFlat({}, hoverLabelAttrs.bordercolor, {
35774 arrayOk: true,
35775 }),
35776 font: fontAttrs({
35777 arrayOk: true,
35778 editType: 'none',
35779 }),
35780 align: extendFlat({}, hoverLabelAttrs.align, {arrayOk: true}),
35781 namelength: extendFlat({}, hoverLabelAttrs.namelength, {arrayOk: true}),
35782 editType: 'none'
35783 }
35784};
35785
35786},{"../../lib/extend":281,"../../plots/font_attributes":363,"./layout_attributes":198}],189:[function(_dereq_,module,exports){
35787'use strict';
35788
35789var Lib = _dereq_('../../lib');
35790var Registry = _dereq_('../../registry');
35791
35792module.exports = function calc(gd) {
35793 var calcdata = gd.calcdata;
35794 var fullLayout = gd._fullLayout;
35795
35796 function makeCoerceHoverInfo(trace) {
35797 return function(val) {
35798 return Lib.coerceHoverinfo({hoverinfo: val}, {_module: trace._module}, fullLayout);
35799 };
35800 }
35801
35802 for(var i = 0; i < calcdata.length; i++) {
35803 var cd = calcdata[i];
35804 var trace = cd[0].trace;
35805
35806 // don't include hover calc fields for pie traces
35807 // as calcdata items might be sorted by value and
35808 // won't match the data array order.
35809 if(Registry.traceIs(trace, 'pie-like')) continue;
35810
35811 var fillFn = Registry.traceIs(trace, '2dMap') ? paste : Lib.fillArray;
35812
35813 fillFn(trace.hoverinfo, cd, 'hi', makeCoerceHoverInfo(trace));
35814
35815 if(trace.hovertemplate) fillFn(trace.hovertemplate, cd, 'ht');
35816
35817 if(!trace.hoverlabel) continue;
35818
35819 fillFn(trace.hoverlabel.bgcolor, cd, 'hbg');
35820 fillFn(trace.hoverlabel.bordercolor, cd, 'hbc');
35821 fillFn(trace.hoverlabel.font.size, cd, 'hts');
35822 fillFn(trace.hoverlabel.font.color, cd, 'htc');
35823 fillFn(trace.hoverlabel.font.family, cd, 'htf');
35824 fillFn(trace.hoverlabel.namelength, cd, 'hnl');
35825 fillFn(trace.hoverlabel.align, cd, 'hta');
35826 }
35827};
35828
35829function paste(traceAttr, cd, cdAttr, fn) {
35830 fn = fn || Lib.identity;
35831
35832 if(Array.isArray(traceAttr)) {
35833 cd[0][cdAttr] = fn(traceAttr);
35834 }
35835}
35836
35837},{"../../lib":287,"../../registry":376}],190:[function(_dereq_,module,exports){
35838'use strict';
35839
35840var Registry = _dereq_('../../registry');
35841var hover = _dereq_('./hover').hover;
35842
35843module.exports = function click(gd, evt, subplot) {
35844 var annotationsDone = Registry.getComponentMethod('annotations', 'onClick')(gd, gd._hoverdata);
35845
35846 // fallback to fail-safe in case the plot type's hover method doesn't pass the subplot.
35847 // Ternary, for example, didn't, but it was caught because tested.
35848 if(subplot !== undefined) {
35849 // The true flag at the end causes it to re-run the hover computation to figure out *which*
35850 // point is being clicked. Without this, clicking is somewhat unreliable.
35851 hover(gd, evt, subplot, true);
35852 }
35853
35854 function emitClick() { gd.emit('plotly_click', {points: gd._hoverdata, event: evt}); }
35855
35856 if(gd._hoverdata && evt && evt.target) {
35857 if(annotationsDone && annotationsDone.then) {
35858 annotationsDone.then(emitClick);
35859 } else emitClick();
35860
35861 // why do we get a double event without this???
35862 if(evt.stopImmediatePropagation) evt.stopImmediatePropagation();
35863 }
35864};
35865
35866},{"../../registry":376,"./hover":194}],191:[function(_dereq_,module,exports){
35867'use strict';
35868
35869module.exports = {
35870 // hover labels for multiple horizontal bars get tilted by this angle
35871 YANGLE: 60,
35872
35873 // size and display constants for hover text
35874
35875 // pixel size of hover arrows
35876 HOVERARROWSIZE: 6,
35877 // pixels padding around text
35878 HOVERTEXTPAD: 3,
35879 // hover font
35880 HOVERFONTSIZE: 13,
35881 HOVERFONT: 'Arial, sans-serif',
35882
35883 // minimum time (msec) between hover calls
35884 HOVERMINTIME: 50,
35885
35886 // ID suffix (with fullLayout._uid) for hover events in the throttle cache
35887 HOVERID: '-hover'
35888};
35889
35890},{}],192:[function(_dereq_,module,exports){
35891'use strict';
35892
35893var Lib = _dereq_('../../lib');
35894var attributes = _dereq_('./attributes');
35895var handleHoverLabelDefaults = _dereq_('./hoverlabel_defaults');
35896
35897module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
35898 function coerce(attr, dflt) {
35899 return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
35900 }
35901
35902 var opts = Lib.extendFlat({}, layout.hoverlabel);
35903 if(traceOut.hovertemplate) opts.namelength = -1;
35904
35905 handleHoverLabelDefaults(traceIn, traceOut, coerce, opts);
35906};
35907
35908},{"../../lib":287,"./attributes":188,"./hoverlabel_defaults":195}],193:[function(_dereq_,module,exports){
35909'use strict';
35910
35911var Lib = _dereq_('../../lib');
35912
35913// look for either subplot or xaxis and yaxis attributes
35914// does not handle splom case
35915exports.getSubplot = function(trace) {
35916 return trace.subplot || (trace.xaxis + trace.yaxis) || trace.geo;
35917};
35918
35919// is trace in given list of subplots?
35920// does handle splom case
35921exports.isTraceInSubplots = function(trace, subplots) {
35922 if(trace.type === 'splom') {
35923 var xaxes = trace.xaxes || [];
35924 var yaxes = trace.yaxes || [];
35925 for(var i = 0; i < xaxes.length; i++) {
35926 for(var j = 0; j < yaxes.length; j++) {
35927 if(subplots.indexOf(xaxes[i] + yaxes[j]) !== -1) {
35928 return true;
35929 }
35930 }
35931 }
35932 return false;
35933 }
35934
35935 return subplots.indexOf(exports.getSubplot(trace)) !== -1;
35936};
35937
35938// convenience functions for mapping all relevant axes
35939exports.flat = function(subplots, v) {
35940 var out = new Array(subplots.length);
35941 for(var i = 0; i < subplots.length; i++) {
35942 out[i] = v;
35943 }
35944 return out;
35945};
35946
35947exports.p2c = function(axArray, v) {
35948 var out = new Array(axArray.length);
35949 for(var i = 0; i < axArray.length; i++) {
35950 out[i] = axArray[i].p2c(v);
35951 }
35952 return out;
35953};
35954
35955exports.getDistanceFunction = function(mode, dx, dy, dxy) {
35956 if(mode === 'closest') return dxy || exports.quadrature(dx, dy);
35957 return mode.charAt(0) === 'x' ? dx : dy;
35958};
35959
35960exports.getClosest = function(cd, distfn, pointData) {
35961 // do we already have a point number? (array mode only)
35962 if(pointData.index !== false) {
35963 if(pointData.index >= 0 && pointData.index < cd.length) {
35964 pointData.distance = 0;
35965 } else pointData.index = false;
35966 } else {
35967 // apply the distance function to each data point
35968 // this is the longest loop... if this bogs down, we may need
35969 // to create pre-sorted data (by x or y), not sure how to
35970 // do this for 'closest'
35971 for(var i = 0; i < cd.length; i++) {
35972 var newDistance = distfn(cd[i]);
35973 if(newDistance <= pointData.distance) {
35974 pointData.index = i;
35975 pointData.distance = newDistance;
35976 }
35977 }
35978 }
35979 return pointData;
35980};
35981
35982/*
35983 * pseudo-distance function for hover effects on areas: inside the region
35984 * distance is finite (`passVal`), outside it's Infinity.
35985 *
35986 * @param {number} v0: signed difference between the current position and the left edge
35987 * @param {number} v1: signed difference between the current position and the right edge
35988 * @param {number} passVal: the value to return on success
35989 */
35990exports.inbox = function(v0, v1, passVal) {
35991 return (v0 * v1 < 0 || v0 === 0) ? passVal : Infinity;
35992};
35993
35994exports.quadrature = function(dx, dy) {
35995 return function(di) {
35996 var x = dx(di);
35997 var y = dy(di);
35998 return Math.sqrt(x * x + y * y);
35999 };
36000};
36001
36002/** Fill event data point object for hover and selection.
36003 * Invokes _module.eventData if present.
36004 *
36005 * N.B. note that point 'index' corresponds to input data array index
36006 * whereas 'number' is its post-transform version.
36007 *
36008 * If the hovered/selected pt corresponds to an multiple input points
36009 * (e.g. for histogram and transformed traces), 'pointNumbers` and 'pointIndices'
36010 * are include in the event data.
36011 *
36012 * @param {object} pt
36013 * @param {object} trace
36014 * @param {object} cd
36015 * @return {object}
36016 */
36017exports.makeEventData = function(pt, trace, cd) {
36018 // hover uses 'index', select uses 'pointNumber'
36019 var pointNumber = 'index' in pt ? pt.index : pt.pointNumber;
36020
36021 var out = {
36022 data: trace._input,
36023 fullData: trace,
36024 curveNumber: trace.index,
36025 pointNumber: pointNumber
36026 };
36027
36028 if(trace._indexToPoints) {
36029 var pointIndices = trace._indexToPoints[pointNumber];
36030
36031 if(pointIndices.length === 1) {
36032 out.pointIndex = pointIndices[0];
36033 } else {
36034 out.pointIndices = pointIndices;
36035 }
36036 } else {
36037 out.pointIndex = pointNumber;
36038 }
36039
36040 if(trace._module.eventData) {
36041 out = trace._module.eventData(out, pt, trace, cd, pointNumber);
36042 } else {
36043 if('xVal' in pt) out.x = pt.xVal;
36044 else if('x' in pt) out.x = pt.x;
36045
36046 if('yVal' in pt) out.y = pt.yVal;
36047 else if('y' in pt) out.y = pt.y;
36048
36049 if(pt.xa) out.xaxis = pt.xa;
36050 if(pt.ya) out.yaxis = pt.ya;
36051 if(pt.zLabelVal !== undefined) out.z = pt.zLabelVal;
36052 }
36053
36054 exports.appendArrayPointValue(out, trace, pointNumber);
36055
36056 return out;
36057};
36058
36059/** Appends values inside array attributes corresponding to given point number
36060 *
36061 * @param {object} pointData : point data object (gets mutated here)
36062 * @param {object} trace : full trace object
36063 * @param {number|Array(number)} pointNumber : point number. May be a length-2 array
36064 * [row, col] to dig into 2D arrays
36065 */
36066exports.appendArrayPointValue = function(pointData, trace, pointNumber) {
36067 var arrayAttrs = trace._arrayAttrs;
36068
36069 if(!arrayAttrs) {
36070 return;
36071 }
36072
36073 for(var i = 0; i < arrayAttrs.length; i++) {
36074 var astr = arrayAttrs[i];
36075 var key = getPointKey(astr);
36076
36077 if(pointData[key] === undefined) {
36078 var val = Lib.nestedProperty(trace, astr).get();
36079 var pointVal = getPointData(val, pointNumber);
36080
36081 if(pointVal !== undefined) pointData[key] = pointVal;
36082 }
36083 }
36084};
36085
36086/**
36087 * Appends values inside array attributes corresponding to given point number array
36088 * For use when pointData references a plot entity that arose (or potentially arose)
36089 * from multiple points in the input data
36090 *
36091 * @param {object} pointData : point data object (gets mutated here)
36092 * @param {object} trace : full trace object
36093 * @param {Array(number)|Array(Array(number))} pointNumbers : Array of point numbers.
36094 * Each entry in the array may itself be a length-2 array [row, col] to dig into 2D arrays
36095 */
36096exports.appendArrayMultiPointValues = function(pointData, trace, pointNumbers) {
36097 var arrayAttrs = trace._arrayAttrs;
36098
36099 if(!arrayAttrs) {
36100 return;
36101 }
36102
36103 for(var i = 0; i < arrayAttrs.length; i++) {
36104 var astr = arrayAttrs[i];
36105 var key = getPointKey(astr);
36106
36107 if(pointData[key] === undefined) {
36108 var val = Lib.nestedProperty(trace, astr).get();
36109 var keyVal = new Array(pointNumbers.length);
36110
36111 for(var j = 0; j < pointNumbers.length; j++) {
36112 keyVal[j] = getPointData(val, pointNumbers[j]);
36113 }
36114 pointData[key] = keyVal;
36115 }
36116 }
36117};
36118
36119var pointKeyMap = {
36120 ids: 'id',
36121 locations: 'location',
36122 labels: 'label',
36123 values: 'value',
36124 'marker.colors': 'color',
36125 parents: 'parent'
36126};
36127
36128function getPointKey(astr) {
36129 return pointKeyMap[astr] || astr;
36130}
36131
36132function getPointData(val, pointNumber) {
36133 if(Array.isArray(pointNumber)) {
36134 if(Array.isArray(val) && Array.isArray(val[pointNumber[0]])) {
36135 return val[pointNumber[0]][pointNumber[1]];
36136 }
36137 } else {
36138 return val[pointNumber];
36139 }
36140}
36141
36142var xyHoverMode = {
36143 x: true,
36144 y: true
36145};
36146
36147var unifiedHoverMode = {
36148 'x unified': true,
36149 'y unified': true
36150};
36151
36152exports.isUnifiedHover = function(hovermode) {
36153 if(typeof hovermode !== 'string') return false;
36154 return !!unifiedHoverMode[hovermode];
36155};
36156
36157exports.isXYhover = function(hovermode) {
36158 if(typeof hovermode !== 'string') return false;
36159 return !!xyHoverMode[hovermode];
36160};
36161
36162},{"../../lib":287}],194:[function(_dereq_,module,exports){
36163'use strict';
36164
36165var d3 = _dereq_('@plotly/d3');
36166var isNumeric = _dereq_('fast-isnumeric');
36167var tinycolor = _dereq_('tinycolor2');
36168
36169var Lib = _dereq_('../../lib');
36170var strTranslate = Lib.strTranslate;
36171var strRotate = Lib.strRotate;
36172var Events = _dereq_('../../lib/events');
36173var svgTextUtils = _dereq_('../../lib/svg_text_utils');
36174var overrideCursor = _dereq_('../../lib/override_cursor');
36175var Drawing = _dereq_('../drawing');
36176var Color = _dereq_('../color');
36177var dragElement = _dereq_('../dragelement');
36178var Axes = _dereq_('../../plots/cartesian/axes');
36179var Registry = _dereq_('../../registry');
36180
36181var helpers = _dereq_('./helpers');
36182var constants = _dereq_('./constants');
36183
36184var legendSupplyDefaults = _dereq_('../legend/defaults');
36185var legendDraw = _dereq_('../legend/draw');
36186
36187// hover labels for multiple horizontal bars get tilted by some angle,
36188// then need to be offset differently if they overlap
36189var YANGLE = constants.YANGLE;
36190var YA_RADIANS = Math.PI * YANGLE / 180;
36191
36192// expansion of projected height
36193var YFACTOR = 1 / Math.sin(YA_RADIANS);
36194
36195// to make the appropriate post-rotation x offset,
36196// you need both x and y offsets
36197var YSHIFTX = Math.cos(YA_RADIANS);
36198var YSHIFTY = Math.sin(YA_RADIANS);
36199
36200// size and display constants for hover text
36201var HOVERARROWSIZE = constants.HOVERARROWSIZE;
36202var HOVERTEXTPAD = constants.HOVERTEXTPAD;
36203
36204var multipleHoverPoints = {
36205 box: true,
36206 ohlc: true,
36207 violin: true,
36208 candlestick: true
36209};
36210
36211var cartesianScatterPoints = {
36212 scatter: true,
36213 scattergl: true,
36214 splom: true
36215};
36216
36217// fx.hover: highlight data on hover
36218// evt can be a mousemove event, or an object with data about what points
36219// to hover on
36220// {xpx,ypx[,hovermode]} - pixel locations from top left
36221// (with optional overriding hovermode)
36222// {xval,yval[,hovermode]} - data values
36223// [{curveNumber,(pointNumber|xval and/or yval)}] -
36224// array of specific points to highlight
36225// pointNumber is a single integer if gd.data[curveNumber] is 1D,
36226// or a two-element array if it's 2D
36227// xval and yval are data values,
36228// 1D data may specify either or both,
36229// 2D data must specify both
36230// subplot is an id string (default "xy")
36231// makes use of gl.hovermode, which can be:
36232// x (find the points with the closest x values, ie a column),
36233// closest (find the single closest point)
36234// internally there are two more that occasionally get used:
36235// y (pick out a row - only used for multiple horizontal bar charts)
36236// array (used when the user specifies an explicit
36237// array of points to hover on)
36238//
36239// We wrap the hovers in a timer, to limit their frequency.
36240// The actual rendering is done by private function _hover.
36241exports.hover = function hover(gd, evt, subplot, noHoverEvent) {
36242 gd = Lib.getGraphDiv(gd);
36243
36244 Lib.throttle(
36245 gd._fullLayout._uid + constants.HOVERID,
36246 constants.HOVERMINTIME,
36247 function() { _hover(gd, evt, subplot, noHoverEvent); }
36248 );
36249};
36250
36251/*
36252 * Draw a single hover item or an array of hover item in a pre-existing svg container somewhere
36253 * hoverItem should have keys:
36254 * - x and y (or x0, x1, y0, and y1):
36255 * the pixel position to mark, relative to opts.container
36256 * - xLabel, yLabel, zLabel, text, and name:
36257 * info to go in the label
36258 * - color:
36259 * the background color for the label.
36260 * - idealAlign (optional):
36261 * 'left' or 'right' for which side of the x/y box to try to put this on first
36262 * - borderColor (optional):
36263 * color for the border, defaults to strongest contrast with color
36264 * - fontFamily (optional):
36265 * string, the font for this label, defaults to constants.HOVERFONT
36266 * - fontSize (optional):
36267 * the label font size, defaults to constants.HOVERFONTSIZE
36268 * - fontColor (optional):
36269 * defaults to borderColor
36270 * opts should have keys:
36271 * - bgColor:
36272 * the background color this is against, used if the trace is
36273 * non-opaque, and for the name, which goes outside the box
36274 * - container:
36275 * a <svg> or <g> element to add the hover label to
36276 * - outerContainer:
36277 * normally a parent of `container`, sets the bounding box to use to
36278 * constrain the hover label and determine whether to show it on the left or right
36279 * opts can have optional keys:
36280 * - anchorIndex:
36281 the index of the hover item used as an anchor for positioning.
36282 The other hover items will be pushed up or down to prevent overlap.
36283 */
36284exports.loneHover = function loneHover(hoverItems, opts) {
36285 var multiHover = true;
36286 if(!Array.isArray(hoverItems)) {
36287 multiHover = false;
36288 hoverItems = [hoverItems];
36289 }
36290
36291 var gd = opts.gd;
36292 var gTop = getTopOffset(gd);
36293 var gLeft = getLeftOffset(gd);
36294
36295 var pointsData = hoverItems.map(function(hoverItem) {
36296 var _x0 = hoverItem._x0 || hoverItem.x0 || hoverItem.x || 0;
36297 var _x1 = hoverItem._x1 || hoverItem.x1 || hoverItem.x || 0;
36298 var _y0 = hoverItem._y0 || hoverItem.y0 || hoverItem.y || 0;
36299 var _y1 = hoverItem._y1 || hoverItem.y1 || hoverItem.y || 0;
36300
36301 var eventData = hoverItem.eventData;
36302 if(eventData) {
36303 var x0 = Math.min(_x0, _x1);
36304 var x1 = Math.max(_x0, _x1);
36305 var y0 = Math.min(_y0, _y1);
36306 var y1 = Math.max(_y0, _y1);
36307
36308 var trace = hoverItem.trace;
36309 if(Registry.traceIs(trace, 'gl3d')) {
36310 var container = gd._fullLayout[trace.scene]._scene.container;
36311 var dx = container.offsetLeft;
36312 var dy = container.offsetTop;
36313 x0 += dx;
36314 x1 += dx;
36315 y0 += dy;
36316 y1 += dy;
36317 } // TODO: handle heatmapgl
36318
36319 eventData.bbox = {
36320 x0: x0 + gLeft,
36321 x1: x1 + gLeft,
36322 y0: y0 + gTop,
36323 y1: y1 + gTop
36324 };
36325
36326 if(opts.inOut_bbox) {
36327 opts.inOut_bbox.push(eventData.bbox);
36328 }
36329 } else {
36330 eventData = false;
36331 }
36332
36333 return {
36334 color: hoverItem.color || Color.defaultLine,
36335 x0: hoverItem.x0 || hoverItem.x || 0,
36336 x1: hoverItem.x1 || hoverItem.x || 0,
36337 y0: hoverItem.y0 || hoverItem.y || 0,
36338 y1: hoverItem.y1 || hoverItem.y || 0,
36339 xLabel: hoverItem.xLabel,
36340 yLabel: hoverItem.yLabel,
36341 zLabel: hoverItem.zLabel,
36342 text: hoverItem.text,
36343 name: hoverItem.name,
36344 idealAlign: hoverItem.idealAlign,
36345
36346 // optional extra bits of styling
36347 borderColor: hoverItem.borderColor,
36348 fontFamily: hoverItem.fontFamily,
36349 fontSize: hoverItem.fontSize,
36350 fontColor: hoverItem.fontColor,
36351 nameLength: hoverItem.nameLength,
36352 textAlign: hoverItem.textAlign,
36353
36354 // filler to make createHoverText happy
36355 trace: hoverItem.trace || {
36356 index: 0,
36357 hoverinfo: ''
36358 },
36359 xa: {_offset: 0},
36360 ya: {_offset: 0},
36361 index: 0,
36362
36363 hovertemplate: hoverItem.hovertemplate || false,
36364 hovertemplateLabels: hoverItem.hovertemplateLabels || false,
36365
36366 eventData: eventData
36367 };
36368 });
36369
36370 var container3 = d3.select(opts.container);
36371 var outerContainer3 = opts.outerContainer ? d3.select(opts.outerContainer) : container3;
36372
36373 var fullOpts = {
36374 hovermode: 'closest',
36375 rotateLabels: false,
36376 bgColor: opts.bgColor || Color.background,
36377 container: container3,
36378 outerContainer: outerContainer3
36379 };
36380
36381 var hoverLabel = createHoverText(pointsData, fullOpts, gd);
36382
36383 // Fix vertical overlap
36384 var tooltipSpacing = 5;
36385 var lastBottomY = 0;
36386 var anchor = 0;
36387 hoverLabel
36388 .sort(function(a, b) {return a.y0 - b.y0;})
36389 .each(function(d, i) {
36390 var topY = d.y0 - d.by / 2;
36391
36392 if((topY - tooltipSpacing) < lastBottomY) {
36393 d.offset = (lastBottomY - topY) + tooltipSpacing;
36394 } else {
36395 d.offset = 0;
36396 }
36397
36398 lastBottomY = topY + d.by + d.offset;
36399
36400 if(i === opts.anchorIndex || 0) anchor = d.offset;
36401 })
36402 .each(function(d) {
36403 d.offset -= anchor;
36404 });
36405
36406 var scaleX = gd._fullLayout._invScaleX;
36407 var scaleY = gd._fullLayout._invScaleY;
36408 alignHoverText(hoverLabel, fullOpts.rotateLabels, scaleX, scaleY);
36409
36410 return multiHover ? hoverLabel : hoverLabel.node();
36411};
36412
36413// The actual implementation is here:
36414function _hover(gd, evt, subplot, noHoverEvent) {
36415 if(!subplot) subplot = 'xy';
36416
36417 // if the user passed in an array of subplots,
36418 // use those instead of finding overlayed plots
36419 var subplots = Array.isArray(subplot) ? subplot : [subplot];
36420
36421 var fullLayout = gd._fullLayout;
36422 var plots = fullLayout._plots || [];
36423 var plotinfo = plots[subplot];
36424 var hasCartesian = fullLayout._has('cartesian');
36425
36426 // list of all overlaid subplots to look at
36427 if(plotinfo) {
36428 var overlayedSubplots = plotinfo.overlays.map(function(pi) {
36429 return pi.id;
36430 });
36431
36432 subplots = subplots.concat(overlayedSubplots);
36433 }
36434
36435 var len = subplots.length;
36436 var xaArray = new Array(len);
36437 var yaArray = new Array(len);
36438 var supportsCompare = false;
36439
36440 for(var i = 0; i < len; i++) {
36441 var spId = subplots[i];
36442
36443 if(plots[spId]) {
36444 // 'cartesian' case
36445 supportsCompare = true;
36446 xaArray[i] = plots[spId].xaxis;
36447 yaArray[i] = plots[spId].yaxis;
36448 } else if(fullLayout[spId] && fullLayout[spId]._subplot) {
36449 // other subplot types
36450 var _subplot = fullLayout[spId]._subplot;
36451 xaArray[i] = _subplot.xaxis;
36452 yaArray[i] = _subplot.yaxis;
36453 } else {
36454 Lib.warn('Unrecognized subplot: ' + spId);
36455 return;
36456 }
36457 }
36458
36459 var hovermode = evt.hovermode || fullLayout.hovermode;
36460
36461 if(hovermode && !supportsCompare) hovermode = 'closest';
36462
36463 if(['x', 'y', 'closest', 'x unified', 'y unified'].indexOf(hovermode) === -1 || !gd.calcdata ||
36464 gd.querySelector('.zoombox') || gd._dragging) {
36465 return dragElement.unhoverRaw(gd, evt);
36466 }
36467
36468 var hoverdistance = fullLayout.hoverdistance;
36469 if(hoverdistance === -1) hoverdistance = Infinity;
36470
36471 var spikedistance = fullLayout.spikedistance;
36472 if(spikedistance === -1) spikedistance = Infinity;
36473
36474 // hoverData: the set of candidate points we've found to highlight
36475 var hoverData = [];
36476
36477 // searchData: the data to search in. Mostly this is just a copy of
36478 // gd.calcdata, filtered to the subplot and overlays we're on
36479 // but if a point array is supplied it will be a mapping
36480 // of indicated curves
36481 var searchData = [];
36482
36483 // [x|y]valArray: the axis values of the hover event
36484 // mapped onto each of the currently selected overlaid subplots
36485 var xvalArray, yvalArray;
36486
36487 var itemnum, curvenum, cd, trace, subplotId, subploti, _mode,
36488 xval, yval, pointData, closedataPreviousLength;
36489
36490 // spikePoints: the set of candidate points we've found to draw spikes to
36491 var spikePoints = {
36492 hLinePoint: null,
36493 vLinePoint: null
36494 };
36495
36496 // does subplot have one (or more) horizontal traces?
36497 // This is used to determine whether we rotate the labels or not
36498 var hasOneHorizontalTrace = false;
36499
36500 // Figure out what we're hovering on:
36501 // mouse location or user-supplied data
36502
36503 if(Array.isArray(evt)) {
36504 // user specified an array of points to highlight
36505 hovermode = 'array';
36506 for(itemnum = 0; itemnum < evt.length; itemnum++) {
36507 cd = gd.calcdata[evt[itemnum].curveNumber || 0];
36508 if(cd) {
36509 trace = cd[0].trace;
36510 if(cd[0].trace.hoverinfo !== 'skip') {
36511 searchData.push(cd);
36512 if(trace.orientation === 'h') {
36513 hasOneHorizontalTrace = true;
36514 }
36515 }
36516 }
36517 }
36518 } else {
36519 for(curvenum = 0; curvenum < gd.calcdata.length; curvenum++) {
36520 cd = gd.calcdata[curvenum];
36521 trace = cd[0].trace;
36522 if(trace.hoverinfo !== 'skip' && helpers.isTraceInSubplots(trace, subplots)) {
36523 searchData.push(cd);
36524 if(trace.orientation === 'h') {
36525 hasOneHorizontalTrace = true;
36526 }
36527 }
36528 }
36529
36530 // [x|y]px: the pixels (from top left) of the mouse location
36531 // on the currently selected plot area
36532 // add pointerX|Y property for drawing the spikes in spikesnap 'cursor' situation
36533 var hasUserCalledHover = !evt.target;
36534 var xpx, ypx;
36535
36536 if(hasUserCalledHover) {
36537 if('xpx' in evt) xpx = evt.xpx;
36538 else xpx = xaArray[0]._length / 2;
36539
36540 if('ypx' in evt) ypx = evt.ypx;
36541 else ypx = yaArray[0]._length / 2;
36542 } else {
36543 // fire the beforehover event and quit if it returns false
36544 // note that we're only calling this on real mouse events, so
36545 // manual calls to fx.hover will always run.
36546 if(Events.triggerHandler(gd, 'plotly_beforehover', evt) === false) {
36547 return;
36548 }
36549
36550 // Discover event target, traversing open shadow roots.
36551 var target = evt.composedPath && evt.composedPath()[0];
36552 if(!target) {
36553 // Fallback for browsers not supporting composedPath
36554 target = evt.target;
36555 }
36556 var dbb = target.getBoundingClientRect();
36557
36558 xpx = evt.clientX - dbb.left;
36559 ypx = evt.clientY - dbb.top;
36560
36561 fullLayout._calcInverseTransform(gd);
36562 var transformedCoords = Lib.apply3DTransform(fullLayout._invTransform)(xpx, ypx);
36563
36564 xpx = transformedCoords[0];
36565 ypx = transformedCoords[1];
36566
36567 // in case hover was called from mouseout into hovertext,
36568 // it's possible you're not actually over the plot anymore
36569 if(xpx < 0 || xpx > xaArray[0]._length || ypx < 0 || ypx > yaArray[0]._length) {
36570 return dragElement.unhoverRaw(gd, evt);
36571 }
36572 }
36573
36574 evt.pointerX = xpx + xaArray[0]._offset;
36575 evt.pointerY = ypx + yaArray[0]._offset;
36576
36577 if('xval' in evt) xvalArray = helpers.flat(subplots, evt.xval);
36578 else xvalArray = helpers.p2c(xaArray, xpx);
36579
36580 if('yval' in evt) yvalArray = helpers.flat(subplots, evt.yval);
36581 else yvalArray = helpers.p2c(yaArray, ypx);
36582
36583 if(!isNumeric(xvalArray[0]) || !isNumeric(yvalArray[0])) {
36584 Lib.warn('Fx.hover failed', evt, gd);
36585 return dragElement.unhoverRaw(gd, evt);
36586 }
36587 }
36588
36589 // the pixel distance to beat as a matching point
36590 // in 'x' or 'y' mode this resets for each trace
36591 var distance = Infinity;
36592
36593 // find the closest point in each trace
36594 // this is minimum dx and/or dy, depending on mode
36595 // and the pixel position for the label (labelXpx, labelYpx)
36596 function findHoverPoints(customXVal, customYVal) {
36597 for(curvenum = 0; curvenum < searchData.length; curvenum++) {
36598 cd = searchData[curvenum];
36599
36600 // filter out invisible or broken data
36601 if(!cd || !cd[0] || !cd[0].trace) continue;
36602
36603 trace = cd[0].trace;
36604
36605 if(trace.visible !== true || trace._length === 0) continue;
36606
36607 // Explicitly bail out for these two. I don't know how to otherwise prevent
36608 // the rest of this function from running and failing
36609 if(['carpet', 'contourcarpet'].indexOf(trace._module.name) !== -1) continue;
36610
36611 if(trace.type === 'splom') {
36612 // splom traces do not generate overlay subplots,
36613 // it is safe to assume here splom traces correspond to the 0th subplot
36614 subploti = 0;
36615 subplotId = subplots[subploti];
36616 } else {
36617 subplotId = helpers.getSubplot(trace);
36618 subploti = subplots.indexOf(subplotId);
36619 }
36620
36621 // within one trace mode can sometimes be overridden
36622 _mode = hovermode;
36623 if(helpers.isUnifiedHover(_mode)) {
36624 _mode = _mode.charAt(0);
36625 }
36626
36627 // container for new point, also used to pass info into module.hoverPoints
36628 pointData = {
36629 // trace properties
36630 cd: cd,
36631 trace: trace,
36632 xa: xaArray[subploti],
36633 ya: yaArray[subploti],
36634
36635 // max distances for hover and spikes - for points that want to show but do not
36636 // want to override other points, set distance/spikeDistance equal to max*Distance
36637 // and it will not get filtered out but it will be guaranteed to have a greater
36638 // distance than any point that calculated a real distance.
36639 maxHoverDistance: hoverdistance,
36640 maxSpikeDistance: spikedistance,
36641
36642 // point properties - override all of these
36643 index: false, // point index in trace - only used by plotly.js hoverdata consumers
36644 distance: Math.min(distance, hoverdistance), // pixel distance or pseudo-distance
36645
36646 // distance/pseudo-distance for spikes. This distance should always be calculated
36647 // as if in "closest" mode, and should only be set if this point should
36648 // generate a spike.
36649 spikeDistance: Infinity,
36650
36651 // in some cases the spikes have different positioning from the hover label
36652 // they don't need x0/x1, just one position
36653 xSpike: undefined,
36654 ySpike: undefined,
36655
36656 // where and how to display the hover label
36657 color: Color.defaultLine, // trace color
36658 name: trace.name,
36659 x0: undefined,
36660 x1: undefined,
36661 y0: undefined,
36662 y1: undefined,
36663 xLabelVal: undefined,
36664 yLabelVal: undefined,
36665 zLabelVal: undefined,
36666 text: undefined
36667 };
36668
36669 // add ref to subplot object (non-cartesian case)
36670 if(fullLayout[subplotId]) {
36671 pointData.subplot = fullLayout[subplotId]._subplot;
36672 }
36673 // add ref to splom scene
36674 if(fullLayout._splomScenes && fullLayout._splomScenes[trace.uid]) {
36675 pointData.scene = fullLayout._splomScenes[trace.uid];
36676 }
36677
36678 closedataPreviousLength = hoverData.length;
36679
36680 // for a highlighting array, figure out what
36681 // we're searching for with this element
36682 if(_mode === 'array') {
36683 var selection = evt[curvenum];
36684 if('pointNumber' in selection) {
36685 pointData.index = selection.pointNumber;
36686 _mode = 'closest';
36687 } else {
36688 _mode = '';
36689 if('xval' in selection) {
36690 xval = selection.xval;
36691 _mode = 'x';
36692 }
36693 if('yval' in selection) {
36694 yval = selection.yval;
36695 _mode = _mode ? 'closest' : 'y';
36696 }
36697 }
36698 } else if(customXVal !== undefined && customYVal !== undefined) {
36699 xval = customXVal;
36700 yval = customYVal;
36701 } else {
36702 xval = xvalArray[subploti];
36703 yval = yvalArray[subploti];
36704 }
36705
36706 // Now if there is range to look in, find the points to hover.
36707 if(hoverdistance !== 0) {
36708 if(trace._module && trace._module.hoverPoints) {
36709 var newPoints = trace._module.hoverPoints(pointData, xval, yval, _mode, {
36710 finiteRange: true,
36711 hoverLayer: fullLayout._hoverlayer
36712 });
36713
36714 if(newPoints) {
36715 var newPoint;
36716 for(var newPointNum = 0; newPointNum < newPoints.length; newPointNum++) {
36717 newPoint = newPoints[newPointNum];
36718 if(isNumeric(newPoint.x0) && isNumeric(newPoint.y0)) {
36719 hoverData.push(cleanPoint(newPoint, hovermode));
36720 }
36721 }
36722 }
36723 } else {
36724 Lib.log('Unrecognized trace type in hover:', trace);
36725 }
36726 }
36727
36728 // in closest mode, remove any existing (farther) points
36729 // and don't look any farther than this latest point (or points, some
36730 // traces like box & violin make multiple hover labels at once)
36731 if(hovermode === 'closest' && hoverData.length > closedataPreviousLength) {
36732 hoverData.splice(0, closedataPreviousLength);
36733 distance = hoverData[0].distance;
36734 }
36735
36736 // Now if there is range to look in, find the points to draw the spikelines
36737 // Do it only if there is no hoverData
36738 if(hasCartesian && (spikedistance !== 0)) {
36739 if(hoverData.length === 0) {
36740 pointData.distance = spikedistance;
36741 pointData.index = false;
36742 var closestPoints = trace._module.hoverPoints(pointData, xval, yval, 'closest', {
36743 hoverLayer: fullLayout._hoverlayer
36744 });
36745 if(closestPoints) {
36746 closestPoints = closestPoints.filter(function(point) {
36747 // some hover points, like scatter fills, do not allow spikes,
36748 // so will generate a hover point but without a valid spikeDistance
36749 return point.spikeDistance <= spikedistance;
36750 });
36751 }
36752 if(closestPoints && closestPoints.length) {
36753 var tmpPoint;
36754 var closestVPoints = closestPoints.filter(function(point) {
36755 return point.xa.showspikes && point.xa.spikesnap !== 'hovered data';
36756 });
36757 if(closestVPoints.length) {
36758 var closestVPt = closestVPoints[0];
36759 if(isNumeric(closestVPt.x0) && isNumeric(closestVPt.y0)) {
36760 tmpPoint = fillSpikePoint(closestVPt);
36761 if(!spikePoints.vLinePoint || (spikePoints.vLinePoint.spikeDistance > tmpPoint.spikeDistance)) {
36762 spikePoints.vLinePoint = tmpPoint;
36763 }
36764 }
36765 }
36766
36767 var closestHPoints = closestPoints.filter(function(point) {
36768 return point.ya.showspikes && point.ya.spikesnap !== 'hovered data';
36769 });
36770 if(closestHPoints.length) {
36771 var closestHPt = closestHPoints[0];
36772 if(isNumeric(closestHPt.x0) && isNumeric(closestHPt.y0)) {
36773 tmpPoint = fillSpikePoint(closestHPt);
36774 if(!spikePoints.hLinePoint || (spikePoints.hLinePoint.spikeDistance > tmpPoint.spikeDistance)) {
36775 spikePoints.hLinePoint = tmpPoint;
36776 }
36777 }
36778 }
36779 }
36780 }
36781 }
36782 }
36783 }
36784
36785 findHoverPoints();
36786
36787 function selectClosestPoint(pointsData, spikedistance, spikeOnWinning) {
36788 var resultPoint = null;
36789 var minDistance = Infinity;
36790 var thisSpikeDistance;
36791
36792 for(var i = 0; i < pointsData.length; i++) {
36793 thisSpikeDistance = pointsData[i].spikeDistance;
36794 if(spikeOnWinning && i === 0) thisSpikeDistance = -Infinity;
36795
36796 if(thisSpikeDistance <= minDistance && thisSpikeDistance <= spikedistance) {
36797 resultPoint = pointsData[i];
36798 minDistance = thisSpikeDistance;
36799 }
36800 }
36801 return resultPoint;
36802 }
36803
36804 function fillSpikePoint(point) {
36805 if(!point) return null;
36806 return {
36807 xa: point.xa,
36808 ya: point.ya,
36809 x: point.xSpike !== undefined ? point.xSpike : (point.x0 + point.x1) / 2,
36810 y: point.ySpike !== undefined ? point.ySpike : (point.y0 + point.y1) / 2,
36811 distance: point.distance,
36812 spikeDistance: point.spikeDistance,
36813 curveNumber: point.trace.index,
36814 color: point.color,
36815 pointNumber: point.index
36816 };
36817 }
36818
36819 var spikelineOpts = {
36820 fullLayout: fullLayout,
36821 container: fullLayout._hoverlayer,
36822 outerContainer: fullLayout._paperdiv,
36823 event: evt
36824 };
36825 var oldspikepoints = gd._spikepoints;
36826 var newspikepoints = {
36827 vLinePoint: spikePoints.vLinePoint,
36828 hLinePoint: spikePoints.hLinePoint
36829 };
36830 gd._spikepoints = newspikepoints;
36831
36832 var sortHoverData = function() {
36833 hoverData.sort(function(d1, d2) { return d1.distance - d2.distance; });
36834
36835 // move period positioned points and box/bar-like traces to the end of the list
36836 hoverData = orderRangePoints(hoverData, hovermode);
36837 };
36838 sortHoverData();
36839
36840 var axLetter = hovermode.charAt(0);
36841 var spikeOnWinning = (axLetter === 'x' || axLetter === 'y') && hoverData[0] && cartesianScatterPoints[hoverData[0].trace.type];
36842
36843 // Now if it is not restricted by spikedistance option, set the points to draw the spikelines
36844 if(hasCartesian && (spikedistance !== 0)) {
36845 if(hoverData.length !== 0) {
36846 var tmpHPointData = hoverData.filter(function(point) {
36847 return point.ya.showspikes;
36848 });
36849 var tmpHPoint = selectClosestPoint(tmpHPointData, spikedistance, spikeOnWinning);
36850 spikePoints.hLinePoint = fillSpikePoint(tmpHPoint);
36851
36852 var tmpVPointData = hoverData.filter(function(point) {
36853 return point.xa.showspikes;
36854 });
36855 var tmpVPoint = selectClosestPoint(tmpVPointData, spikedistance, spikeOnWinning);
36856 spikePoints.vLinePoint = fillSpikePoint(tmpVPoint);
36857 }
36858 }
36859
36860 // if hoverData is empty check for the spikes to draw and quit if there are none
36861 if(hoverData.length === 0) {
36862 var result = dragElement.unhoverRaw(gd, evt);
36863 if(hasCartesian && ((spikePoints.hLinePoint !== null) || (spikePoints.vLinePoint !== null))) {
36864 if(spikesChanged(oldspikepoints)) {
36865 createSpikelines(gd, spikePoints, spikelineOpts);
36866 }
36867 }
36868 return result;
36869 }
36870
36871 if(hasCartesian) {
36872 if(spikesChanged(oldspikepoints)) {
36873 createSpikelines(gd, spikePoints, spikelineOpts);
36874 }
36875 }
36876
36877 if(
36878 helpers.isXYhover(_mode) &&
36879 hoverData[0].length !== 0 &&
36880 hoverData[0].trace.type !== 'splom' // TODO: add support for splom
36881 ) {
36882 // pick winning point
36883 var winningPoint = hoverData[0];
36884 // discard other points
36885 if(multipleHoverPoints[winningPoint.trace.type]) {
36886 hoverData = hoverData.filter(function(d) {
36887 return d.trace.index === winningPoint.trace.index;
36888 });
36889 } else {
36890 hoverData = [winningPoint];
36891 }
36892 var initLen = hoverData.length;
36893
36894 var winX = getCoord('x', winningPoint, fullLayout);
36895 var winY = getCoord('y', winningPoint, fullLayout);
36896
36897 // in compare mode, select every point at position
36898 findHoverPoints(winX, winY);
36899
36900 var finalPoints = [];
36901 var seen = {};
36902 var id = 0;
36903 var insert = function(newHd) {
36904 var key = multipleHoverPoints[newHd.trace.type] ? hoverDataKey(newHd) : newHd.trace.index;
36905 if(!seen[key]) {
36906 id++;
36907 seen[key] = id;
36908 finalPoints.push(newHd);
36909 } else {
36910 var oldId = seen[key] - 1;
36911 var oldHd = finalPoints[oldId];
36912 if(oldId > 0 &&
36913 Math.abs(newHd.distance) <
36914 Math.abs(oldHd.distance)
36915 ) {
36916 // replace with closest
36917 finalPoints[oldId] = newHd;
36918 }
36919 }
36920 };
36921
36922 var k;
36923 // insert the winnig point(s) first
36924 for(k = 0; k < initLen; k++) {
36925 insert(hoverData[k]);
36926 }
36927 // override from the end
36928 for(k = hoverData.length - 1; k > initLen - 1; k--) {
36929 insert(hoverData[k]);
36930 }
36931 hoverData = finalPoints;
36932 sortHoverData();
36933 }
36934
36935 // lastly, emit custom hover/unhover events
36936 var oldhoverdata = gd._hoverdata;
36937 var newhoverdata = [];
36938
36939 var gTop = getTopOffset(gd);
36940 var gLeft = getLeftOffset(gd);
36941
36942 // pull out just the data that's useful to
36943 // other people and send it to the event
36944 for(itemnum = 0; itemnum < hoverData.length; itemnum++) {
36945 var pt = hoverData[itemnum];
36946 var eventData = helpers.makeEventData(pt, pt.trace, pt.cd);
36947
36948 if(pt.hovertemplate !== false) {
36949 var ht = false;
36950 if(pt.cd[pt.index] && pt.cd[pt.index].ht) {
36951 ht = pt.cd[pt.index].ht;
36952 }
36953 pt.hovertemplate = ht || pt.trace.hovertemplate || false;
36954 }
36955
36956 if(pt.xa && pt.ya) {
36957 var _x0 = pt.x0 + pt.xa._offset;
36958 var _x1 = pt.x1 + pt.xa._offset;
36959 var _y0 = pt.y0 + pt.ya._offset;
36960 var _y1 = pt.y1 + pt.ya._offset;
36961
36962 var x0 = Math.min(_x0, _x1);
36963 var x1 = Math.max(_x0, _x1);
36964 var y0 = Math.min(_y0, _y1);
36965 var y1 = Math.max(_y0, _y1);
36966
36967 eventData.bbox = {
36968 x0: x0 + gLeft,
36969 x1: x1 + gLeft,
36970 y0: y0 + gTop,
36971 y1: y1 + gTop
36972 };
36973 }
36974
36975 pt.eventData = [eventData];
36976 newhoverdata.push(eventData);
36977 }
36978
36979 gd._hoverdata = newhoverdata;
36980
36981 var rotateLabels = (
36982 (hovermode === 'y' && (searchData.length > 1 || hoverData.length > 1)) ||
36983 (hovermode === 'closest' && hasOneHorizontalTrace && hoverData.length > 1)
36984 );
36985
36986 var bgColor = Color.combine(
36987 fullLayout.plot_bgcolor || Color.background,
36988 fullLayout.paper_bgcolor
36989 );
36990
36991 var labelOpts = {
36992 hovermode: hovermode,
36993 rotateLabels: rotateLabels,
36994 bgColor: bgColor,
36995 container: fullLayout._hoverlayer,
36996 outerContainer: fullLayout._paperdiv,
36997 commonLabelOpts: fullLayout.hoverlabel,
36998 hoverdistance: fullLayout.hoverdistance
36999 };
37000
37001 var hoverLabels = createHoverText(hoverData, labelOpts, gd);
37002
37003 if(!helpers.isUnifiedHover(hovermode)) {
37004 hoverAvoidOverlaps(hoverLabels, rotateLabels ? 'xa' : 'ya', fullLayout);
37005 alignHoverText(hoverLabels, rotateLabels, fullLayout._invScaleX, fullLayout._invScaleY);
37006 } // TODO: tagName hack is needed to appease geo.js's hack of using evt.target=true
37007 // we should improve the "fx" API so other plots can use it without these hack.
37008 if(evt.target && evt.target.tagName) {
37009 var hasClickToShow = Registry.getComponentMethod('annotations', 'hasClickToShow')(gd, newhoverdata);
37010 overrideCursor(d3.select(evt.target), hasClickToShow ? 'pointer' : '');
37011 }
37012
37013 // don't emit events if called manually
37014 if(!evt.target || noHoverEvent || !hoverChanged(gd, evt, oldhoverdata)) return;
37015
37016 if(oldhoverdata) {
37017 gd.emit('plotly_unhover', {
37018 event: evt,
37019 points: oldhoverdata
37020 });
37021 }
37022
37023 gd.emit('plotly_hover', {
37024 event: evt,
37025 points: gd._hoverdata,
37026 xaxes: xaArray,
37027 yaxes: yaArray,
37028 xvals: xvalArray,
37029 yvals: yvalArray
37030 });
37031}
37032
37033function hoverDataKey(d) {
37034 return [d.trace.index, d.index, d.x0, d.y0, d.name, d.attr, d.xa ? d.xa._id : '', d.ya ? d.ya._id : ''].join(',');
37035}
37036
37037var EXTRA_STRING_REGEX = /<extra>([\s\S]*)<\/extra>/;
37038
37039function createHoverText(hoverData, opts, gd) {
37040 var fullLayout = gd._fullLayout;
37041 var hovermode = opts.hovermode;
37042 var rotateLabels = opts.rotateLabels;
37043 var bgColor = opts.bgColor;
37044 var container = opts.container;
37045 var outerContainer = opts.outerContainer;
37046 var commonLabelOpts = opts.commonLabelOpts || {};
37047
37048 // opts.fontFamily/Size are used for the common label
37049 // and as defaults for each hover label, though the individual labels
37050 // can override this.
37051 var fontFamily = opts.fontFamily || constants.HOVERFONT;
37052 var fontSize = opts.fontSize || constants.HOVERFONTSIZE;
37053
37054 var c0 = hoverData[0];
37055 var xa = c0.xa;
37056 var ya = c0.ya;
37057 var axLetter = hovermode.charAt(0);
37058 var t0 = c0[axLetter + 'Label'];
37059 var outerContainerBB = outerContainer.node().getBoundingClientRect();
37060 var outerTop = outerContainerBB.top;
37061 var outerWidth = outerContainerBB.width;
37062 var outerHeight = outerContainerBB.height;
37063
37064 // show the common label, if any, on the axis
37065 // never show a common label in array mode,
37066 // even if sometimes there could be one
37067 var showCommonLabel = (
37068 (t0 !== undefined) &&
37069 (c0.distance <= opts.hoverdistance) &&
37070 (hovermode === 'x' || hovermode === 'y')
37071 );
37072
37073 // all hover traces hoverinfo must contain the hovermode
37074 // to have common labels
37075 if(showCommonLabel) {
37076 var allHaveZ = true;
37077 var i, traceHoverinfo;
37078 for(i = 0; i < hoverData.length; i++) {
37079 if(allHaveZ && hoverData[i].zLabel === undefined) allHaveZ = false;
37080
37081 traceHoverinfo = hoverData[i].hoverinfo || hoverData[i].trace.hoverinfo;
37082 if(traceHoverinfo) {
37083 var parts = Array.isArray(traceHoverinfo) ? traceHoverinfo : traceHoverinfo.split('+');
37084 if(parts.indexOf('all') === -1 &&
37085 parts.indexOf(hovermode) === -1) {
37086 showCommonLabel = false;
37087 break;
37088 }
37089 }
37090 }
37091
37092 // xyz labels put all info in their main label, so have no need of a common label
37093 if(allHaveZ) showCommonLabel = false;
37094 }
37095
37096 var commonLabel = container.selectAll('g.axistext')
37097 .data(showCommonLabel ? [0] : []);
37098 commonLabel.enter().append('g')
37099 .classed('axistext', true);
37100 commonLabel.exit().remove();
37101
37102 commonLabel.each(function() {
37103 var label = d3.select(this);
37104 var lpath = Lib.ensureSingle(label, 'path', '', function(s) {
37105 s.style({'stroke-width': '1px'});
37106 });
37107 var ltext = Lib.ensureSingle(label, 'text', '', function(s) {
37108 // prohibit tex interpretation until we can handle
37109 // tex and regular text together
37110 s.attr('data-notex', 1);
37111 });
37112
37113 var commonBgColor = commonLabelOpts.bgcolor || Color.defaultLine;
37114 var commonStroke = commonLabelOpts.bordercolor || Color.contrast(commonBgColor);
37115 var contrastColor = Color.contrast(commonBgColor);
37116 var commonLabelFont = {
37117 family: commonLabelOpts.font.family || fontFamily,
37118 size: commonLabelOpts.font.size || fontSize,
37119 color: commonLabelOpts.font.color || contrastColor
37120 };
37121
37122 lpath.style({
37123 fill: commonBgColor,
37124 stroke: commonStroke
37125 });
37126
37127 ltext.text(t0)
37128 .call(Drawing.font, commonLabelFont)
37129 .call(svgTextUtils.positionText, 0, 0)
37130 .call(svgTextUtils.convertToTspans, gd);
37131
37132 label.attr('transform', '');
37133
37134 var tbb = ltext.node().getBoundingClientRect();
37135 var lx, ly;
37136
37137 if(hovermode === 'x') {
37138 var topsign = xa.side === 'top' ? '-' : '';
37139
37140 ltext.attr('text-anchor', 'middle')
37141 .call(svgTextUtils.positionText, 0, (xa.side === 'top' ?
37142 (outerTop - tbb.bottom - HOVERARROWSIZE - HOVERTEXTPAD) :
37143 (outerTop - tbb.top + HOVERARROWSIZE + HOVERTEXTPAD)));
37144
37145 lx = xa._offset + (c0.x0 + c0.x1) / 2;
37146 ly = ya._offset + (xa.side === 'top' ? 0 : ya._length);
37147
37148 var halfWidth = tbb.width / 2 + HOVERTEXTPAD;
37149
37150 if(lx < halfWidth) {
37151 lx = halfWidth;
37152
37153 lpath.attr('d', 'M-' + (halfWidth - HOVERARROWSIZE) + ',0' +
37154 'L-' + (halfWidth - HOVERARROWSIZE * 2) + ',' + topsign + HOVERARROWSIZE +
37155 'H' + (HOVERTEXTPAD + tbb.width / 2) +
37156 'v' + topsign + (HOVERTEXTPAD * 2 + tbb.height) +
37157 'H-' + halfWidth +
37158 'V' + topsign + HOVERARROWSIZE +
37159 'Z');
37160 } else if(lx > (fullLayout.width - halfWidth)) {
37161 lx = fullLayout.width - halfWidth;
37162
37163 lpath.attr('d', 'M' + (halfWidth - HOVERARROWSIZE) + ',0' +
37164 'L' + halfWidth + ',' + topsign + HOVERARROWSIZE +
37165 'v' + topsign + (HOVERTEXTPAD * 2 + tbb.height) +
37166 'H-' + halfWidth +
37167 'V' + topsign + HOVERARROWSIZE +
37168 'H' + (halfWidth - HOVERARROWSIZE * 2) + 'Z');
37169 } else {
37170 lpath.attr('d', 'M0,0' +
37171 'L' + HOVERARROWSIZE + ',' + topsign + HOVERARROWSIZE +
37172 'H' + (HOVERTEXTPAD + tbb.width / 2) +
37173 'v' + topsign + (HOVERTEXTPAD * 2 + tbb.height) +
37174 'H-' + (HOVERTEXTPAD + tbb.width / 2) +
37175 'V' + topsign + HOVERARROWSIZE +
37176 'H-' + HOVERARROWSIZE + 'Z');
37177 }
37178 } else {
37179 var anchor;
37180 var sgn;
37181 var leftsign;
37182 if(ya.side === 'right') {
37183 anchor = 'start';
37184 sgn = 1;
37185 leftsign = '';
37186 lx = xa._offset + xa._length;
37187 } else {
37188 anchor = 'end';
37189 sgn = -1;
37190 leftsign = '-';
37191 lx = xa._offset;
37192 }
37193
37194 ly = ya._offset + (c0.y0 + c0.y1) / 2;
37195
37196 ltext.attr('text-anchor', anchor);
37197
37198 lpath.attr('d', 'M0,0' +
37199 'L' + leftsign + HOVERARROWSIZE + ',' + HOVERARROWSIZE +
37200 'V' + (HOVERTEXTPAD + tbb.height / 2) +
37201 'h' + leftsign + (HOVERTEXTPAD * 2 + tbb.width) +
37202 'V-' + (HOVERTEXTPAD + tbb.height / 2) +
37203 'H' + leftsign + HOVERARROWSIZE + 'V-' + HOVERARROWSIZE + 'Z');
37204
37205 var halfHeight = tbb.height / 2;
37206 var lty = outerTop - tbb.top - halfHeight;
37207 var clipId = 'clip' + fullLayout._uid + 'commonlabel' + ya._id;
37208 var clipPath;
37209
37210 if(lx < (tbb.width + 2 * HOVERTEXTPAD + HOVERARROWSIZE)) {
37211 clipPath = 'M-' + (HOVERARROWSIZE + HOVERTEXTPAD) + '-' + halfHeight +
37212 'h-' + (tbb.width - HOVERTEXTPAD) +
37213 'V' + halfHeight +
37214 'h' + (tbb.width - HOVERTEXTPAD) + 'Z';
37215
37216 var ltx = tbb.width - lx + HOVERTEXTPAD;
37217 svgTextUtils.positionText(ltext, ltx, lty);
37218
37219 // shift each line (except the longest) so that start-of-line
37220 // is always visible
37221 if(anchor === 'end') {
37222 ltext.selectAll('tspan').each(function() {
37223 var s = d3.select(this);
37224 var dummy = Drawing.tester.append('text')
37225 .text(s.text())
37226 .call(Drawing.font, commonLabelFont);
37227 var dummyBB = dummy.node().getBoundingClientRect();
37228 if(Math.round(dummyBB.width) < Math.round(tbb.width)) {
37229 s.attr('x', ltx - dummyBB.width);
37230 }
37231 dummy.remove();
37232 });
37233 }
37234 } else {
37235 svgTextUtils.positionText(ltext, sgn * (HOVERTEXTPAD + HOVERARROWSIZE), lty);
37236 clipPath = null;
37237 }
37238
37239 var textClip = fullLayout._topclips.selectAll('#' + clipId).data(clipPath ? [0] : []);
37240 textClip.enter().append('clipPath').attr('id', clipId).append('path');
37241 textClip.exit().remove();
37242 textClip.select('path').attr('d', clipPath);
37243 Drawing.setClipUrl(ltext, clipPath ? clipId : null, gd);
37244 }
37245
37246 label.attr('transform', strTranslate(lx, ly));
37247 });
37248
37249 // Show a single hover label
37250 if(helpers.isUnifiedHover(hovermode)) {
37251 // Delete leftover hover labels from other hovermodes
37252 container.selectAll('g.hovertext').remove();
37253
37254 // Return early if nothing is hovered on
37255 if(hoverData.length === 0) return;
37256
37257 // mock legend
37258 var hoverlabel = fullLayout.hoverlabel;
37259 var font = hoverlabel.font;
37260 var mockLayoutIn = {
37261 showlegend: true,
37262 legend: {
37263 title: {text: t0, font: font},
37264 font: font,
37265 bgcolor: hoverlabel.bgcolor,
37266 bordercolor: hoverlabel.bordercolor,
37267 borderwidth: 1,
37268 tracegroupgap: 7,
37269 traceorder: fullLayout.legend ? fullLayout.legend.traceorder : undefined,
37270 orientation: 'v'
37271 }
37272 };
37273 var mockLayoutOut = {};
37274 legendSupplyDefaults(mockLayoutIn, mockLayoutOut, gd._fullData);
37275 var mockLegend = mockLayoutOut.legend;
37276
37277 // prepare items for the legend
37278 mockLegend.entries = [];
37279 for(var j = 0; j < hoverData.length; j++) {
37280 var texts = getHoverLabelText(hoverData[j], true, hovermode, fullLayout, t0);
37281 var text = texts[0];
37282 var name = texts[1];
37283 var pt = hoverData[j];
37284 pt.name = name;
37285 if(name !== '') {
37286 pt.text = name + ' : ' + text;
37287 } else {
37288 pt.text = text;
37289 }
37290
37291 // pass through marker's calcdata to style legend items
37292 var cd = pt.cd[pt.index];
37293 if(cd) {
37294 if(cd.mc) pt.mc = cd.mc;
37295 if(cd.mcc) pt.mc = cd.mcc;
37296 if(cd.mlc) pt.mlc = cd.mlc;
37297 if(cd.mlcc) pt.mlc = cd.mlcc;
37298 if(cd.mlw) pt.mlw = cd.mlw;
37299 if(cd.mrc) pt.mrc = cd.mrc;
37300 if(cd.dir) pt.dir = cd.dir;
37301 }
37302 pt._distinct = true;
37303
37304 mockLegend.entries.push([pt]);
37305 }
37306 mockLegend.entries.sort(function(a, b) { return a[0].trace.index - b[0].trace.index;});
37307 mockLegend.layer = container;
37308
37309 // Draw unified hover label
37310 mockLegend._inHover = true;
37311 mockLegend._groupTitleFont = font;
37312 legendDraw(gd, mockLegend);
37313
37314 // Position the hover
37315 var legendContainer = container.select('g.legend');
37316 var tbb = legendContainer.node().getBoundingClientRect();
37317 var tWidth = tbb.width + 2 * HOVERTEXTPAD;
37318 var tHeight = tbb.height + 2 * HOVERTEXTPAD;
37319 var winningPoint = hoverData[0];
37320 var avgX = (winningPoint.x0 + winningPoint.x1) / 2;
37321 var avgY = (winningPoint.y0 + winningPoint.y1) / 2;
37322 // When a scatter (or e.g. heatmap) point wins, it's OK for the hovelabel to occlude the bar and other points.
37323 var pointWon = !(
37324 Registry.traceIs(winningPoint.trace, 'bar-like') ||
37325 Registry.traceIs(winningPoint.trace, 'box-violin')
37326 );
37327
37328 var lyBottom, lyTop;
37329 if(axLetter === 'y') {
37330 if(pointWon) {
37331 lyTop = avgY - HOVERTEXTPAD;
37332 lyBottom = avgY + HOVERTEXTPAD;
37333 } else {
37334 lyTop = Math.min.apply(null, hoverData.map(function(c) { return Math.min(c.y0, c.y1); }));
37335 lyBottom = Math.max.apply(null, hoverData.map(function(c) { return Math.max(c.y0, c.y1); }));
37336 }
37337 } else {
37338 lyTop = lyBottom = Lib.mean(hoverData.map(function(c) { return (c.y0 + c.y1) / 2; })) - tHeight / 2;
37339 }
37340
37341 var lxRight, lxLeft;
37342 if(axLetter === 'x') {
37343 if(pointWon) {
37344 lxRight = avgX + HOVERTEXTPAD;
37345 lxLeft = avgX - HOVERTEXTPAD;
37346 } else {
37347 lxRight = Math.max.apply(null, hoverData.map(function(c) { return Math.max(c.x0, c.x1); }));
37348 lxLeft = Math.min.apply(null, hoverData.map(function(c) { return Math.min(c.x0, c.x1); }));
37349 }
37350 } else {
37351 lxRight = lxLeft = Lib.mean(hoverData.map(function(c) { return (c.x0 + c.x1) / 2; })) - tWidth / 2;
37352 }
37353
37354 var xOffset = xa._offset;
37355 var yOffset = ya._offset;
37356 lyBottom += yOffset;
37357 lxRight += xOffset;
37358 lxLeft += xOffset - tWidth;
37359 lyTop += yOffset - tHeight;
37360
37361 var lx, ly; // top and left positions of the hover box
37362
37363 // horizontal alignment to end up on screen
37364 if(lxRight + tWidth < outerWidth && lxRight >= 0) {
37365 lx = lxRight;
37366 } else if(lxLeft + tWidth < outerWidth && lxLeft >= 0) {
37367 lx = lxLeft;
37368 } else if(xOffset + tWidth < outerWidth) {
37369 lx = xOffset; // subplot left corner
37370 } else {
37371 // closest left or right side of the paper
37372 if(lxRight - avgX < avgX - lxLeft + tWidth) {
37373 lx = outerWidth - tWidth;
37374 } else {
37375 lx = 0;
37376 }
37377 }
37378 lx += HOVERTEXTPAD;
37379
37380 // vertical alignement to end up on screen
37381 if(lyBottom + tHeight < outerHeight && lyBottom >= 0) {
37382 ly = lyBottom;
37383 } else if(lyTop + tHeight < outerHeight && lyTop >= 0) {
37384 ly = lyTop;
37385 } else if(yOffset + tHeight < outerHeight) {
37386 ly = yOffset; // subplot top corner
37387 } else {
37388 // closest top or bottom side of the paper
37389 if(lyBottom - avgY < avgY - lyTop + tHeight) {
37390 ly = outerHeight - tHeight;
37391 } else {
37392 ly = 0;
37393 }
37394 }
37395 ly += HOVERTEXTPAD;
37396
37397 legendContainer.attr('transform', strTranslate(lx - 1, ly - 1));
37398 return legendContainer;
37399 }
37400
37401 // show all the individual labels
37402
37403 // first create the objects
37404 var hoverLabels = container.selectAll('g.hovertext')
37405 .data(hoverData, function(d) {
37406 // N.B. when multiple items have the same result key-function value,
37407 // only the first of those items in hoverData gets rendered
37408 return hoverDataKey(d);
37409 });
37410 hoverLabels.enter().append('g')
37411 .classed('hovertext', true)
37412 .each(function() {
37413 var g = d3.select(this);
37414 // trace name label (rect and text.name)
37415 g.append('rect')
37416 .call(Color.fill, Color.addOpacity(bgColor, 0.8));
37417 g.append('text').classed('name', true);
37418 // trace data label (path and text.nums)
37419 g.append('path')
37420 .style('stroke-width', '1px');
37421 g.append('text').classed('nums', true)
37422 .call(Drawing.font, fontFamily, fontSize);
37423 });
37424 hoverLabels.exit().remove();
37425
37426 // then put the text in, position the pointer to the data,
37427 // and figure out sizes
37428 hoverLabels.each(function(d) {
37429 var g = d3.select(this).attr('transform', '');
37430
37431 var dColor = d.color;
37432 if(Array.isArray(dColor)) {
37433 dColor = dColor[d.eventData[0].pointNumber];
37434 }
37435
37436 // combine possible non-opaque trace color with bgColor
37437 var color0 = d.bgcolor || dColor;
37438 // color for 'nums' part of the label
37439 var numsColor = Color.combine(
37440 Color.opacity(color0) ? color0 : Color.defaultLine,
37441 bgColor
37442 );
37443 // color for 'name' part of the label
37444 var nameColor = Color.combine(
37445 Color.opacity(dColor) ? dColor : Color.defaultLine,
37446 bgColor
37447 );
37448 // find a contrasting color for border and text
37449 var contrastColor = d.borderColor || Color.contrast(numsColor);
37450
37451 var texts = getHoverLabelText(d, showCommonLabel, hovermode, fullLayout, t0, g);
37452 var text = texts[0];
37453 var name = texts[1];
37454
37455 // main label
37456 var tx = g.select('text.nums')
37457 .call(Drawing.font,
37458 d.fontFamily || fontFamily,
37459 d.fontSize || fontSize,
37460 d.fontColor || contrastColor)
37461 .text(text)
37462 .attr('data-notex', 1)
37463 .call(svgTextUtils.positionText, 0, 0)
37464 .call(svgTextUtils.convertToTspans, gd);
37465
37466 var tx2 = g.select('text.name');
37467 var tx2width = 0;
37468 var tx2height = 0;
37469
37470 // secondary label for non-empty 'name'
37471 if(name && name !== text) {
37472 tx2.call(Drawing.font,
37473 d.fontFamily || fontFamily,
37474 d.fontSize || fontSize,
37475 nameColor)
37476 .text(name)
37477 .attr('data-notex', 1)
37478 .call(svgTextUtils.positionText, 0, 0)
37479 .call(svgTextUtils.convertToTspans, gd);
37480
37481 var t2bb = tx2.node().getBoundingClientRect();
37482 tx2width = t2bb.width + 2 * HOVERTEXTPAD;
37483 tx2height = t2bb.height + 2 * HOVERTEXTPAD;
37484 } else {
37485 tx2.remove();
37486 g.select('rect').remove();
37487 }
37488
37489 g.select('path').style({
37490 fill: numsColor,
37491 stroke: contrastColor
37492 });
37493
37494 var tbb = tx.node().getBoundingClientRect();
37495 var htx = d.xa._offset + (d.x0 + d.x1) / 2;
37496 var hty = d.ya._offset + (d.y0 + d.y1) / 2;
37497 var dx = Math.abs(d.x1 - d.x0);
37498 var dy = Math.abs(d.y1 - d.y0);
37499 var txTotalWidth = tbb.width + HOVERARROWSIZE + HOVERTEXTPAD + tx2width;
37500 var anchorStartOK, anchorEndOK;
37501
37502 d.ty0 = outerTop - tbb.top;
37503 d.bx = tbb.width + 2 * HOVERTEXTPAD;
37504 d.by = Math.max(tbb.height + 2 * HOVERTEXTPAD, tx2height);
37505 d.anchor = 'start';
37506 d.txwidth = tbb.width;
37507 d.tx2width = tx2width;
37508 d.offset = 0;
37509
37510 if(rotateLabels) {
37511 d.pos = htx;
37512 anchorStartOK = hty + dy / 2 + txTotalWidth <= outerHeight;
37513 anchorEndOK = hty - dy / 2 - txTotalWidth >= 0;
37514 if((d.idealAlign === 'top' || !anchorStartOK) && anchorEndOK) {
37515 hty -= dy / 2;
37516 d.anchor = 'end';
37517 } else if(anchorStartOK) {
37518 hty += dy / 2;
37519 d.anchor = 'start';
37520 } else d.anchor = 'middle';
37521 } else {
37522 d.pos = hty;
37523 anchorStartOK = htx + dx / 2 + txTotalWidth <= outerWidth;
37524 anchorEndOK = htx - dx / 2 - txTotalWidth >= 0;
37525
37526 if((d.idealAlign === 'left' || !anchorStartOK) && anchorEndOK) {
37527 htx -= dx / 2;
37528 d.anchor = 'end';
37529 } else if(anchorStartOK) {
37530 htx += dx / 2;
37531 d.anchor = 'start';
37532 } else {
37533 d.anchor = 'middle';
37534
37535 var txHalfWidth = txTotalWidth / 2;
37536 var overflowR = htx + txHalfWidth - outerWidth;
37537 var overflowL = htx - txHalfWidth;
37538 if(overflowR > 0) htx -= overflowR;
37539 if(overflowL < 0) htx += -overflowL;
37540 }
37541 }
37542
37543 tx.attr('text-anchor', d.anchor);
37544 if(tx2width) tx2.attr('text-anchor', d.anchor);
37545 g.attr('transform', strTranslate(htx, hty) +
37546 (rotateLabels ? strRotate(YANGLE) : ''));
37547 });
37548
37549 return hoverLabels;
37550}
37551
37552function getHoverLabelText(d, showCommonLabel, hovermode, fullLayout, t0, g) {
37553 var name = '';
37554 var text = '';
37555 // to get custom 'name' labels pass cleanPoint
37556 if(d.nameOverride !== undefined) d.name = d.nameOverride;
37557
37558 if(d.name) {
37559 if(d.trace._meta) {
37560 d.name = Lib.templateString(d.name, d.trace._meta);
37561 }
37562 name = plainText(d.name, d.nameLength);
37563 }
37564
37565 var h0 = hovermode.charAt(0);
37566 var h1 = h0 === 'x' ? 'y' : 'x';
37567
37568 if(d.zLabel !== undefined) {
37569 if(d.xLabel !== undefined) text += 'x: ' + d.xLabel + '<br>';
37570 if(d.yLabel !== undefined) text += 'y: ' + d.yLabel + '<br>';
37571 if(d.trace.type !== 'choropleth' && d.trace.type !== 'choroplethmapbox') {
37572 text += (text ? 'z: ' : '') + d.zLabel;
37573 }
37574 } else if(showCommonLabel && d[h0 + 'Label'] === t0) {
37575 text = d[h1 + 'Label'] || '';
37576 } else if(d.xLabel === undefined) {
37577 if(d.yLabel !== undefined && d.trace.type !== 'scattercarpet') {
37578 text = d.yLabel;
37579 }
37580 } else if(d.yLabel === undefined) text = d.xLabel;
37581 else text = '(' + d.xLabel + ', ' + d.yLabel + ')';
37582
37583 if((d.text || d.text === 0) && !Array.isArray(d.text)) {
37584 text += (text ? '<br>' : '') + d.text;
37585 }
37586
37587 // used by other modules (initially just ternary) that
37588 // manage their own hoverinfo independent of cleanPoint
37589 // the rest of this will still apply, so such modules
37590 // can still put things in (x|y|z)Label, text, and name
37591 // and hoverinfo will still determine their visibility
37592 if(d.extraText !== undefined) text += (text ? '<br>' : '') + d.extraText;
37593
37594 // if 'text' is empty at this point,
37595 // and hovertemplate is not defined,
37596 // put 'name' in main label and don't show secondary label
37597 if(g && text === '' && !d.hovertemplate) {
37598 // if 'name' is also empty, remove entire label
37599 if(name === '') g.remove();
37600 text = name;
37601 }
37602
37603 // hovertemplate
37604 var hovertemplate = d.hovertemplate || false;
37605 if(hovertemplate) {
37606 var labels = d.hovertemplateLabels || d;
37607
37608 if(d[h0 + 'Label'] !== t0) {
37609 labels[h0 + 'other'] = labels[h0 + 'Val'];
37610 labels[h0 + 'otherLabel'] = labels[h0 + 'Label'];
37611 }
37612
37613 text = Lib.hovertemplateString(
37614 hovertemplate,
37615 labels,
37616 fullLayout._d3locale,
37617 d.eventData[0] || {},
37618 d.trace._meta
37619 );
37620
37621 text = text.replace(EXTRA_STRING_REGEX, function(match, extra) {
37622 // assign name for secondary text label
37623 name = plainText(extra, d.nameLength);
37624 // remove from main text label
37625 return '';
37626 });
37627 }
37628 return [text, name];
37629}
37630
37631// Make groups of touching points, and within each group
37632// move each point so that no labels overlap, but the average
37633// label position is the same as it was before moving. Incidentally,
37634// this is equivalent to saying all the labels are on equal linear
37635// springs about their initial position. Initially, each point is
37636// its own group, but as we find overlaps we will clump the points.
37637//
37638// Also, there are hard constraints at the edges of the graphs,
37639// that push all groups to the middle so they are visible. I don't
37640// know what happens if the group spans all the way from one edge to
37641// the other, though it hardly matters - there's just too much
37642// information then.
37643function hoverAvoidOverlaps(hoverLabels, axKey, fullLayout) {
37644 var nummoves = 0;
37645 var axSign = 1;
37646 var nLabels = hoverLabels.size();
37647
37648 // make groups of touching points
37649 var pointgroups = new Array(nLabels);
37650 var k = 0;
37651
37652 hoverLabels.each(function(d) {
37653 var ax = d[axKey];
37654 var axIsX = ax._id.charAt(0) === 'x';
37655 var rng = ax.range;
37656
37657 if(k === 0 && rng && ((rng[0] > rng[1]) !== axIsX)) {
37658 axSign = -1;
37659 }
37660 pointgroups[k++] = [{
37661 datum: d,
37662 traceIndex: d.trace.index,
37663 dp: 0,
37664 pos: d.pos,
37665 posref: d.posref,
37666 size: d.by * (axIsX ? YFACTOR : 1) / 2,
37667 pmin: 0,
37668 pmax: (axIsX ? fullLayout.width : fullLayout.height)
37669 }];
37670 });
37671
37672 pointgroups.sort(function(a, b) {
37673 return (a[0].posref - b[0].posref) ||
37674 // for equal positions, sort trace indices increasing or decreasing
37675 // depending on whether the axis is reversed or not... so stacked
37676 // traces will generally keep their order even if one trace adds
37677 // nothing to the stack.
37678 (axSign * (b[0].traceIndex - a[0].traceIndex));
37679 });
37680
37681 var donepositioning, topOverlap, bottomOverlap, i, j, pti, sumdp;
37682
37683 function constrainGroup(grp) {
37684 var minPt = grp[0];
37685 var maxPt = grp[grp.length - 1];
37686
37687 // overlap with the top - positive vals are overlaps
37688 topOverlap = minPt.pmin - minPt.pos - minPt.dp + minPt.size;
37689
37690 // overlap with the bottom - positive vals are overlaps
37691 bottomOverlap = maxPt.pos + maxPt.dp + maxPt.size - minPt.pmax;
37692
37693 // check for min overlap first, so that we always
37694 // see the largest labels
37695 // allow for .01px overlap, so we don't get an
37696 // infinite loop from rounding errors
37697 if(topOverlap > 0.01) {
37698 for(j = grp.length - 1; j >= 0; j--) grp[j].dp += topOverlap;
37699 donepositioning = false;
37700 }
37701 if(bottomOverlap < 0.01) return;
37702 if(topOverlap < -0.01) {
37703 // make sure we're not pushing back and forth
37704 for(j = grp.length - 1; j >= 0; j--) grp[j].dp -= bottomOverlap;
37705 donepositioning = false;
37706 }
37707 if(!donepositioning) return;
37708
37709 // no room to fix positioning, delete off-screen points
37710
37711 // first see how many points we need to delete
37712 var deleteCount = 0;
37713 for(i = 0; i < grp.length; i++) {
37714 pti = grp[i];
37715 if(pti.pos + pti.dp + pti.size > minPt.pmax) deleteCount++;
37716 }
37717
37718 // start by deleting points whose data is off screen
37719 for(i = grp.length - 1; i >= 0; i--) {
37720 if(deleteCount <= 0) break;
37721 pti = grp[i];
37722
37723 // pos has already been constrained to [pmin,pmax]
37724 // so look for points close to that to delete
37725 if(pti.pos > minPt.pmax - 1) {
37726 pti.del = true;
37727 deleteCount--;
37728 }
37729 }
37730 for(i = 0; i < grp.length; i++) {
37731 if(deleteCount <= 0) break;
37732 pti = grp[i];
37733
37734 // pos has already been constrained to [pmin,pmax]
37735 // so look for points close to that to delete
37736 if(pti.pos < minPt.pmin + 1) {
37737 pti.del = true;
37738 deleteCount--;
37739
37740 // shift the whole group minus into this new space
37741 bottomOverlap = pti.size * 2;
37742 for(j = grp.length - 1; j >= 0; j--) grp[j].dp -= bottomOverlap;
37743 }
37744 }
37745 // then delete points that go off the bottom
37746 for(i = grp.length - 1; i >= 0; i--) {
37747 if(deleteCount <= 0) break;
37748 pti = grp[i];
37749 if(pti.pos + pti.dp + pti.size > minPt.pmax) {
37750 pti.del = true;
37751 deleteCount--;
37752 }
37753 }
37754 }
37755
37756 // loop through groups, combining them if they overlap,
37757 // until nothing moves
37758 while(!donepositioning && nummoves <= nLabels) {
37759 // to avoid infinite loops, don't move more times
37760 // than there are traces
37761 nummoves++;
37762
37763 // assume nothing will move in this iteration,
37764 // reverse this if it does
37765 donepositioning = true;
37766 i = 0;
37767 while(i < pointgroups.length - 1) {
37768 // the higher (g0) and lower (g1) point group
37769 var g0 = pointgroups[i];
37770 var g1 = pointgroups[i + 1];
37771
37772 // the lowest point in the higher group (p0)
37773 // the highest point in the lower group (p1)
37774 var p0 = g0[g0.length - 1];
37775 var p1 = g1[0];
37776 topOverlap = p0.pos + p0.dp + p0.size - p1.pos - p1.dp + p1.size;
37777
37778 // Only group points that lie on the same axes
37779 if(topOverlap > 0.01 && (p0.pmin === p1.pmin) && (p0.pmax === p1.pmax)) {
37780 // push the new point(s) added to this group out of the way
37781 for(j = g1.length - 1; j >= 0; j--) g1[j].dp += topOverlap;
37782
37783 // add them to the group
37784 g0.push.apply(g0, g1);
37785 pointgroups.splice(i + 1, 1);
37786
37787 // adjust for minimum average movement
37788 sumdp = 0;
37789 for(j = g0.length - 1; j >= 0; j--) sumdp += g0[j].dp;
37790 bottomOverlap = sumdp / g0.length;
37791 for(j = g0.length - 1; j >= 0; j--) g0[j].dp -= bottomOverlap;
37792 donepositioning = false;
37793 } else i++;
37794 }
37795
37796 // check if we're going off the plot on either side and fix
37797 pointgroups.forEach(constrainGroup);
37798 }
37799
37800 // now put these offsets into hoverData
37801 for(i = pointgroups.length - 1; i >= 0; i--) {
37802 var grp = pointgroups[i];
37803 for(j = grp.length - 1; j >= 0; j--) {
37804 var pt = grp[j];
37805 var hoverPt = pt.datum;
37806 hoverPt.offset = pt.dp;
37807 hoverPt.del = pt.del;
37808 }
37809 }
37810}
37811
37812function alignHoverText(hoverLabels, rotateLabels, scaleX, scaleY) {
37813 var pX = function(x) { return x * scaleX; };
37814 var pY = function(y) { return y * scaleY; };
37815
37816 // finally set the text positioning relative to the data and draw the
37817 // box around it
37818 hoverLabels.each(function(d) {
37819 var g = d3.select(this);
37820 if(d.del) return g.remove();
37821
37822 var tx = g.select('text.nums');
37823 var anchor = d.anchor;
37824 var horzSign = anchor === 'end' ? -1 : 1;
37825 var alignShift = {start: 1, end: -1, middle: 0}[anchor];
37826 var txx = alignShift * (HOVERARROWSIZE + HOVERTEXTPAD);
37827 var tx2x = txx + alignShift * (d.txwidth + HOVERTEXTPAD);
37828 var offsetX = 0;
37829 var offsetY = d.offset;
37830
37831 var isMiddle = anchor === 'middle';
37832 if(isMiddle) {
37833 txx -= d.tx2width / 2;
37834 tx2x += d.txwidth / 2 + HOVERTEXTPAD;
37835 }
37836 if(rotateLabels) {
37837 offsetY *= -YSHIFTY;
37838 offsetX = d.offset * YSHIFTX;
37839 }
37840
37841 g.select('path')
37842 .attr('d', isMiddle ?
37843 // middle aligned: rect centered on data
37844 ('M-' + pX(d.bx / 2 + d.tx2width / 2) + ',' + pY(offsetY - d.by / 2) +
37845 'h' + pX(d.bx) + 'v' + pY(d.by) + 'h-' + pX(d.bx) + 'Z') :
37846 // left or right aligned: side rect with arrow to data
37847 ('M0,0L' + pX(horzSign * HOVERARROWSIZE + offsetX) + ',' + pY(HOVERARROWSIZE + offsetY) +
37848 'v' + pY(d.by / 2 - HOVERARROWSIZE) +
37849 'h' + pX(horzSign * d.bx) +
37850 'v-' + pY(d.by) +
37851 'H' + pX(horzSign * HOVERARROWSIZE + offsetX) +
37852 'V' + pY(offsetY - HOVERARROWSIZE) +
37853 'Z'));
37854
37855 var posX = offsetX + txx;
37856 var posY = offsetY + d.ty0 - d.by / 2 + HOVERTEXTPAD;
37857 var textAlign = d.textAlign || 'auto';
37858
37859 if(textAlign !== 'auto') {
37860 if(textAlign === 'left' && anchor !== 'start') {
37861 tx.attr('text-anchor', 'start');
37862 posX = isMiddle ?
37863 -d.bx / 2 - d.tx2width / 2 + HOVERTEXTPAD :
37864 -d.bx - HOVERTEXTPAD;
37865 } else if(textAlign === 'right' && anchor !== 'end') {
37866 tx.attr('text-anchor', 'end');
37867 posX = isMiddle ?
37868 d.bx / 2 - d.tx2width / 2 - HOVERTEXTPAD :
37869 d.bx + HOVERTEXTPAD;
37870 }
37871 }
37872
37873 tx.call(svgTextUtils.positionText, pX(posX), pY(posY));
37874
37875 if(d.tx2width) {
37876 g.select('text.name')
37877 .call(svgTextUtils.positionText,
37878 pX(tx2x + alignShift * HOVERTEXTPAD + offsetX),
37879 pY(offsetY + d.ty0 - d.by / 2 + HOVERTEXTPAD));
37880 g.select('rect')
37881 .call(Drawing.setRect,
37882 pX(tx2x + (alignShift - 1) * d.tx2width / 2 + offsetX),
37883 pY(offsetY - d.by / 2 - 1),
37884 pX(d.tx2width), pY(d.by + 2));
37885 }
37886 });
37887}
37888
37889function cleanPoint(d, hovermode) {
37890 var index = d.index;
37891 var trace = d.trace || {};
37892 var cd0 = d.cd[0];
37893 var cd = d.cd[index] || {};
37894
37895 function pass(v) {
37896 return v || (isNumeric(v) && v === 0);
37897 }
37898
37899 var getVal = Array.isArray(index) ?
37900 function(calcKey, traceKey) {
37901 var v = Lib.castOption(cd0, index, calcKey);
37902 return pass(v) ? v : Lib.extractOption({}, trace, '', traceKey);
37903 } :
37904 function(calcKey, traceKey) {
37905 return Lib.extractOption(cd, trace, calcKey, traceKey);
37906 };
37907
37908 function fill(key, calcKey, traceKey) {
37909 var val = getVal(calcKey, traceKey);
37910 if(pass(val)) d[key] = val;
37911 }
37912
37913 fill('hoverinfo', 'hi', 'hoverinfo');
37914 fill('bgcolor', 'hbg', 'hoverlabel.bgcolor');
37915 fill('borderColor', 'hbc', 'hoverlabel.bordercolor');
37916 fill('fontFamily', 'htf', 'hoverlabel.font.family');
37917 fill('fontSize', 'hts', 'hoverlabel.font.size');
37918 fill('fontColor', 'htc', 'hoverlabel.font.color');
37919 fill('nameLength', 'hnl', 'hoverlabel.namelength');
37920 fill('textAlign', 'hta', 'hoverlabel.align');
37921
37922 d.posref = (hovermode === 'y' || (hovermode === 'closest' && trace.orientation === 'h')) ?
37923 (d.xa._offset + (d.x0 + d.x1) / 2) :
37924 (d.ya._offset + (d.y0 + d.y1) / 2);
37925
37926 // then constrain all the positions to be on the plot
37927 d.x0 = Lib.constrain(d.x0, 0, d.xa._length);
37928 d.x1 = Lib.constrain(d.x1, 0, d.xa._length);
37929 d.y0 = Lib.constrain(d.y0, 0, d.ya._length);
37930 d.y1 = Lib.constrain(d.y1, 0, d.ya._length);
37931
37932 // and convert the x and y label values into formatted text
37933 if(d.xLabelVal !== undefined) {
37934 d.xLabel = ('xLabel' in d) ? d.xLabel : Axes.hoverLabelText(d.xa, d.xLabelVal, trace.xhoverformat);
37935 d.xVal = d.xa.c2d(d.xLabelVal);
37936 }
37937 if(d.yLabelVal !== undefined) {
37938 d.yLabel = ('yLabel' in d) ? d.yLabel : Axes.hoverLabelText(d.ya, d.yLabelVal, trace.yhoverformat);
37939 d.yVal = d.ya.c2d(d.yLabelVal);
37940 }
37941
37942 // Traces like heatmaps generate the zLabel in their hoverPoints function
37943 if(d.zLabelVal !== undefined && d.zLabel === undefined) {
37944 d.zLabel = String(d.zLabelVal);
37945 }
37946
37947 // for box means and error bars, add the range to the label
37948 if(!isNaN(d.xerr) && !(d.xa.type === 'log' && d.xerr <= 0)) {
37949 var xeText = Axes.tickText(d.xa, d.xa.c2l(d.xerr), 'hover').text;
37950 if(d.xerrneg !== undefined) {
37951 d.xLabel += ' +' + xeText + ' / -' +
37952 Axes.tickText(d.xa, d.xa.c2l(d.xerrneg), 'hover').text;
37953 } else d.xLabel += ' ± ' + xeText;
37954
37955 // small distance penalty for error bars, so that if there are
37956 // traces with errors and some without, the error bar label will
37957 // hoist up to the point
37958 if(hovermode === 'x') d.distance += 1;
37959 }
37960 if(!isNaN(d.yerr) && !(d.ya.type === 'log' && d.yerr <= 0)) {
37961 var yeText = Axes.tickText(d.ya, d.ya.c2l(d.yerr), 'hover').text;
37962 if(d.yerrneg !== undefined) {
37963 d.yLabel += ' +' + yeText + ' / -' +
37964 Axes.tickText(d.ya, d.ya.c2l(d.yerrneg), 'hover').text;
37965 } else d.yLabel += ' ± ' + yeText;
37966
37967 if(hovermode === 'y') d.distance += 1;
37968 }
37969
37970 var infomode = d.hoverinfo || d.trace.hoverinfo;
37971
37972 if(infomode && infomode !== 'all') {
37973 infomode = Array.isArray(infomode) ? infomode : infomode.split('+');
37974 if(infomode.indexOf('x') === -1) d.xLabel = undefined;
37975 if(infomode.indexOf('y') === -1) d.yLabel = undefined;
37976 if(infomode.indexOf('z') === -1) d.zLabel = undefined;
37977 if(infomode.indexOf('text') === -1) d.text = undefined;
37978 if(infomode.indexOf('name') === -1) d.name = undefined;
37979 }
37980
37981 return d;
37982}
37983
37984function createSpikelines(gd, closestPoints, opts) {
37985 var container = opts.container;
37986 var fullLayout = opts.fullLayout;
37987 var gs = fullLayout._size;
37988 var evt = opts.event;
37989 var showY = !!closestPoints.hLinePoint;
37990 var showX = !!closestPoints.vLinePoint;
37991
37992 var xa, ya;
37993
37994 // Remove old spikeline items
37995 container.selectAll('.spikeline').remove();
37996
37997 if(!(showX || showY)) return;
37998
37999 var contrastColor = Color.combine(fullLayout.plot_bgcolor, fullLayout.paper_bgcolor);
38000
38001 // Horizontal line (to y-axis)
38002 if(showY) {
38003 var hLinePoint = closestPoints.hLinePoint;
38004 var hLinePointX, hLinePointY;
38005
38006 xa = hLinePoint && hLinePoint.xa;
38007 ya = hLinePoint && hLinePoint.ya;
38008 var ySnap = ya.spikesnap;
38009
38010 if(ySnap === 'cursor') {
38011 hLinePointX = evt.pointerX;
38012 hLinePointY = evt.pointerY;
38013 } else {
38014 hLinePointX = xa._offset + hLinePoint.x;
38015 hLinePointY = ya._offset + hLinePoint.y;
38016 }
38017 var dfltHLineColor = tinycolor.readability(hLinePoint.color, contrastColor) < 1.5 ?
38018 Color.contrast(contrastColor) : hLinePoint.color;
38019 var yMode = ya.spikemode;
38020 var yThickness = ya.spikethickness;
38021 var yColor = ya.spikecolor || dfltHLineColor;
38022 var xEdge = Axes.getPxPosition(gd, ya);
38023 var xBase, xEndSpike;
38024
38025 if(yMode.indexOf('toaxis') !== -1 || yMode.indexOf('across') !== -1) {
38026 if(yMode.indexOf('toaxis') !== -1) {
38027 xBase = xEdge;
38028 xEndSpike = hLinePointX;
38029 }
38030 if(yMode.indexOf('across') !== -1) {
38031 var xAcross0 = ya._counterDomainMin;
38032 var xAcross1 = ya._counterDomainMax;
38033 if(ya.anchor === 'free') {
38034 xAcross0 = Math.min(xAcross0, ya.position);
38035 xAcross1 = Math.max(xAcross1, ya.position);
38036 }
38037 xBase = gs.l + xAcross0 * gs.w;
38038 xEndSpike = gs.l + xAcross1 * gs.w;
38039 }
38040
38041 // Foreground horizontal line (to y-axis)
38042 container.insert('line', ':first-child')
38043 .attr({
38044 x1: xBase,
38045 x2: xEndSpike,
38046 y1: hLinePointY,
38047 y2: hLinePointY,
38048 'stroke-width': yThickness,
38049 stroke: yColor,
38050 'stroke-dasharray': Drawing.dashStyle(ya.spikedash, yThickness)
38051 })
38052 .classed('spikeline', true)
38053 .classed('crisp', true);
38054
38055 // Background horizontal Line (to y-axis)
38056 container.insert('line', ':first-child')
38057 .attr({
38058 x1: xBase,
38059 x2: xEndSpike,
38060 y1: hLinePointY,
38061 y2: hLinePointY,
38062 'stroke-width': yThickness + 2,
38063 stroke: contrastColor
38064 })
38065 .classed('spikeline', true)
38066 .classed('crisp', true);
38067 }
38068 // Y axis marker
38069 if(yMode.indexOf('marker') !== -1) {
38070 container.insert('circle', ':first-child')
38071 .attr({
38072 cx: xEdge + (ya.side !== 'right' ? yThickness : -yThickness),
38073 cy: hLinePointY,
38074 r: yThickness,
38075 fill: yColor
38076 })
38077 .classed('spikeline', true);
38078 }
38079 }
38080
38081 if(showX) {
38082 var vLinePoint = closestPoints.vLinePoint;
38083 var vLinePointX, vLinePointY;
38084
38085 xa = vLinePoint && vLinePoint.xa;
38086 ya = vLinePoint && vLinePoint.ya;
38087 var xSnap = xa.spikesnap;
38088
38089 if(xSnap === 'cursor') {
38090 vLinePointX = evt.pointerX;
38091 vLinePointY = evt.pointerY;
38092 } else {
38093 vLinePointX = xa._offset + vLinePoint.x;
38094 vLinePointY = ya._offset + vLinePoint.y;
38095 }
38096 var dfltVLineColor = tinycolor.readability(vLinePoint.color, contrastColor) < 1.5 ?
38097 Color.contrast(contrastColor) : vLinePoint.color;
38098 var xMode = xa.spikemode;
38099 var xThickness = xa.spikethickness;
38100 var xColor = xa.spikecolor || dfltVLineColor;
38101 var yEdge = Axes.getPxPosition(gd, xa);
38102 var yBase, yEndSpike;
38103
38104 if(xMode.indexOf('toaxis') !== -1 || xMode.indexOf('across') !== -1) {
38105 if(xMode.indexOf('toaxis') !== -1) {
38106 yBase = yEdge;
38107 yEndSpike = vLinePointY;
38108 }
38109 if(xMode.indexOf('across') !== -1) {
38110 var yAcross0 = xa._counterDomainMin;
38111 var yAcross1 = xa._counterDomainMax;
38112 if(xa.anchor === 'free') {
38113 yAcross0 = Math.min(yAcross0, xa.position);
38114 yAcross1 = Math.max(yAcross1, xa.position);
38115 }
38116 yBase = gs.t + (1 - yAcross1) * gs.h;
38117 yEndSpike = gs.t + (1 - yAcross0) * gs.h;
38118 }
38119
38120 // Foreground vertical line (to x-axis)
38121 container.insert('line', ':first-child')
38122 .attr({
38123 x1: vLinePointX,
38124 x2: vLinePointX,
38125 y1: yBase,
38126 y2: yEndSpike,
38127 'stroke-width': xThickness,
38128 stroke: xColor,
38129 'stroke-dasharray': Drawing.dashStyle(xa.spikedash, xThickness)
38130 })
38131 .classed('spikeline', true)
38132 .classed('crisp', true);
38133
38134 // Background vertical line (to x-axis)
38135 container.insert('line', ':first-child')
38136 .attr({
38137 x1: vLinePointX,
38138 x2: vLinePointX,
38139 y1: yBase,
38140 y2: yEndSpike,
38141 'stroke-width': xThickness + 2,
38142 stroke: contrastColor
38143 })
38144 .classed('spikeline', true)
38145 .classed('crisp', true);
38146 }
38147
38148 // X axis marker
38149 if(xMode.indexOf('marker') !== -1) {
38150 container.insert('circle', ':first-child')
38151 .attr({
38152 cx: vLinePointX,
38153 cy: yEdge - (xa.side !== 'top' ? xThickness : -xThickness),
38154 r: xThickness,
38155 fill: xColor
38156 })
38157 .classed('spikeline', true);
38158 }
38159 }
38160}
38161
38162function hoverChanged(gd, evt, oldhoverdata) {
38163 // don't emit any events if nothing changed
38164 if(!oldhoverdata || oldhoverdata.length !== gd._hoverdata.length) return true;
38165
38166 for(var i = oldhoverdata.length - 1; i >= 0; i--) {
38167 var oldPt = oldhoverdata[i];
38168 var newPt = gd._hoverdata[i];
38169
38170 if(oldPt.curveNumber !== newPt.curveNumber ||
38171 String(oldPt.pointNumber) !== String(newPt.pointNumber) ||
38172 String(oldPt.pointNumbers) !== String(newPt.pointNumbers)
38173 ) {
38174 return true;
38175 }
38176 }
38177 return false;
38178}
38179
38180function spikesChanged(gd, oldspikepoints) {
38181 // don't relayout the plot because of new spikelines if spikelines points didn't change
38182 if(!oldspikepoints) return true;
38183 if(oldspikepoints.vLinePoint !== gd._spikepoints.vLinePoint ||
38184 oldspikepoints.hLinePoint !== gd._spikepoints.hLinePoint
38185 ) return true;
38186 return false;
38187}
38188
38189function plainText(s, len) {
38190 return svgTextUtils.plainText(s || '', {
38191 len: len,
38192 allowedTags: ['br', 'sub', 'sup', 'b', 'i', 'em']
38193 });
38194}
38195
38196function orderRangePoints(hoverData, hovermode) {
38197 var axLetter = hovermode.charAt(0);
38198
38199 var first = [];
38200 var second = [];
38201 var last = [];
38202
38203 for(var i = 0; i < hoverData.length; i++) {
38204 var d = hoverData[i];
38205
38206 if(
38207 Registry.traceIs(d.trace, 'bar-like') ||
38208 Registry.traceIs(d.trace, 'box-violin')
38209 ) {
38210 last.push(d);
38211 } else if(d.trace[axLetter + 'period']) {
38212 second.push(d);
38213 } else {
38214 first.push(d);
38215 }
38216 }
38217
38218 return first.concat(second).concat(last);
38219}
38220
38221function getCoord(axLetter, winningPoint, fullLayout) {
38222 var ax = winningPoint[axLetter + 'a'];
38223 var val = winningPoint[axLetter + 'Val'];
38224
38225 var cd0 = winningPoint.cd[0];
38226
38227 if(ax.type === 'category') val = ax._categoriesMap[val];
38228 else if(ax.type === 'date') {
38229 var periodalignment = winningPoint.trace[axLetter + 'periodalignment'];
38230 if(periodalignment) {
38231 var d = winningPoint.cd[winningPoint.index];
38232
38233 var start = d[axLetter + 'Start'];
38234 if(start === undefined) start = d[axLetter];
38235
38236 var end = d[axLetter + 'End'];
38237 if(end === undefined) end = d[axLetter];
38238
38239 var diff = end - start;
38240
38241 if(periodalignment === 'end') {
38242 val += diff;
38243 } else if(periodalignment === 'middle') {
38244 val += diff / 2;
38245 }
38246 }
38247
38248 val = ax.d2c(val);
38249 }
38250
38251 if(cd0 && cd0.t && cd0.t.posLetter === ax._id) {
38252 if(
38253 fullLayout.boxmode === 'group' ||
38254 fullLayout.violinmode === 'group'
38255 ) {
38256 val += cd0.t.dPos;
38257 }
38258 }
38259
38260 return val;
38261}
38262
38263// Top/left hover offsets relative to graph div. As long as hover content is
38264// a sibling of the graph div, it will be positioned correctly relative to
38265// the offset parent, whatever that may be.
38266function getTopOffset(gd) { return gd.offsetTop + gd.clientTop; }
38267function getLeftOffset(gd) { return gd.offsetLeft + gd.clientLeft; }
38268
38269},{"../../lib":287,"../../lib/events":280,"../../lib/override_cursor":298,"../../lib/svg_text_utils":310,"../../plots/cartesian/axes":334,"../../registry":376,"../color":157,"../dragelement":176,"../drawing":179,"../legend/defaults":209,"../legend/draw":210,"./constants":191,"./helpers":193,"@plotly/d3":20,"fast-isnumeric":33,"tinycolor2":121}],195:[function(_dereq_,module,exports){
38270'use strict';
38271
38272var Lib = _dereq_('../../lib');
38273var Color = _dereq_('../color');
38274var isUnifiedHover = _dereq_('./helpers').isUnifiedHover;
38275
38276module.exports = function handleHoverLabelDefaults(contIn, contOut, coerce, opts) {
38277 opts = opts || {};
38278
38279 function inheritFontAttr(attr) {
38280 if(!opts.font[attr]) {
38281 opts.font[attr] = contOut.legend ? contOut.legend.font[attr] : contOut.font[attr];
38282 }
38283 }
38284
38285 // In unified hover, inherit from layout.legend if available or layout
38286 if(contOut && isUnifiedHover(contOut.hovermode)) {
38287 if(!opts.font) opts.font = {};
38288 inheritFontAttr('size');
38289 inheritFontAttr('family');
38290 inheritFontAttr('color');
38291
38292 if(contOut.legend) {
38293 if(!opts.bgcolor) opts.bgcolor = Color.combine(contOut.legend.bgcolor, contOut.paper_bgcolor);
38294 if(!opts.bordercolor) opts.bordercolor = contOut.legend.bordercolor;
38295 } else {
38296 if(!opts.bgcolor) opts.bgcolor = contOut.paper_bgcolor;
38297 }
38298 }
38299
38300 coerce('hoverlabel.bgcolor', opts.bgcolor);
38301 coerce('hoverlabel.bordercolor', opts.bordercolor);
38302 coerce('hoverlabel.namelength', opts.namelength);
38303 Lib.coerceFont(coerce, 'hoverlabel.font', opts.font);
38304 coerce('hoverlabel.align', opts.align);
38305};
38306
38307},{"../../lib":287,"../color":157,"./helpers":193}],196:[function(_dereq_,module,exports){
38308'use strict';
38309
38310var Lib = _dereq_('../../lib');
38311var layoutAttributes = _dereq_('./layout_attributes');
38312
38313module.exports = function handleHoverModeDefaults(layoutIn, layoutOut) {
38314 function coerce(attr, dflt) {
38315 // don't coerce if it is already coerced in other place e.g. in cartesian defaults
38316 if(layoutOut[attr] !== undefined) return layoutOut[attr];
38317
38318 return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt);
38319 }
38320
38321 coerce('clickmode');
38322 return coerce('hovermode');
38323};
38324
38325},{"../../lib":287,"./layout_attributes":198}],197:[function(_dereq_,module,exports){
38326'use strict';
38327
38328var d3 = _dereq_('@plotly/d3');
38329var Lib = _dereq_('../../lib');
38330var dragElement = _dereq_('../dragelement');
38331var helpers = _dereq_('./helpers');
38332var layoutAttributes = _dereq_('./layout_attributes');
38333var hoverModule = _dereq_('./hover');
38334
38335module.exports = {
38336 moduleType: 'component',
38337 name: 'fx',
38338
38339 constants: _dereq_('./constants'),
38340 schema: {
38341 layout: layoutAttributes
38342 },
38343
38344 attributes: _dereq_('./attributes'),
38345 layoutAttributes: layoutAttributes,
38346
38347 supplyLayoutGlobalDefaults: _dereq_('./layout_global_defaults'),
38348 supplyDefaults: _dereq_('./defaults'),
38349 supplyLayoutDefaults: _dereq_('./layout_defaults'),
38350
38351 calc: _dereq_('./calc'),
38352
38353 getDistanceFunction: helpers.getDistanceFunction,
38354 getClosest: helpers.getClosest,
38355 inbox: helpers.inbox,
38356 quadrature: helpers.quadrature,
38357 appendArrayPointValue: helpers.appendArrayPointValue,
38358
38359 castHoverOption: castHoverOption,
38360 castHoverinfo: castHoverinfo,
38361
38362 hover: hoverModule.hover,
38363 unhover: dragElement.unhover,
38364
38365 loneHover: hoverModule.loneHover,
38366 loneUnhover: loneUnhover,
38367
38368 click: _dereq_('./click')
38369};
38370
38371function loneUnhover(containerOrSelection) {
38372 // duck type whether the arg is a d3 selection because ie9 doesn't
38373 // handle instanceof like modern browsers do.
38374 var selection = Lib.isD3Selection(containerOrSelection) ?
38375 containerOrSelection :
38376 d3.select(containerOrSelection);
38377
38378 selection.selectAll('g.hovertext').remove();
38379 selection.selectAll('.spikeline').remove();
38380}
38381
38382// helpers for traces that use Fx.loneHover
38383
38384function castHoverOption(trace, ptNumber, attr) {
38385 return Lib.castOption(trace, ptNumber, 'hoverlabel.' + attr);
38386}
38387
38388function castHoverinfo(trace, fullLayout, ptNumber) {
38389 function _coerce(val) {
38390 return Lib.coerceHoverinfo({hoverinfo: val}, {_module: trace._module}, fullLayout);
38391 }
38392
38393 return Lib.castOption(trace, ptNumber, 'hoverinfo', _coerce);
38394}
38395
38396},{"../../lib":287,"../dragelement":176,"./attributes":188,"./calc":189,"./click":190,"./constants":191,"./defaults":192,"./helpers":193,"./hover":194,"./layout_attributes":198,"./layout_defaults":199,"./layout_global_defaults":200,"@plotly/d3":20}],198:[function(_dereq_,module,exports){
38397'use strict';
38398
38399var constants = _dereq_('./constants');
38400
38401var fontAttrs = _dereq_('../../plots/font_attributes')({
38402 editType: 'none',
38403});
38404fontAttrs.family.dflt = constants.HOVERFONT;
38405fontAttrs.size.dflt = constants.HOVERFONTSIZE;
38406
38407module.exports = {
38408 clickmode: {
38409 valType: 'flaglist',
38410 flags: ['event', 'select'],
38411 dflt: 'event',
38412 editType: 'plot',
38413 extras: ['none'],
38414 },
38415 dragmode: {
38416 valType: 'enumerated',
38417 values: [
38418 'zoom',
38419 'pan',
38420 'select',
38421 'lasso',
38422 'drawclosedpath',
38423 'drawopenpath',
38424 'drawline',
38425 'drawrect',
38426 'drawcircle',
38427 'orbit',
38428 'turntable',
38429 false
38430 ],
38431 dflt: 'zoom',
38432 editType: 'modebar',
38433 },
38434 hovermode: {
38435 valType: 'enumerated',
38436 values: ['x', 'y', 'closest', false, 'x unified', 'y unified'],
38437 dflt: 'closest',
38438 editType: 'modebar',
38439 },
38440 hoverdistance: {
38441 valType: 'integer',
38442 min: -1,
38443 dflt: 20,
38444 editType: 'none',
38445 },
38446 spikedistance: {
38447 valType: 'integer',
38448 min: -1,
38449 dflt: -1,
38450 editType: 'none',
38451 },
38452 hoverlabel: {
38453 bgcolor: {
38454 valType: 'color',
38455 editType: 'none',
38456 },
38457 bordercolor: {
38458 valType: 'color',
38459 editType: 'none',
38460 },
38461 font: fontAttrs,
38462 align: {
38463 valType: 'enumerated',
38464 values: ['left', 'right', 'auto'],
38465 dflt: 'auto',
38466 editType: 'none',
38467 },
38468 namelength: {
38469 valType: 'integer',
38470 min: -1,
38471 dflt: 15,
38472 editType: 'none',
38473 },
38474 editType: 'none'
38475 },
38476 selectdirection: {
38477 valType: 'enumerated',
38478 values: ['h', 'v', 'd', 'any'],
38479 dflt: 'any',
38480 editType: 'none'
38481 }
38482};
38483
38484},{"../../plots/font_attributes":363,"./constants":191}],199:[function(_dereq_,module,exports){
38485'use strict';
38486
38487var Lib = _dereq_('../../lib');
38488var layoutAttributes = _dereq_('./layout_attributes');
38489var handleHoverModeDefaults = _dereq_('./hovermode_defaults');
38490var handleHoverLabelDefaults = _dereq_('./hoverlabel_defaults');
38491
38492module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) {
38493 function coerce(attr, dflt) {
38494 return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt);
38495 }
38496
38497 var hoverMode = handleHoverModeDefaults(layoutIn, layoutOut);
38498 if(hoverMode) {
38499 coerce('hoverdistance');
38500 coerce('spikedistance');
38501 }
38502
38503 var dragMode = coerce('dragmode');
38504 if(dragMode === 'select') coerce('selectdirection');
38505
38506 // if only mapbox or geo subplots is present on graph,
38507 // reset 'zoom' dragmode to 'pan' until 'zoom' is implemented,
38508 // so that the correct modebar button is active
38509 var hasMapbox = layoutOut._has('mapbox');
38510 var hasGeo = layoutOut._has('geo');
38511 var len = layoutOut._basePlotModules.length;
38512
38513 if(layoutOut.dragmode === 'zoom' && (
38514 ((hasMapbox || hasGeo) && len === 1) ||
38515 (hasMapbox && hasGeo && len === 2)
38516 )) {
38517 layoutOut.dragmode = 'pan';
38518 }
38519
38520 handleHoverLabelDefaults(layoutIn, layoutOut, coerce);
38521};
38522
38523},{"../../lib":287,"./hoverlabel_defaults":195,"./hovermode_defaults":196,"./layout_attributes":198}],200:[function(_dereq_,module,exports){
38524'use strict';
38525
38526var Lib = _dereq_('../../lib');
38527var handleHoverLabelDefaults = _dereq_('./hoverlabel_defaults');
38528var layoutAttributes = _dereq_('./layout_attributes');
38529
38530module.exports = function supplyLayoutGlobalDefaults(layoutIn, layoutOut) {
38531 function coerce(attr, dflt) {
38532 return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt);
38533 }
38534
38535 handleHoverLabelDefaults(layoutIn, layoutOut, coerce);
38536};
38537
38538},{"../../lib":287,"./hoverlabel_defaults":195,"./layout_attributes":198}],201:[function(_dereq_,module,exports){
38539'use strict';
38540
38541var Lib = _dereq_('../../lib');
38542var counterRegex = _dereq_('../../lib/regex').counter;
38543var domainAttrs = _dereq_('../../plots/domain').attributes;
38544var cartesianIdRegex = _dereq_('../../plots/cartesian/constants').idRegex;
38545var Template = _dereq_('../../plot_api/plot_template');
38546
38547var gridAttrs = {
38548 rows: {
38549 valType: 'integer',
38550 min: 1,
38551 editType: 'plot',
38552 },
38553 roworder: {
38554 valType: 'enumerated',
38555 values: ['top to bottom', 'bottom to top'],
38556 dflt: 'top to bottom',
38557 editType: 'plot',
38558 },
38559 columns: {
38560 valType: 'integer',
38561 min: 1,
38562 editType: 'plot',
38563 },
38564 subplots: {
38565 valType: 'info_array',
38566 freeLength: true,
38567 dimensions: 2,
38568 items: {valType: 'enumerated', values: [counterRegex('xy').toString(), ''], editType: 'plot'},
38569 editType: 'plot',
38570 },
38571 xaxes: {
38572 valType: 'info_array',
38573 freeLength: true,
38574 items: {valType: 'enumerated', values: [cartesianIdRegex.x.toString(), ''], editType: 'plot'},
38575 editType: 'plot',
38576 },
38577 yaxes: {
38578 valType: 'info_array',
38579 freeLength: true,
38580 items: {valType: 'enumerated', values: [cartesianIdRegex.y.toString(), ''], editType: 'plot'},
38581 editType: 'plot',
38582 },
38583 pattern: {
38584 valType: 'enumerated',
38585 values: ['independent', 'coupled'],
38586 dflt: 'coupled',
38587 editType: 'plot',
38588 },
38589 xgap: {
38590 valType: 'number',
38591 min: 0,
38592 max: 1,
38593 editType: 'plot',
38594 },
38595 ygap: {
38596 valType: 'number',
38597 min: 0,
38598 max: 1,
38599 editType: 'plot',
38600 },
38601 domain: domainAttrs({name: 'grid', editType: 'plot', noGridCell: true}, {
38602 }),
38603 xside: {
38604 valType: 'enumerated',
38605 values: ['bottom', 'bottom plot', 'top plot', 'top'],
38606 dflt: 'bottom plot',
38607 editType: 'plot',
38608 },
38609 yside: {
38610 valType: 'enumerated',
38611 values: ['left', 'left plot', 'right plot', 'right'],
38612 dflt: 'left plot',
38613 editType: 'plot',
38614 },
38615 editType: 'plot'
38616};
38617
38618function getAxes(layout, grid, axLetter) {
38619 var gridVal = grid[axLetter + 'axes'];
38620 var splomVal = Object.keys((layout._splomAxes || {})[axLetter] || {});
38621
38622 if(Array.isArray(gridVal)) return gridVal;
38623 if(splomVal.length) return splomVal;
38624}
38625
38626// the shape of the grid - this needs to be done BEFORE supplyDataDefaults
38627// so that non-subplot traces can place themselves in the grid
38628function sizeDefaults(layoutIn, layoutOut) {
38629 var gridIn = layoutIn.grid || {};
38630 var xAxes = getAxes(layoutOut, gridIn, 'x');
38631 var yAxes = getAxes(layoutOut, gridIn, 'y');
38632
38633 if(!layoutIn.grid && !xAxes && !yAxes) return;
38634
38635 var hasSubplotGrid = Array.isArray(gridIn.subplots) && Array.isArray(gridIn.subplots[0]);
38636 var hasXaxes = Array.isArray(xAxes);
38637 var hasYaxes = Array.isArray(yAxes);
38638 var isSplomGenerated = (
38639 hasXaxes && xAxes !== gridIn.xaxes &&
38640 hasYaxes && yAxes !== gridIn.yaxes
38641 );
38642
38643 var dfltRows, dfltColumns;
38644
38645 if(hasSubplotGrid) {
38646 dfltRows = gridIn.subplots.length;
38647 dfltColumns = gridIn.subplots[0].length;
38648 } else {
38649 if(hasYaxes) dfltRows = yAxes.length;
38650 if(hasXaxes) dfltColumns = xAxes.length;
38651 }
38652
38653 var gridOut = Template.newContainer(layoutOut, 'grid');
38654
38655 function coerce(attr, dflt) {
38656 return Lib.coerce(gridIn, gridOut, gridAttrs, attr, dflt);
38657 }
38658
38659 var rows = coerce('rows', dfltRows);
38660 var columns = coerce('columns', dfltColumns);
38661
38662 if(!(rows * columns > 1)) {
38663 delete layoutOut.grid;
38664 return;
38665 }
38666
38667 if(!hasSubplotGrid && !hasXaxes && !hasYaxes) {
38668 var useDefaultSubplots = coerce('pattern') === 'independent';
38669 if(useDefaultSubplots) hasSubplotGrid = true;
38670 }
38671 gridOut._hasSubplotGrid = hasSubplotGrid;
38672
38673 var rowOrder = coerce('roworder');
38674 var reversed = rowOrder === 'top to bottom';
38675
38676 var dfltGapX = hasSubplotGrid ? 0.2 : 0.1;
38677 var dfltGapY = hasSubplotGrid ? 0.3 : 0.1;
38678
38679 var dfltSideX, dfltSideY;
38680 if(isSplomGenerated && layoutOut._splomGridDflt) {
38681 dfltSideX = layoutOut._splomGridDflt.xside;
38682 dfltSideY = layoutOut._splomGridDflt.yside;
38683 }
38684
38685 gridOut._domains = {
38686 x: fillGridPositions('x', coerce, dfltGapX, dfltSideX, columns),
38687 y: fillGridPositions('y', coerce, dfltGapY, dfltSideY, rows, reversed)
38688 };
38689}
38690
38691// coerce x or y sizing attributes and return an array of domains for this direction
38692function fillGridPositions(axLetter, coerce, dfltGap, dfltSide, len, reversed) {
38693 var dirGap = coerce(axLetter + 'gap', dfltGap);
38694 var domain = coerce('domain.' + axLetter);
38695 coerce(axLetter + 'side', dfltSide);
38696
38697 var out = new Array(len);
38698 var start = domain[0];
38699 var step = (domain[1] - start) / (len - dirGap);
38700 var cellDomain = step * (1 - dirGap);
38701 for(var i = 0; i < len; i++) {
38702 var cellStart = start + step * i;
38703 out[reversed ? (len - 1 - i) : i] = [cellStart, cellStart + cellDomain];
38704 }
38705 return out;
38706}
38707
38708// the (cartesian) contents of the grid - this needs to happen AFTER supplyDataDefaults
38709// so that we know what cartesian subplots are available
38710function contentDefaults(layoutIn, layoutOut) {
38711 var gridOut = layoutOut.grid;
38712 // make sure we got to the end of handleGridSizing
38713 if(!gridOut || !gridOut._domains) return;
38714
38715 var gridIn = layoutIn.grid || {};
38716 var subplots = layoutOut._subplots;
38717 var hasSubplotGrid = gridOut._hasSubplotGrid;
38718 var rows = gridOut.rows;
38719 var columns = gridOut.columns;
38720 var useDefaultSubplots = gridOut.pattern === 'independent';
38721
38722 var i, j, xId, yId, subplotId, subplotsOut, yPos;
38723
38724 var axisMap = gridOut._axisMap = {};
38725
38726 if(hasSubplotGrid) {
38727 var subplotsIn = gridIn.subplots || [];
38728 subplotsOut = gridOut.subplots = new Array(rows);
38729 var index = 1;
38730
38731 for(i = 0; i < rows; i++) {
38732 var rowOut = subplotsOut[i] = new Array(columns);
38733 var rowIn = subplotsIn[i] || [];
38734 for(j = 0; j < columns; j++) {
38735 if(useDefaultSubplots) {
38736 subplotId = (index === 1) ? 'xy' : ('x' + index + 'y' + index);
38737 index++;
38738 } else subplotId = rowIn[j];
38739
38740 rowOut[j] = '';
38741
38742 if(subplots.cartesian.indexOf(subplotId) !== -1) {
38743 yPos = subplotId.indexOf('y');
38744 xId = subplotId.slice(0, yPos);
38745 yId = subplotId.slice(yPos);
38746 if((axisMap[xId] !== undefined && axisMap[xId] !== j) ||
38747 (axisMap[yId] !== undefined && axisMap[yId] !== i)
38748 ) {
38749 continue;
38750 }
38751
38752 rowOut[j] = subplotId;
38753 axisMap[xId] = j;
38754 axisMap[yId] = i;
38755 }
38756 }
38757 }
38758 } else {
38759 var xAxes = getAxes(layoutOut, gridIn, 'x');
38760 var yAxes = getAxes(layoutOut, gridIn, 'y');
38761 gridOut.xaxes = fillGridAxes(xAxes, subplots.xaxis, columns, axisMap, 'x');
38762 gridOut.yaxes = fillGridAxes(yAxes, subplots.yaxis, rows, axisMap, 'y');
38763 }
38764
38765 var anchors = gridOut._anchors = {};
38766 var reversed = gridOut.roworder === 'top to bottom';
38767
38768 for(var axisId in axisMap) {
38769 var axLetter = axisId.charAt(0);
38770 var side = gridOut[axLetter + 'side'];
38771
38772 var i0, inc, iFinal;
38773
38774 if(side.length < 8) {
38775 // grid edge - ie not "* plot" - make these as free axes
38776 // since we're not guaranteed to have a subplot there at all
38777 anchors[axisId] = 'free';
38778 } else if(axLetter === 'x') {
38779 if((side.charAt(0) === 't') === reversed) {
38780 i0 = 0;
38781 inc = 1;
38782 iFinal = rows;
38783 } else {
38784 i0 = rows - 1;
38785 inc = -1;
38786 iFinal = -1;
38787 }
38788 if(hasSubplotGrid) {
38789 var column = axisMap[axisId];
38790 for(i = i0; i !== iFinal; i += inc) {
38791 subplotId = subplotsOut[i][column];
38792 if(!subplotId) continue;
38793 yPos = subplotId.indexOf('y');
38794 if(subplotId.slice(0, yPos) === axisId) {
38795 anchors[axisId] = subplotId.slice(yPos);
38796 break;
38797 }
38798 }
38799 } else {
38800 for(i = i0; i !== iFinal; i += inc) {
38801 yId = gridOut.yaxes[i];
38802 if(subplots.cartesian.indexOf(axisId + yId) !== -1) {
38803 anchors[axisId] = yId;
38804 break;
38805 }
38806 }
38807 }
38808 } else {
38809 if((side.charAt(0) === 'l')) {
38810 i0 = 0;
38811 inc = 1;
38812 iFinal = columns;
38813 } else {
38814 i0 = columns - 1;
38815 inc = -1;
38816 iFinal = -1;
38817 }
38818 if(hasSubplotGrid) {
38819 var row = axisMap[axisId];
38820 for(i = i0; i !== iFinal; i += inc) {
38821 subplotId = subplotsOut[row][i];
38822 if(!subplotId) continue;
38823 yPos = subplotId.indexOf('y');
38824 if(subplotId.slice(yPos) === axisId) {
38825 anchors[axisId] = subplotId.slice(0, yPos);
38826 break;
38827 }
38828 }
38829 } else {
38830 for(i = i0; i !== iFinal; i += inc) {
38831 xId = gridOut.xaxes[i];
38832 if(subplots.cartesian.indexOf(xId + axisId) !== -1) {
38833 anchors[axisId] = xId;
38834 break;
38835 }
38836 }
38837 }
38838 }
38839 }
38840}
38841
38842function fillGridAxes(axesIn, axesAllowed, len, axisMap, axLetter) {
38843 var out = new Array(len);
38844 var i;
38845
38846 function fillOneAxis(i, axisId) {
38847 if(axesAllowed.indexOf(axisId) !== -1 && axisMap[axisId] === undefined) {
38848 out[i] = axisId;
38849 axisMap[axisId] = i;
38850 } else out[i] = '';
38851 }
38852
38853 if(Array.isArray(axesIn)) {
38854 for(i = 0; i < len; i++) {
38855 fillOneAxis(i, axesIn[i]);
38856 }
38857 } else {
38858 // default axis list is the first `len` axis ids
38859 fillOneAxis(0, axLetter);
38860 for(i = 1; i < len; i++) {
38861 fillOneAxis(i, axLetter + (i + 1));
38862 }
38863 }
38864
38865 return out;
38866}
38867
38868module.exports = {
38869 moduleType: 'component',
38870 name: 'grid',
38871
38872 schema: {
38873 layout: {grid: gridAttrs}
38874 },
38875
38876 layoutAttributes: gridAttrs,
38877 sizeDefaults: sizeDefaults,
38878 contentDefaults: contentDefaults
38879};
38880
38881},{"../../lib":287,"../../lib/regex":303,"../../plot_api/plot_template":323,"../../plots/cartesian/constants":341,"../../plots/domain":362}],202:[function(_dereq_,module,exports){
38882'use strict';
38883
38884var cartesianConstants = _dereq_('../../plots/cartesian/constants');
38885var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray;
38886var axisPlaceableObjs = _dereq_('../../constants/axis_placeable_objects');
38887
38888
38889module.exports = templatedArray('image', {
38890 visible: {
38891 valType: 'boolean',
38892 dflt: true,
38893 editType: 'arraydraw',
38894 },
38895
38896 source: {
38897 valType: 'string',
38898 editType: 'arraydraw',
38899 },
38900
38901 layer: {
38902 valType: 'enumerated',
38903 values: ['below', 'above'],
38904 dflt: 'above',
38905 editType: 'arraydraw',
38906 },
38907
38908 sizex: {
38909 valType: 'number',
38910 dflt: 0,
38911 editType: 'arraydraw',
38912 },
38913
38914 sizey: {
38915 valType: 'number',
38916 dflt: 0,
38917 editType: 'arraydraw',
38918 },
38919
38920 sizing: {
38921 valType: 'enumerated',
38922 values: ['fill', 'contain', 'stretch'],
38923 dflt: 'contain',
38924 editType: 'arraydraw',
38925 },
38926
38927 opacity: {
38928 valType: 'number',
38929 min: 0,
38930 max: 1,
38931 dflt: 1,
38932 editType: 'arraydraw',
38933 },
38934
38935 x: {
38936 valType: 'any',
38937 dflt: 0,
38938 editType: 'arraydraw',
38939 },
38940
38941 y: {
38942 valType: 'any',
38943 dflt: 0,
38944 editType: 'arraydraw',
38945 },
38946
38947 xanchor: {
38948 valType: 'enumerated',
38949 values: ['left', 'center', 'right'],
38950 dflt: 'left',
38951 editType: 'arraydraw',
38952 },
38953
38954 yanchor: {
38955 valType: 'enumerated',
38956 values: ['top', 'middle', 'bottom'],
38957 dflt: 'top',
38958 editType: 'arraydraw',
38959 },
38960
38961 xref: {
38962 valType: 'enumerated',
38963 values: [
38964 'paper',
38965 cartesianConstants.idRegex.x.toString()
38966 ],
38967 dflt: 'paper',
38968 editType: 'arraydraw',
38969 },
38970
38971 yref: {
38972 valType: 'enumerated',
38973 values: [
38974 'paper',
38975 cartesianConstants.idRegex.y.toString()
38976 ],
38977 dflt: 'paper',
38978 editType: 'arraydraw',
38979 },
38980 editType: 'arraydraw'
38981});
38982
38983},{"../../constants/axis_placeable_objects":263,"../../plot_api/plot_template":323,"../../plots/cartesian/constants":341}],203:[function(_dereq_,module,exports){
38984'use strict';
38985
38986var isNumeric = _dereq_('fast-isnumeric');
38987var toLogRange = _dereq_('../../lib/to_log_range');
38988
38989/*
38990 * convertCoords: when converting an axis between log and linear
38991 * you need to alter any images on that axis to keep them
38992 * pointing at the same data point.
38993 * In v3.0 this will become obsolete (or perhaps size will still need conversion?)
38994 * we convert size by declaring that the maximum extent *in data units* should be
38995 * the same, assuming the image is anchored by its center (could remove that restriction
38996 * if we think it's important) even though the actual left and right values will not be
38997 * quite the same since the scale becomes nonlinear (and central anchor means the pixel
38998 * center of the image, not the data units center)
38999 *
39000 * gd: the plot div
39001 * ax: the axis being changed
39002 * newType: the type it's getting
39003 * doExtra: function(attr, val) from inside relayout that sets the attribute.
39004 * Use this to make the changes as it's aware if any other changes in the
39005 * same relayout call should override this conversion.
39006 */
39007module.exports = function convertCoords(gd, ax, newType, doExtra) {
39008 ax = ax || {};
39009
39010 var toLog = (newType === 'log') && (ax.type === 'linear');
39011 var fromLog = (newType === 'linear') && (ax.type === 'log');
39012
39013 if(!(toLog || fromLog)) return;
39014
39015 var images = gd._fullLayout.images;
39016 var axLetter = ax._id.charAt(0);
39017 var image;
39018 var attrPrefix;
39019
39020 for(var i = 0; i < images.length; i++) {
39021 image = images[i];
39022 attrPrefix = 'images[' + i + '].';
39023
39024 if(image[axLetter + 'ref'] === ax._id) {
39025 var currentPos = image[axLetter];
39026 var currentSize = image['size' + axLetter];
39027 var newPos = null;
39028 var newSize = null;
39029
39030 if(toLog) {
39031 newPos = toLogRange(currentPos, ax.range);
39032
39033 // this is the inverse of the conversion we do in fromLog below
39034 // so that the conversion is reversible (notice the fromLog conversion
39035 // is like sinh, and this one looks like arcsinh)
39036 var dx = currentSize / Math.pow(10, newPos) / 2;
39037 newSize = 2 * Math.log(dx + Math.sqrt(1 + dx * dx)) / Math.LN10;
39038 } else {
39039 newPos = Math.pow(10, currentPos);
39040 newSize = newPos * (Math.pow(10, currentSize / 2) - Math.pow(10, -currentSize / 2));
39041 }
39042
39043 // if conversion failed, delete the value so it can get a default later on
39044 if(!isNumeric(newPos)) {
39045 newPos = null;
39046 newSize = null;
39047 } else if(!isNumeric(newSize)) newSize = null;
39048
39049 doExtra(attrPrefix + axLetter, newPos);
39050 doExtra(attrPrefix + 'size' + axLetter, newSize);
39051 }
39052 }
39053};
39054
39055},{"../../lib/to_log_range":312,"fast-isnumeric":33}],204:[function(_dereq_,module,exports){
39056'use strict';
39057
39058var Lib = _dereq_('../../lib');
39059var Axes = _dereq_('../../plots/cartesian/axes');
39060var handleArrayContainerDefaults = _dereq_('../../plots/array_container_defaults');
39061
39062var attributes = _dereq_('./attributes');
39063var name = 'images';
39064
39065module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) {
39066 var opts = {
39067 name: name,
39068 handleItemDefaults: imageDefaults
39069 };
39070
39071 handleArrayContainerDefaults(layoutIn, layoutOut, opts);
39072};
39073
39074
39075function imageDefaults(imageIn, imageOut, fullLayout) {
39076 function coerce(attr, dflt) {
39077 return Lib.coerce(imageIn, imageOut, attributes, attr, dflt);
39078 }
39079
39080 var source = coerce('source');
39081 var visible = coerce('visible', !!source);
39082
39083 if(!visible) return imageOut;
39084
39085 coerce('layer');
39086 coerce('xanchor');
39087 coerce('yanchor');
39088 coerce('sizex');
39089 coerce('sizey');
39090 coerce('sizing');
39091 coerce('opacity');
39092
39093 var gdMock = { _fullLayout: fullLayout };
39094 var axLetters = ['x', 'y'];
39095
39096 for(var i = 0; i < 2; i++) {
39097 // 'paper' is the fallback axref
39098 var axLetter = axLetters[i];
39099 var axRef = Axes.coerceRef(imageIn, imageOut, gdMock, axLetter, 'paper', undefined);
39100
39101 if(axRef !== 'paper') {
39102 var ax = Axes.getFromId(gdMock, axRef);
39103 ax._imgIndices.push(imageOut._index);
39104 }
39105
39106 Axes.coercePosition(imageOut, gdMock, coerce, axRef, axLetter, 0);
39107 }
39108
39109 return imageOut;
39110}
39111
39112},{"../../lib":287,"../../plots/array_container_defaults":329,"../../plots/cartesian/axes":334,"./attributes":202}],205:[function(_dereq_,module,exports){
39113'use strict';
39114
39115var d3 = _dereq_('@plotly/d3');
39116var Drawing = _dereq_('../drawing');
39117var Axes = _dereq_('../../plots/cartesian/axes');
39118var axisIds = _dereq_('../../plots/cartesian/axis_ids');
39119var xmlnsNamespaces = _dereq_('../../constants/xmlns_namespaces');
39120
39121module.exports = function draw(gd) {
39122 var fullLayout = gd._fullLayout;
39123 var imageDataAbove = [];
39124 var imageDataSubplot = {};
39125 var imageDataBelow = [];
39126 var subplot;
39127 var i;
39128
39129 // Sort into top, subplot, and bottom layers
39130 for(i = 0; i < fullLayout.images.length; i++) {
39131 var img = fullLayout.images[i];
39132
39133 if(img.visible) {
39134 if(img.layer === 'below' && img.xref !== 'paper' && img.yref !== 'paper') {
39135 subplot = axisIds.ref2id(img.xref) + axisIds.ref2id(img.yref);
39136
39137 var plotinfo = fullLayout._plots[subplot];
39138
39139 if(!plotinfo) {
39140 // Fall back to _imageLowerLayer in case the requested subplot doesn't exist.
39141 // This can happen if you reference the image to an x / y axis combination
39142 // that doesn't have any data on it (and layer is below)
39143 imageDataBelow.push(img);
39144 continue;
39145 }
39146
39147 if(plotinfo.mainplot) {
39148 subplot = plotinfo.mainplot.id;
39149 }
39150
39151 if(!imageDataSubplot[subplot]) {
39152 imageDataSubplot[subplot] = [];
39153 }
39154 imageDataSubplot[subplot].push(img);
39155 } else if(img.layer === 'above') {
39156 imageDataAbove.push(img);
39157 } else {
39158 imageDataBelow.push(img);
39159 }
39160 }
39161 }
39162
39163
39164 var anchors = {
39165 x: {
39166 left: { sizing: 'xMin', offset: 0 },
39167 center: { sizing: 'xMid', offset: -1 / 2 },
39168 right: { sizing: 'xMax', offset: -1 }
39169 },
39170 y: {
39171 top: { sizing: 'YMin', offset: 0 },
39172 middle: { sizing: 'YMid', offset: -1 / 2 },
39173 bottom: { sizing: 'YMax', offset: -1 }
39174 }
39175 };
39176
39177
39178 // Images must be converted to dataURL's for exporting.
39179 function setImage(d) {
39180 var thisImage = d3.select(this);
39181
39182 if(this._imgSrc === d.source) {
39183 return;
39184 }
39185
39186 thisImage.attr('xmlns', xmlnsNamespaces.svg);
39187
39188 if(d.source && d.source.slice(0, 5) === 'data:') {
39189 thisImage.attr('xlink:href', d.source);
39190 this._imgSrc = d.source;
39191 } else {
39192 var imagePromise = new Promise(function(resolve) {
39193 var img = new Image();
39194 this.img = img;
39195
39196 // If not set, a `tainted canvas` error is thrown
39197 img.setAttribute('crossOrigin', 'anonymous');
39198 img.onerror = errorHandler;
39199 img.onload = function() {
39200 var canvas = document.createElement('canvas');
39201 canvas.width = this.width;
39202 canvas.height = this.height;
39203
39204 var ctx = canvas.getContext('2d');
39205 ctx.drawImage(this, 0, 0);
39206
39207 var dataURL = canvas.toDataURL('image/png');
39208
39209 thisImage.attr('xlink:href', dataURL);
39210
39211 // resolve promise in onload handler instead of on 'load' to support IE11
39212 // see https://github.com/plotly/plotly.js/issues/1685
39213 // for more details
39214 resolve();
39215 };
39216
39217 thisImage.on('error', errorHandler);
39218
39219 img.src = d.source;
39220 this._imgSrc = d.source;
39221
39222 function errorHandler() {
39223 thisImage.remove();
39224 resolve();
39225 }
39226 }.bind(this));
39227
39228 gd._promises.push(imagePromise);
39229 }
39230 }
39231
39232 function applyAttributes(d) {
39233 var thisImage = d3.select(this);
39234
39235 // Axes if specified
39236 var xa = Axes.getFromId(gd, d.xref);
39237 var ya = Axes.getFromId(gd, d.yref);
39238 var xIsDomain = Axes.getRefType(d.xref) === 'domain';
39239 var yIsDomain = Axes.getRefType(d.yref) === 'domain';
39240
39241 var size = fullLayout._size;
39242 var width, height;
39243 if(xa !== undefined) {
39244 width = ((typeof(d.xref) === 'string') && xIsDomain) ?
39245 xa._length * d.sizex :
39246 Math.abs(xa.l2p(d.sizex) - xa.l2p(0));
39247 } else {
39248 width = d.sizex * size.w;
39249 }
39250 if(ya !== undefined) {
39251 height = ((typeof(d.yref) === 'string') && yIsDomain) ?
39252 ya._length * d.sizey :
39253 Math.abs(ya.l2p(d.sizey) - ya.l2p(0));
39254 } else {
39255 height = d.sizey * size.h;
39256 }
39257
39258 // Offsets for anchor positioning
39259 var xOffset = width * anchors.x[d.xanchor].offset;
39260 var yOffset = height * anchors.y[d.yanchor].offset;
39261
39262 var sizing = anchors.x[d.xanchor].sizing + anchors.y[d.yanchor].sizing;
39263
39264 // Final positions
39265 var xPos, yPos;
39266 if(xa !== undefined) {
39267 xPos = ((typeof(d.xref) === 'string') && xIsDomain) ?
39268 xa._length * d.x + xa._offset :
39269 xa.r2p(d.x) + xa._offset;
39270 } else {
39271 xPos = d.x * size.w + size.l;
39272 }
39273 xPos += xOffset;
39274 if(ya !== undefined) {
39275 yPos = ((typeof(d.yref) === 'string') && yIsDomain) ?
39276 // consistent with "paper" yref value, where positive values
39277 // move up the page
39278 ya._length * (1 - d.y) + ya._offset :
39279 ya.r2p(d.y) + ya._offset;
39280 } else {
39281 yPos = size.h - d.y * size.h + size.t;
39282 }
39283 yPos += yOffset;
39284
39285 // Construct the proper aspectRatio attribute
39286 switch(d.sizing) {
39287 case 'fill':
39288 sizing += ' slice';
39289 break;
39290
39291 case 'stretch':
39292 sizing = 'none';
39293 break;
39294 }
39295
39296 thisImage.attr({
39297 x: xPos,
39298 y: yPos,
39299 width: width,
39300 height: height,
39301 preserveAspectRatio: sizing,
39302 opacity: d.opacity
39303 });
39304
39305
39306 // Set proper clipping on images
39307 var xId = xa && (Axes.getRefType(d.xref) !== 'domain') ? xa._id : '';
39308 var yId = ya && (Axes.getRefType(d.yref) !== 'domain') ? ya._id : '';
39309 var clipAxes = xId + yId;
39310
39311 Drawing.setClipUrl(
39312 thisImage,
39313 clipAxes ? ('clip' + fullLayout._uid + clipAxes) : null,
39314 gd
39315 );
39316 }
39317
39318 var imagesBelow = fullLayout._imageLowerLayer.selectAll('image')
39319 .data(imageDataBelow);
39320 var imagesAbove = fullLayout._imageUpperLayer.selectAll('image')
39321 .data(imageDataAbove);
39322
39323 imagesBelow.enter().append('image');
39324 imagesAbove.enter().append('image');
39325
39326 imagesBelow.exit().remove();
39327 imagesAbove.exit().remove();
39328
39329 imagesBelow.each(function(d) {
39330 setImage.bind(this)(d);
39331 applyAttributes.bind(this)(d);
39332 });
39333 imagesAbove.each(function(d) {
39334 setImage.bind(this)(d);
39335 applyAttributes.bind(this)(d);
39336 });
39337
39338 var allSubplots = Object.keys(fullLayout._plots);
39339 for(i = 0; i < allSubplots.length; i++) {
39340 subplot = allSubplots[i];
39341 var subplotObj = fullLayout._plots[subplot];
39342
39343 // filter out overlaid plots (which have their images on the main plot)
39344 // and gl2d plots (which don't support below images, at least not yet)
39345 if(!subplotObj.imagelayer) continue;
39346
39347 var imagesOnSubplot = subplotObj.imagelayer.selectAll('image')
39348 // even if there are no images on this subplot, we need to run
39349 // enter and exit in case there were previously
39350 .data(imageDataSubplot[subplot] || []);
39351
39352 imagesOnSubplot.enter().append('image');
39353 imagesOnSubplot.exit().remove();
39354
39355 imagesOnSubplot.each(function(d) {
39356 setImage.bind(this)(d);
39357 applyAttributes.bind(this)(d);
39358 });
39359 }
39360};
39361
39362},{"../../constants/xmlns_namespaces":268,"../../plots/cartesian/axes":334,"../../plots/cartesian/axis_ids":338,"../drawing":179,"@plotly/d3":20}],206:[function(_dereq_,module,exports){
39363'use strict';
39364
39365module.exports = {
39366 moduleType: 'component',
39367 name: 'images',
39368
39369 layoutAttributes: _dereq_('./attributes'),
39370 supplyLayoutDefaults: _dereq_('./defaults'),
39371 includeBasePlot: _dereq_('../../plots/cartesian/include_components')('images'),
39372
39373 draw: _dereq_('./draw'),
39374
39375 convertCoords: _dereq_('./convert_coords')
39376};
39377
39378},{"../../plots/cartesian/include_components":347,"./attributes":202,"./convert_coords":203,"./defaults":204,"./draw":205}],207:[function(_dereq_,module,exports){
39379'use strict';
39380
39381var fontAttrs = _dereq_('../../plots/font_attributes');
39382var colorAttrs = _dereq_('../color/attributes');
39383
39384
39385module.exports = {
39386 bgcolor: {
39387 valType: 'color',
39388 editType: 'legend',
39389 },
39390 bordercolor: {
39391 valType: 'color',
39392 dflt: colorAttrs.defaultLine,
39393 editType: 'legend',
39394 },
39395 borderwidth: {
39396 valType: 'number',
39397 min: 0,
39398 dflt: 0,
39399 editType: 'legend',
39400 },
39401 font: fontAttrs({
39402 editType: 'legend',
39403 }),
39404 orientation: {
39405 valType: 'enumerated',
39406 values: ['v', 'h'],
39407 dflt: 'v',
39408 editType: 'legend',
39409 },
39410 traceorder: {
39411 valType: 'flaglist',
39412 flags: ['reversed', 'grouped'],
39413 extras: ['normal'],
39414 editType: 'legend',
39415 },
39416 tracegroupgap: {
39417 valType: 'number',
39418 min: 0,
39419 dflt: 10,
39420 editType: 'legend',
39421 },
39422 itemsizing: {
39423 valType: 'enumerated',
39424 values: ['trace', 'constant'],
39425 dflt: 'trace',
39426 editType: 'legend',
39427 },
39428 itemwidth: {
39429 valType: 'number',
39430 min: 30,
39431 dflt: 30,
39432 editType: 'legend',
39433 },
39434 itemclick: {
39435 valType: 'enumerated',
39436 values: ['toggle', 'toggleothers', false],
39437 dflt: 'toggle',
39438 editType: 'legend',
39439 },
39440 itemdoubleclick: {
39441 valType: 'enumerated',
39442 values: ['toggle', 'toggleothers', false],
39443 dflt: 'toggleothers',
39444 editType: 'legend',
39445 },
39446 groupclick: {
39447 valType: 'enumerated',
39448 values: ['toggleitem', 'togglegroup'],
39449 dflt: 'togglegroup',
39450 editType: 'legend',
39451 },
39452 x: {
39453 valType: 'number',
39454 min: -2,
39455 max: 3,
39456 editType: 'legend',
39457 },
39458 xanchor: {
39459 valType: 'enumerated',
39460 values: ['auto', 'left', 'center', 'right'],
39461 dflt: 'left',
39462 editType: 'legend',
39463 },
39464 y: {
39465 valType: 'number',
39466 min: -2,
39467 max: 3,
39468 editType: 'legend',
39469 },
39470 yanchor: {
39471 valType: 'enumerated',
39472 values: ['auto', 'top', 'middle', 'bottom'],
39473 editType: 'legend',
39474 },
39475 uirevision: {
39476 valType: 'any',
39477 editType: 'none',
39478 },
39479 valign: {
39480 valType: 'enumerated',
39481 values: ['top', 'middle', 'bottom'],
39482 dflt: 'middle',
39483 editType: 'legend',
39484 },
39485 title: {
39486 text: {
39487 valType: 'string',
39488 dflt: '',
39489 editType: 'legend',
39490 },
39491 font: fontAttrs({
39492 editType: 'legend',
39493 }),
39494 side: {
39495 valType: 'enumerated',
39496 values: ['top', 'left', 'top left'],
39497 editType: 'legend',
39498 },
39499 editType: 'legend',
39500 },
39501 editType: 'legend'
39502};
39503
39504},{"../../plots/font_attributes":363,"../color/attributes":156}],208:[function(_dereq_,module,exports){
39505'use strict';
39506
39507module.exports = {
39508 scrollBarWidth: 6,
39509 scrollBarMinHeight: 20,
39510 scrollBarColor: '#808BA4',
39511 scrollBarMargin: 4,
39512 scrollBarEnterAttrs: {rx: 20, ry: 3, width: 0, height: 0},
39513
39514 // number of px between legend title and (left) side of legend (always in x direction and from inner border)
39515 titlePad: 2,
39516 // number of px between each legend item (x and/or y direction)
39517 itemGap: 5
39518};
39519
39520},{}],209:[function(_dereq_,module,exports){
39521'use strict';
39522
39523var Registry = _dereq_('../../registry');
39524var Lib = _dereq_('../../lib');
39525var Template = _dereq_('../../plot_api/plot_template');
39526
39527var attributes = _dereq_('./attributes');
39528var basePlotLayoutAttributes = _dereq_('../../plots/layout_attributes');
39529var helpers = _dereq_('./helpers');
39530
39531
39532module.exports = function legendDefaults(layoutIn, layoutOut, fullData) {
39533 var containerIn = layoutIn.legend || {};
39534
39535 var legendTraceCount = 0;
39536 var legendReallyHasATrace = false;
39537 var defaultOrder = 'normal';
39538
39539 for(var i = 0; i < fullData.length; i++) {
39540 var trace = fullData[i];
39541
39542 if(!trace.visible) continue;
39543
39544 // Note that we explicitly count any trace that is either shown or
39545 // *would* be shown by default, toward the two traces you need to
39546 // ensure the legend is shown by default, because this can still help
39547 // disambiguate.
39548 if(trace.showlegend || (
39549 trace._dfltShowLegend && !(
39550 trace._module &&
39551 trace._module.attributes &&
39552 trace._module.attributes.showlegend &&
39553 trace._module.attributes.showlegend.dflt === false
39554 )
39555 )) {
39556 legendTraceCount++;
39557 if(trace.showlegend) {
39558 legendReallyHasATrace = true;
39559 // Always show the legend by default if there's a pie,
39560 // or if there's only one trace but it's explicitly shown
39561 if(Registry.traceIs(trace, 'pie-like') ||
39562 trace._input.showlegend === true
39563 ) {
39564 legendTraceCount++;
39565 }
39566 }
39567 }
39568
39569 if((Registry.traceIs(trace, 'bar') && layoutOut.barmode === 'stack') ||
39570 ['tonextx', 'tonexty'].indexOf(trace.fill) !== -1) {
39571 defaultOrder = helpers.isGrouped({traceorder: defaultOrder}) ?
39572 'grouped+reversed' : 'reversed';
39573 }
39574
39575 if(trace.legendgroup !== undefined && trace.legendgroup !== '') {
39576 defaultOrder = helpers.isReversed({traceorder: defaultOrder}) ?
39577 'reversed+grouped' : 'grouped';
39578 }
39579 }
39580
39581 var showLegend = Lib.coerce(layoutIn, layoutOut,
39582 basePlotLayoutAttributes, 'showlegend',
39583 legendReallyHasATrace && legendTraceCount > 1);
39584
39585 if(showLegend === false && !containerIn.uirevision) return;
39586
39587 var containerOut = Template.newContainer(layoutOut, 'legend');
39588
39589 function coerce(attr, dflt) {
39590 return Lib.coerce(containerIn, containerOut, attributes, attr, dflt);
39591 }
39592
39593 coerce('uirevision', layoutOut.uirevision);
39594
39595 if(showLegend === false) return;
39596
39597 coerce('bgcolor', layoutOut.paper_bgcolor);
39598 coerce('bordercolor');
39599 coerce('borderwidth');
39600 var itemFont = Lib.coerceFont(coerce, 'font', layoutOut.font);
39601
39602 var orientation = coerce('orientation');
39603 var isHorizontal = orientation === 'h';
39604 var defaultX, defaultY, defaultYAnchor;
39605
39606 if(isHorizontal) {
39607 defaultX = 0;
39608
39609 if(Registry.getComponentMethod('rangeslider', 'isVisible')(layoutIn.xaxis)) {
39610 defaultY = 1.1;
39611 defaultYAnchor = 'bottom';
39612 } else {
39613 // maybe use y=1.1 / yanchor=bottom as above
39614 // to avoid https://github.com/plotly/plotly.js/issues/1199
39615 // in v3
39616 defaultY = -0.1;
39617 defaultYAnchor = 'top';
39618 }
39619 } else {
39620 defaultX = 1.02;
39621 defaultY = 1;
39622 defaultYAnchor = 'auto';
39623 }
39624
39625 coerce('traceorder', defaultOrder);
39626 if(helpers.isGrouped(layoutOut.legend)) coerce('tracegroupgap');
39627
39628 coerce('itemsizing');
39629 coerce('itemwidth');
39630
39631 coerce('itemclick');
39632 coerce('itemdoubleclick');
39633 coerce('groupclick');
39634
39635 coerce('x', defaultX);
39636 coerce('xanchor');
39637 coerce('y', defaultY);
39638 coerce('yanchor', defaultYAnchor);
39639 coerce('valign');
39640 Lib.noneOrAll(containerIn, containerOut, ['x', 'y']);
39641
39642 var titleText = coerce('title.text');
39643 if(titleText) {
39644 coerce('title.side', isHorizontal ? 'left' : 'top');
39645 var dfltTitleFont = Lib.extendFlat({}, itemFont, {
39646 size: Lib.bigFont(itemFont.size)
39647 });
39648
39649 Lib.coerceFont(coerce, 'title.font', dfltTitleFont);
39650 }
39651};
39652
39653},{"../../lib":287,"../../plot_api/plot_template":323,"../../plots/layout_attributes":367,"../../registry":376,"./attributes":207,"./helpers":213}],210:[function(_dereq_,module,exports){
39654'use strict';
39655
39656var d3 = _dereq_('@plotly/d3');
39657
39658var Lib = _dereq_('../../lib');
39659var Plots = _dereq_('../../plots/plots');
39660var Registry = _dereq_('../../registry');
39661var Events = _dereq_('../../lib/events');
39662var dragElement = _dereq_('../dragelement');
39663var Drawing = _dereq_('../drawing');
39664var Color = _dereq_('../color');
39665var svgTextUtils = _dereq_('../../lib/svg_text_utils');
39666var handleClick = _dereq_('./handle_click');
39667
39668var constants = _dereq_('./constants');
39669var alignmentConstants = _dereq_('../../constants/alignment');
39670var LINE_SPACING = alignmentConstants.LINE_SPACING;
39671var FROM_TL = alignmentConstants.FROM_TL;
39672var FROM_BR = alignmentConstants.FROM_BR;
39673
39674var getLegendData = _dereq_('./get_legend_data');
39675var style = _dereq_('./style');
39676var helpers = _dereq_('./helpers');
39677
39678var MAIN_TITLE = 1;
39679
39680module.exports = function draw(gd, opts) {
39681 if(!opts) opts = gd._fullLayout.legend || {};
39682 return _draw(gd, opts);
39683};
39684
39685function _draw(gd, legendObj) {
39686 var fullLayout = gd._fullLayout;
39687 var clipId = 'legend' + fullLayout._uid;
39688 var layer;
39689
39690 var inHover = legendObj._inHover;
39691 if(inHover) {
39692 layer = legendObj.layer;
39693 clipId += '-hover';
39694 } else {
39695 layer = fullLayout._infolayer;
39696 }
39697
39698 if(!layer) return;
39699
39700 if(!gd._legendMouseDownTime) gd._legendMouseDownTime = 0;
39701
39702 var legendData;
39703 if(!inHover) {
39704 if(!gd.calcdata) return;
39705 legendData = fullLayout.showlegend && getLegendData(gd.calcdata, legendObj);
39706 } else {
39707 if(!legendObj.entries) return;
39708 legendData = getLegendData(legendObj.entries, legendObj);
39709 }
39710
39711 var hiddenSlices = fullLayout.hiddenlabels || [];
39712
39713 if(!inHover && (!fullLayout.showlegend || !legendData.length)) {
39714 layer.selectAll('.legend').remove();
39715 fullLayout._topdefs.select('#' + clipId).remove();
39716 return Plots.autoMargin(gd, 'legend');
39717 }
39718
39719 var legend = Lib.ensureSingle(layer, 'g', 'legend', function(s) {
39720 if(!inHover) s.attr('pointer-events', 'all');
39721 });
39722
39723 var clipPath = Lib.ensureSingleById(fullLayout._topdefs, 'clipPath', clipId, function(s) {
39724 s.append('rect');
39725 });
39726
39727 var bg = Lib.ensureSingle(legend, 'rect', 'bg', function(s) {
39728 s.attr('shape-rendering', 'crispEdges');
39729 });
39730 bg.call(Color.stroke, legendObj.bordercolor)
39731 .call(Color.fill, legendObj.bgcolor)
39732 .style('stroke-width', legendObj.borderwidth + 'px');
39733
39734 var scrollBox = Lib.ensureSingle(legend, 'g', 'scrollbox');
39735
39736 var title = legendObj.title;
39737 legendObj._titleWidth = 0;
39738 legendObj._titleHeight = 0;
39739 if(title.text) {
39740 var titleEl = Lib.ensureSingle(scrollBox, 'text', 'legendtitletext');
39741 titleEl.attr('text-anchor', 'start')
39742 .call(Drawing.font, title.font)
39743 .text(title.text);
39744
39745 textLayout(titleEl, scrollBox, gd, legendObj, MAIN_TITLE); // handle mathjax or multi-line text and compute title height
39746 } else {
39747 scrollBox.selectAll('.legendtitletext').remove();
39748 }
39749
39750 var scrollBar = Lib.ensureSingle(legend, 'rect', 'scrollbar', function(s) {
39751 s.attr(constants.scrollBarEnterAttrs)
39752 .call(Color.fill, constants.scrollBarColor);
39753 });
39754
39755 var groups = scrollBox.selectAll('g.groups').data(legendData);
39756 groups.enter().append('g').attr('class', 'groups');
39757 groups.exit().remove();
39758
39759 var traces = groups.selectAll('g.traces').data(Lib.identity);
39760 traces.enter().append('g').attr('class', 'traces');
39761 traces.exit().remove();
39762
39763 traces.style('opacity', function(d) {
39764 var trace = d[0].trace;
39765 if(Registry.traceIs(trace, 'pie-like')) {
39766 return hiddenSlices.indexOf(d[0].label) !== -1 ? 0.5 : 1;
39767 } else {
39768 return trace.visible === 'legendonly' ? 0.5 : 1;
39769 }
39770 })
39771 .each(function() { d3.select(this).call(drawTexts, gd, legendObj); })
39772 .call(style, gd, legendObj)
39773 .each(function() { if(!inHover) d3.select(this).call(setupTraceToggle, gd); });
39774
39775 Lib.syncOrAsync([
39776 Plots.previousPromises,
39777 function() { return computeLegendDimensions(gd, groups, traces, legendObj); },
39778 function() {
39779 // IF expandMargin return a Promise (which is truthy),
39780 // we're under a doAutoMargin redraw, so we don't have to
39781 // draw the remaining pieces below
39782 if(!inHover && expandMargin(gd)) return;
39783
39784 var gs = fullLayout._size;
39785 var bw = legendObj.borderwidth;
39786
39787 var lx = gs.l + gs.w * legendObj.x - FROM_TL[getXanchor(legendObj)] * legendObj._width;
39788 var ly = gs.t + gs.h * (1 - legendObj.y) - FROM_TL[getYanchor(legendObj)] * legendObj._effHeight;
39789
39790 if(!inHover && fullLayout.margin.autoexpand) {
39791 var lx0 = lx;
39792 var ly0 = ly;
39793
39794 lx = Lib.constrain(lx, 0, fullLayout.width - legendObj._width);
39795 ly = Lib.constrain(ly, 0, fullLayout.height - legendObj._effHeight);
39796
39797 if(lx !== lx0) {
39798 Lib.log('Constrain legend.x to make legend fit inside graph');
39799 }
39800 if(ly !== ly0) {
39801 Lib.log('Constrain legend.y to make legend fit inside graph');
39802 }
39803 }
39804
39805 // Set size and position of all the elements that make up a legend:
39806 // legend, background and border, scroll box and scroll bar as well as title
39807 if(!inHover) Drawing.setTranslate(legend, lx, ly);
39808
39809 // to be safe, remove previous listeners
39810 scrollBar.on('.drag', null);
39811 legend.on('wheel', null);
39812
39813 if(inHover || legendObj._height <= legendObj._maxHeight || gd._context.staticPlot) {
39814 // if scrollbar should not be shown.
39815 var height = legendObj._effHeight;
39816
39817 // if unified hover, let it be its full size
39818 if(inHover) height = legendObj._height;
39819
39820 bg.attr({
39821 width: legendObj._width - bw,
39822 height: height - bw,
39823 x: bw / 2,
39824 y: bw / 2
39825 });
39826
39827 Drawing.setTranslate(scrollBox, 0, 0);
39828
39829 clipPath.select('rect').attr({
39830 width: legendObj._width - 2 * bw,
39831 height: height - 2 * bw,
39832 x: bw,
39833 y: bw
39834 });
39835
39836 Drawing.setClipUrl(scrollBox, clipId, gd);
39837
39838 Drawing.setRect(scrollBar, 0, 0, 0, 0);
39839 delete legendObj._scrollY;
39840 } else {
39841 var scrollBarHeight = Math.max(constants.scrollBarMinHeight,
39842 legendObj._effHeight * legendObj._effHeight / legendObj._height);
39843 var scrollBarYMax = legendObj._effHeight -
39844 scrollBarHeight -
39845 2 * constants.scrollBarMargin;
39846 var scrollBoxYMax = legendObj._height - legendObj._effHeight;
39847 var scrollRatio = scrollBarYMax / scrollBoxYMax;
39848
39849 var scrollBoxY = Math.min(legendObj._scrollY || 0, scrollBoxYMax);
39850
39851 // increase the background and clip-path width
39852 // by the scrollbar width and margin
39853 bg.attr({
39854 width: legendObj._width -
39855 2 * bw +
39856 constants.scrollBarWidth +
39857 constants.scrollBarMargin,
39858 height: legendObj._effHeight - bw,
39859 x: bw / 2,
39860 y: bw / 2
39861 });
39862
39863 clipPath.select('rect').attr({
39864 width: legendObj._width -
39865 2 * bw +
39866 constants.scrollBarWidth +
39867 constants.scrollBarMargin,
39868 height: legendObj._effHeight - 2 * bw,
39869 x: bw,
39870 y: bw + scrollBoxY
39871 });
39872
39873 Drawing.setClipUrl(scrollBox, clipId, gd);
39874
39875 scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio);
39876
39877 // scroll legend by mousewheel or touchpad swipe up/down
39878 legend.on('wheel', function() {
39879 scrollBoxY = Lib.constrain(
39880 legendObj._scrollY +
39881 ((d3.event.deltaY / scrollBarYMax) * scrollBoxYMax),
39882 0, scrollBoxYMax);
39883 scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio);
39884 if(scrollBoxY !== 0 && scrollBoxY !== scrollBoxYMax) {
39885 d3.event.preventDefault();
39886 }
39887 });
39888
39889 var eventY0, eventY1, scrollBoxY0;
39890
39891 var getScrollBarDragY = function(scrollBoxY0, eventY0, eventY1) {
39892 var y = ((eventY1 - eventY0) / scrollRatio) + scrollBoxY0;
39893 return Lib.constrain(y, 0, scrollBoxYMax);
39894 };
39895
39896 var getNaturalDragY = function(scrollBoxY0, eventY0, eventY1) {
39897 var y = ((eventY0 - eventY1) / scrollRatio) + scrollBoxY0;
39898 return Lib.constrain(y, 0, scrollBoxYMax);
39899 };
39900
39901 // scroll legend by dragging scrollBAR
39902 var scrollBarDrag = d3.behavior.drag()
39903 .on('dragstart', function() {
39904 var e = d3.event.sourceEvent;
39905 if(e.type === 'touchstart') {
39906 eventY0 = e.changedTouches[0].clientY;
39907 } else {
39908 eventY0 = e.clientY;
39909 }
39910 scrollBoxY0 = scrollBoxY;
39911 })
39912 .on('drag', function() {
39913 var e = d3.event.sourceEvent;
39914 if(e.buttons === 2 || e.ctrlKey) return;
39915 if(e.type === 'touchmove') {
39916 eventY1 = e.changedTouches[0].clientY;
39917 } else {
39918 eventY1 = e.clientY;
39919 }
39920 scrollBoxY = getScrollBarDragY(scrollBoxY0, eventY0, eventY1);
39921 scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio);
39922 });
39923 scrollBar.call(scrollBarDrag);
39924
39925 // scroll legend by touch-dragging scrollBOX
39926 var scrollBoxTouchDrag = d3.behavior.drag()
39927 .on('dragstart', function() {
39928 var e = d3.event.sourceEvent;
39929 if(e.type === 'touchstart') {
39930 eventY0 = e.changedTouches[0].clientY;
39931 scrollBoxY0 = scrollBoxY;
39932 }
39933 })
39934 .on('drag', function() {
39935 var e = d3.event.sourceEvent;
39936 if(e.type === 'touchmove') {
39937 eventY1 = e.changedTouches[0].clientY;
39938 scrollBoxY = getNaturalDragY(scrollBoxY0, eventY0, eventY1);
39939 scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio);
39940 }
39941 });
39942 scrollBox.call(scrollBoxTouchDrag);
39943 }
39944
39945 function scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio) {
39946 legendObj._scrollY = gd._fullLayout.legend._scrollY = scrollBoxY;
39947 Drawing.setTranslate(scrollBox, 0, -scrollBoxY);
39948
39949 Drawing.setRect(
39950 scrollBar,
39951 legendObj._width,
39952 constants.scrollBarMargin + scrollBoxY * scrollRatio,
39953 constants.scrollBarWidth,
39954 scrollBarHeight
39955 );
39956 clipPath.select('rect').attr('y', bw + scrollBoxY);
39957 }
39958
39959 if(gd._context.edits.legendPosition) {
39960 var xf, yf, x0, y0;
39961
39962 legend.classed('cursor-move', true);
39963
39964 dragElement.init({
39965 element: legend.node(),
39966 gd: gd,
39967 prepFn: function() {
39968 var transform = Drawing.getTranslate(legend);
39969 x0 = transform.x;
39970 y0 = transform.y;
39971 },
39972 moveFn: function(dx, dy) {
39973 var newX = x0 + dx;
39974 var newY = y0 + dy;
39975
39976 Drawing.setTranslate(legend, newX, newY);
39977
39978 xf = dragElement.align(newX, 0, gs.l, gs.l + gs.w, legendObj.xanchor);
39979 yf = dragElement.align(newY, 0, gs.t + gs.h, gs.t, legendObj.yanchor);
39980 },
39981 doneFn: function() {
39982 if(xf !== undefined && yf !== undefined) {
39983 Registry.call('_guiRelayout', gd, {'legend.x': xf, 'legend.y': yf});
39984 }
39985 },
39986 clickFn: function(numClicks, e) {
39987 var clickedTrace = layer.selectAll('g.traces').filter(function() {
39988 var bbox = this.getBoundingClientRect();
39989 return (
39990 e.clientX >= bbox.left && e.clientX <= bbox.right &&
39991 e.clientY >= bbox.top && e.clientY <= bbox.bottom
39992 );
39993 });
39994 if(clickedTrace.size() > 0) {
39995 clickOrDoubleClick(gd, legend, clickedTrace, numClicks, e);
39996 }
39997 }
39998 });
39999 }
40000 }], gd);
40001}
40002
40003function clickOrDoubleClick(gd, legend, legendItem, numClicks, evt) {
40004 var trace = legendItem.data()[0][0].trace;
40005 var evtData = {
40006 event: evt,
40007 node: legendItem.node(),
40008 curveNumber: trace.index,
40009 expandedIndex: trace._expandedIndex,
40010 data: gd.data,
40011 layout: gd.layout,
40012 frames: gd._transitionData._frames,
40013 config: gd._context,
40014 fullData: gd._fullData,
40015 fullLayout: gd._fullLayout
40016 };
40017
40018 if(trace._group) {
40019 evtData.group = trace._group;
40020 }
40021 if(Registry.traceIs(trace, 'pie-like')) {
40022 evtData.label = legendItem.datum()[0].label;
40023 }
40024
40025 var clickVal = Events.triggerHandler(gd, 'plotly_legendclick', evtData);
40026 if(clickVal === false) return;
40027
40028 if(numClicks === 1) {
40029 legend._clickTimeout = setTimeout(function() {
40030 if(!gd._fullLayout) return;
40031 handleClick(legendItem, gd, numClicks);
40032 }, gd._context.doubleClickDelay);
40033 } else if(numClicks === 2) {
40034 if(legend._clickTimeout) clearTimeout(legend._clickTimeout);
40035 gd._legendMouseDownTime = 0;
40036
40037 var dblClickVal = Events.triggerHandler(gd, 'plotly_legenddoubleclick', evtData);
40038 if(dblClickVal !== false) handleClick(legendItem, gd, numClicks);
40039 }
40040}
40041
40042function drawTexts(g, gd, legendObj) {
40043 var legendItem = g.data()[0][0];
40044 var trace = legendItem.trace;
40045 var isPieLike = Registry.traceIs(trace, 'pie-like');
40046 var isEditable = !legendObj._inHover && gd._context.edits.legendText && !isPieLike;
40047 var maxNameLength = legendObj._maxNameLength;
40048
40049 var name, font;
40050 if(legendItem.groupTitle) {
40051 name = legendItem.groupTitle.text;
40052 font = legendItem.groupTitle.font;
40053 } else {
40054 font = legendObj.font;
40055 if(!legendObj.entries) {
40056 name = isPieLike ? legendItem.label : trace.name;
40057 if(trace._meta) {
40058 name = Lib.templateString(name, trace._meta);
40059 }
40060 } else {
40061 name = legendItem.text;
40062 }
40063 }
40064
40065 var textEl = Lib.ensureSingle(g, 'text', 'legendtext');
40066
40067 textEl.attr('text-anchor', 'start')
40068 .call(Drawing.font, font)
40069 .text(isEditable ? ensureLength(name, maxNameLength) : name);
40070
40071 var textGap = legendObj.itemwidth + constants.itemGap * 2;
40072 svgTextUtils.positionText(textEl, textGap, 0);
40073
40074 if(isEditable) {
40075 textEl.call(svgTextUtils.makeEditable, {gd: gd, text: name})
40076 .call(textLayout, g, gd, legendObj)
40077 .on('edit', function(newName) {
40078 this.text(ensureLength(newName, maxNameLength))
40079 .call(textLayout, g, gd, legendObj);
40080
40081 var fullInput = legendItem.trace._fullInput || {};
40082 var update = {};
40083
40084 if(Registry.hasTransform(fullInput, 'groupby')) {
40085 var groupbyIndices = Registry.getTransformIndices(fullInput, 'groupby');
40086 var index = groupbyIndices[groupbyIndices.length - 1];
40087
40088 var kcont = Lib.keyedContainer(fullInput, 'transforms[' + index + '].styles', 'target', 'value.name');
40089
40090 kcont.set(legendItem.trace._group, newName);
40091
40092 update = kcont.constructUpdate();
40093 } else {
40094 update.name = newName;
40095 }
40096
40097 return Registry.call('_guiRestyle', gd, update, trace.index);
40098 });
40099 } else {
40100 textLayout(textEl, g, gd, legendObj);
40101 }
40102}
40103
40104/*
40105 * Make sure we have a reasonably clickable region.
40106 * If this string is missing or very short, pad it with spaces out to at least
40107 * 4 characters, up to the max length of other labels, on the assumption that
40108 * most characters are wider than spaces so a string of spaces will usually be
40109 * no wider than the real labels.
40110 */
40111function ensureLength(str, maxLength) {
40112 var targetLength = Math.max(4, maxLength);
40113 if(str && str.trim().length >= targetLength / 2) return str;
40114 str = str || '';
40115 for(var i = targetLength - str.length; i > 0; i--) str += ' ';
40116 return str;
40117}
40118
40119function setupTraceToggle(g, gd) {
40120 var doubleClickDelay = gd._context.doubleClickDelay;
40121 var newMouseDownTime;
40122 var numClicks = 1;
40123
40124 var traceToggle = Lib.ensureSingle(g, 'rect', 'legendtoggle', function(s) {
40125 if(!gd._context.staticPlot) {
40126 s.style('cursor', 'pointer').attr('pointer-events', 'all');
40127 }
40128 s.call(Color.fill, 'rgba(0,0,0,0)');
40129 });
40130
40131 if(gd._context.staticPlot) return;
40132
40133 traceToggle.on('mousedown', function() {
40134 newMouseDownTime = (new Date()).getTime();
40135 if(newMouseDownTime - gd._legendMouseDownTime < doubleClickDelay) {
40136 // in a click train
40137 numClicks += 1;
40138 } else {
40139 // new click train
40140 numClicks = 1;
40141 gd._legendMouseDownTime = newMouseDownTime;
40142 }
40143 });
40144 traceToggle.on('mouseup', function() {
40145 if(gd._dragged || gd._editing) return;
40146 var legend = gd._fullLayout.legend;
40147
40148 if((new Date()).getTime() - gd._legendMouseDownTime > doubleClickDelay) {
40149 numClicks = Math.max(numClicks - 1, 1);
40150 }
40151
40152 clickOrDoubleClick(gd, legend, g, numClicks, d3.event);
40153 });
40154}
40155
40156function textLayout(s, g, gd, legendObj, aTitle) {
40157 if(legendObj._inHover) s.attr('data-notex', true); // do not process MathJax for unified hover
40158 svgTextUtils.convertToTspans(s, gd, function() {
40159 computeTextDimensions(g, gd, legendObj, aTitle);
40160 });
40161}
40162
40163function computeTextDimensions(g, gd, legendObj, aTitle) {
40164 var legendItem = g.data()[0][0];
40165 if(!legendObj._inHover && legendItem && !legendItem.trace.showlegend) {
40166 g.remove();
40167 return;
40168 }
40169
40170 var mathjaxGroup = g.select('g[class*=math-group]');
40171 var mathjaxNode = mathjaxGroup.node();
40172 if(!legendObj) legendObj = gd._fullLayout.legend;
40173 var bw = legendObj.borderwidth;
40174 var font;
40175 if(aTitle === MAIN_TITLE) {
40176 font = legendObj.title.font;
40177 } else if(legendItem.groupTitle) {
40178 font = legendItem.groupTitle.font;
40179 } else {
40180 font = legendObj.font;
40181 }
40182 var lineHeight = font.size * LINE_SPACING;
40183 var height, width;
40184
40185 if(mathjaxNode) {
40186 var mathjaxBB = Drawing.bBox(mathjaxNode);
40187
40188 height = mathjaxBB.height;
40189 width = mathjaxBB.width;
40190
40191 if(aTitle === MAIN_TITLE) {
40192 Drawing.setTranslate(mathjaxGroup, bw, bw + height * 0.75);
40193 } else { // legend item
40194 Drawing.setTranslate(mathjaxGroup, 0, height * 0.25);
40195 }
40196 } else {
40197 var textEl = g.select(aTitle === MAIN_TITLE ?
40198 '.legendtitletext' : '.legendtext'
40199 );
40200 var textLines = svgTextUtils.lineCount(textEl);
40201 var textNode = textEl.node();
40202
40203 height = lineHeight * textLines;
40204 width = textNode ? Drawing.bBox(textNode).width : 0;
40205
40206 // approximation to height offset to center the font
40207 // to avoid getBoundingClientRect
40208 if(aTitle === MAIN_TITLE) {
40209 if(legendObj.title.side === 'left') {
40210 // add extra space between legend title and itmes
40211 width += constants.itemGap * 2;
40212 }
40213
40214 svgTextUtils.positionText(textEl,
40215 bw + constants.titlePad,
40216 bw + lineHeight
40217 );
40218 } else { // legend item
40219 var x = constants.itemGap * 2 + legendObj.itemwidth;
40220 if(legendItem.groupTitle) {
40221 x = constants.itemGap;
40222 width -= legendObj.itemwidth;
40223 }
40224
40225 svgTextUtils.positionText(textEl,
40226 x,
40227 -lineHeight * ((textLines - 1) / 2 - 0.3)
40228 );
40229 }
40230 }
40231
40232 if(aTitle === MAIN_TITLE) {
40233 legendObj._titleWidth = width;
40234 legendObj._titleHeight = height;
40235 } else { // legend item
40236 legendItem.lineHeight = lineHeight;
40237 legendItem.height = Math.max(height, 16) + 3;
40238 legendItem.width = width;
40239 }
40240}
40241
40242function getTitleSize(legendObj) {
40243 var w = 0;
40244 var h = 0;
40245
40246 var side = legendObj.title.side;
40247 if(side) {
40248 if(side.indexOf('left') !== -1) {
40249 w = legendObj._titleWidth;
40250 }
40251 if(side.indexOf('top') !== -1) {
40252 h = legendObj._titleHeight;
40253 }
40254 }
40255
40256 return [w, h];
40257}
40258
40259/*
40260 * Computes in fullLayout.legend:
40261 *
40262 * - _height: legend height including items past scrollbox height
40263 * - _maxHeight: maximum legend height before scrollbox is required
40264 * - _effHeight: legend height w/ or w/o scrollbox
40265 *
40266 * - _width: legend width
40267 * - _maxWidth (for orientation:h only): maximum width before starting new row
40268 */
40269function computeLegendDimensions(gd, groups, traces, legendObj) {
40270 var fullLayout = gd._fullLayout;
40271 if(!legendObj) legendObj = fullLayout.legend;
40272 var gs = fullLayout._size;
40273
40274 var isVertical = helpers.isVertical(legendObj);
40275 var isGrouped = helpers.isGrouped(legendObj);
40276
40277 var bw = legendObj.borderwidth;
40278 var bw2 = 2 * bw;
40279 var itemGap = constants.itemGap;
40280 var textGap = legendObj.itemwidth + itemGap * 2;
40281 var endPad = 2 * (bw + itemGap);
40282
40283 var yanchor = getYanchor(legendObj);
40284 var isBelowPlotArea = legendObj.y < 0 || (legendObj.y === 0 && yanchor === 'top');
40285 var isAbovePlotArea = legendObj.y > 1 || (legendObj.y === 1 && yanchor === 'bottom');
40286
40287 var traceGroupGap = legendObj.tracegroupgap;
40288
40289 // - if below/above plot area, give it the maximum potential margin-push value
40290 // - otherwise, extend the height of the plot area
40291 legendObj._maxHeight = Math.max(
40292 (isBelowPlotArea || isAbovePlotArea) ? fullLayout.height / 2 : gs.h,
40293 30
40294 );
40295
40296 var toggleRectWidth = 0;
40297 legendObj._width = 0;
40298 legendObj._height = 0;
40299 var titleSize = getTitleSize(legendObj);
40300
40301 if(isVertical) {
40302 traces.each(function(d) {
40303 var h = d[0].height;
40304 Drawing.setTranslate(this,
40305 bw + titleSize[0],
40306 bw + titleSize[1] + legendObj._height + h / 2 + itemGap
40307 );
40308 legendObj._height += h;
40309 legendObj._width = Math.max(legendObj._width, d[0].width);
40310 });
40311
40312 toggleRectWidth = textGap + legendObj._width;
40313 legendObj._width += itemGap + textGap + bw2;
40314 legendObj._height += endPad;
40315
40316 if(isGrouped) {
40317 groups.each(function(d, i) {
40318 Drawing.setTranslate(this, 0, i * legendObj.tracegroupgap);
40319 });
40320 legendObj._height += (legendObj._lgroupsLength - 1) * legendObj.tracegroupgap;
40321 }
40322 } else {
40323 var xanchor = getXanchor(legendObj);
40324 var isLeftOfPlotArea = legendObj.x < 0 || (legendObj.x === 0 && xanchor === 'right');
40325 var isRightOfPlotArea = legendObj.x > 1 || (legendObj.x === 1 && xanchor === 'left');
40326 var isBeyondPlotAreaY = isAbovePlotArea || isBelowPlotArea;
40327 var hw = fullLayout.width / 2;
40328
40329 // - if placed within x-margins, extend the width of the plot area
40330 // - else if below/above plot area and anchored in the margin, extend to opposite margin,
40331 // - otherwise give it the maximum potential margin-push value
40332 legendObj._maxWidth = Math.max(
40333 isLeftOfPlotArea ? ((isBeyondPlotAreaY && xanchor === 'left') ? gs.l + gs.w : hw) :
40334 isRightOfPlotArea ? ((isBeyondPlotAreaY && xanchor === 'right') ? gs.r + gs.w : hw) :
40335 gs.w,
40336 2 * textGap);
40337 var maxItemWidth = 0;
40338 var combinedItemWidth = 0;
40339 traces.each(function(d) {
40340 var w = d[0].width + textGap;
40341 maxItemWidth = Math.max(maxItemWidth, w);
40342 combinedItemWidth += w;
40343 });
40344
40345 toggleRectWidth = null;
40346 var maxRowWidth = 0;
40347
40348 if(isGrouped) {
40349 var maxGroupHeightInRow = 0;
40350 var groupOffsetX = 0;
40351 var groupOffsetY = 0;
40352 groups.each(function() {
40353 var maxWidthInGroup = 0;
40354 var offsetY = 0;
40355 d3.select(this).selectAll('g.traces').each(function(d) {
40356 var h = d[0].height;
40357 Drawing.setTranslate(this,
40358 titleSize[0],
40359 titleSize[1] + bw + itemGap + h / 2 + offsetY
40360 );
40361 offsetY += h;
40362 maxWidthInGroup = Math.max(maxWidthInGroup, textGap + d[0].width);
40363 });
40364 maxGroupHeightInRow = Math.max(maxGroupHeightInRow, offsetY);
40365
40366 var next = maxWidthInGroup + itemGap;
40367
40368 if((next + bw + groupOffsetX) > legendObj._maxWidth) {
40369 maxRowWidth = Math.max(maxRowWidth, groupOffsetX);
40370 groupOffsetX = 0;
40371 groupOffsetY += maxGroupHeightInRow + traceGroupGap;
40372 maxGroupHeightInRow = offsetY;
40373 }
40374
40375 Drawing.setTranslate(this, groupOffsetX, groupOffsetY);
40376
40377 groupOffsetX += next;
40378 });
40379
40380 legendObj._width = Math.max(maxRowWidth, groupOffsetX) + bw;
40381 legendObj._height = groupOffsetY + maxGroupHeightInRow + endPad;
40382 } else {
40383 var nTraces = traces.size();
40384 var oneRowLegend = (combinedItemWidth + bw2 + (nTraces - 1) * itemGap) < legendObj._maxWidth;
40385
40386 var maxItemHeightInRow = 0;
40387 var offsetX = 0;
40388 var offsetY = 0;
40389 var rowWidth = 0;
40390 traces.each(function(d) {
40391 var h = d[0].height;
40392 var w = textGap + d[0].width;
40393 var next = (oneRowLegend ? w : maxItemWidth) + itemGap;
40394
40395 if((next + bw + offsetX - itemGap) >= legendObj._maxWidth) {
40396 maxRowWidth = Math.max(maxRowWidth, rowWidth);
40397 offsetX = 0;
40398 offsetY += maxItemHeightInRow;
40399 legendObj._height += maxItemHeightInRow;
40400 maxItemHeightInRow = 0;
40401 }
40402
40403 Drawing.setTranslate(this,
40404 titleSize[0] + bw + offsetX,
40405 titleSize[1] + bw + offsetY + h / 2 + itemGap
40406 );
40407
40408 rowWidth = offsetX + w + itemGap;
40409 offsetX += next;
40410 maxItemHeightInRow = Math.max(maxItemHeightInRow, h);
40411 });
40412
40413 if(oneRowLegend) {
40414 legendObj._width = offsetX + bw2;
40415 legendObj._height = maxItemHeightInRow + endPad;
40416 } else {
40417 legendObj._width = Math.max(maxRowWidth, rowWidth) + bw2;
40418 legendObj._height += maxItemHeightInRow + endPad;
40419 }
40420 }
40421 }
40422
40423 legendObj._width = Math.ceil(
40424 Math.max(
40425 legendObj._width + titleSize[0],
40426 legendObj._titleWidth + 2 * (bw + constants.titlePad)
40427 )
40428 );
40429
40430 legendObj._height = Math.ceil(
40431 Math.max(
40432 legendObj._height + titleSize[1],
40433 legendObj._titleHeight + 2 * (bw + constants.itemGap)
40434 )
40435 );
40436
40437 legendObj._effHeight = Math.min(legendObj._height, legendObj._maxHeight);
40438
40439 var edits = gd._context.edits;
40440 var isEditable = edits.legendText || edits.legendPosition;
40441 traces.each(function(d) {
40442 var traceToggle = d3.select(this).select('.legendtoggle');
40443 var h = d[0].height;
40444 var w = isEditable ? textGap : (toggleRectWidth || (textGap + d[0].width));
40445 if(!isVertical) w += itemGap / 2;
40446 Drawing.setRect(traceToggle, 0, -h / 2, w, h);
40447 });
40448}
40449
40450function expandMargin(gd) {
40451 var fullLayout = gd._fullLayout;
40452 var legendObj = fullLayout.legend;
40453 var xanchor = getXanchor(legendObj);
40454 var yanchor = getYanchor(legendObj);
40455
40456 return Plots.autoMargin(gd, 'legend', {
40457 x: legendObj.x,
40458 y: legendObj.y,
40459 l: legendObj._width * (FROM_TL[xanchor]),
40460 r: legendObj._width * (FROM_BR[xanchor]),
40461 b: legendObj._effHeight * (FROM_BR[yanchor]),
40462 t: legendObj._effHeight * (FROM_TL[yanchor])
40463 });
40464}
40465
40466function getXanchor(legendObj) {
40467 return Lib.isRightAnchor(legendObj) ? 'right' :
40468 Lib.isCenterAnchor(legendObj) ? 'center' :
40469 'left';
40470}
40471
40472function getYanchor(legendObj) {
40473 return Lib.isBottomAnchor(legendObj) ? 'bottom' :
40474 Lib.isMiddleAnchor(legendObj) ? 'middle' :
40475 'top';
40476}
40477
40478},{"../../constants/alignment":262,"../../lib":287,"../../lib/events":280,"../../lib/svg_text_utils":310,"../../plots/plots":369,"../../registry":376,"../color":157,"../dragelement":176,"../drawing":179,"./constants":208,"./get_legend_data":211,"./handle_click":212,"./helpers":213,"./style":215,"@plotly/d3":20}],211:[function(_dereq_,module,exports){
40479'use strict';
40480
40481var Registry = _dereq_('../../registry');
40482var helpers = _dereq_('./helpers');
40483
40484module.exports = function getLegendData(calcdata, opts) {
40485 var inHover = opts._inHover;
40486 var grouped = helpers.isGrouped(opts);
40487 var reversed = helpers.isReversed(opts);
40488
40489 var lgroupToTraces = {};
40490 var lgroups = [];
40491 var hasOneNonBlankGroup = false;
40492 var slicesShown = {};
40493 var lgroupi = 0;
40494 var maxNameLength = 0;
40495 var i, j;
40496
40497 function addOneItem(legendGroup, legendItem) {
40498 // each '' legend group is treated as a separate group
40499 if(legendGroup === '' || !helpers.isGrouped(opts)) {
40500 // TODO: check this against fullData legendgroups?
40501 var uniqueGroup = '~~i' + lgroupi;
40502 lgroups.push(uniqueGroup);
40503 lgroupToTraces[uniqueGroup] = [legendItem];
40504 lgroupi++;
40505 } else if(lgroups.indexOf(legendGroup) === -1) {
40506 lgroups.push(legendGroup);
40507 hasOneNonBlankGroup = true;
40508 lgroupToTraces[legendGroup] = [legendItem];
40509 } else {
40510 lgroupToTraces[legendGroup].push(legendItem);
40511 }
40512 }
40513
40514 // build an { legendgroup: [cd0, cd0], ... } object
40515 for(i = 0; i < calcdata.length; i++) {
40516 var cd = calcdata[i];
40517 var cd0 = cd[0];
40518 var trace = cd0.trace;
40519 var lgroup = trace.legendgroup;
40520
40521 if(!inHover && (!trace.visible || !trace.showlegend)) continue;
40522
40523 if(Registry.traceIs(trace, 'pie-like')) {
40524 if(!slicesShown[lgroup]) slicesShown[lgroup] = {};
40525
40526 for(j = 0; j < cd.length; j++) {
40527 var labelj = cd[j].label;
40528
40529 if(!slicesShown[lgroup][labelj]) {
40530 addOneItem(lgroup, {
40531 label: labelj,
40532 color: cd[j].color,
40533 i: cd[j].i,
40534 trace: trace,
40535 pts: cd[j].pts
40536 });
40537
40538 slicesShown[lgroup][labelj] = true;
40539 maxNameLength = Math.max(maxNameLength, (labelj || '').length);
40540 }
40541 }
40542 } else {
40543 addOneItem(lgroup, cd0);
40544 maxNameLength = Math.max(maxNameLength, (trace.name || '').length);
40545 }
40546 }
40547
40548 // won't draw a legend in this case
40549 if(!lgroups.length) return [];
40550
40551 // collapse all groups into one if all groups are blank
40552 var shouldCollapse = !hasOneNonBlankGroup || !grouped;
40553
40554 var legendData = [];
40555 for(i = 0; i < lgroups.length; i++) {
40556 var t = lgroupToTraces[lgroups[i]];
40557 if(shouldCollapse) {
40558 legendData.push(t[0]);
40559 } else {
40560 legendData.push(t);
40561 }
40562 }
40563 if(shouldCollapse) legendData = [legendData];
40564
40565 for(i = 0; i < legendData.length; i++) {
40566 // find minimum rank within group
40567 var groupMinRank = Infinity;
40568 for(j = 0; j < legendData[i].length; j++) {
40569 var rank = legendData[i][j].trace.legendrank;
40570 if(groupMinRank > rank) groupMinRank = rank;
40571 }
40572
40573 // record on first group element
40574 legendData[i][0]._groupMinRank = groupMinRank;
40575 legendData[i][0]._preGroupSort = i;
40576 }
40577
40578 var orderFn1 = function(a, b) {
40579 return (
40580 (a[0]._groupMinRank - b[0]._groupMinRank) ||
40581 (a[0]._preGroupSort - b[0]._preGroupSort) // fallback for old Chrome < 70 https://bugs.chromium.org/p/v8/issues/detail?id=90
40582 );
40583 };
40584
40585 var orderFn2 = function(a, b) {
40586 return (
40587 (a.trace.legendrank - b.trace.legendrank) ||
40588 (a._preSort - b._preSort) // fallback for old Chrome < 70 https://bugs.chromium.org/p/v8/issues/detail?id=90
40589 );
40590 };
40591
40592 // sort considering minimum group legendrank
40593 legendData.forEach(function(a, k) { a[0]._preGroupSort = k; });
40594 legendData.sort(orderFn1);
40595 for(i = 0; i < legendData.length; i++) {
40596 // sort considering trace.legendrank and legend.traceorder
40597 legendData[i].forEach(function(a, k) { a._preSort = k; });
40598 legendData[i].sort(orderFn2);
40599
40600 var firstItemTrace = legendData[i][0].trace;
40601
40602 var groupTitle = null;
40603 // get group title text
40604 for(j = 0; j < legendData[i].length; j++) {
40605 var gt = legendData[i][j].trace.legendgrouptitle;
40606 if(gt && gt.text) {
40607 groupTitle = gt;
40608 if(inHover) gt.font = opts._groupTitleFont;
40609 break;
40610 }
40611 }
40612
40613 // reverse order
40614 if(reversed) legendData[i].reverse();
40615
40616 if(groupTitle) {
40617 var hasPieLike = false;
40618 for(j = 0; j < legendData[i].length; j++) {
40619 if(Registry.traceIs(legendData[i][j].trace, 'pie-like')) {
40620 hasPieLike = true;
40621 break;
40622 }
40623 }
40624
40625 // set group title text
40626 legendData[i].unshift({
40627 i: -1,
40628 groupTitle: groupTitle,
40629 noClick: hasPieLike,
40630 trace: {
40631 showlegend: firstItemTrace.showlegend,
40632 legendgroup: firstItemTrace.legendgroup,
40633 visible: opts.groupclick === 'toggleitem' ? true : firstItemTrace.visible
40634 }
40635 });
40636 }
40637
40638 // rearrange lgroupToTraces into a d3-friendly array of arrays
40639 for(j = 0; j < legendData[i].length; j++) {
40640 legendData[i][j] = [
40641 legendData[i][j]
40642 ];
40643 }
40644 }
40645
40646 // number of legend groups - needed in legend/draw.js
40647 opts._lgroupsLength = legendData.length;
40648 // maximum name/label length - needed in legend/draw.js
40649 opts._maxNameLength = maxNameLength;
40650
40651 return legendData;
40652};
40653
40654},{"../../registry":376,"./helpers":213}],212:[function(_dereq_,module,exports){
40655'use strict';
40656
40657var Lib = _dereq_('../../lib');
40658var Registry = _dereq_('../../registry');
40659
40660var SHOWISOLATETIP = true;
40661
40662module.exports = function handleClick(g, gd, numClicks) {
40663 var fullLayout = gd._fullLayout;
40664
40665 if(gd._dragged || gd._editing) return;
40666
40667 var itemClick = fullLayout.legend.itemclick;
40668 var itemDoubleClick = fullLayout.legend.itemdoubleclick;
40669 var groupClick = fullLayout.legend.groupclick;
40670
40671 if(numClicks === 1 && itemClick === 'toggle' && itemDoubleClick === 'toggleothers' &&
40672 SHOWISOLATETIP && gd.data && gd._context.showTips
40673 ) {
40674 Lib.notifier(Lib._(gd, 'Double-click on legend to isolate one trace'), 'long');
40675 SHOWISOLATETIP = false;
40676 } else {
40677 SHOWISOLATETIP = false;
40678 }
40679
40680 var mode;
40681 if(numClicks === 1) mode = itemClick;
40682 else if(numClicks === 2) mode = itemDoubleClick;
40683 if(!mode) return;
40684
40685 var toggleGroup = groupClick === 'togglegroup';
40686
40687 var hiddenSlices = fullLayout.hiddenlabels ?
40688 fullLayout.hiddenlabels.slice() :
40689 [];
40690
40691 var legendItem = g.data()[0][0];
40692 if(legendItem.groupTitle && legendItem.noClick) return;
40693
40694 var fullData = gd._fullData;
40695 var fullTrace = legendItem.trace;
40696 var legendgroup = fullTrace.legendgroup;
40697
40698 var i, j, kcont, key, keys, val;
40699 var attrUpdate = {};
40700 var attrIndices = [];
40701 var carrs = [];
40702 var carrIdx = [];
40703
40704 function insertUpdate(traceIndex, key, value) {
40705 var attrIndex = attrIndices.indexOf(traceIndex);
40706 var valueArray = attrUpdate[key];
40707 if(!valueArray) {
40708 valueArray = attrUpdate[key] = [];
40709 }
40710
40711 if(attrIndices.indexOf(traceIndex) === -1) {
40712 attrIndices.push(traceIndex);
40713 attrIndex = attrIndices.length - 1;
40714 }
40715
40716 valueArray[attrIndex] = value;
40717
40718 return attrIndex;
40719 }
40720
40721 function setVisibility(fullTrace, visibility) {
40722 if(legendItem.groupTitle && !toggleGroup) return;
40723
40724 var fullInput = fullTrace._fullInput;
40725 if(Registry.hasTransform(fullInput, 'groupby')) {
40726 var kcont = carrs[fullInput.index];
40727 if(!kcont) {
40728 var groupbyIndices = Registry.getTransformIndices(fullInput, 'groupby');
40729 var lastGroupbyIndex = groupbyIndices[groupbyIndices.length - 1];
40730 kcont = Lib.keyedContainer(fullInput, 'transforms[' + lastGroupbyIndex + '].styles', 'target', 'value.visible');
40731 carrs[fullInput.index] = kcont;
40732 }
40733
40734 var curState = kcont.get(fullTrace._group);
40735
40736 // If not specified, assume visible. This happens if there are other style
40737 // properties set for a group but not the visibility. There are many similar
40738 // ways to do this (e.g. why not just `curState = fullTrace.visible`??? The
40739 // answer is: because it breaks other things like groupby trace names in
40740 // subtle ways.)
40741 if(curState === undefined) {
40742 curState = true;
40743 }
40744
40745 if(curState !== false) {
40746 // true -> legendonly. All others toggle to true:
40747 kcont.set(fullTrace._group, visibility);
40748 }
40749 carrIdx[fullInput.index] = insertUpdate(fullInput.index, 'visible', fullInput.visible === false ? false : true);
40750 } else {
40751 // false -> false (not possible since will not be visible in legend)
40752 // true -> legendonly
40753 // legendonly -> true
40754 var nextVisibility = fullInput.visible === false ? false : visibility;
40755
40756 insertUpdate(fullInput.index, 'visible', nextVisibility);
40757 }
40758 }
40759
40760 if(Registry.traceIs(fullTrace, 'pie-like')) {
40761 var thisLabel = legendItem.label;
40762 var thisLabelIndex = hiddenSlices.indexOf(thisLabel);
40763
40764 if(mode === 'toggle') {
40765 if(thisLabelIndex === -1) hiddenSlices.push(thisLabel);
40766 else hiddenSlices.splice(thisLabelIndex, 1);
40767 } else if(mode === 'toggleothers') {
40768 hiddenSlices = [];
40769 gd.calcdata[0].forEach(function(d) {
40770 if(thisLabel !== d.label) {
40771 hiddenSlices.push(d.label);
40772 }
40773 });
40774 if(gd._fullLayout.hiddenlabels && gd._fullLayout.hiddenlabels.length === hiddenSlices.length && thisLabelIndex === -1) {
40775 hiddenSlices = [];
40776 }
40777 }
40778
40779 Registry.call('_guiRelayout', gd, 'hiddenlabels', hiddenSlices);
40780 } else {
40781 var hasLegendgroup = legendgroup && legendgroup.length;
40782 var traceIndicesInGroup = [];
40783 var tracei;
40784 if(hasLegendgroup) {
40785 for(i = 0; i < fullData.length; i++) {
40786 tracei = fullData[i];
40787 if(!tracei.visible) continue;
40788 if(tracei.legendgroup === legendgroup) {
40789 traceIndicesInGroup.push(i);
40790 }
40791 }
40792 }
40793
40794 if(mode === 'toggle') {
40795 var nextVisibility;
40796
40797 switch(fullTrace.visible) {
40798 case true:
40799 nextVisibility = 'legendonly';
40800 break;
40801 case false:
40802 nextVisibility = false;
40803 break;
40804 case 'legendonly':
40805 nextVisibility = true;
40806 break;
40807 }
40808
40809 if(hasLegendgroup) {
40810 if(toggleGroup) {
40811 for(i = 0; i < fullData.length; i++) {
40812 if(fullData[i].visible !== false && fullData[i].legendgroup === legendgroup) {
40813 setVisibility(fullData[i], nextVisibility);
40814 }
40815 }
40816 } else {
40817 setVisibility(fullTrace, nextVisibility);
40818 }
40819 } else {
40820 setVisibility(fullTrace, nextVisibility);
40821 }
40822 } else if(mode === 'toggleothers') {
40823 // Compute the clicked index. expandedIndex does what we want for expanded traces
40824 // but also culls hidden traces. That means we have some work to do.
40825 var isClicked, isInGroup, notInLegend, otherState;
40826 var isIsolated = true;
40827 for(i = 0; i < fullData.length; i++) {
40828 isClicked = fullData[i] === fullTrace;
40829 notInLegend = fullData[i].showlegend !== true;
40830 if(isClicked || notInLegend) continue;
40831
40832 isInGroup = (hasLegendgroup && fullData[i].legendgroup === legendgroup);
40833
40834 if(!isInGroup && fullData[i].visible === true && !Registry.traceIs(fullData[i], 'notLegendIsolatable')) {
40835 isIsolated = false;
40836 break;
40837 }
40838 }
40839
40840 for(i = 0; i < fullData.length; i++) {
40841 // False is sticky; we don't change it.
40842 if(fullData[i].visible === false) continue;
40843
40844 if(Registry.traceIs(fullData[i], 'notLegendIsolatable')) {
40845 continue;
40846 }
40847
40848 switch(fullTrace.visible) {
40849 case 'legendonly':
40850 setVisibility(fullData[i], true);
40851 break;
40852 case true:
40853 otherState = isIsolated ? true : 'legendonly';
40854 isClicked = fullData[i] === fullTrace;
40855 // N.B. consider traces that have a set legendgroup as toggleable
40856 notInLegend = (fullData[i].showlegend !== true && !fullData[i].legendgroup);
40857 isInGroup = isClicked || (hasLegendgroup && fullData[i].legendgroup === legendgroup);
40858 setVisibility(fullData[i], (isInGroup || notInLegend) ? true : otherState);
40859 break;
40860 }
40861 }
40862 }
40863
40864 for(i = 0; i < carrs.length; i++) {
40865 kcont = carrs[i];
40866 if(!kcont) continue;
40867 var update = kcont.constructUpdate();
40868
40869 var updateKeys = Object.keys(update);
40870 for(j = 0; j < updateKeys.length; j++) {
40871 key = updateKeys[j];
40872 val = attrUpdate[key] = attrUpdate[key] || [];
40873 val[carrIdx[i]] = update[key];
40874 }
40875 }
40876
40877 // The length of the value arrays should be equal and any unspecified
40878 // values should be explicitly undefined for them to get properly culled
40879 // as updates and not accidentally reset to the default value. This fills
40880 // out sparse arrays with the required number of undefined values:
40881 keys = Object.keys(attrUpdate);
40882 for(i = 0; i < keys.length; i++) {
40883 key = keys[i];
40884 for(j = 0; j < attrIndices.length; j++) {
40885 // Use hasOwnProperty to protect against falsy values:
40886 if(!attrUpdate[key].hasOwnProperty(j)) {
40887 attrUpdate[key][j] = undefined;
40888 }
40889 }
40890 }
40891
40892 Registry.call('_guiRestyle', gd, attrUpdate, attrIndices);
40893 }
40894};
40895
40896},{"../../lib":287,"../../registry":376}],213:[function(_dereq_,module,exports){
40897'use strict';
40898
40899exports.isGrouped = function isGrouped(legendLayout) {
40900 return (legendLayout.traceorder || '').indexOf('grouped') !== -1;
40901};
40902
40903exports.isVertical = function isVertical(legendLayout) {
40904 return legendLayout.orientation !== 'h';
40905};
40906
40907exports.isReversed = function isReversed(legendLayout) {
40908 return (legendLayout.traceorder || '').indexOf('reversed') !== -1;
40909};
40910
40911},{}],214:[function(_dereq_,module,exports){
40912'use strict';
40913
40914
40915module.exports = {
40916 moduleType: 'component',
40917 name: 'legend',
40918
40919 layoutAttributes: _dereq_('./attributes'),
40920 supplyLayoutDefaults: _dereq_('./defaults'),
40921
40922 draw: _dereq_('./draw'),
40923 style: _dereq_('./style')
40924};
40925
40926},{"./attributes":207,"./defaults":209,"./draw":210,"./style":215}],215:[function(_dereq_,module,exports){
40927'use strict';
40928
40929var d3 = _dereq_('@plotly/d3');
40930
40931var Registry = _dereq_('../../registry');
40932var Lib = _dereq_('../../lib');
40933var strTranslate = Lib.strTranslate;
40934var Drawing = _dereq_('../drawing');
40935var Color = _dereq_('../color');
40936var extractOpts = _dereq_('../colorscale/helpers').extractOpts;
40937
40938var subTypes = _dereq_('../../traces/scatter/subtypes');
40939var stylePie = _dereq_('../../traces/pie/style_one');
40940var pieCastOption = _dereq_('../../traces/pie/helpers').castOption;
40941
40942var constants = _dereq_('./constants');
40943
40944var CST_MARKER_SIZE = 12;
40945var CST_LINE_WIDTH = 5;
40946var CST_MARKER_LINE_WIDTH = 2;
40947var MAX_LINE_WIDTH = 10;
40948var MAX_MARKER_LINE_WIDTH = 5;
40949
40950module.exports = function style(s, gd, legend) {
40951 var fullLayout = gd._fullLayout;
40952 if(!legend) legend = fullLayout.legend;
40953 var constantItemSizing = legend.itemsizing === 'constant';
40954 var itemWidth = legend.itemwidth;
40955 var centerPos = (itemWidth + constants.itemGap * 2) / 2;
40956 var centerTransform = strTranslate(centerPos, 0);
40957
40958 var boundLineWidth = function(mlw, cont, max, cst) {
40959 var v;
40960 if(mlw + 1) {
40961 v = mlw;
40962 } else if(cont && cont.width > 0) {
40963 v = cont.width;
40964 } else {
40965 return 0;
40966 }
40967 return constantItemSizing ? cst : Math.min(v, max);
40968 };
40969
40970 s.each(function(d) {
40971 var traceGroup = d3.select(this);
40972
40973 var layers = Lib.ensureSingle(traceGroup, 'g', 'layers');
40974 layers.style('opacity', d[0].trace.opacity);
40975
40976 var valign = legend.valign;
40977 var lineHeight = d[0].lineHeight;
40978 var height = d[0].height;
40979
40980 if(valign === 'middle' || !lineHeight || !height) {
40981 layers.attr('transform', null);
40982 } else {
40983 var factor = {top: 1, bottom: -1}[valign];
40984 var markerOffsetY = factor * (0.5 * (lineHeight - height + 3));
40985 layers.attr('transform', strTranslate(0, markerOffsetY));
40986 }
40987
40988 var fill = layers
40989 .selectAll('g.legendfill')
40990 .data([d]);
40991 fill.enter().append('g')
40992 .classed('legendfill', true);
40993
40994 var line = layers
40995 .selectAll('g.legendlines')
40996 .data([d]);
40997 line.enter().append('g')
40998 .classed('legendlines', true);
40999
41000 var symbol = layers
41001 .selectAll('g.legendsymbols')
41002 .data([d]);
41003 symbol.enter().append('g')
41004 .classed('legendsymbols', true);
41005
41006 symbol.selectAll('g.legendpoints')
41007 .data([d])
41008 .enter().append('g')
41009 .classed('legendpoints', true);
41010 })
41011 .each(styleSpatial)
41012 .each(styleWaterfalls)
41013 .each(styleFunnels)
41014 .each(styleBars)
41015 .each(styleBoxes)
41016 .each(styleFunnelareas)
41017 .each(stylePies)
41018 .each(styleLines)
41019 .each(stylePoints)
41020 .each(styleCandles)
41021 .each(styleOHLC);
41022
41023 function styleLines(d) {
41024 var styleGuide = getStyleGuide(d);
41025 var showFill = styleGuide.showFill;
41026 var showLine = styleGuide.showLine;
41027 var showGradientLine = styleGuide.showGradientLine;
41028 var showGradientFill = styleGuide.showGradientFill;
41029 var anyFill = styleGuide.anyFill;
41030 var anyLine = styleGuide.anyLine;
41031
41032 var d0 = d[0];
41033 var trace = d0.trace;
41034 var dMod, tMod;
41035
41036 var cOpts = extractOpts(trace);
41037 var colorscale = cOpts.colorscale;
41038 var reversescale = cOpts.reversescale;
41039
41040 var fillGradient = function(s) {
41041 if(s.size()) {
41042 var gradientID = 'legendfill-' + trace.uid;
41043 Drawing.gradient(s, gd, gradientID,
41044 getGradientDirection(reversescale),
41045 colorscale, 'fill');
41046 }
41047 };
41048
41049 var lineGradient = function(s) {
41050 if(s.size()) {
41051 var gradientID = 'legendline-' + trace.uid;
41052 Drawing.lineGroupStyle(s);
41053 Drawing.gradient(s, gd, gradientID,
41054 getGradientDirection(reversescale),
41055 colorscale, 'stroke');
41056 }
41057 };
41058
41059 // with fill and no markers or text, move the line and fill up a bit
41060 // so it's more centered
41061
41062 var pathStart = (subTypes.hasMarkers(trace) || !anyFill) ? 'M5,0' :
41063 // with a line leave it slightly below center, to leave room for the
41064 // line thickness and because the line is usually more prominent
41065 anyLine ? 'M5,-2' : 'M5,-3';
41066
41067 var this3 = d3.select(this);
41068
41069 var fill = this3.select('.legendfill').selectAll('path')
41070 .data(showFill || showGradientFill ? [d] : []);
41071 fill.enter().append('path').classed('js-fill', true);
41072 fill.exit().remove();
41073 fill.attr('d', pathStart + 'h' + itemWidth + 'v6h-' + itemWidth + 'z')
41074 .call(showFill ? Drawing.fillGroupStyle : fillGradient);
41075
41076 if(showLine || showGradientLine) {
41077 var lw = boundLineWidth(undefined, trace.line, MAX_LINE_WIDTH, CST_LINE_WIDTH);
41078 tMod = Lib.minExtend(trace, {line: {width: lw}});
41079 dMod = [Lib.minExtend(d0, {trace: tMod})];
41080 }
41081
41082 var line = this3.select('.legendlines').selectAll('path')
41083 .data(showLine || showGradientLine ? [dMod] : []);
41084 line.enter().append('path').classed('js-line', true);
41085 line.exit().remove();
41086
41087 // this is ugly... but you can't apply a gradient to a perfectly
41088 // horizontal or vertical line. Presumably because then
41089 // the system doesn't know how to scale vertical variation, even
41090 // though there *is* no vertical variation in this case.
41091 // so add an invisibly small angle to the line
41092 // This issue (and workaround) exist across (Mac) Chrome, FF, and Safari
41093 line.attr('d', pathStart + (showGradientLine ? 'l' + itemWidth + ',0.0001' : 'h' + itemWidth))
41094 .call(showLine ? Drawing.lineGroupStyle : lineGradient);
41095 }
41096
41097 function stylePoints(d) {
41098 var styleGuide = getStyleGuide(d);
41099 var anyFill = styleGuide.anyFill;
41100 var anyLine = styleGuide.anyLine;
41101 var showLine = styleGuide.showLine;
41102 var showMarker = styleGuide.showMarker;
41103
41104 var d0 = d[0];
41105 var trace = d0.trace;
41106 var showText = !showMarker && !anyLine && !anyFill && subTypes.hasText(trace);
41107 var dMod, tMod;
41108
41109 // 'scatter3d' don't use gd.calcdata,
41110 // use d0.trace to infer arrayOk attributes
41111
41112 function boundVal(attrIn, arrayToValFn, bounds, cst) {
41113 var valIn = Lib.nestedProperty(trace, attrIn).get();
41114 var valToBound = (Lib.isArrayOrTypedArray(valIn) && arrayToValFn) ?
41115 arrayToValFn(valIn) :
41116 valIn;
41117
41118 if(constantItemSizing && valToBound && cst !== undefined) {
41119 valToBound = cst;
41120 }
41121
41122 if(bounds) {
41123 if(valToBound < bounds[0]) return bounds[0];
41124 else if(valToBound > bounds[1]) return bounds[1];
41125 }
41126 return valToBound;
41127 }
41128
41129 function pickFirst(array) {
41130 if(d0._distinct && d0.index && array[d0.index]) return array[d0.index];
41131 return array[0];
41132 }
41133
41134 // constrain text, markers, etc so they'll fit on the legend
41135 if(showMarker || showText || showLine) {
41136 var dEdit = {};
41137 var tEdit = {};
41138
41139 if(showMarker) {
41140 dEdit.mc = boundVal('marker.color', pickFirst);
41141 dEdit.mx = boundVal('marker.symbol', pickFirst);
41142 dEdit.mo = boundVal('marker.opacity', Lib.mean, [0.2, 1]);
41143 dEdit.mlc = boundVal('marker.line.color', pickFirst);
41144 dEdit.mlw = boundVal('marker.line.width', Lib.mean, [0, 5], CST_MARKER_LINE_WIDTH);
41145 tEdit.marker = {
41146 sizeref: 1,
41147 sizemin: 1,
41148 sizemode: 'diameter'
41149 };
41150
41151 var ms = boundVal('marker.size', Lib.mean, [2, 16], CST_MARKER_SIZE);
41152 dEdit.ms = ms;
41153 tEdit.marker.size = ms;
41154 }
41155
41156 if(showLine) {
41157 tEdit.line = {
41158 width: boundVal('line.width', pickFirst, [0, 10], CST_LINE_WIDTH)
41159 };
41160 }
41161
41162 if(showText) {
41163 dEdit.tx = 'Aa';
41164 dEdit.tp = boundVal('textposition', pickFirst);
41165 dEdit.ts = 10;
41166 dEdit.tc = boundVal('textfont.color', pickFirst);
41167 dEdit.tf = boundVal('textfont.family', pickFirst);
41168 }
41169
41170 dMod = [Lib.minExtend(d0, dEdit)];
41171 tMod = Lib.minExtend(trace, tEdit);
41172
41173 // always show legend items in base state
41174 tMod.selectedpoints = null;
41175
41176 // never show texttemplate
41177 tMod.texttemplate = null;
41178 }
41179
41180 var ptgroup = d3.select(this).select('g.legendpoints');
41181
41182 var pts = ptgroup.selectAll('path.scatterpts')
41183 .data(showMarker ? dMod : []);
41184 // make sure marker is on the bottom, in case it enters after text
41185 pts.enter().insert('path', ':first-child')
41186 .classed('scatterpts', true)
41187 .attr('transform', centerTransform);
41188 pts.exit().remove();
41189 pts.call(Drawing.pointStyle, tMod, gd);
41190
41191 // 'mrc' is set in pointStyle and used in textPointStyle:
41192 // constrain it here
41193 if(showMarker) dMod[0].mrc = 3;
41194
41195 var txt = ptgroup.selectAll('g.pointtext')
41196 .data(showText ? dMod : []);
41197 txt.enter()
41198 .append('g').classed('pointtext', true)
41199 .append('text').attr('transform', centerTransform);
41200 txt.exit().remove();
41201 txt.selectAll('text').call(Drawing.textPointStyle, tMod, gd);
41202 }
41203
41204 function styleWaterfalls(d) {
41205 var trace = d[0].trace;
41206 var isWaterfall = trace.type === 'waterfall';
41207
41208 if(d[0]._distinct && isWaterfall) {
41209 var cont = d[0].trace[d[0].dir].marker;
41210 d[0].mc = cont.color;
41211 d[0].mlw = cont.line.width;
41212 d[0].mlc = cont.line.color;
41213 return styleBarLike(d, this, 'waterfall');
41214 }
41215
41216 var ptsData = [];
41217 if(trace.visible && isWaterfall) {
41218 ptsData = d[0].hasTotals ?
41219 [['increasing', 'M-6,-6V6H0Z'], ['totals', 'M6,6H0L-6,-6H-0Z'], ['decreasing', 'M6,6V-6H0Z']] :
41220 [['increasing', 'M-6,-6V6H6Z'], ['decreasing', 'M6,6V-6H-6Z']];
41221 }
41222
41223 var pts = d3.select(this).select('g.legendpoints')
41224 .selectAll('path.legendwaterfall')
41225 .data(ptsData);
41226 pts.enter().append('path').classed('legendwaterfall', true)
41227 .attr('transform', centerTransform)
41228 .style('stroke-miterlimit', 1);
41229 pts.exit().remove();
41230
41231 pts.each(function(dd) {
41232 var pt = d3.select(this);
41233 var cont = trace[dd[0]].marker;
41234 var lw = boundLineWidth(undefined, cont.line, MAX_MARKER_LINE_WIDTH, CST_MARKER_LINE_WIDTH);
41235
41236 pt.attr('d', dd[1])
41237 .style('stroke-width', lw + 'px')
41238 .call(Color.fill, cont.color);
41239
41240 if(lw) {
41241 pt.call(Color.stroke, cont.line.color);
41242 }
41243 });
41244 }
41245
41246 function styleBars(d) {
41247 styleBarLike(d, this);
41248 }
41249
41250 function styleFunnels(d) {
41251 styleBarLike(d, this, 'funnel');
41252 }
41253
41254 function styleBarLike(d, lThis, desiredType) {
41255 var trace = d[0].trace;
41256 var marker = trace.marker || {};
41257 var markerLine = marker.line || {};
41258
41259 var isVisible = (!desiredType) ? Registry.traceIs(trace, 'bar') :
41260 (trace.visible && trace.type === desiredType);
41261
41262 var barpath = d3.select(lThis).select('g.legendpoints')
41263 .selectAll('path.legend' + desiredType)
41264 .data(isVisible ? [d] : []);
41265 barpath.enter().append('path').classed('legend' + desiredType, true)
41266 .attr('d', 'M6,6H-6V-6H6Z')
41267 .attr('transform', centerTransform);
41268 barpath.exit().remove();
41269
41270 barpath.each(function(d) {
41271 var p = d3.select(this);
41272 var d0 = d[0];
41273 var w = boundLineWidth(d0.mlw, marker.line, MAX_MARKER_LINE_WIDTH, CST_MARKER_LINE_WIDTH);
41274
41275 p.style('stroke-width', w + 'px');
41276
41277 var mcc = d0.mcc;
41278 if(!legend._inHover && 'mc' in d0) {
41279 // not in unified hover but
41280 // for legend use the color in the middle of scale
41281 var cOpts = extractOpts(marker);
41282 var mid = cOpts.mid;
41283 if(mid === undefined) mid = (cOpts.max + cOpts.min) / 2;
41284 mcc = Drawing.tryColorscale(marker, '')(mid);
41285 }
41286 var fillColor = mcc || d0.mc || marker.color;
41287
41288 var markerPattern = marker.pattern;
41289 var patternShape = markerPattern && Drawing.getPatternAttr(markerPattern.shape, 0, '');
41290
41291 if(patternShape) {
41292 var patternBGColor = Drawing.getPatternAttr(markerPattern.bgcolor, 0, null);
41293 var patternFGColor = Drawing.getPatternAttr(markerPattern.fgcolor, 0, null);
41294 var patternFGOpacity = markerPattern.fgopacity;
41295 var patternSize = dimAttr(markerPattern.size, 8, 10);
41296 var patternSolidity = dimAttr(markerPattern.solidity, 0.5, 1);
41297 var patternID = 'legend-' + trace.uid;
41298 p.call(
41299 Drawing.pattern, 'legend', gd, patternID,
41300 patternShape, patternSize, patternSolidity,
41301 mcc, markerPattern.fillmode,
41302 patternBGColor, patternFGColor, patternFGOpacity
41303 );
41304 } else {
41305 p.call(Color.fill, fillColor);
41306 }
41307
41308 if(w) Color.stroke(p, d0.mlc || markerLine.color);
41309 });
41310 }
41311
41312 function styleBoxes(d) {
41313 var trace = d[0].trace;
41314
41315 var pts = d3.select(this).select('g.legendpoints')
41316 .selectAll('path.legendbox')
41317 .data(trace.visible && Registry.traceIs(trace, 'box-violin') ? [d] : []);
41318 pts.enter().append('path').classed('legendbox', true)
41319 // if we want the median bar, prepend M6,0H-6
41320 .attr('d', 'M6,6H-6V-6H6Z')
41321 .attr('transform', centerTransform);
41322 pts.exit().remove();
41323
41324 pts.each(function() {
41325 var p = d3.select(this);
41326
41327 if((trace.boxpoints === 'all' || trace.points === 'all') &&
41328 Color.opacity(trace.fillcolor) === 0 && Color.opacity((trace.line || {}).color) === 0
41329 ) {
41330 var tMod = Lib.minExtend(trace, {
41331 marker: {
41332 size: constantItemSizing ? CST_MARKER_SIZE : Lib.constrain(trace.marker.size, 2, 16),
41333 sizeref: 1,
41334 sizemin: 1,
41335 sizemode: 'diameter'
41336 }
41337 });
41338 pts.call(Drawing.pointStyle, tMod, gd);
41339 } else {
41340 var w = boundLineWidth(undefined, trace.line, MAX_MARKER_LINE_WIDTH, CST_MARKER_LINE_WIDTH);
41341
41342 p.style('stroke-width', w + 'px')
41343 .call(Color.fill, trace.fillcolor);
41344
41345 if(w) Color.stroke(p, trace.line.color);
41346 }
41347 });
41348 }
41349
41350 function styleCandles(d) {
41351 var trace = d[0].trace;
41352
41353 var pts = d3.select(this).select('g.legendpoints')
41354 .selectAll('path.legendcandle')
41355 .data(trace.visible && trace.type === 'candlestick' ? [d, d] : []);
41356 pts.enter().append('path').classed('legendcandle', true)
41357 .attr('d', function(_, i) {
41358 if(i) return 'M-15,0H-8M-8,6V-6H8Z'; // increasing
41359 return 'M15,0H8M8,-6V6H-8Z'; // decreasing
41360 })
41361 .attr('transform', centerTransform)
41362 .style('stroke-miterlimit', 1);
41363 pts.exit().remove();
41364
41365 pts.each(function(_, i) {
41366 var p = d3.select(this);
41367 var cont = trace[i ? 'increasing' : 'decreasing'];
41368 var w = boundLineWidth(undefined, cont.line, MAX_MARKER_LINE_WIDTH, CST_MARKER_LINE_WIDTH);
41369
41370 p.style('stroke-width', w + 'px')
41371 .call(Color.fill, cont.fillcolor);
41372
41373 if(w) Color.stroke(p, cont.line.color);
41374 });
41375 }
41376
41377 function styleOHLC(d) {
41378 var trace = d[0].trace;
41379
41380 var pts = d3.select(this).select('g.legendpoints')
41381 .selectAll('path.legendohlc')
41382 .data(trace.visible && trace.type === 'ohlc' ? [d, d] : []);
41383 pts.enter().append('path').classed('legendohlc', true)
41384 .attr('d', function(_, i) {
41385 if(i) return 'M-15,0H0M-8,-6V0'; // increasing
41386 return 'M15,0H0M8,6V0'; // decreasing
41387 })
41388 .attr('transform', centerTransform)
41389 .style('stroke-miterlimit', 1);
41390 pts.exit().remove();
41391
41392 pts.each(function(_, i) {
41393 var p = d3.select(this);
41394 var cont = trace[i ? 'increasing' : 'decreasing'];
41395 var w = boundLineWidth(undefined, cont.line, MAX_MARKER_LINE_WIDTH, CST_MARKER_LINE_WIDTH);
41396
41397 p.style('fill', 'none')
41398 .call(Drawing.dashLine, cont.line.dash, w);
41399
41400 if(w) Color.stroke(p, cont.line.color);
41401 });
41402 }
41403
41404 function stylePies(d) {
41405 stylePieLike(d, this, 'pie');
41406 }
41407
41408 function styleFunnelareas(d) {
41409 stylePieLike(d, this, 'funnelarea');
41410 }
41411
41412 function stylePieLike(d, lThis, desiredType) {
41413 var d0 = d[0];
41414 var trace = d0.trace;
41415
41416 var isVisible = (!desiredType) ? Registry.traceIs(trace, desiredType) :
41417 (trace.visible && trace.type === desiredType);
41418
41419 var pts = d3.select(lThis).select('g.legendpoints')
41420 .selectAll('path.legend' + desiredType)
41421 .data(isVisible ? [d] : []);
41422 pts.enter().append('path').classed('legend' + desiredType, true)
41423 .attr('d', 'M6,6H-6V-6H6Z')
41424 .attr('transform', centerTransform);
41425 pts.exit().remove();
41426
41427 if(pts.size()) {
41428 var cont = (trace.marker || {}).line;
41429 var lw = boundLineWidth(pieCastOption(cont.width, d0.pts), cont, MAX_MARKER_LINE_WIDTH, CST_MARKER_LINE_WIDTH);
41430
41431 var tMod = Lib.minExtend(trace, {marker: {line: {width: lw}}});
41432 // since minExtend do not slice more than 3 items we need to patch line.color here
41433 tMod.marker.line.color = cont.color;
41434
41435 var d0Mod = Lib.minExtend(d0, {trace: tMod});
41436
41437 stylePie(pts, d0Mod, tMod);
41438 }
41439 }
41440
41441 function styleSpatial(d) { // i.e. maninly traces having z and colorscale
41442 var trace = d[0].trace;
41443
41444 var useGradient;
41445 var ptsData = [];
41446 if(trace.visible) {
41447 switch(trace.type) {
41448 case 'histogram2d' :
41449 case 'heatmap' :
41450 ptsData = [
41451 ['M-15,-2V4H15V-2Z'] // similar to contour
41452 ];
41453 useGradient = true;
41454 break;
41455 case 'choropleth' :
41456 case 'choroplethmapbox' :
41457 ptsData = [
41458 ['M-6,-6V6H6V-6Z']
41459 ];
41460 useGradient = true;
41461 break;
41462 case 'densitymapbox' :
41463 ptsData = [
41464 ['M-6,0 a6,6 0 1,0 12,0 a 6,6 0 1,0 -12,0']
41465 ];
41466 useGradient = 'radial';
41467 break;
41468 case 'cone' :
41469 ptsData = [
41470 ['M-6,2 A2,2 0 0,0 -6,6 V6L6,4Z'],
41471 ['M-6,-6 A2,2 0 0,0 -6,-2 L6,-4Z'],
41472 ['M-6,-2 A2,2 0 0,0 -6,2 L6,0Z']
41473 ];
41474 useGradient = false;
41475 break;
41476 case 'streamtube' :
41477 ptsData = [
41478 ['M-6,2 A2,2 0 0,0 -6,6 H6 A2,2 0 0,1 6,2 Z'],
41479 ['M-6,-6 A2,2 0 0,0 -6,-2 H6 A2,2 0 0,1 6,-6 Z'],
41480 ['M-6,-2 A2,2 0 0,0 -6,2 H6 A2,2 0 0,1 6,-2 Z']
41481 ];
41482 useGradient = false;
41483 break;
41484 case 'surface' :
41485 ptsData = [
41486 ['M-6,-6 A2,3 0 0,0 -6,0 H6 A2,3 0 0,1 6,-6 Z'],
41487 ['M-6,1 A2,3 0 0,1 -6,6 H6 A2,3 0 0,0 6,0 Z']
41488 ];
41489 useGradient = true;
41490 break;
41491 case 'mesh3d' :
41492 ptsData = [
41493 ['M-6,6H0L-6,-6Z'],
41494 ['M6,6H0L6,-6Z'],
41495 ['M-6,-6H6L0,6Z']
41496 ];
41497 useGradient = false;
41498 break;
41499 case 'volume' :
41500 ptsData = [
41501 ['M-6,6H0L-6,-6Z'],
41502 ['M6,6H0L6,-6Z'],
41503 ['M-6,-6H6L0,6Z']
41504 ];
41505 useGradient = true;
41506 break;
41507 case 'isosurface':
41508 ptsData = [
41509 ['M-6,6H0L-6,-6Z'],
41510 ['M6,6H0L6,-6Z'],
41511 ['M-6,-6 A12,24 0 0,0 6,-6 L0,6Z']
41512 ];
41513 useGradient = false;
41514 break;
41515 }
41516 }
41517
41518 var pts = d3.select(this).select('g.legendpoints')
41519 .selectAll('path.legend3dandfriends')
41520 .data(ptsData);
41521 pts.enter().append('path').classed('legend3dandfriends', true)
41522 .attr('transform', centerTransform)
41523 .style('stroke-miterlimit', 1);
41524 pts.exit().remove();
41525
41526 pts.each(function(dd, i) {
41527 var pt = d3.select(this);
41528
41529 var cOpts = extractOpts(trace);
41530 var colorscale = cOpts.colorscale;
41531 var reversescale = cOpts.reversescale;
41532 var fillGradient = function(s) {
41533 if(s.size()) {
41534 var gradientID = 'legendfill-' + trace.uid;
41535 Drawing.gradient(s, gd, gradientID,
41536 getGradientDirection(reversescale, useGradient === 'radial'),
41537 colorscale, 'fill');
41538 }
41539 };
41540
41541 var fillColor;
41542 if(!colorscale) {
41543 var color = trace.vertexcolor || trace.facecolor || trace.color;
41544 fillColor = Lib.isArrayOrTypedArray(color) ? (color[i] || color[0]) : color;
41545 } else {
41546 if(!useGradient) {
41547 var len = colorscale.length;
41548 fillColor =
41549 i === 0 ? colorscale[reversescale ? len - 1 : 0][1] : // minimum
41550 i === 1 ? colorscale[reversescale ? 0 : len - 1][1] : // maximum
41551 colorscale[Math.floor((len - 1) / 2)][1]; // middle
41552 }
41553 }
41554
41555 pt.attr('d', dd[0]);
41556 if(fillColor) {
41557 pt.call(Color.fill, fillColor);
41558 } else {
41559 pt.call(fillGradient);
41560 }
41561 });
41562 }
41563};
41564
41565function getGradientDirection(reversescale, isRadial) {
41566 var str = isRadial ? 'radial' : 'horizontal';
41567 return str + (reversescale ? '' : 'reversed');
41568}
41569
41570function getStyleGuide(d) {
41571 var trace = d[0].trace;
41572 var contours = trace.contours;
41573 var showLine = subTypes.hasLines(trace);
41574 var showMarker = subTypes.hasMarkers(trace);
41575
41576 var showFill = trace.visible && trace.fill && trace.fill !== 'none';
41577 var showGradientLine = false;
41578 var showGradientFill = false;
41579
41580 if(contours) {
41581 var coloring = contours.coloring;
41582
41583 if(coloring === 'lines') {
41584 showGradientLine = true;
41585 } else {
41586 showLine = coloring === 'none' || coloring === 'heatmap' || contours.showlines;
41587 }
41588
41589 if(contours.type === 'constraint') {
41590 showFill = contours._operation !== '=';
41591 } else if(coloring === 'fill' || coloring === 'heatmap') {
41592 showGradientFill = true;
41593 }
41594 }
41595
41596 return {
41597 showMarker: showMarker,
41598 showLine: showLine,
41599 showFill: showFill,
41600 showGradientLine: showGradientLine,
41601 showGradientFill: showGradientFill,
41602 anyLine: showLine || showGradientLine,
41603 anyFill: showFill || showGradientFill,
41604 };
41605}
41606
41607function dimAttr(v, dflt, max) {
41608 if(v && Lib.isArrayOrTypedArray(v)) return dflt;
41609 if(v > max) return max;
41610 return v;
41611}
41612
41613},{"../../lib":287,"../../registry":376,"../../traces/pie/helpers":489,"../../traces/pie/style_one":495,"../../traces/scatter/subtypes":522,"../color":157,"../colorscale/helpers":168,"../drawing":179,"./constants":208,"@plotly/d3":20}],216:[function(_dereq_,module,exports){
41614'use strict';
41615
41616var constants = _dereq_('./constants');
41617
41618module.exports = {
41619 editType: 'modebar',
41620
41621 orientation: {
41622 valType: 'enumerated',
41623 values: ['v', 'h'],
41624 dflt: 'h',
41625 editType: 'modebar',
41626 },
41627 bgcolor: {
41628 valType: 'color',
41629 editType: 'modebar',
41630 },
41631 color: {
41632 valType: 'color',
41633 editType: 'modebar',
41634 },
41635 activecolor: {
41636 valType: 'color',
41637 editType: 'modebar',
41638 },
41639 uirevision: {
41640 valType: 'any',
41641 editType: 'none',
41642 },
41643 add: {
41644 valType: 'string',
41645 arrayOk: true,
41646 dflt: '',
41647 editType: 'modebar',
41648 },
41649 remove: {
41650 valType: 'string',
41651 arrayOk: true,
41652 dflt: '',
41653 editType: 'modebar',
41654 }
41655};
41656
41657},{"./constants":218}],217:[function(_dereq_,module,exports){
41658'use strict';
41659
41660var Registry = _dereq_('../../registry');
41661var Plots = _dereq_('../../plots/plots');
41662var axisIds = _dereq_('../../plots/cartesian/axis_ids');
41663var Icons = _dereq_('../../fonts/ploticon');
41664var eraseActiveShape = _dereq_('../shapes/draw').eraseActiveShape;
41665var Lib = _dereq_('../../lib');
41666var _ = Lib._;
41667
41668var modeBarButtons = module.exports = {};
41669
41670/**
41671 * ModeBar buttons configuration
41672 *
41673 * @param {string} name
41674 * name / id of the buttons (for tracking)
41675 * @param {string} title
41676 * text that appears while hovering over the button,
41677 * enter null, false or '' for no hover text
41678 * @param {string} icon
41679 * svg icon object associated with the button
41680 * can be linked to Plotly.Icons to use the default plotly icons
41681 * @param {string} [gravity]
41682 * icon positioning
41683 * @param {function} click
41684 * click handler associated with the button, a function of
41685 * 'gd' (the main graph object) and
41686 * 'ev' (the event object)
41687 * @param {string} [attr]
41688 * attribute associated with button,
41689 * use this with 'val' to keep track of the state
41690 * @param {*} [val]
41691 * initial 'attr' value, can be a function of gd
41692 * @param {boolean} [toggle]
41693 * is the button a toggle button?
41694 */
41695modeBarButtons.toImage = {
41696 name: 'toImage',
41697 title: function(gd) {
41698 var opts = gd._context.toImageButtonOptions || {};
41699 var format = opts.format || 'png';
41700 return format === 'png' ?
41701 _(gd, 'Download plot as a png') : // legacy text
41702 _(gd, 'Download plot'); // generic non-PNG text
41703 },
41704 icon: Icons.camera,
41705 click: function(gd) {
41706 var toImageButtonOptions = gd._context.toImageButtonOptions;
41707 var opts = {format: toImageButtonOptions.format || 'png'};
41708
41709 Lib.notifier(_(gd, 'Taking snapshot - this may take a few seconds'), 'long');
41710
41711 if(opts.format !== 'svg' && Lib.isIE()) {
41712 Lib.notifier(_(gd, 'IE only supports svg. Changing format to svg.'), 'long');
41713 opts.format = 'svg';
41714 }
41715
41716 ['filename', 'width', 'height', 'scale'].forEach(function(key) {
41717 if(key in toImageButtonOptions) {
41718 opts[key] = toImageButtonOptions[key];
41719 }
41720 });
41721
41722 Registry.call('downloadImage', gd, opts)
41723 .then(function(filename) {
41724 Lib.notifier(_(gd, 'Snapshot succeeded') + ' - ' + filename, 'long');
41725 })
41726 .catch(function() {
41727 Lib.notifier(_(gd, 'Sorry, there was a problem downloading your snapshot!'), 'long');
41728 });
41729 }
41730};
41731
41732modeBarButtons.sendDataToCloud = {
41733 name: 'sendDataToCloud',
41734 title: function(gd) { return _(gd, 'Edit in Chart Studio'); },
41735 icon: Icons.disk,
41736 click: function(gd) {
41737 Plots.sendDataToCloud(gd);
41738 }
41739};
41740
41741modeBarButtons.editInChartStudio = {
41742 name: 'editInChartStudio',
41743 title: function(gd) { return _(gd, 'Edit in Chart Studio'); },
41744 icon: Icons.pencil,
41745 click: function(gd) {
41746 Plots.sendDataToCloud(gd);
41747 }
41748};
41749
41750modeBarButtons.zoom2d = {
41751 name: 'zoom2d',
41752 _cat: 'zoom',
41753 title: function(gd) { return _(gd, 'Zoom'); },
41754 attr: 'dragmode',
41755 val: 'zoom',
41756 icon: Icons.zoombox,
41757 click: handleCartesian
41758};
41759
41760modeBarButtons.pan2d = {
41761 name: 'pan2d',
41762 _cat: 'pan',
41763 title: function(gd) { return _(gd, 'Pan'); },
41764 attr: 'dragmode',
41765 val: 'pan',
41766 icon: Icons.pan,
41767 click: handleCartesian
41768};
41769
41770modeBarButtons.select2d = {
41771 name: 'select2d',
41772 _cat: 'select',
41773 title: function(gd) { return _(gd, 'Box Select'); },
41774 attr: 'dragmode',
41775 val: 'select',
41776 icon: Icons.selectbox,
41777 click: handleCartesian
41778};
41779
41780modeBarButtons.lasso2d = {
41781 name: 'lasso2d',
41782 _cat: 'lasso',
41783 title: function(gd) { return _(gd, 'Lasso Select'); },
41784 attr: 'dragmode',
41785 val: 'lasso',
41786 icon: Icons.lasso,
41787 click: handleCartesian
41788};
41789
41790modeBarButtons.drawclosedpath = {
41791 name: 'drawclosedpath',
41792 title: function(gd) { return _(gd, 'Draw closed freeform'); },
41793 attr: 'dragmode',
41794 val: 'drawclosedpath',
41795 icon: Icons.drawclosedpath,
41796 click: handleCartesian
41797};
41798
41799modeBarButtons.drawopenpath = {
41800 name: 'drawopenpath',
41801 title: function(gd) { return _(gd, 'Draw open freeform'); },
41802 attr: 'dragmode',
41803 val: 'drawopenpath',
41804 icon: Icons.drawopenpath,
41805 click: handleCartesian
41806};
41807
41808modeBarButtons.drawline = {
41809 name: 'drawline',
41810 title: function(gd) { return _(gd, 'Draw line'); },
41811 attr: 'dragmode',
41812 val: 'drawline',
41813 icon: Icons.drawline,
41814 click: handleCartesian
41815};
41816
41817modeBarButtons.drawrect = {
41818 name: 'drawrect',
41819 title: function(gd) { return _(gd, 'Draw rectangle'); },
41820 attr: 'dragmode',
41821 val: 'drawrect',
41822 icon: Icons.drawrect,
41823 click: handleCartesian
41824};
41825
41826modeBarButtons.drawcircle = {
41827 name: 'drawcircle',
41828 title: function(gd) { return _(gd, 'Draw circle'); },
41829 attr: 'dragmode',
41830 val: 'drawcircle',
41831 icon: Icons.drawcircle,
41832 click: handleCartesian
41833};
41834
41835modeBarButtons.eraseshape = {
41836 name: 'eraseshape',
41837 title: function(gd) { return _(gd, 'Erase active shape'); },
41838 icon: Icons.eraseshape,
41839 click: eraseActiveShape
41840};
41841
41842modeBarButtons.zoomIn2d = {
41843 name: 'zoomIn2d',
41844 _cat: 'zoomin',
41845 title: function(gd) { return _(gd, 'Zoom in'); },
41846 attr: 'zoom',
41847 val: 'in',
41848 icon: Icons.zoom_plus,
41849 click: handleCartesian
41850};
41851
41852modeBarButtons.zoomOut2d = {
41853 name: 'zoomOut2d',
41854 _cat: 'zoomout',
41855 title: function(gd) { return _(gd, 'Zoom out'); },
41856 attr: 'zoom',
41857 val: 'out',
41858 icon: Icons.zoom_minus,
41859 click: handleCartesian
41860};
41861
41862modeBarButtons.autoScale2d = {
41863 name: 'autoScale2d',
41864 _cat: 'autoscale',
41865 title: function(gd) { return _(gd, 'Autoscale'); },
41866 attr: 'zoom',
41867 val: 'auto',
41868 icon: Icons.autoscale,
41869 click: handleCartesian
41870};
41871
41872modeBarButtons.resetScale2d = {
41873 name: 'resetScale2d',
41874 _cat: 'resetscale',
41875 title: function(gd) { return _(gd, 'Reset axes'); },
41876 attr: 'zoom',
41877 val: 'reset',
41878 icon: Icons.home,
41879 click: handleCartesian
41880};
41881
41882modeBarButtons.hoverClosestCartesian = {
41883 name: 'hoverClosestCartesian',
41884 _cat: 'hoverclosest',
41885 title: function(gd) { return _(gd, 'Show closest data on hover'); },
41886 attr: 'hovermode',
41887 val: 'closest',
41888 icon: Icons.tooltip_basic,
41889 gravity: 'ne',
41890 click: handleCartesian
41891};
41892
41893modeBarButtons.hoverCompareCartesian = {
41894 name: 'hoverCompareCartesian',
41895 _cat: 'hoverCompare',
41896 title: function(gd) { return _(gd, 'Compare data on hover'); },
41897 attr: 'hovermode',
41898 val: function(gd) {
41899 return gd._fullLayout._isHoriz ? 'y' : 'x';
41900 },
41901 icon: Icons.tooltip_compare,
41902 gravity: 'ne',
41903 click: handleCartesian
41904};
41905
41906function handleCartesian(gd, ev) {
41907 var button = ev.currentTarget;
41908 var astr = button.getAttribute('data-attr');
41909 var val = button.getAttribute('data-val') || true;
41910 var fullLayout = gd._fullLayout;
41911 var aobj = {};
41912 var axList = axisIds.list(gd, null, true);
41913 var allSpikesEnabled = fullLayout._cartesianSpikesEnabled;
41914
41915 var ax, i;
41916
41917 if(astr === 'zoom') {
41918 var mag = (val === 'in') ? 0.5 : 2;
41919 var r0 = (1 + mag) / 2;
41920 var r1 = (1 - mag) / 2;
41921 var axName;
41922
41923 for(i = 0; i < axList.length; i++) {
41924 ax = axList[i];
41925
41926 if(!ax.fixedrange) {
41927 axName = ax._name;
41928 if(val === 'auto') {
41929 aobj[axName + '.autorange'] = true;
41930 } else if(val === 'reset') {
41931 if(ax._rangeInitial === undefined) {
41932 aobj[axName + '.autorange'] = true;
41933 } else {
41934 var rangeInitial = ax._rangeInitial.slice();
41935 aobj[axName + '.range[0]'] = rangeInitial[0];
41936 aobj[axName + '.range[1]'] = rangeInitial[1];
41937 }
41938
41939 // N.B. "reset" also resets showspikes
41940 if(ax._showSpikeInitial !== undefined) {
41941 aobj[axName + '.showspikes'] = ax._showSpikeInitial;
41942 if(allSpikesEnabled === 'on' && !ax._showSpikeInitial) {
41943 allSpikesEnabled = 'off';
41944 }
41945 }
41946 } else {
41947 var rangeNow = [
41948 ax.r2l(ax.range[0]),
41949 ax.r2l(ax.range[1]),
41950 ];
41951
41952 var rangeNew = [
41953 r0 * rangeNow[0] + r1 * rangeNow[1],
41954 r0 * rangeNow[1] + r1 * rangeNow[0]
41955 ];
41956
41957 aobj[axName + '.range[0]'] = ax.l2r(rangeNew[0]);
41958 aobj[axName + '.range[1]'] = ax.l2r(rangeNew[1]);
41959 }
41960 }
41961 }
41962 } else {
41963 // if ALL traces have orientation 'h', 'hovermode': 'x' otherwise: 'y'
41964 if(astr === 'hovermode' && (val === 'x' || val === 'y')) {
41965 val = fullLayout._isHoriz ? 'y' : 'x';
41966 button.setAttribute('data-val', val);
41967 }
41968
41969 aobj[astr] = val;
41970 }
41971
41972 fullLayout._cartesianSpikesEnabled = allSpikesEnabled;
41973
41974 Registry.call('_guiRelayout', gd, aobj);
41975}
41976
41977modeBarButtons.zoom3d = {
41978 name: 'zoom3d',
41979 _cat: 'zoom',
41980 title: function(gd) { return _(gd, 'Zoom'); },
41981 attr: 'scene.dragmode',
41982 val: 'zoom',
41983 icon: Icons.zoombox,
41984 click: handleDrag3d
41985};
41986
41987modeBarButtons.pan3d = {
41988 name: 'pan3d',
41989 _cat: 'pan',
41990 title: function(gd) { return _(gd, 'Pan'); },
41991 attr: 'scene.dragmode',
41992 val: 'pan',
41993 icon: Icons.pan,
41994 click: handleDrag3d
41995};
41996
41997modeBarButtons.orbitRotation = {
41998 name: 'orbitRotation',
41999 title: function(gd) { return _(gd, 'Orbital rotation'); },
42000 attr: 'scene.dragmode',
42001 val: 'orbit',
42002 icon: Icons['3d_rotate'],
42003 click: handleDrag3d
42004};
42005
42006modeBarButtons.tableRotation = {
42007 name: 'tableRotation',
42008 title: function(gd) { return _(gd, 'Turntable rotation'); },
42009 attr: 'scene.dragmode',
42010 val: 'turntable',
42011 icon: Icons['z-axis'],
42012 click: handleDrag3d
42013};
42014
42015function handleDrag3d(gd, ev) {
42016 var button = ev.currentTarget;
42017 var attr = button.getAttribute('data-attr');
42018 var val = button.getAttribute('data-val') || true;
42019 var sceneIds = gd._fullLayout._subplots.gl3d || [];
42020 var layoutUpdate = {};
42021
42022 var parts = attr.split('.');
42023
42024 for(var i = 0; i < sceneIds.length; i++) {
42025 layoutUpdate[sceneIds[i] + '.' + parts[1]] = val;
42026 }
42027
42028 // for multi-type subplots
42029 var val2d = (val === 'pan') ? val : 'zoom';
42030 layoutUpdate.dragmode = val2d;
42031
42032 Registry.call('_guiRelayout', gd, layoutUpdate);
42033}
42034
42035modeBarButtons.resetCameraDefault3d = {
42036 name: 'resetCameraDefault3d',
42037 _cat: 'resetCameraDefault',
42038 title: function(gd) { return _(gd, 'Reset camera to default'); },
42039 attr: 'resetDefault',
42040 icon: Icons.home,
42041 click: handleCamera3d
42042};
42043
42044modeBarButtons.resetCameraLastSave3d = {
42045 name: 'resetCameraLastSave3d',
42046 _cat: 'resetCameraLastSave',
42047 title: function(gd) { return _(gd, 'Reset camera to last save'); },
42048 attr: 'resetLastSave',
42049 icon: Icons.movie,
42050 click: handleCamera3d
42051};
42052
42053function handleCamera3d(gd, ev) {
42054 var button = ev.currentTarget;
42055 var attr = button.getAttribute('data-attr');
42056 var resetLastSave = attr === 'resetLastSave';
42057 var resetDefault = attr === 'resetDefault';
42058
42059 var fullLayout = gd._fullLayout;
42060 var sceneIds = fullLayout._subplots.gl3d || [];
42061 var aobj = {};
42062
42063 for(var i = 0; i < sceneIds.length; i++) {
42064 var sceneId = sceneIds[i];
42065 var camera = sceneId + '.camera';
42066 var aspectratio = sceneId + '.aspectratio';
42067 var aspectmode = sceneId + '.aspectmode';
42068 var scene = fullLayout[sceneId]._scene;
42069 var didUpdate;
42070
42071 if(resetLastSave) {
42072 aobj[camera + '.up'] = scene.viewInitial.up;
42073 aobj[camera + '.eye'] = scene.viewInitial.eye;
42074 aobj[camera + '.center'] = scene.viewInitial.center;
42075 didUpdate = true;
42076 } else if(resetDefault) {
42077 aobj[camera + '.up'] = null;
42078 aobj[camera + '.eye'] = null;
42079 aobj[camera + '.center'] = null;
42080 didUpdate = true;
42081 }
42082
42083 if(didUpdate) {
42084 aobj[aspectratio + '.x'] = scene.viewInitial.aspectratio.x;
42085 aobj[aspectratio + '.y'] = scene.viewInitial.aspectratio.y;
42086 aobj[aspectratio + '.z'] = scene.viewInitial.aspectratio.z;
42087 aobj[aspectmode] = scene.viewInitial.aspectmode;
42088 }
42089 }
42090
42091 Registry.call('_guiRelayout', gd, aobj);
42092}
42093
42094modeBarButtons.hoverClosest3d = {
42095 name: 'hoverClosest3d',
42096 _cat: 'hoverclosest',
42097 title: function(gd) { return _(gd, 'Toggle show closest data on hover'); },
42098 attr: 'hovermode',
42099 val: null,
42100 toggle: true,
42101 icon: Icons.tooltip_basic,
42102 gravity: 'ne',
42103 click: handleHover3d
42104};
42105
42106function getNextHover3d(gd, ev) {
42107 var button = ev.currentTarget;
42108 var val = button._previousVal;
42109 var fullLayout = gd._fullLayout;
42110 var sceneIds = fullLayout._subplots.gl3d || [];
42111
42112 var axes = ['xaxis', 'yaxis', 'zaxis'];
42113
42114 // initialize 'current spike' object to be stored in the DOM
42115 var currentSpikes = {};
42116 var layoutUpdate = {};
42117
42118 if(val) {
42119 layoutUpdate = val;
42120 button._previousVal = null;
42121 } else {
42122 for(var i = 0; i < sceneIds.length; i++) {
42123 var sceneId = sceneIds[i];
42124 var sceneLayout = fullLayout[sceneId];
42125
42126 var hovermodeAStr = sceneId + '.hovermode';
42127 currentSpikes[hovermodeAStr] = sceneLayout.hovermode;
42128 layoutUpdate[hovermodeAStr] = false;
42129
42130 // copy all the current spike attrs
42131 for(var j = 0; j < 3; j++) {
42132 var axis = axes[j];
42133 var spikeAStr = sceneId + '.' + axis + '.showspikes';
42134 layoutUpdate[spikeAStr] = false;
42135 currentSpikes[spikeAStr] = sceneLayout[axis].showspikes;
42136 }
42137 }
42138
42139 button._previousVal = currentSpikes;
42140 }
42141 return layoutUpdate;
42142}
42143
42144function handleHover3d(gd, ev) {
42145 var layoutUpdate = getNextHover3d(gd, ev);
42146 Registry.call('_guiRelayout', gd, layoutUpdate);
42147}
42148
42149modeBarButtons.zoomInGeo = {
42150 name: 'zoomInGeo',
42151 _cat: 'zoomin',
42152 title: function(gd) { return _(gd, 'Zoom in'); },
42153 attr: 'zoom',
42154 val: 'in',
42155 icon: Icons.zoom_plus,
42156 click: handleGeo
42157};
42158
42159modeBarButtons.zoomOutGeo = {
42160 name: 'zoomOutGeo',
42161 _cat: 'zoomout',
42162 title: function(gd) { return _(gd, 'Zoom out'); },
42163 attr: 'zoom',
42164 val: 'out',
42165 icon: Icons.zoom_minus,
42166 click: handleGeo
42167};
42168
42169modeBarButtons.resetGeo = {
42170 name: 'resetGeo',
42171 _cat: 'reset',
42172 title: function(gd) { return _(gd, 'Reset'); },
42173 attr: 'reset',
42174 val: null,
42175 icon: Icons.autoscale,
42176 click: handleGeo
42177};
42178
42179modeBarButtons.hoverClosestGeo = {
42180 name: 'hoverClosestGeo',
42181 _cat: 'hoverclosest',
42182 title: function(gd) { return _(gd, 'Toggle show closest data on hover'); },
42183 attr: 'hovermode',
42184 val: null,
42185 toggle: true,
42186 icon: Icons.tooltip_basic,
42187 gravity: 'ne',
42188 click: toggleHover
42189};
42190
42191function handleGeo(gd, ev) {
42192 var button = ev.currentTarget;
42193 var attr = button.getAttribute('data-attr');
42194 var val = button.getAttribute('data-val') || true;
42195 var fullLayout = gd._fullLayout;
42196 var geoIds = fullLayout._subplots.geo || [];
42197
42198 for(var i = 0; i < geoIds.length; i++) {
42199 var id = geoIds[i];
42200 var geoLayout = fullLayout[id];
42201
42202 if(attr === 'zoom') {
42203 var scale = geoLayout.projection.scale;
42204 var newScale = (val === 'in') ? 2 * scale : 0.5 * scale;
42205
42206 Registry.call('_guiRelayout', gd, id + '.projection.scale', newScale);
42207 }
42208 }
42209
42210 if(attr === 'reset') {
42211 resetView(gd, 'geo');
42212 }
42213}
42214
42215modeBarButtons.hoverClosestGl2d = {
42216 name: 'hoverClosestGl2d',
42217 _cat: 'hoverclosest',
42218 title: function(gd) { return _(gd, 'Toggle show closest data on hover'); },
42219 attr: 'hovermode',
42220 val: null,
42221 toggle: true,
42222 icon: Icons.tooltip_basic,
42223 gravity: 'ne',
42224 click: toggleHover
42225};
42226
42227modeBarButtons.hoverClosestPie = {
42228 name: 'hoverClosestPie',
42229 _cat: 'hoverclosest',
42230 title: function(gd) { return _(gd, 'Toggle show closest data on hover'); },
42231 attr: 'hovermode',
42232 val: 'closest',
42233 icon: Icons.tooltip_basic,
42234 gravity: 'ne',
42235 click: toggleHover
42236};
42237
42238function getNextHover(gd) {
42239 var fullLayout = gd._fullLayout;
42240
42241 if(fullLayout.hovermode) return false;
42242
42243 if(fullLayout._has('cartesian')) {
42244 return fullLayout._isHoriz ? 'y' : 'x';
42245 }
42246 return 'closest';
42247}
42248
42249function toggleHover(gd) {
42250 var newHover = getNextHover(gd);
42251 Registry.call('_guiRelayout', gd, 'hovermode', newHover);
42252}
42253
42254modeBarButtons.resetViewSankey = {
42255 name: 'resetSankeyGroup',
42256 title: function(gd) { return _(gd, 'Reset view'); },
42257 icon: Icons.home,
42258 click: function(gd) {
42259 var aObj = {
42260 'node.groups': [],
42261 'node.x': [],
42262 'node.y': []
42263 };
42264 for(var i = 0; i < gd._fullData.length; i++) {
42265 var viewInitial = gd._fullData[i]._viewInitial;
42266 aObj['node.groups'].push(viewInitial.node.groups.slice());
42267 aObj['node.x'].push(viewInitial.node.x.slice());
42268 aObj['node.y'].push(viewInitial.node.y.slice());
42269 }
42270 Registry.call('restyle', gd, aObj);
42271 }
42272};
42273
42274// buttons when more then one plot types are present
42275
42276modeBarButtons.toggleHover = {
42277 name: 'toggleHover',
42278 title: function(gd) { return _(gd, 'Toggle show closest data on hover'); },
42279 attr: 'hovermode',
42280 val: null,
42281 toggle: true,
42282 icon: Icons.tooltip_basic,
42283 gravity: 'ne',
42284 click: function(gd, ev) {
42285 var layoutUpdate = getNextHover3d(gd, ev);
42286 layoutUpdate.hovermode = getNextHover(gd);
42287
42288 Registry.call('_guiRelayout', gd, layoutUpdate);
42289 }
42290};
42291
42292modeBarButtons.resetViews = {
42293 name: 'resetViews',
42294 title: function(gd) { return _(gd, 'Reset views'); },
42295 icon: Icons.home,
42296 click: function(gd, ev) {
42297 var button = ev.currentTarget;
42298
42299 button.setAttribute('data-attr', 'zoom');
42300 button.setAttribute('data-val', 'reset');
42301 handleCartesian(gd, ev);
42302
42303 button.setAttribute('data-attr', 'resetLastSave');
42304 handleCamera3d(gd, ev);
42305
42306 resetView(gd, 'geo');
42307 resetView(gd, 'mapbox');
42308 }
42309};
42310
42311modeBarButtons.toggleSpikelines = {
42312 name: 'toggleSpikelines',
42313 title: function(gd) { return _(gd, 'Toggle Spike Lines'); },
42314 icon: Icons.spikeline,
42315 attr: '_cartesianSpikesEnabled',
42316 val: 'on',
42317 click: function(gd) {
42318 var fullLayout = gd._fullLayout;
42319 var allSpikesEnabled = fullLayout._cartesianSpikesEnabled;
42320
42321 fullLayout._cartesianSpikesEnabled = allSpikesEnabled === 'on' ? 'off' : 'on';
42322 Registry.call('_guiRelayout', gd, setSpikelineVisibility(gd));
42323 }
42324};
42325
42326function setSpikelineVisibility(gd) {
42327 var fullLayout = gd._fullLayout;
42328 var areSpikesOn = fullLayout._cartesianSpikesEnabled === 'on';
42329 var axList = axisIds.list(gd, null, true);
42330 var aobj = {};
42331
42332 for(var i = 0; i < axList.length; i++) {
42333 var ax = axList[i];
42334 aobj[ax._name + '.showspikes'] = areSpikesOn ? true : ax._showSpikeInitial;
42335 }
42336
42337 return aobj;
42338}
42339
42340modeBarButtons.resetViewMapbox = {
42341 name: 'resetViewMapbox',
42342 _cat: 'resetView',
42343 title: function(gd) { return _(gd, 'Reset view'); },
42344 attr: 'reset',
42345 icon: Icons.home,
42346 click: function(gd) {
42347 resetView(gd, 'mapbox');
42348 }
42349};
42350
42351modeBarButtons.zoomInMapbox = {
42352 name: 'zoomInMapbox',
42353 _cat: 'zoomin',
42354 title: function(gd) { return _(gd, 'Zoom in'); },
42355 attr: 'zoom',
42356 val: 'in',
42357 icon: Icons.zoom_plus,
42358 click: handleMapboxZoom
42359};
42360
42361modeBarButtons.zoomOutMapbox = {
42362 name: 'zoomOutMapbox',
42363 _cat: 'zoomout',
42364 title: function(gd) { return _(gd, 'Zoom out'); },
42365 attr: 'zoom',
42366 val: 'out',
42367 icon: Icons.zoom_minus,
42368 click: handleMapboxZoom
42369};
42370
42371function handleMapboxZoom(gd, ev) {
42372 var button = ev.currentTarget;
42373 var val = button.getAttribute('data-val');
42374 var fullLayout = gd._fullLayout;
42375 var subplotIds = fullLayout._subplots.mapbox || [];
42376 var scalar = 1.05;
42377 var aObj = {};
42378
42379 for(var i = 0; i < subplotIds.length; i++) {
42380 var id = subplotIds[i];
42381 var current = fullLayout[id].zoom;
42382 var next = (val === 'in') ? scalar * current : current / scalar;
42383 aObj[id + '.zoom'] = next;
42384 }
42385
42386 Registry.call('_guiRelayout', gd, aObj);
42387}
42388
42389function resetView(gd, subplotType) {
42390 var fullLayout = gd._fullLayout;
42391 var subplotIds = fullLayout._subplots[subplotType] || [];
42392 var aObj = {};
42393
42394 for(var i = 0; i < subplotIds.length; i++) {
42395 var id = subplotIds[i];
42396 var subplotObj = fullLayout[id]._subplot;
42397 var viewInitial = subplotObj.viewInitial;
42398 var viewKeys = Object.keys(viewInitial);
42399
42400 for(var j = 0; j < viewKeys.length; j++) {
42401 var key = viewKeys[j];
42402 aObj[id + '.' + key] = viewInitial[key];
42403 }
42404 }
42405
42406 Registry.call('_guiRelayout', gd, aObj);
42407}
42408
42409},{"../../fonts/ploticon":270,"../../lib":287,"../../plots/cartesian/axis_ids":338,"../../plots/plots":369,"../../registry":376,"../shapes/draw":241}],218:[function(_dereq_,module,exports){
42410'use strict';
42411
42412var modeBarButtons = _dereq_('./buttons');
42413var buttonList = Object.keys(modeBarButtons);
42414
42415var DRAW_MODES = [
42416 'drawline',
42417 'drawopenpath',
42418 'drawclosedpath',
42419 'drawcircle',
42420 'drawrect',
42421 'eraseshape'
42422];
42423
42424var backButtons = [
42425 'v1hovermode',
42426 'hoverclosest',
42427 'hovercompare',
42428 'togglehover',
42429 'togglespikelines'
42430].concat(DRAW_MODES);
42431
42432var foreButtons = [];
42433var addToForeButtons = function(b) {
42434 if(backButtons.indexOf(b._cat || b.name) !== -1) return;
42435 // for convenience add lowercase shotname e.g. zoomin as well fullname zoomInGeo
42436 var name = b.name;
42437 var _cat = (b._cat || b.name).toLowerCase();
42438 if(foreButtons.indexOf(name) === -1) foreButtons.push(name);
42439 if(foreButtons.indexOf(_cat) === -1) foreButtons.push(_cat);
42440};
42441buttonList.forEach(function(k) {
42442 addToForeButtons(modeBarButtons[k]);
42443});
42444foreButtons.sort();
42445
42446module.exports = {
42447 DRAW_MODES: DRAW_MODES,
42448 backButtons: backButtons,
42449 foreButtons: foreButtons
42450};
42451
42452},{"./buttons":217}],219:[function(_dereq_,module,exports){
42453'use strict';
42454
42455var Lib = _dereq_('../../lib');
42456var Color = _dereq_('../color');
42457var Template = _dereq_('../../plot_api/plot_template');
42458var attributes = _dereq_('./attributes');
42459
42460module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) {
42461 var containerIn = layoutIn.modebar || {};
42462 var containerOut = Template.newContainer(layoutOut, 'modebar');
42463
42464 function coerce(attr, dflt) {
42465 return Lib.coerce(containerIn, containerOut, attributes, attr, dflt);
42466 }
42467
42468 coerce('orientation');
42469 coerce('bgcolor', Color.addOpacity(layoutOut.paper_bgcolor, 0.5));
42470 var defaultColor = Color.contrast(Color.rgb(layoutOut.modebar.bgcolor));
42471 coerce('color', Color.addOpacity(defaultColor, 0.3));
42472 coerce('activecolor', Color.addOpacity(defaultColor, 0.7));
42473 coerce('uirevision', layoutOut.uirevision);
42474 coerce('add');
42475 coerce('remove');
42476};
42477
42478},{"../../lib":287,"../../plot_api/plot_template":323,"../color":157,"./attributes":216}],220:[function(_dereq_,module,exports){
42479'use strict';
42480
42481module.exports = {
42482 moduleType: 'component',
42483 name: 'modebar',
42484
42485 layoutAttributes: _dereq_('./attributes'),
42486 supplyLayoutDefaults: _dereq_('./defaults'),
42487
42488 manage: _dereq_('./manage')
42489};
42490
42491},{"./attributes":216,"./defaults":219,"./manage":221}],221:[function(_dereq_,module,exports){
42492'use strict';
42493
42494var axisIds = _dereq_('../../plots/cartesian/axis_ids');
42495var scatterSubTypes = _dereq_('../../traces/scatter/subtypes');
42496var Registry = _dereq_('../../registry');
42497var isUnifiedHover = _dereq_('../fx/helpers').isUnifiedHover;
42498
42499var createModeBar = _dereq_('./modebar');
42500var modeBarButtons = _dereq_('./buttons');
42501var DRAW_MODES = _dereq_('./constants').DRAW_MODES;
42502
42503/**
42504 * ModeBar wrapper around 'create' and 'update',
42505 * chooses buttons to pass to ModeBar constructor based on
42506 * plot type and plot config.
42507 *
42508 * @param {object} gd main plot object
42509 *
42510 */
42511module.exports = function manageModeBar(gd) {
42512 var fullLayout = gd._fullLayout;
42513 var context = gd._context;
42514 var modeBar = fullLayout._modeBar;
42515
42516 if(!context.displayModeBar && !context.watermark) {
42517 if(modeBar) {
42518 modeBar.destroy();
42519 delete fullLayout._modeBar;
42520 }
42521 return;
42522 }
42523
42524 if(!Array.isArray(context.modeBarButtonsToRemove)) {
42525 throw new Error([
42526 '*modeBarButtonsToRemove* configuration options',
42527 'must be an array.'
42528 ].join(' '));
42529 }
42530
42531 if(!Array.isArray(context.modeBarButtonsToAdd)) {
42532 throw new Error([
42533 '*modeBarButtonsToAdd* configuration options',
42534 'must be an array.'
42535 ].join(' '));
42536 }
42537
42538 var customButtons = context.modeBarButtons;
42539 var buttonGroups;
42540
42541 if(Array.isArray(customButtons) && customButtons.length) {
42542 buttonGroups = fillCustomButton(customButtons);
42543 } else if(!context.displayModeBar && context.watermark) {
42544 buttonGroups = [];
42545 } else {
42546 buttonGroups = getButtonGroups(gd);
42547 }
42548
42549 if(modeBar) modeBar.update(gd, buttonGroups);
42550 else fullLayout._modeBar = createModeBar(gd, buttonGroups);
42551};
42552
42553// logic behind which buttons are displayed by default
42554function getButtonGroups(gd) {
42555 var fullLayout = gd._fullLayout;
42556 var fullData = gd._fullData;
42557 var context = gd._context;
42558
42559 function match(name, B) {
42560 if(typeof B === 'string') {
42561 if(B.toLowerCase() === name.toLowerCase()) return true;
42562 } else {
42563 var v0 = B.name;
42564 var v1 = (B._cat || B.name);
42565
42566 if(v0 === name || v1 === name.toLowerCase()) return true;
42567 }
42568 return false;
42569 }
42570
42571 var layoutAdd = fullLayout.modebar.add;
42572 if(typeof layoutAdd === 'string') layoutAdd = [layoutAdd];
42573
42574 var layoutRemove = fullLayout.modebar.remove;
42575 if(typeof layoutRemove === 'string') layoutRemove = [layoutRemove];
42576
42577 var buttonsToAdd = context.modeBarButtonsToAdd.concat(
42578 layoutAdd.filter(function(e) {
42579 for(var i = 0; i < context.modeBarButtonsToRemove.length; i++) {
42580 if(match(e, context.modeBarButtonsToRemove[i])) return false;
42581 }
42582 return true;
42583 })
42584 );
42585
42586 var buttonsToRemove = context.modeBarButtonsToRemove.concat(
42587 layoutRemove.filter(function(e) {
42588 for(var i = 0; i < context.modeBarButtonsToAdd.length; i++) {
42589 if(match(e, context.modeBarButtonsToAdd[i])) return false;
42590 }
42591 return true;
42592 })
42593 );
42594
42595 var hasCartesian = fullLayout._has('cartesian');
42596 var hasGL3D = fullLayout._has('gl3d');
42597 var hasGeo = fullLayout._has('geo');
42598 var hasPie = fullLayout._has('pie');
42599 var hasFunnelarea = fullLayout._has('funnelarea');
42600 var hasGL2D = fullLayout._has('gl2d');
42601 var hasTernary = fullLayout._has('ternary');
42602 var hasMapbox = fullLayout._has('mapbox');
42603 var hasPolar = fullLayout._has('polar');
42604 var hasSankey = fullLayout._has('sankey');
42605 var allAxesFixed = areAllAxesFixed(fullLayout);
42606 var hasUnifiedHoverLabel = isUnifiedHover(fullLayout.hovermode);
42607
42608 var groups = [];
42609
42610 function addGroup(newGroup) {
42611 if(!newGroup.length) return;
42612
42613 var out = [];
42614
42615 for(var i = 0; i < newGroup.length; i++) {
42616 var name = newGroup[i];
42617 var B = modeBarButtons[name];
42618 var v0 = B.name.toLowerCase();
42619 var v1 = (B._cat || B.name).toLowerCase();
42620 var found = false;
42621 for(var q = 0; q < buttonsToRemove.length; q++) {
42622 var t = buttonsToRemove[q].toLowerCase();
42623 if(t === v0 || t === v1) {
42624 found = true;
42625 break;
42626 }
42627 }
42628 if(found) continue;
42629 out.push(modeBarButtons[name]);
42630 }
42631
42632 groups.push(out);
42633 }
42634
42635 // buttons common to all plot types
42636 var commonGroup = ['toImage'];
42637 if(context.showEditInChartStudio) commonGroup.push('editInChartStudio');
42638 else if(context.showSendToCloud) commonGroup.push('sendDataToCloud');
42639 addGroup(commonGroup);
42640
42641 var zoomGroup = [];
42642 var hoverGroup = [];
42643 var resetGroup = [];
42644 var dragModeGroup = [];
42645
42646 if((hasCartesian || hasGL2D || hasPie || hasFunnelarea || hasTernary) + hasGeo + hasGL3D + hasMapbox + hasPolar > 1) {
42647 // graphs with more than one plot types get 'union buttons'
42648 // which reset the view or toggle hover labels across all subplots.
42649 hoverGroup = ['toggleHover'];
42650 resetGroup = ['resetViews'];
42651 } else if(hasGeo) {
42652 zoomGroup = ['zoomInGeo', 'zoomOutGeo'];
42653 hoverGroup = ['hoverClosestGeo'];
42654 resetGroup = ['resetGeo'];
42655 } else if(hasGL3D) {
42656 hoverGroup = ['hoverClosest3d'];
42657 resetGroup = ['resetCameraDefault3d', 'resetCameraLastSave3d'];
42658 } else if(hasMapbox) {
42659 zoomGroup = ['zoomInMapbox', 'zoomOutMapbox'];
42660 hoverGroup = ['toggleHover'];
42661 resetGroup = ['resetViewMapbox'];
42662 } else if(hasGL2D) {
42663 hoverGroup = ['hoverClosestGl2d'];
42664 } else if(hasPie) {
42665 hoverGroup = ['hoverClosestPie'];
42666 } else if(hasSankey) {
42667 hoverGroup = ['hoverClosestCartesian', 'hoverCompareCartesian'];
42668 resetGroup = ['resetViewSankey'];
42669 } else { // hasPolar, hasTernary
42670 // always show at least one hover icon.
42671 hoverGroup = ['toggleHover'];
42672 }
42673 // if we have cartesian, allow switching between closest and compare
42674 // regardless of what other types are on the plot, since they'll all
42675 // just treat any truthy hovermode as 'closest'
42676 if(hasCartesian) {
42677 hoverGroup = ['toggleSpikelines', 'hoverClosestCartesian', 'hoverCompareCartesian'];
42678 }
42679 if(hasNoHover(fullData) || hasUnifiedHoverLabel) {
42680 hoverGroup = [];
42681 }
42682
42683 if((hasCartesian || hasGL2D) && !allAxesFixed) {
42684 zoomGroup = ['zoomIn2d', 'zoomOut2d', 'autoScale2d'];
42685 if(resetGroup[0] !== 'resetViews') resetGroup = ['resetScale2d'];
42686 }
42687
42688 if(hasGL3D) {
42689 dragModeGroup = ['zoom3d', 'pan3d', 'orbitRotation', 'tableRotation'];
42690 } else if(((hasCartesian || hasGL2D) && !allAxesFixed) || hasTernary) {
42691 dragModeGroup = ['zoom2d', 'pan2d'];
42692 } else if(hasMapbox || hasGeo) {
42693 dragModeGroup = ['pan2d'];
42694 } else if(hasPolar) {
42695 dragModeGroup = ['zoom2d'];
42696 }
42697 if(isSelectable(fullData)) {
42698 dragModeGroup.push('select2d', 'lasso2d');
42699 }
42700
42701 var enabledHoverGroup = [];
42702 var enableHover = function(a) {
42703 // return if already added
42704 if(enabledHoverGroup.indexOf(a) !== -1) return;
42705 // should be in hoverGroup
42706 if(hoverGroup.indexOf(a) !== -1) {
42707 enabledHoverGroup.push(a);
42708 }
42709 };
42710 if(Array.isArray(buttonsToAdd)) {
42711 var newList = [];
42712 for(var i = 0; i < buttonsToAdd.length; i++) {
42713 var b = buttonsToAdd[i];
42714 if(typeof b === 'string') {
42715 b = b.toLowerCase();
42716
42717 if(DRAW_MODES.indexOf(b) !== -1) {
42718 // accept pre-defined drag modes i.e. shape drawing features as string
42719 if(
42720 fullLayout._has('mapbox') || // draw shapes in paper coordinate (could be improved in future to support data coordinate, when there is no pitch)
42721 fullLayout._has('cartesian') // draw shapes in data coordinate
42722 ) {
42723 dragModeGroup.push(b);
42724 }
42725 } else if(b === 'togglespikelines') {
42726 enableHover('toggleSpikelines');
42727 } else if(b === 'togglehover') {
42728 enableHover('toggleHover');
42729 } else if(b === 'hovercompare') {
42730 enableHover('hoverCompareCartesian');
42731 } else if(b === 'hoverclosest') {
42732 enableHover('hoverClosestCartesian');
42733 enableHover('hoverClosestGeo');
42734 enableHover('hoverClosest3d');
42735 enableHover('hoverClosestGl2d');
42736 enableHover('hoverClosestPie');
42737 } else if(b === 'v1hovermode') {
42738 enableHover('toggleHover');
42739 enableHover('hoverClosestCartesian');
42740 enableHover('hoverCompareCartesian');
42741 enableHover('hoverClosestGeo');
42742 enableHover('hoverClosest3d');
42743 enableHover('hoverClosestGl2d');
42744 enableHover('hoverClosestPie');
42745 }
42746 } else newList.push(b);
42747 }
42748 buttonsToAdd = newList;
42749 }
42750
42751 addGroup(dragModeGroup);
42752 addGroup(zoomGroup.concat(resetGroup));
42753 addGroup(enabledHoverGroup);
42754
42755 return appendButtonsToGroups(groups, buttonsToAdd);
42756}
42757
42758function areAllAxesFixed(fullLayout) {
42759 var axList = axisIds.list({_fullLayout: fullLayout}, null, true);
42760
42761 for(var i = 0; i < axList.length; i++) {
42762 if(!axList[i].fixedrange) {
42763 return false;
42764 }
42765 }
42766
42767 return true;
42768}
42769
42770// look for traces that support selection
42771// to be updated as we add more selectPoints handlers
42772function isSelectable(fullData) {
42773 var selectable = false;
42774
42775 for(var i = 0; i < fullData.length; i++) {
42776 if(selectable) break;
42777
42778 var trace = fullData[i];
42779
42780 if(!trace._module || !trace._module.selectPoints) continue;
42781
42782 if(Registry.traceIs(trace, 'scatter-like')) {
42783 if(scatterSubTypes.hasMarkers(trace) || scatterSubTypes.hasText(trace)) {
42784 selectable = true;
42785 }
42786 } else if(Registry.traceIs(trace, 'box-violin')) {
42787 if(trace.boxpoints === 'all' || trace.points === 'all') {
42788 selectable = true;
42789 }
42790 } else {
42791 // assume that in general if the trace module has selectPoints,
42792 // then it's selectable. Scatter is an exception to this because it must
42793 // have markers or text, not just be a scatter type.
42794
42795 selectable = true;
42796 }
42797 }
42798
42799 return selectable;
42800}
42801
42802// check whether all trace are 'noHover'
42803function hasNoHover(fullData) {
42804 for(var i = 0; i < fullData.length; i++) {
42805 if(!Registry.traceIs(fullData[i], 'noHover')) return false;
42806 }
42807 return true;
42808}
42809
42810function appendButtonsToGroups(groups, buttons) {
42811 if(buttons.length) {
42812 if(Array.isArray(buttons[0])) {
42813 for(var i = 0; i < buttons.length; i++) {
42814 groups.push(buttons[i]);
42815 }
42816 } else groups.push(buttons);
42817 }
42818
42819 return groups;
42820}
42821
42822// fill in custom buttons referring to default mode bar buttons
42823function fillCustomButton(customButtons) {
42824 for(var i = 0; i < customButtons.length; i++) {
42825 var buttonGroup = customButtons[i];
42826
42827 for(var j = 0; j < buttonGroup.length; j++) {
42828 var button = buttonGroup[j];
42829
42830 if(typeof button === 'string') {
42831 if(modeBarButtons[button] !== undefined) {
42832 customButtons[i][j] = modeBarButtons[button];
42833 } else {
42834 throw new Error([
42835 '*modeBarButtons* configuration options',
42836 'invalid button name'
42837 ].join(' '));
42838 }
42839 }
42840 }
42841 }
42842
42843 return customButtons;
42844}
42845
42846},{"../../plots/cartesian/axis_ids":338,"../../registry":376,"../../traces/scatter/subtypes":522,"../fx/helpers":193,"./buttons":217,"./constants":218,"./modebar":222}],222:[function(_dereq_,module,exports){
42847'use strict';
42848
42849var d3 = _dereq_('@plotly/d3');
42850var isNumeric = _dereq_('fast-isnumeric');
42851
42852var Lib = _dereq_('../../lib');
42853var Icons = _dereq_('../../fonts/ploticon');
42854var Parser = new DOMParser();
42855
42856/**
42857 * UI controller for interactive plots
42858 * @Class
42859 * @Param {object} opts
42860 * @Param {object} opts.buttons nested arrays of grouped buttons config objects
42861 * @Param {object} opts.container container div to append modeBar
42862 * @Param {object} opts.graphInfo primary plot object containing data and layout
42863 */
42864function ModeBar(opts) {
42865 this.container = opts.container;
42866 this.element = document.createElement('div');
42867
42868 this.update(opts.graphInfo, opts.buttons);
42869
42870 this.container.appendChild(this.element);
42871}
42872
42873var proto = ModeBar.prototype;
42874
42875/**
42876 * Update modeBar (buttons and logo)
42877 *
42878 * @param {object} graphInfo primary plot object containing data and layout
42879 * @param {array of arrays} buttons nested arrays of grouped buttons to initialize
42880 *
42881 */
42882proto.update = function(graphInfo, buttons) {
42883 this.graphInfo = graphInfo;
42884
42885 var context = this.graphInfo._context;
42886 var fullLayout = this.graphInfo._fullLayout;
42887 var modeBarId = 'modebar-' + fullLayout._uid;
42888
42889 this.element.setAttribute('id', modeBarId);
42890 this._uid = modeBarId;
42891
42892 this.element.className = 'modebar';
42893 if(context.displayModeBar === 'hover') this.element.className += ' modebar--hover ease-bg';
42894
42895 if(fullLayout.modebar.orientation === 'v') {
42896 this.element.className += ' vertical';
42897 buttons = buttons.reverse();
42898 }
42899
42900 var style = fullLayout.modebar;
42901 var bgSelector = context.displayModeBar === 'hover' ? '.js-plotly-plot .plotly:hover ' : '';
42902
42903 Lib.deleteRelatedStyleRule(modeBarId);
42904 Lib.addRelatedStyleRule(modeBarId, bgSelector + '#' + modeBarId + ' .modebar-group', 'background-color: ' + style.bgcolor);
42905 Lib.addRelatedStyleRule(modeBarId, '#' + modeBarId + ' .modebar-btn .icon path', 'fill: ' + style.color);
42906 Lib.addRelatedStyleRule(modeBarId, '#' + modeBarId + ' .modebar-btn:hover .icon path', 'fill: ' + style.activecolor);
42907 Lib.addRelatedStyleRule(modeBarId, '#' + modeBarId + ' .modebar-btn.active .icon path', 'fill: ' + style.activecolor);
42908
42909 // if buttons or logo have changed, redraw modebar interior
42910 var needsNewButtons = !this.hasButtons(buttons);
42911 var needsNewLogo = (this.hasLogo !== context.displaylogo);
42912 var needsNewLocale = (this.locale !== context.locale);
42913
42914 this.locale = context.locale;
42915
42916 if(needsNewButtons || needsNewLogo || needsNewLocale) {
42917 this.removeAllButtons();
42918
42919 this.updateButtons(buttons);
42920
42921 if(context.watermark || context.displaylogo) {
42922 var logoGroup = this.getLogo();
42923 if(context.watermark) {
42924 logoGroup.className = logoGroup.className + ' watermark';
42925 }
42926
42927 if(fullLayout.modebar.orientation === 'v') {
42928 this.element.insertBefore(logoGroup, this.element.childNodes[0]);
42929 } else {
42930 this.element.appendChild(logoGroup);
42931 }
42932
42933 this.hasLogo = true;
42934 }
42935 }
42936
42937 this.updateActiveButton();
42938};
42939
42940proto.updateButtons = function(buttons) {
42941 var _this = this;
42942
42943 this.buttons = buttons;
42944 this.buttonElements = [];
42945 this.buttonsNames = [];
42946
42947 this.buttons.forEach(function(buttonGroup) {
42948 var group = _this.createGroup();
42949
42950 buttonGroup.forEach(function(buttonConfig) {
42951 var buttonName = buttonConfig.name;
42952 if(!buttonName) {
42953 throw new Error('must provide button \'name\' in button config');
42954 }
42955 if(_this.buttonsNames.indexOf(buttonName) !== -1) {
42956 throw new Error('button name \'' + buttonName + '\' is taken');
42957 }
42958 _this.buttonsNames.push(buttonName);
42959
42960 var button = _this.createButton(buttonConfig);
42961 _this.buttonElements.push(button);
42962 group.appendChild(button);
42963 });
42964
42965 _this.element.appendChild(group);
42966 });
42967};
42968
42969/**
42970 * Empty div for containing a group of buttons
42971 * @Return {HTMLelement}
42972 */
42973proto.createGroup = function() {
42974 var group = document.createElement('div');
42975 group.className = 'modebar-group';
42976 return group;
42977};
42978
42979/**
42980 * Create a new button div and set constant and configurable attributes
42981 * @Param {object} config (see ./buttons.js for more info)
42982 * @Return {HTMLelement}
42983 */
42984proto.createButton = function(config) {
42985 var _this = this;
42986 var button = document.createElement('a');
42987
42988 button.setAttribute('rel', 'tooltip');
42989 button.className = 'modebar-btn';
42990
42991 var title = config.title;
42992 if(title === undefined) title = config.name;
42993 // for localization: allow title to be a callable that takes gd as arg
42994 else if(typeof title === 'function') title = title(this.graphInfo);
42995
42996 if(title || title === 0) button.setAttribute('data-title', title);
42997
42998 if(config.attr !== undefined) button.setAttribute('data-attr', config.attr);
42999
43000 var val = config.val;
43001 if(val !== undefined) {
43002 if(typeof val === 'function') val = val(this.graphInfo);
43003 button.setAttribute('data-val', val);
43004 }
43005
43006 var click = config.click;
43007 if(typeof click !== 'function') {
43008 throw new Error('must provide button \'click\' function in button config');
43009 } else {
43010 button.addEventListener('click', function(ev) {
43011 config.click(_this.graphInfo, ev);
43012
43013 // only needed for 'hoverClosestGeo' which does not call relayout
43014 _this.updateActiveButton(ev.currentTarget);
43015 });
43016 }
43017
43018 button.setAttribute('data-toggle', config.toggle || false);
43019 if(config.toggle) d3.select(button).classed('active', true);
43020
43021 var icon = config.icon;
43022 if(typeof icon === 'function') {
43023 button.appendChild(icon());
43024 } else {
43025 button.appendChild(this.createIcon(icon || Icons.question));
43026 }
43027 button.setAttribute('data-gravity', config.gravity || 'n');
43028
43029 return button;
43030};
43031
43032/**
43033 * Add an icon to a button
43034 * @Param {object} thisIcon
43035 * @Param {number} thisIcon.width
43036 * @Param {string} thisIcon.path
43037 * @Param {string} thisIcon.color
43038 * @Return {HTMLelement}
43039 */
43040proto.createIcon = function(thisIcon) {
43041 var iconHeight = isNumeric(thisIcon.height) ?
43042 Number(thisIcon.height) :
43043 thisIcon.ascent - thisIcon.descent;
43044 var svgNS = 'http://www.w3.org/2000/svg';
43045 var icon;
43046
43047 if(thisIcon.path) {
43048 icon = document.createElementNS(svgNS, 'svg');
43049 icon.setAttribute('viewBox', [0, 0, thisIcon.width, iconHeight].join(' '));
43050 icon.setAttribute('class', 'icon');
43051
43052 var path = document.createElementNS(svgNS, 'path');
43053 path.setAttribute('d', thisIcon.path);
43054
43055 if(thisIcon.transform) {
43056 path.setAttribute('transform', thisIcon.transform);
43057 } else if(thisIcon.ascent !== undefined) {
43058 // Legacy icon transform calculation
43059 path.setAttribute('transform', 'matrix(1 0 0 -1 0 ' + thisIcon.ascent + ')');
43060 }
43061
43062 icon.appendChild(path);
43063 }
43064
43065 if(thisIcon.svg) {
43066 var svgDoc = Parser.parseFromString(thisIcon.svg, 'application/xml');
43067 icon = svgDoc.childNodes[0];
43068 }
43069
43070 icon.setAttribute('height', '1em');
43071 icon.setAttribute('width', '1em');
43072
43073 return icon;
43074};
43075
43076/**
43077 * Updates active button with attribute specified in layout
43078 * @Param {object} graphInfo plot object containing data and layout
43079 * @Return {HTMLelement}
43080 */
43081proto.updateActiveButton = function(buttonClicked) {
43082 var fullLayout = this.graphInfo._fullLayout;
43083 var dataAttrClicked = (buttonClicked !== undefined) ?
43084 buttonClicked.getAttribute('data-attr') :
43085 null;
43086
43087 this.buttonElements.forEach(function(button) {
43088 var thisval = button.getAttribute('data-val') || true;
43089 var dataAttr = button.getAttribute('data-attr');
43090 var isToggleButton = (button.getAttribute('data-toggle') === 'true');
43091 var button3 = d3.select(button);
43092
43093 // Use 'data-toggle' and 'buttonClicked' to toggle buttons
43094 // that have no one-to-one equivalent in fullLayout
43095 if(isToggleButton) {
43096 if(dataAttr === dataAttrClicked) {
43097 button3.classed('active', !button3.classed('active'));
43098 }
43099 } else {
43100 var val = (dataAttr === null) ?
43101 dataAttr :
43102 Lib.nestedProperty(fullLayout, dataAttr).get();
43103
43104 button3.classed('active', val === thisval);
43105 }
43106 });
43107};
43108
43109/**
43110 * Check if modeBar is configured as button configuration argument
43111 *
43112 * @Param {object} buttons 2d array of grouped button config objects
43113 * @Return {boolean}
43114 */
43115proto.hasButtons = function(buttons) {
43116 var currentButtons = this.buttons;
43117
43118 if(!currentButtons) return false;
43119
43120 if(buttons.length !== currentButtons.length) return false;
43121
43122 for(var i = 0; i < buttons.length; ++i) {
43123 if(buttons[i].length !== currentButtons[i].length) return false;
43124 for(var j = 0; j < buttons[i].length; j++) {
43125 if(buttons[i][j].name !== currentButtons[i][j].name) return false;
43126 }
43127 }
43128
43129 return true;
43130};
43131
43132/**
43133 * @return {HTMLDivElement} The logo image wrapped in a group
43134 */
43135proto.getLogo = function() {
43136 var group = this.createGroup();
43137 var a = document.createElement('a');
43138
43139 a.href = 'https://plotly.com/';
43140 a.target = '_blank';
43141 a.setAttribute('data-title', Lib._(this.graphInfo, 'Produced with Plotly'));
43142 a.className = 'modebar-btn plotlyjsicon modebar-btn--logo';
43143
43144 a.appendChild(this.createIcon(Icons.newplotlylogo));
43145
43146 group.appendChild(a);
43147 return group;
43148};
43149
43150proto.removeAllButtons = function() {
43151 while(this.element.firstChild) {
43152 this.element.removeChild(this.element.firstChild);
43153 }
43154
43155 this.hasLogo = false;
43156};
43157
43158proto.destroy = function() {
43159 Lib.removeElement(this.container.querySelector('.modebar'));
43160 Lib.deleteRelatedStyleRule(this._uid);
43161};
43162
43163function createModeBar(gd, buttons) {
43164 var fullLayout = gd._fullLayout;
43165
43166 var modeBar = new ModeBar({
43167 graphInfo: gd,
43168 container: fullLayout._modebardiv.node(),
43169 buttons: buttons
43170 });
43171
43172 if(fullLayout._privateplot) {
43173 d3.select(modeBar.element).append('span')
43174 .classed('badge-private float--left', true)
43175 .text('PRIVATE');
43176 }
43177
43178 return modeBar;
43179}
43180
43181module.exports = createModeBar;
43182
43183},{"../../fonts/ploticon":270,"../../lib":287,"@plotly/d3":20,"fast-isnumeric":33}],223:[function(_dereq_,module,exports){
43184'use strict';
43185
43186var fontAttrs = _dereq_('../../plots/font_attributes');
43187var colorAttrs = _dereq_('../color/attributes');
43188var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray;
43189
43190var buttonAttrs = templatedArray('button', {
43191 visible: {
43192 valType: 'boolean',
43193 dflt: true,
43194 editType: 'plot',
43195 },
43196 step: {
43197 valType: 'enumerated',
43198 values: ['month', 'year', 'day', 'hour', 'minute', 'second', 'all'],
43199 dflt: 'month',
43200 editType: 'plot',
43201 },
43202 stepmode: {
43203 valType: 'enumerated',
43204 values: ['backward', 'todate'],
43205 dflt: 'backward',
43206 editType: 'plot',
43207 },
43208 count: {
43209 valType: 'number',
43210 min: 0,
43211 dflt: 1,
43212 editType: 'plot',
43213 },
43214 label: {
43215 valType: 'string',
43216 editType: 'plot',
43217 },
43218 editType: 'plot',
43219});
43220
43221module.exports = {
43222 visible: {
43223 valType: 'boolean',
43224 editType: 'plot',
43225 },
43226
43227 buttons: buttonAttrs,
43228
43229 x: {
43230 valType: 'number',
43231 min: -2,
43232 max: 3,
43233 editType: 'plot',
43234 },
43235 xanchor: {
43236 valType: 'enumerated',
43237 values: ['auto', 'left', 'center', 'right'],
43238 dflt: 'left',
43239 editType: 'plot',
43240 },
43241 y: {
43242 valType: 'number',
43243 min: -2,
43244 max: 3,
43245 editType: 'plot',
43246 },
43247 yanchor: {
43248 valType: 'enumerated',
43249 values: ['auto', 'top', 'middle', 'bottom'],
43250 dflt: 'bottom',
43251 editType: 'plot',
43252 },
43253
43254 font: fontAttrs({
43255 editType: 'plot',
43256 }),
43257
43258 bgcolor: {
43259 valType: 'color',
43260 dflt: colorAttrs.lightLine,
43261 editType: 'plot',
43262 },
43263 activecolor: {
43264 valType: 'color',
43265 editType: 'plot',
43266 },
43267 bordercolor: {
43268 valType: 'color',
43269 dflt: colorAttrs.defaultLine,
43270 editType: 'plot',
43271 },
43272 borderwidth: {
43273 valType: 'number',
43274 min: 0,
43275 dflt: 0,
43276 editType: 'plot',
43277 },
43278 editType: 'plot'
43279};
43280
43281},{"../../plot_api/plot_template":323,"../../plots/font_attributes":363,"../color/attributes":156}],224:[function(_dereq_,module,exports){
43282'use strict';
43283
43284
43285module.exports = {
43286
43287 // 'y' position pad above counter axis domain
43288 yPad: 0.02,
43289
43290 // minimum button width (regardless of text size)
43291 minButtonWidth: 30,
43292
43293 // buttons rect radii
43294 rx: 3,
43295 ry: 3,
43296
43297 // light fraction used to compute the 'activecolor' default
43298 lightAmount: 25,
43299 darkAmount: 10
43300};
43301
43302},{}],225:[function(_dereq_,module,exports){
43303'use strict';
43304
43305var Lib = _dereq_('../../lib');
43306var Color = _dereq_('../color');
43307var Template = _dereq_('../../plot_api/plot_template');
43308var handleArrayContainerDefaults = _dereq_('../../plots/array_container_defaults');
43309
43310var attributes = _dereq_('./attributes');
43311var constants = _dereq_('./constants');
43312
43313
43314module.exports = function handleDefaults(containerIn, containerOut, layout, counterAxes, calendar) {
43315 var selectorIn = containerIn.rangeselector || {};
43316 var selectorOut = Template.newContainer(containerOut, 'rangeselector');
43317
43318 function coerce(attr, dflt) {
43319 return Lib.coerce(selectorIn, selectorOut, attributes, attr, dflt);
43320 }
43321
43322 var buttons = handleArrayContainerDefaults(selectorIn, selectorOut, {
43323 name: 'buttons',
43324 handleItemDefaults: buttonDefaults,
43325 calendar: calendar
43326 });
43327
43328 var visible = coerce('visible', buttons.length > 0);
43329 if(visible) {
43330 var posDflt = getPosDflt(containerOut, layout, counterAxes);
43331 coerce('x', posDflt[0]);
43332 coerce('y', posDflt[1]);
43333 Lib.noneOrAll(containerIn, containerOut, ['x', 'y']);
43334
43335 coerce('xanchor');
43336 coerce('yanchor');
43337
43338 Lib.coerceFont(coerce, 'font', layout.font);
43339
43340 var bgColor = coerce('bgcolor');
43341 coerce('activecolor', Color.contrast(bgColor, constants.lightAmount, constants.darkAmount));
43342 coerce('bordercolor');
43343 coerce('borderwidth');
43344 }
43345};
43346
43347function buttonDefaults(buttonIn, buttonOut, selectorOut, opts) {
43348 var calendar = opts.calendar;
43349
43350 function coerce(attr, dflt) {
43351 return Lib.coerce(buttonIn, buttonOut, attributes.buttons, attr, dflt);
43352 }
43353
43354 var visible = coerce('visible');
43355
43356 if(visible) {
43357 var step = coerce('step');
43358 if(step !== 'all') {
43359 if(calendar && calendar !== 'gregorian' && (step === 'month' || step === 'year')) {
43360 buttonOut.stepmode = 'backward';
43361 } else {
43362 coerce('stepmode');
43363 }
43364
43365 coerce('count');
43366 }
43367
43368 coerce('label');
43369 }
43370}
43371
43372function getPosDflt(containerOut, layout, counterAxes) {
43373 var anchoredList = counterAxes.filter(function(ax) {
43374 return layout[ax].anchor === containerOut._id;
43375 });
43376
43377 var posY = 0;
43378 for(var i = 0; i < anchoredList.length; i++) {
43379 var domain = layout[anchoredList[i]].domain;
43380 if(domain) posY = Math.max(domain[1], posY);
43381 }
43382
43383 return [containerOut.domain[0], posY + constants.yPad];
43384}
43385
43386},{"../../lib":287,"../../plot_api/plot_template":323,"../../plots/array_container_defaults":329,"../color":157,"./attributes":223,"./constants":224}],226:[function(_dereq_,module,exports){
43387'use strict';
43388
43389var d3 = _dereq_('@plotly/d3');
43390
43391var Registry = _dereq_('../../registry');
43392var Plots = _dereq_('../../plots/plots');
43393var Color = _dereq_('../color');
43394var Drawing = _dereq_('../drawing');
43395var Lib = _dereq_('../../lib');
43396var strTranslate = Lib.strTranslate;
43397var svgTextUtils = _dereq_('../../lib/svg_text_utils');
43398var axisIds = _dereq_('../../plots/cartesian/axis_ids');
43399
43400var alignmentConstants = _dereq_('../../constants/alignment');
43401var LINE_SPACING = alignmentConstants.LINE_SPACING;
43402var FROM_TL = alignmentConstants.FROM_TL;
43403var FROM_BR = alignmentConstants.FROM_BR;
43404
43405var constants = _dereq_('./constants');
43406var getUpdateObject = _dereq_('./get_update_object');
43407
43408module.exports = function draw(gd) {
43409 var fullLayout = gd._fullLayout;
43410
43411 var selectors = fullLayout._infolayer.selectAll('.rangeselector')
43412 .data(makeSelectorData(gd), selectorKeyFunc);
43413
43414 selectors.enter().append('g')
43415 .classed('rangeselector', true);
43416
43417 selectors.exit().remove();
43418
43419 selectors.style({
43420 cursor: 'pointer',
43421 'pointer-events': 'all'
43422 });
43423
43424 selectors.each(function(d) {
43425 var selector = d3.select(this);
43426 var axisLayout = d;
43427 var selectorLayout = axisLayout.rangeselector;
43428
43429 var buttons = selector.selectAll('g.button')
43430 .data(Lib.filterVisible(selectorLayout.buttons));
43431
43432 buttons.enter().append('g')
43433 .classed('button', true);
43434
43435 buttons.exit().remove();
43436
43437 buttons.each(function(d) {
43438 var button = d3.select(this);
43439 var update = getUpdateObject(axisLayout, d);
43440
43441 d._isActive = isActive(axisLayout, d, update);
43442
43443 button.call(drawButtonRect, selectorLayout, d);
43444 button.call(drawButtonText, selectorLayout, d, gd);
43445
43446 button.on('click', function() {
43447 if(gd._dragged) return;
43448
43449 Registry.call('_guiRelayout', gd, update);
43450 });
43451
43452 button.on('mouseover', function() {
43453 d._isHovered = true;
43454 button.call(drawButtonRect, selectorLayout, d);
43455 });
43456
43457 button.on('mouseout', function() {
43458 d._isHovered = false;
43459 button.call(drawButtonRect, selectorLayout, d);
43460 });
43461 });
43462
43463 reposition(gd, buttons, selectorLayout, axisLayout._name, selector);
43464 });
43465};
43466
43467function makeSelectorData(gd) {
43468 var axes = axisIds.list(gd, 'x', true);
43469 var data = [];
43470
43471 for(var i = 0; i < axes.length; i++) {
43472 var axis = axes[i];
43473
43474 if(axis.rangeselector && axis.rangeselector.visible) {
43475 data.push(axis);
43476 }
43477 }
43478
43479 return data;
43480}
43481
43482function selectorKeyFunc(d) {
43483 return d._id;
43484}
43485
43486function isActive(axisLayout, opts, update) {
43487 if(opts.step === 'all') {
43488 return axisLayout.autorange === true;
43489 } else {
43490 var keys = Object.keys(update);
43491
43492 return (
43493 axisLayout.range[0] === update[keys[0]] &&
43494 axisLayout.range[1] === update[keys[1]]
43495 );
43496 }
43497}
43498
43499function drawButtonRect(button, selectorLayout, d) {
43500 var rect = Lib.ensureSingle(button, 'rect', 'selector-rect', function(s) {
43501 s.attr('shape-rendering', 'crispEdges');
43502 });
43503
43504 rect.attr({
43505 'rx': constants.rx,
43506 'ry': constants.ry
43507 });
43508
43509 rect.call(Color.stroke, selectorLayout.bordercolor)
43510 .call(Color.fill, getFillColor(selectorLayout, d))
43511 .style('stroke-width', selectorLayout.borderwidth + 'px');
43512}
43513
43514function getFillColor(selectorLayout, d) {
43515 return (d._isActive || d._isHovered) ?
43516 selectorLayout.activecolor :
43517 selectorLayout.bgcolor;
43518}
43519
43520function drawButtonText(button, selectorLayout, d, gd) {
43521 function textLayout(s) {
43522 svgTextUtils.convertToTspans(s, gd);
43523 }
43524
43525 var text = Lib.ensureSingle(button, 'text', 'selector-text', function(s) {
43526 s.attr('text-anchor', 'middle');
43527 });
43528
43529 text.call(Drawing.font, selectorLayout.font)
43530 .text(getLabel(d, gd._fullLayout._meta))
43531 .call(textLayout);
43532}
43533
43534function getLabel(opts, _meta) {
43535 if(opts.label) {
43536 return _meta ?
43537 Lib.templateString(opts.label, _meta) :
43538 opts.label;
43539 }
43540
43541 if(opts.step === 'all') return 'all';
43542
43543 return opts.count + opts.step.charAt(0);
43544}
43545
43546function reposition(gd, buttons, opts, axName, selector) {
43547 var width = 0;
43548 var height = 0;
43549
43550 var borderWidth = opts.borderwidth;
43551
43552 buttons.each(function() {
43553 var button = d3.select(this);
43554 var text = button.select('.selector-text');
43555
43556 var tHeight = opts.font.size * LINE_SPACING;
43557 var hEff = Math.max(tHeight * svgTextUtils.lineCount(text), 16) + 3;
43558
43559 height = Math.max(height, hEff);
43560 });
43561
43562 buttons.each(function() {
43563 var button = d3.select(this);
43564 var rect = button.select('.selector-rect');
43565 var text = button.select('.selector-text');
43566
43567 var tWidth = text.node() && Drawing.bBox(text.node()).width;
43568 var tHeight = opts.font.size * LINE_SPACING;
43569 var tLines = svgTextUtils.lineCount(text);
43570
43571 var wEff = Math.max(tWidth + 10, constants.minButtonWidth);
43572
43573 // TODO add MathJax support
43574
43575 // TODO add buttongap attribute
43576
43577 button.attr('transform', strTranslate(borderWidth + width, borderWidth));
43578
43579 rect.attr({
43580 x: 0,
43581 y: 0,
43582 width: wEff,
43583 height: height
43584 });
43585
43586 svgTextUtils.positionText(text, wEff / 2,
43587 height / 2 - ((tLines - 1) * tHeight / 2) + 3);
43588
43589 width += wEff + 5;
43590 });
43591
43592 var graphSize = gd._fullLayout._size;
43593 var lx = graphSize.l + graphSize.w * opts.x;
43594 var ly = graphSize.t + graphSize.h * (1 - opts.y);
43595
43596 var xanchor = 'left';
43597 if(Lib.isRightAnchor(opts)) {
43598 lx -= width;
43599 xanchor = 'right';
43600 }
43601 if(Lib.isCenterAnchor(opts)) {
43602 lx -= width / 2;
43603 xanchor = 'center';
43604 }
43605
43606 var yanchor = 'top';
43607 if(Lib.isBottomAnchor(opts)) {
43608 ly -= height;
43609 yanchor = 'bottom';
43610 }
43611 if(Lib.isMiddleAnchor(opts)) {
43612 ly -= height / 2;
43613 yanchor = 'middle';
43614 }
43615
43616 width = Math.ceil(width);
43617 height = Math.ceil(height);
43618 lx = Math.round(lx);
43619 ly = Math.round(ly);
43620
43621 Plots.autoMargin(gd, axName + '-range-selector', {
43622 x: opts.x,
43623 y: opts.y,
43624 l: width * FROM_TL[xanchor],
43625 r: width * FROM_BR[xanchor],
43626 b: height * FROM_BR[yanchor],
43627 t: height * FROM_TL[yanchor]
43628 });
43629
43630 selector.attr('transform', strTranslate(lx, ly));
43631}
43632
43633},{"../../constants/alignment":262,"../../lib":287,"../../lib/svg_text_utils":310,"../../plots/cartesian/axis_ids":338,"../../plots/plots":369,"../../registry":376,"../color":157,"../drawing":179,"./constants":224,"./get_update_object":227,"@plotly/d3":20}],227:[function(_dereq_,module,exports){
43634'use strict';
43635
43636var d3Time = _dereq_('d3-time');
43637var titleCase = _dereq_('../../lib').titleCase;
43638
43639module.exports = function getUpdateObject(axisLayout, buttonLayout) {
43640 var axName = axisLayout._name;
43641 var update = {};
43642
43643 if(buttonLayout.step === 'all') {
43644 update[axName + '.autorange'] = true;
43645 } else {
43646 var xrange = getXRange(axisLayout, buttonLayout);
43647
43648 update[axName + '.range[0]'] = xrange[0];
43649 update[axName + '.range[1]'] = xrange[1];
43650 }
43651
43652 return update;
43653};
43654
43655function getXRange(axisLayout, buttonLayout) {
43656 var currentRange = axisLayout.range;
43657 var base = new Date(axisLayout.r2l(currentRange[1]));
43658 var step = buttonLayout.step;
43659
43660 var utcStep = d3Time['utc' + titleCase(step)];
43661
43662 var count = buttonLayout.count;
43663 var range0;
43664
43665 switch(buttonLayout.stepmode) {
43666 case 'backward':
43667 range0 = axisLayout.l2r(+utcStep.offset(base, -count));
43668 break;
43669
43670 case 'todate':
43671 var base2 = utcStep.offset(base, -count);
43672
43673 range0 = axisLayout.l2r(+utcStep.ceil(base2));
43674 break;
43675 }
43676
43677 var range1 = currentRange[1];
43678
43679 return [range0, range1];
43680}
43681
43682},{"../../lib":287,"d3-time":32}],228:[function(_dereq_,module,exports){
43683'use strict';
43684
43685module.exports = {
43686 moduleType: 'component',
43687 name: 'rangeselector',
43688
43689 schema: {
43690 subplots: {
43691 xaxis: {rangeselector: _dereq_('./attributes')}
43692 }
43693 },
43694
43695 layoutAttributes: _dereq_('./attributes'),
43696 handleDefaults: _dereq_('./defaults'),
43697
43698 draw: _dereq_('./draw')
43699};
43700
43701},{"./attributes":223,"./defaults":225,"./draw":226}],229:[function(_dereq_,module,exports){
43702'use strict';
43703
43704var colorAttributes = _dereq_('../color/attributes');
43705
43706module.exports = {
43707 bgcolor: {
43708 valType: 'color',
43709 dflt: colorAttributes.background,
43710 editType: 'plot',
43711 },
43712 bordercolor: {
43713 valType: 'color',
43714 dflt: colorAttributes.defaultLine,
43715 editType: 'plot',
43716 },
43717 borderwidth: {
43718 valType: 'integer',
43719 dflt: 0,
43720 min: 0,
43721 editType: 'plot',
43722 },
43723 autorange: {
43724 valType: 'boolean',
43725 dflt: true,
43726 editType: 'calc',
43727 impliedEdits: {'range[0]': undefined, 'range[1]': undefined},
43728 },
43729 range: {
43730 valType: 'info_array',
43731 items: [
43732 {valType: 'any', editType: 'calc', impliedEdits: {'^autorange': false}},
43733 {valType: 'any', editType: 'calc', impliedEdits: {'^autorange': false}}
43734 ],
43735 editType: 'calc',
43736 impliedEdits: {'autorange': false},
43737 },
43738 thickness: {
43739 valType: 'number',
43740 dflt: 0.15,
43741 min: 0,
43742 max: 1,
43743 editType: 'plot',
43744 },
43745 visible: {
43746 valType: 'boolean',
43747 dflt: true,
43748 editType: 'calc',
43749 },
43750 editType: 'calc'
43751};
43752
43753},{"../color/attributes":156}],230:[function(_dereq_,module,exports){
43754'use strict';
43755
43756var listAxes = _dereq_('../../plots/cartesian/axis_ids').list;
43757var getAutoRange = _dereq_('../../plots/cartesian/autorange').getAutoRange;
43758var constants = _dereq_('./constants');
43759
43760module.exports = function calcAutorange(gd) {
43761 var axes = listAxes(gd, 'x', true);
43762
43763 // Compute new slider range using axis autorange if necessary.
43764 //
43765 // Copy back range to input range slider container to skip
43766 // this step in subsequent draw calls.
43767
43768 for(var i = 0; i < axes.length; i++) {
43769 var ax = axes[i];
43770 var opts = ax[constants.name];
43771
43772 if(opts && opts.visible && opts.autorange) {
43773 opts._input.autorange = true;
43774 opts._input.range = opts.range = getAutoRange(gd, ax);
43775 }
43776 }
43777};
43778
43779},{"../../plots/cartesian/autorange":333,"../../plots/cartesian/axis_ids":338,"./constants":231}],231:[function(_dereq_,module,exports){
43780'use strict';
43781
43782module.exports = {
43783
43784 // attribute container name
43785 name: 'rangeslider',
43786
43787 // class names
43788
43789 containerClassName: 'rangeslider-container',
43790 bgClassName: 'rangeslider-bg',
43791 rangePlotClassName: 'rangeslider-rangeplot',
43792
43793 maskMinClassName: 'rangeslider-mask-min',
43794 maskMaxClassName: 'rangeslider-mask-max',
43795 slideBoxClassName: 'rangeslider-slidebox',
43796
43797 grabberMinClassName: 'rangeslider-grabber-min',
43798 grabAreaMinClassName: 'rangeslider-grabarea-min',
43799 handleMinClassName: 'rangeslider-handle-min',
43800
43801 grabberMaxClassName: 'rangeslider-grabber-max',
43802 grabAreaMaxClassName: 'rangeslider-grabarea-max',
43803 handleMaxClassName: 'rangeslider-handle-max',
43804
43805 maskMinOppAxisClassName: 'rangeslider-mask-min-opp-axis',
43806 maskMaxOppAxisClassName: 'rangeslider-mask-max-opp-axis',
43807
43808 // style constants
43809
43810 maskColor: 'rgba(0,0,0,0.4)',
43811 maskOppAxisColor: 'rgba(0,0,0,0.2)',
43812
43813 slideBoxFill: 'transparent',
43814 slideBoxCursor: 'ew-resize',
43815
43816 grabAreaFill: 'transparent',
43817 grabAreaCursor: 'col-resize',
43818 grabAreaWidth: 10,
43819
43820 handleWidth: 4,
43821 handleRadius: 1,
43822 handleStrokeWidth: 1,
43823
43824 extraPad: 15
43825};
43826
43827},{}],232:[function(_dereq_,module,exports){
43828'use strict';
43829
43830var Lib = _dereq_('../../lib');
43831var Template = _dereq_('../../plot_api/plot_template');
43832var axisIds = _dereq_('../../plots/cartesian/axis_ids');
43833
43834var attributes = _dereq_('./attributes');
43835var oppAxisAttrs = _dereq_('./oppaxis_attributes');
43836
43837module.exports = function handleDefaults(layoutIn, layoutOut, axName) {
43838 var axIn = layoutIn[axName];
43839 var axOut = layoutOut[axName];
43840
43841 if(!(axIn.rangeslider || layoutOut._requestRangeslider[axOut._id])) return;
43842
43843 // not super proud of this (maybe store _ in axis object instead
43844 if(!Lib.isPlainObject(axIn.rangeslider)) {
43845 axIn.rangeslider = {};
43846 }
43847
43848 var containerIn = axIn.rangeslider;
43849 var containerOut = Template.newContainer(axOut, 'rangeslider');
43850
43851 function coerce(attr, dflt) {
43852 return Lib.coerce(containerIn, containerOut, attributes, attr, dflt);
43853 }
43854
43855 var rangeContainerIn, rangeContainerOut;
43856 function coerceRange(attr, dflt) {
43857 return Lib.coerce(rangeContainerIn, rangeContainerOut, oppAxisAttrs, attr, dflt);
43858 }
43859
43860 var visible = coerce('visible');
43861 if(!visible) return;
43862
43863 coerce('bgcolor', layoutOut.plot_bgcolor);
43864 coerce('bordercolor');
43865 coerce('borderwidth');
43866 coerce('thickness');
43867
43868 coerce('autorange', !axOut.isValidRange(containerIn.range));
43869 coerce('range');
43870
43871 var subplots = layoutOut._subplots;
43872 if(subplots) {
43873 var yIds = subplots.cartesian
43874 .filter(function(subplotId) {
43875 return subplotId.substr(0, subplotId.indexOf('y')) === axisIds.name2id(axName);
43876 })
43877 .map(function(subplotId) {
43878 return subplotId.substr(subplotId.indexOf('y'), subplotId.length);
43879 });
43880 var yNames = Lib.simpleMap(yIds, axisIds.id2name);
43881 for(var i = 0; i < yNames.length; i++) {
43882 var yName = yNames[i];
43883
43884 rangeContainerIn = containerIn[yName] || {};
43885 rangeContainerOut = Template.newContainer(containerOut, yName, 'yaxis');
43886
43887 var yAxOut = layoutOut[yName];
43888
43889 var rangemodeDflt;
43890 if(rangeContainerIn.range && yAxOut.isValidRange(rangeContainerIn.range)) {
43891 rangemodeDflt = 'fixed';
43892 }
43893
43894 var rangeMode = coerceRange('rangemode', rangemodeDflt);
43895 if(rangeMode !== 'match') {
43896 coerceRange('range', yAxOut.range.slice());
43897 }
43898 }
43899 }
43900
43901 // to map back range slider (auto) range
43902 containerOut._input = containerIn;
43903};
43904
43905},{"../../lib":287,"../../plot_api/plot_template":323,"../../plots/cartesian/axis_ids":338,"./attributes":229,"./oppaxis_attributes":236}],233:[function(_dereq_,module,exports){
43906'use strict';
43907
43908var d3 = _dereq_('@plotly/d3');
43909
43910var Registry = _dereq_('../../registry');
43911var Plots = _dereq_('../../plots/plots');
43912
43913var Lib = _dereq_('../../lib');
43914var strTranslate = Lib.strTranslate;
43915var Drawing = _dereq_('../drawing');
43916var Color = _dereq_('../color');
43917var Titles = _dereq_('../titles');
43918
43919var Cartesian = _dereq_('../../plots/cartesian');
43920var axisIDs = _dereq_('../../plots/cartesian/axis_ids');
43921
43922var dragElement = _dereq_('../dragelement');
43923var setCursor = _dereq_('../../lib/setcursor');
43924
43925var constants = _dereq_('./constants');
43926
43927module.exports = function(gd) {
43928 var fullLayout = gd._fullLayout;
43929 var rangeSliderData = fullLayout._rangeSliderData;
43930 for(var i = 0; i < rangeSliderData.length; i++) {
43931 var opts = rangeSliderData[i][constants.name];
43932 // fullLayout._uid may not exist when we call makeData
43933 opts._clipId = opts._id + '-' + fullLayout._uid;
43934 }
43935
43936 /*
43937 * <g container />
43938 * <rect bg />
43939 * < .... range plot />
43940 * <rect mask-min />
43941 * <rect mask-max />
43942 * <rect slidebox />
43943 * <g grabber-min />
43944 * <rect handle-min />
43945 * <rect grabare-min />
43946 * <g grabber-max />
43947 * <rect handle-max />
43948 * <rect grabare-max />
43949 *
43950 * ...
43951 */
43952
43953 function keyFunction(axisOpts) {
43954 return axisOpts._name;
43955 }
43956
43957 var rangeSliders = fullLayout._infolayer
43958 .selectAll('g.' + constants.containerClassName)
43959 .data(rangeSliderData, keyFunction);
43960
43961 // remove exiting sliders and their corresponding clip paths
43962 rangeSliders.exit().each(function(axisOpts) {
43963 var opts = axisOpts[constants.name];
43964 fullLayout._topdefs.select('#' + opts._clipId).remove();
43965 }).remove();
43966
43967 // return early if no range slider is visible
43968 if(rangeSliderData.length === 0) return;
43969
43970 rangeSliders.enter().append('g')
43971 .classed(constants.containerClassName, true)
43972 .attr('pointer-events', 'all');
43973
43974 // for all present range sliders
43975 rangeSliders.each(function(axisOpts) {
43976 var rangeSlider = d3.select(this);
43977 var opts = axisOpts[constants.name];
43978 var oppAxisOpts = fullLayout[axisIDs.id2name(axisOpts.anchor)];
43979 var oppAxisRangeOpts = opts[axisIDs.id2name(axisOpts.anchor)];
43980
43981 // update range
43982 // Expand slider range to the axis range
43983 if(opts.range) {
43984 var rng = Lib.simpleMap(opts.range, axisOpts.r2l);
43985 var axRng = Lib.simpleMap(axisOpts.range, axisOpts.r2l);
43986 var newRng;
43987
43988 if(axRng[0] < axRng[1]) {
43989 newRng = [
43990 Math.min(rng[0], axRng[0]),
43991 Math.max(rng[1], axRng[1])
43992 ];
43993 } else {
43994 newRng = [
43995 Math.max(rng[0], axRng[0]),
43996 Math.min(rng[1], axRng[1])
43997 ];
43998 }
43999
44000 opts.range = opts._input.range = Lib.simpleMap(newRng, axisOpts.l2r);
44001 }
44002
44003 axisOpts.cleanRange('rangeslider.range');
44004
44005 // update range slider dimensions
44006
44007 var gs = fullLayout._size;
44008 var domain = axisOpts.domain;
44009
44010 opts._width = gs.w * (domain[1] - domain[0]);
44011
44012 var x = Math.round(gs.l + (gs.w * domain[0]));
44013
44014 var y = Math.round(
44015 gs.t + gs.h * (1 - axisOpts._counterDomainMin) +
44016 (axisOpts.side === 'bottom' ? axisOpts._depth : 0) +
44017 opts._offsetShift + constants.extraPad
44018 );
44019
44020 rangeSlider.attr('transform', strTranslate(x, y));
44021
44022 // update data <--> pixel coordinate conversion methods
44023
44024 opts._rl = Lib.simpleMap(opts.range, axisOpts.r2l);
44025 var rl0 = opts._rl[0];
44026 var rl1 = opts._rl[1];
44027 var drl = rl1 - rl0;
44028
44029 opts.p2d = function(v) {
44030 return (v / opts._width) * drl + rl0;
44031 };
44032
44033 opts.d2p = function(v) {
44034 return (v - rl0) / drl * opts._width;
44035 };
44036
44037 if(axisOpts.rangebreaks) {
44038 var rsBreaks = axisOpts.locateBreaks(rl0, rl1);
44039
44040 if(rsBreaks.length) {
44041 var j, brk;
44042
44043 var lBreaks = 0;
44044 for(j = 0; j < rsBreaks.length; j++) {
44045 brk = rsBreaks[j];
44046 lBreaks += (brk.max - brk.min);
44047 }
44048
44049 // TODO fix for reversed-range axes !!!
44050
44051 // compute slope and piecewise offsets
44052 var m2 = opts._width / (rl1 - rl0 - lBreaks);
44053 var _B = [-m2 * rl0];
44054 for(j = 0; j < rsBreaks.length; j++) {
44055 brk = rsBreaks[j];
44056 _B.push(_B[_B.length - 1] - m2 * (brk.max - brk.min));
44057 }
44058
44059 opts.d2p = function(v) {
44060 var b = _B[0];
44061 for(var j = 0; j < rsBreaks.length; j++) {
44062 var brk = rsBreaks[j];
44063 if(v >= brk.max) b = _B[j + 1];
44064 else if(v < brk.min) break;
44065 }
44066 return b + m2 * v;
44067 };
44068
44069 // fill pixel (i.e. 'p') min/max here,
44070 // to not have to loop through the _rangebreaks twice during `p2d`
44071 for(j = 0; j < rsBreaks.length; j++) {
44072 brk = rsBreaks[j];
44073 brk.pmin = opts.d2p(brk.min);
44074 brk.pmax = opts.d2p(brk.max);
44075 }
44076
44077 opts.p2d = function(v) {
44078 var b = _B[0];
44079 for(var j = 0; j < rsBreaks.length; j++) {
44080 var brk = rsBreaks[j];
44081 if(v >= brk.pmax) b = _B[j + 1];
44082 else if(v < brk.pmin) break;
44083 }
44084 return (v - b) / m2;
44085 };
44086 }
44087 }
44088
44089 if(oppAxisRangeOpts.rangemode !== 'match') {
44090 var range0OppAxis = oppAxisOpts.r2l(oppAxisRangeOpts.range[0]);
44091 var range1OppAxis = oppAxisOpts.r2l(oppAxisRangeOpts.range[1]);
44092 var distOppAxis = range1OppAxis - range0OppAxis;
44093
44094 opts.d2pOppAxis = function(v) {
44095 return (v - range0OppAxis) / distOppAxis * opts._height;
44096 };
44097 }
44098
44099 // update inner nodes
44100
44101 rangeSlider
44102 .call(drawBg, gd, axisOpts, opts)
44103 .call(addClipPath, gd, axisOpts, opts)
44104 .call(drawRangePlot, gd, axisOpts, opts)
44105 .call(drawMasks, gd, axisOpts, opts, oppAxisRangeOpts)
44106 .call(drawSlideBox, gd, axisOpts, opts)
44107 .call(drawGrabbers, gd, axisOpts, opts);
44108
44109 // setup drag element
44110 setupDragElement(rangeSlider, gd, axisOpts, opts);
44111
44112 // update current range
44113 setPixelRange(rangeSlider, gd, axisOpts, opts, oppAxisOpts, oppAxisRangeOpts);
44114
44115 // title goes next to range slider instead of tick labels, so
44116 // just take it over and draw it from here
44117 if(axisOpts.side === 'bottom') {
44118 Titles.draw(gd, axisOpts._id + 'title', {
44119 propContainer: axisOpts,
44120 propName: axisOpts._name + '.title',
44121 placeholder: fullLayout._dfltTitle.x,
44122 attributes: {
44123 x: axisOpts._offset + axisOpts._length / 2,
44124 y: y + opts._height + opts._offsetShift + 10 + 1.5 * axisOpts.title.font.size,
44125 'text-anchor': 'middle'
44126 }
44127 });
44128 }
44129 });
44130};
44131
44132function setupDragElement(rangeSlider, gd, axisOpts, opts) {
44133 if(gd._context.staticPlot) return;
44134
44135 var slideBox = rangeSlider.select('rect.' + constants.slideBoxClassName).node();
44136 var grabAreaMin = rangeSlider.select('rect.' + constants.grabAreaMinClassName).node();
44137 var grabAreaMax = rangeSlider.select('rect.' + constants.grabAreaMaxClassName).node();
44138
44139 function mouseDownHandler() {
44140 var event = d3.event;
44141 var target = event.target;
44142 var startX = event.clientX || event.touches[0].clientX;
44143 var offsetX = startX - rangeSlider.node().getBoundingClientRect().left;
44144 var minVal = opts.d2p(axisOpts._rl[0]);
44145 var maxVal = opts.d2p(axisOpts._rl[1]);
44146
44147 var dragCover = dragElement.coverSlip();
44148
44149 this.addEventListener('touchmove', mouseMove);
44150 this.addEventListener('touchend', mouseUp);
44151 dragCover.addEventListener('mousemove', mouseMove);
44152 dragCover.addEventListener('mouseup', mouseUp);
44153
44154 function mouseMove(e) {
44155 var clientX = e.clientX || e.touches[0].clientX;
44156 var delta = +clientX - startX;
44157 var pixelMin, pixelMax, cursor;
44158
44159 switch(target) {
44160 case slideBox:
44161 cursor = 'ew-resize';
44162 pixelMin = minVal + delta;
44163 pixelMax = maxVal + delta;
44164 break;
44165
44166 case grabAreaMin:
44167 cursor = 'col-resize';
44168 pixelMin = minVal + delta;
44169 pixelMax = maxVal;
44170 break;
44171
44172 case grabAreaMax:
44173 cursor = 'col-resize';
44174 pixelMin = minVal;
44175 pixelMax = maxVal + delta;
44176 break;
44177
44178 default:
44179 cursor = 'ew-resize';
44180 pixelMin = offsetX;
44181 pixelMax = offsetX + delta;
44182 break;
44183 }
44184
44185 if(pixelMax < pixelMin) {
44186 var tmp = pixelMax;
44187 pixelMax = pixelMin;
44188 pixelMin = tmp;
44189 }
44190
44191 opts._pixelMin = pixelMin;
44192 opts._pixelMax = pixelMax;
44193
44194 setCursor(d3.select(dragCover), cursor);
44195 setDataRange(rangeSlider, gd, axisOpts, opts);
44196 }
44197
44198 function mouseUp() {
44199 dragCover.removeEventListener('mousemove', mouseMove);
44200 dragCover.removeEventListener('mouseup', mouseUp);
44201 this.removeEventListener('touchmove', mouseMove);
44202 this.removeEventListener('touchend', mouseUp);
44203 Lib.removeElement(dragCover);
44204 }
44205 }
44206
44207 rangeSlider.on('mousedown', mouseDownHandler);
44208 rangeSlider.on('touchstart', mouseDownHandler);
44209}
44210
44211function setDataRange(rangeSlider, gd, axisOpts, opts) {
44212 function clamp(v) {
44213 return axisOpts.l2r(Lib.constrain(v, opts._rl[0], opts._rl[1]));
44214 }
44215
44216 var dataMin = clamp(opts.p2d(opts._pixelMin));
44217 var dataMax = clamp(opts.p2d(opts._pixelMax));
44218
44219 window.requestAnimationFrame(function() {
44220 Registry.call('_guiRelayout', gd, axisOpts._name + '.range', [dataMin, dataMax]);
44221 });
44222}
44223
44224function setPixelRange(rangeSlider, gd, axisOpts, opts, oppAxisOpts, oppAxisRangeOpts) {
44225 var hw2 = constants.handleWidth / 2;
44226
44227 function clamp(v) {
44228 return Lib.constrain(v, 0, opts._width);
44229 }
44230
44231 function clampOppAxis(v) {
44232 return Lib.constrain(v, 0, opts._height);
44233 }
44234
44235 function clampHandle(v) {
44236 return Lib.constrain(v, -hw2, opts._width + hw2);
44237 }
44238
44239 var pixelMin = clamp(opts.d2p(axisOpts._rl[0]));
44240 var pixelMax = clamp(opts.d2p(axisOpts._rl[1]));
44241
44242 rangeSlider.select('rect.' + constants.slideBoxClassName)
44243 .attr('x', pixelMin)
44244 .attr('width', pixelMax - pixelMin);
44245
44246 rangeSlider.select('rect.' + constants.maskMinClassName)
44247 .attr('width', pixelMin);
44248
44249 rangeSlider.select('rect.' + constants.maskMaxClassName)
44250 .attr('x', pixelMax)
44251 .attr('width', opts._width - pixelMax);
44252
44253 if(oppAxisRangeOpts.rangemode !== 'match') {
44254 var pixelMinOppAxis = opts._height - clampOppAxis(opts.d2pOppAxis(oppAxisOpts._rl[1]));
44255 var pixelMaxOppAxis = opts._height - clampOppAxis(opts.d2pOppAxis(oppAxisOpts._rl[0]));
44256
44257 rangeSlider.select('rect.' + constants.maskMinOppAxisClassName)
44258 .attr('x', pixelMin)
44259 .attr('height', pixelMinOppAxis)
44260 .attr('width', pixelMax - pixelMin);
44261
44262 rangeSlider.select('rect.' + constants.maskMaxOppAxisClassName)
44263 .attr('x', pixelMin)
44264 .attr('y', pixelMaxOppAxis)
44265 .attr('height', opts._height - pixelMaxOppAxis)
44266 .attr('width', pixelMax - pixelMin);
44267
44268 rangeSlider.select('rect.' + constants.slideBoxClassName)
44269 .attr('y', pixelMinOppAxis)
44270 .attr('height', pixelMaxOppAxis - pixelMinOppAxis);
44271 }
44272
44273 // add offset for crispier corners
44274 // https://github.com/plotly/plotly.js/pull/1409
44275 var offset = 0.5;
44276
44277 var xMin = Math.round(clampHandle(pixelMin - hw2)) - offset;
44278 var xMax = Math.round(clampHandle(pixelMax - hw2)) + offset;
44279
44280 rangeSlider.select('g.' + constants.grabberMinClassName)
44281 .attr('transform', strTranslate(xMin, offset));
44282
44283 rangeSlider.select('g.' + constants.grabberMaxClassName)
44284 .attr('transform', strTranslate(xMax, offset));
44285}
44286
44287function drawBg(rangeSlider, gd, axisOpts, opts) {
44288 var bg = Lib.ensureSingle(rangeSlider, 'rect', constants.bgClassName, function(s) {
44289 s.attr({
44290 x: 0,
44291 y: 0,
44292 'shape-rendering': 'crispEdges'
44293 });
44294 });
44295
44296 var borderCorrect = (opts.borderwidth % 2) === 0 ?
44297 opts.borderwidth :
44298 opts.borderwidth - 1;
44299
44300 var offsetShift = -opts._offsetShift;
44301 var lw = Drawing.crispRound(gd, opts.borderwidth);
44302
44303 bg.attr({
44304 width: opts._width + borderCorrect,
44305 height: opts._height + borderCorrect,
44306 transform: strTranslate(offsetShift, offsetShift),
44307 fill: opts.bgcolor,
44308 stroke: opts.bordercolor,
44309 'stroke-width': lw
44310 });
44311}
44312
44313function addClipPath(rangeSlider, gd, axisOpts, opts) {
44314 var fullLayout = gd._fullLayout;
44315
44316 var clipPath = Lib.ensureSingleById(fullLayout._topdefs, 'clipPath', opts._clipId, function(s) {
44317 s.append('rect').attr({ x: 0, y: 0 });
44318 });
44319
44320 clipPath.select('rect').attr({
44321 width: opts._width,
44322 height: opts._height
44323 });
44324}
44325
44326function drawRangePlot(rangeSlider, gd, axisOpts, opts) {
44327 var calcData = gd.calcdata;
44328
44329 var rangePlots = rangeSlider.selectAll('g.' + constants.rangePlotClassName)
44330 .data(axisOpts._subplotsWith, Lib.identity);
44331
44332 rangePlots.enter().append('g')
44333 .attr('class', function(id) { return constants.rangePlotClassName + ' ' + id; })
44334 .call(Drawing.setClipUrl, opts._clipId, gd);
44335
44336 rangePlots.order();
44337
44338 rangePlots.exit().remove();
44339
44340 var mainplotinfo;
44341
44342 rangePlots.each(function(id, i) {
44343 var plotgroup = d3.select(this);
44344 var isMainPlot = (i === 0);
44345
44346 var oppAxisOpts = axisIDs.getFromId(gd, id, 'y');
44347 var oppAxisName = oppAxisOpts._name;
44348 var oppAxisRangeOpts = opts[oppAxisName];
44349
44350 var mockFigure = {
44351 data: [],
44352 layout: {
44353 xaxis: {
44354 type: axisOpts.type,
44355 domain: [0, 1],
44356 range: opts.range.slice(),
44357 calendar: axisOpts.calendar
44358 },
44359 width: opts._width,
44360 height: opts._height,
44361 margin: { t: 0, b: 0, l: 0, r: 0 }
44362 },
44363 _context: gd._context
44364 };
44365
44366 if(axisOpts.rangebreaks) {
44367 mockFigure.layout.xaxis.rangebreaks = axisOpts.rangebreaks;
44368 }
44369
44370 mockFigure.layout[oppAxisName] = {
44371 type: oppAxisOpts.type,
44372 domain: [0, 1],
44373 range: oppAxisRangeOpts.rangemode !== 'match' ? oppAxisRangeOpts.range.slice() : oppAxisOpts.range.slice(),
44374 calendar: oppAxisOpts.calendar
44375 };
44376
44377 if(oppAxisOpts.rangebreaks) {
44378 mockFigure.layout[oppAxisName].rangebreaks = oppAxisOpts.rangebreaks;
44379 }
44380
44381 Plots.supplyDefaults(mockFigure);
44382
44383 var xa = mockFigure._fullLayout.xaxis;
44384 var ya = mockFigure._fullLayout[oppAxisName];
44385
44386 xa.clearCalc();
44387 xa.setScale();
44388 ya.clearCalc();
44389 ya.setScale();
44390
44391 var plotinfo = {
44392 id: id,
44393 plotgroup: plotgroup,
44394 xaxis: xa,
44395 yaxis: ya,
44396 isRangePlot: true
44397 };
44398
44399 if(isMainPlot) mainplotinfo = plotinfo;
44400 else {
44401 plotinfo.mainplot = 'xy';
44402 plotinfo.mainplotinfo = mainplotinfo;
44403 }
44404
44405 Cartesian.rangePlot(gd, plotinfo, filterRangePlotCalcData(calcData, id));
44406 });
44407}
44408
44409function filterRangePlotCalcData(calcData, subplotId) {
44410 var out = [];
44411
44412 for(var i = 0; i < calcData.length; i++) {
44413 var calcTrace = calcData[i];
44414 var trace = calcTrace[0].trace;
44415
44416 if(trace.xaxis + trace.yaxis === subplotId) {
44417 out.push(calcTrace);
44418 }
44419 }
44420
44421 return out;
44422}
44423
44424function drawMasks(rangeSlider, gd, axisOpts, opts, oppAxisRangeOpts) {
44425 var maskMin = Lib.ensureSingle(rangeSlider, 'rect', constants.maskMinClassName, function(s) {
44426 s.attr({
44427 x: 0,
44428 y: 0,
44429 'shape-rendering': 'crispEdges'
44430 });
44431 });
44432
44433 maskMin
44434 .attr('height', opts._height)
44435 .call(Color.fill, constants.maskColor);
44436
44437 var maskMax = Lib.ensureSingle(rangeSlider, 'rect', constants.maskMaxClassName, function(s) {
44438 s.attr({
44439 y: 0,
44440 'shape-rendering': 'crispEdges'
44441 });
44442 });
44443
44444 maskMax
44445 .attr('height', opts._height)
44446 .call(Color.fill, constants.maskColor);
44447
44448 // masks used for oppAxis zoom
44449 if(oppAxisRangeOpts.rangemode !== 'match') {
44450 var maskMinOppAxis = Lib.ensureSingle(rangeSlider, 'rect', constants.maskMinOppAxisClassName, function(s) {
44451 s.attr({
44452 y: 0,
44453 'shape-rendering': 'crispEdges'
44454 });
44455 });
44456
44457 maskMinOppAxis
44458 .attr('width', opts._width)
44459 .call(Color.fill, constants.maskOppAxisColor);
44460
44461 var maskMaxOppAxis = Lib.ensureSingle(rangeSlider, 'rect', constants.maskMaxOppAxisClassName, function(s) {
44462 s.attr({
44463 y: 0,
44464 'shape-rendering': 'crispEdges'
44465 });
44466 });
44467
44468 maskMaxOppAxis
44469 .attr('width', opts._width)
44470 .style('border-top', constants.maskOppBorder)
44471 .call(Color.fill, constants.maskOppAxisColor);
44472 }
44473}
44474
44475function drawSlideBox(rangeSlider, gd, axisOpts, opts) {
44476 if(gd._context.staticPlot) return;
44477
44478 var slideBox = Lib.ensureSingle(rangeSlider, 'rect', constants.slideBoxClassName, function(s) {
44479 s.attr({
44480 y: 0,
44481 cursor: constants.slideBoxCursor,
44482 'shape-rendering': 'crispEdges'
44483 });
44484 });
44485
44486 slideBox.attr({
44487 height: opts._height,
44488 fill: constants.slideBoxFill
44489 });
44490}
44491
44492function drawGrabbers(rangeSlider, gd, axisOpts, opts) {
44493 // <g grabber />
44494 var grabberMin = Lib.ensureSingle(rangeSlider, 'g', constants.grabberMinClassName);
44495 var grabberMax = Lib.ensureSingle(rangeSlider, 'g', constants.grabberMaxClassName);
44496
44497 // <g handle />
44498 var handleFixAttrs = {
44499 x: 0,
44500 width: constants.handleWidth,
44501 rx: constants.handleRadius,
44502 fill: Color.background,
44503 stroke: Color.defaultLine,
44504 'stroke-width': constants.handleStrokeWidth,
44505 'shape-rendering': 'crispEdges'
44506 };
44507 var handleDynamicAttrs = {
44508 y: Math.round(opts._height / 4),
44509 height: Math.round(opts._height / 2),
44510 };
44511 var handleMin = Lib.ensureSingle(grabberMin, 'rect', constants.handleMinClassName, function(s) {
44512 s.attr(handleFixAttrs);
44513 });
44514 handleMin.attr(handleDynamicAttrs);
44515
44516 var handleMax = Lib.ensureSingle(grabberMax, 'rect', constants.handleMaxClassName, function(s) {
44517 s.attr(handleFixAttrs);
44518 });
44519 handleMax.attr(handleDynamicAttrs);
44520
44521 // <g grabarea />
44522 var grabAreaFixAttrs = {
44523 width: constants.grabAreaWidth,
44524 x: 0,
44525 y: 0,
44526 fill: constants.grabAreaFill,
44527 cursor: !gd._context.staticPlot ? constants.grabAreaCursor : undefined,
44528 };
44529
44530 var grabAreaMin = Lib.ensureSingle(grabberMin, 'rect', constants.grabAreaMinClassName, function(s) {
44531 s.attr(grabAreaFixAttrs);
44532 });
44533 grabAreaMin.attr('height', opts._height);
44534
44535 var grabAreaMax = Lib.ensureSingle(grabberMax, 'rect', constants.grabAreaMaxClassName, function(s) {
44536 s.attr(grabAreaFixAttrs);
44537 });
44538 grabAreaMax.attr('height', opts._height);
44539}
44540
44541},{"../../lib":287,"../../lib/setcursor":307,"../../plots/cartesian":348,"../../plots/cartesian/axis_ids":338,"../../plots/plots":369,"../../registry":376,"../color":157,"../dragelement":176,"../drawing":179,"../titles":255,"./constants":231,"@plotly/d3":20}],234:[function(_dereq_,module,exports){
44542'use strict';
44543
44544var axisIDs = _dereq_('../../plots/cartesian/axis_ids');
44545var svgTextUtils = _dereq_('../../lib/svg_text_utils');
44546var constants = _dereq_('./constants');
44547var LINE_SPACING = _dereq_('../../constants/alignment').LINE_SPACING;
44548var name = constants.name;
44549
44550function isVisible(ax) {
44551 var rangeSlider = ax && ax[name];
44552 return rangeSlider && rangeSlider.visible;
44553}
44554exports.isVisible = isVisible;
44555
44556exports.makeData = function(fullLayout) {
44557 var axes = axisIDs.list({ _fullLayout: fullLayout }, 'x', true);
44558 var margin = fullLayout.margin;
44559 var rangeSliderData = [];
44560
44561 if(!fullLayout._has('gl2d')) {
44562 for(var i = 0; i < axes.length; i++) {
44563 var ax = axes[i];
44564
44565 if(isVisible(ax)) {
44566 rangeSliderData.push(ax);
44567
44568 var opts = ax[name];
44569 opts._id = name + ax._id;
44570 opts._height = (fullLayout.height - margin.b - margin.t) * opts.thickness;
44571 opts._offsetShift = Math.floor(opts.borderwidth / 2);
44572 }
44573 }
44574 }
44575
44576 fullLayout._rangeSliderData = rangeSliderData;
44577};
44578
44579exports.autoMarginOpts = function(gd, ax) {
44580 var fullLayout = gd._fullLayout;
44581 var opts = ax[name];
44582 var axLetter = ax._id.charAt(0);
44583
44584 var bottomDepth = 0;
44585 var titleHeight = 0;
44586 if(ax.side === 'bottom') {
44587 bottomDepth = ax._depth;
44588 if(ax.title.text !== fullLayout._dfltTitle[axLetter]) {
44589 // as in rangeslider/draw.js
44590 titleHeight = 1.5 * ax.title.font.size + 10 + opts._offsetShift;
44591 // multi-line extra bump
44592 var extraLines = (ax.title.text.match(svgTextUtils.BR_TAG_ALL) || []).length;
44593 titleHeight += extraLines * ax.title.font.size * LINE_SPACING;
44594 }
44595 }
44596
44597 return {
44598 x: 0,
44599 y: ax._counterDomainMin,
44600 l: 0,
44601 r: 0,
44602 t: 0,
44603 b: opts._height + bottomDepth + Math.max(fullLayout.margin.b, titleHeight),
44604 pad: constants.extraPad + opts._offsetShift * 2
44605 };
44606};
44607
44608},{"../../constants/alignment":262,"../../lib/svg_text_utils":310,"../../plots/cartesian/axis_ids":338,"./constants":231}],235:[function(_dereq_,module,exports){
44609'use strict';
44610
44611var Lib = _dereq_('../../lib');
44612var attrs = _dereq_('./attributes');
44613var oppAxisAttrs = _dereq_('./oppaxis_attributes');
44614var helpers = _dereq_('./helpers');
44615
44616module.exports = {
44617 moduleType: 'component',
44618 name: 'rangeslider',
44619
44620 schema: {
44621 subplots: {
44622 xaxis: {
44623 rangeslider: Lib.extendFlat({}, attrs, {
44624 yaxis: oppAxisAttrs
44625 })
44626 }
44627 }
44628 },
44629
44630 layoutAttributes: _dereq_('./attributes'),
44631 handleDefaults: _dereq_('./defaults'),
44632 calcAutorange: _dereq_('./calc_autorange'),
44633 draw: _dereq_('./draw'),
44634 isVisible: helpers.isVisible,
44635 makeData: helpers.makeData,
44636 autoMarginOpts: helpers.autoMarginOpts
44637};
44638
44639},{"../../lib":287,"./attributes":229,"./calc_autorange":230,"./defaults":232,"./draw":233,"./helpers":234,"./oppaxis_attributes":236}],236:[function(_dereq_,module,exports){
44640'use strict';
44641
44642module.exports = {
44643 // not really a 'subplot' attribute container,
44644 // but this is the flag we use to denote attributes that
44645 // support yaxis, yaxis2, yaxis3, ... counters
44646 _isSubplotObj: true,
44647
44648 rangemode: {
44649 valType: 'enumerated',
44650 values: ['auto', 'fixed', 'match'],
44651 dflt: 'match',
44652 editType: 'calc',
44653 },
44654 range: {
44655 valType: 'info_array',
44656 items: [
44657 {valType: 'any', editType: 'plot'},
44658 {valType: 'any', editType: 'plot'}
44659 ],
44660 editType: 'plot',
44661 },
44662 editType: 'calc'
44663};
44664
44665},{}],237:[function(_dereq_,module,exports){
44666'use strict';
44667
44668var annAttrs = _dereq_('../annotations/attributes');
44669var scatterLineAttrs = _dereq_('../../traces/scatter/attributes').line;
44670var dash = _dereq_('../drawing/attributes').dash;
44671var extendFlat = _dereq_('../../lib/extend').extendFlat;
44672var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray;
44673var axisPlaceableObjs = _dereq_('../../constants/axis_placeable_objects');
44674
44675module.exports = templatedArray('shape', {
44676 visible: {
44677 valType: 'boolean',
44678 dflt: true,
44679 editType: 'calc+arraydraw',
44680 },
44681
44682 type: {
44683 valType: 'enumerated',
44684 values: ['circle', 'rect', 'path', 'line'],
44685 editType: 'calc+arraydraw',
44686 },
44687
44688 layer: {
44689 valType: 'enumerated',
44690 values: ['below', 'above'],
44691 dflt: 'above',
44692 editType: 'arraydraw',
44693 },
44694
44695 xref: extendFlat({}, annAttrs.xref, {
44696 }),
44697 xsizemode: {
44698 valType: 'enumerated',
44699 values: ['scaled', 'pixel'],
44700 dflt: 'scaled',
44701 editType: 'calc+arraydraw',
44702 },
44703 xanchor: {
44704 valType: 'any',
44705 editType: 'calc+arraydraw',
44706 },
44707 x0: {
44708 valType: 'any',
44709 editType: 'calc+arraydraw',
44710 },
44711 x1: {
44712 valType: 'any',
44713 editType: 'calc+arraydraw',
44714 },
44715
44716 yref: extendFlat({}, annAttrs.yref, {
44717 }),
44718 ysizemode: {
44719 valType: 'enumerated',
44720 values: ['scaled', 'pixel'],
44721 dflt: 'scaled',
44722 editType: 'calc+arraydraw',
44723 },
44724 yanchor: {
44725 valType: 'any',
44726 editType: 'calc+arraydraw',
44727 },
44728 y0: {
44729 valType: 'any',
44730 editType: 'calc+arraydraw',
44731 },
44732 y1: {
44733 valType: 'any',
44734 editType: 'calc+arraydraw',
44735 },
44736
44737 path: {
44738 valType: 'string',
44739 editType: 'calc+arraydraw',
44740 },
44741
44742 opacity: {
44743 valType: 'number',
44744 min: 0,
44745 max: 1,
44746 dflt: 1,
44747 editType: 'arraydraw',
44748 },
44749 line: {
44750 color: extendFlat({}, scatterLineAttrs.color, {editType: 'arraydraw'}),
44751 width: extendFlat({}, scatterLineAttrs.width, {editType: 'calc+arraydraw'}),
44752 dash: extendFlat({}, dash, {editType: 'arraydraw'}),
44753 editType: 'calc+arraydraw'
44754 },
44755 fillcolor: {
44756 valType: 'color',
44757 dflt: 'rgba(0,0,0,0)',
44758 editType: 'arraydraw',
44759 },
44760 fillrule: {
44761 valType: 'enumerated',
44762 values: ['evenodd', 'nonzero'],
44763 dflt: 'evenodd',
44764 editType: 'arraydraw',
44765 },
44766 editable: {
44767 valType: 'boolean',
44768 dflt: false,
44769 editType: 'calc+arraydraw',
44770 },
44771
44772 editType: 'arraydraw'
44773});
44774
44775},{"../../constants/axis_placeable_objects":263,"../../lib/extend":281,"../../plot_api/plot_template":323,"../../traces/scatter/attributes":497,"../annotations/attributes":140,"../drawing/attributes":178}],238:[function(_dereq_,module,exports){
44776'use strict';
44777
44778var Lib = _dereq_('../../lib');
44779var Axes = _dereq_('../../plots/cartesian/axes');
44780
44781var constants = _dereq_('./constants');
44782var helpers = _dereq_('./helpers');
44783
44784
44785module.exports = function calcAutorange(gd) {
44786 var fullLayout = gd._fullLayout;
44787 var shapeList = Lib.filterVisible(fullLayout.shapes);
44788
44789 if(!shapeList.length || !gd._fullData.length) return;
44790
44791 for(var i = 0; i < shapeList.length; i++) {
44792 var shape = shapeList[i];
44793 shape._extremes = {};
44794
44795 var ax; var bounds;
44796 var xRefType = Axes.getRefType(shape.xref);
44797 var yRefType = Axes.getRefType(shape.yref);
44798
44799 // paper and axis domain referenced shapes don't affect autorange
44800 if(shape.xref !== 'paper' && xRefType !== 'domain') {
44801 var vx0 = shape.xsizemode === 'pixel' ? shape.xanchor : shape.x0;
44802 var vx1 = shape.xsizemode === 'pixel' ? shape.xanchor : shape.x1;
44803 ax = Axes.getFromId(gd, shape.xref);
44804
44805 bounds = shapeBounds(ax, vx0, vx1, shape.path, constants.paramIsX);
44806 if(bounds) {
44807 shape._extremes[ax._id] = Axes.findExtremes(ax, bounds, calcXPaddingOptions(shape));
44808 }
44809 }
44810
44811 if(shape.yref !== 'paper' && yRefType !== 'domain') {
44812 var vy0 = shape.ysizemode === 'pixel' ? shape.yanchor : shape.y0;
44813 var vy1 = shape.ysizemode === 'pixel' ? shape.yanchor : shape.y1;
44814 ax = Axes.getFromId(gd, shape.yref);
44815
44816 bounds = shapeBounds(ax, vy0, vy1, shape.path, constants.paramIsY);
44817 if(bounds) {
44818 shape._extremes[ax._id] = Axes.findExtremes(ax, bounds, calcYPaddingOptions(shape));
44819 }
44820 }
44821 }
44822};
44823
44824function calcXPaddingOptions(shape) {
44825 return calcPaddingOptions(shape.line.width, shape.xsizemode, shape.x0, shape.x1, shape.path, false);
44826}
44827
44828function calcYPaddingOptions(shape) {
44829 return calcPaddingOptions(shape.line.width, shape.ysizemode, shape.y0, shape.y1, shape.path, true);
44830}
44831
44832function calcPaddingOptions(lineWidth, sizeMode, v0, v1, path, isYAxis) {
44833 var ppad = lineWidth / 2;
44834 var axisDirectionReverted = isYAxis;
44835
44836 if(sizeMode === 'pixel') {
44837 var coords = path ?
44838 helpers.extractPathCoords(path, isYAxis ? constants.paramIsY : constants.paramIsX) :
44839 [v0, v1];
44840 var maxValue = Lib.aggNums(Math.max, null, coords);
44841 var minValue = Lib.aggNums(Math.min, null, coords);
44842 var beforePad = minValue < 0 ? Math.abs(minValue) + ppad : ppad;
44843 var afterPad = maxValue > 0 ? maxValue + ppad : ppad;
44844
44845 return {
44846 ppad: ppad,
44847 ppadplus: axisDirectionReverted ? beforePad : afterPad,
44848 ppadminus: axisDirectionReverted ? afterPad : beforePad
44849 };
44850 } else {
44851 return {ppad: ppad};
44852 }
44853}
44854
44855function shapeBounds(ax, v0, v1, path, paramsToUse) {
44856 var convertVal = (ax.type === 'category' || ax.type === 'multicategory') ? ax.r2c : ax.d2c;
44857
44858 if(v0 !== undefined) return [convertVal(v0), convertVal(v1)];
44859 if(!path) return;
44860
44861 var min = Infinity;
44862 var max = -Infinity;
44863 var segments = path.match(constants.segmentRE);
44864 var i;
44865 var segment;
44866 var drawnParam;
44867 var params;
44868 var val;
44869
44870 if(ax.type === 'date') convertVal = helpers.decodeDate(convertVal);
44871
44872 for(i = 0; i < segments.length; i++) {
44873 segment = segments[i];
44874 drawnParam = paramsToUse[segment.charAt(0)].drawn;
44875 if(drawnParam === undefined) continue;
44876
44877 params = segments[i].substr(1).match(constants.paramRE);
44878 if(!params || params.length < drawnParam) continue;
44879
44880 val = convertVal(params[drawnParam]);
44881 if(val < min) min = val;
44882 if(val > max) max = val;
44883 }
44884 if(max >= min) return [min, max];
44885}
44886
44887},{"../../lib":287,"../../plots/cartesian/axes":334,"./constants":239,"./helpers":248}],239:[function(_dereq_,module,exports){
44888'use strict';
44889
44890
44891module.exports = {
44892 segmentRE: /[MLHVQCTSZ][^MLHVQCTSZ]*/g,
44893 paramRE: /[^\s,]+/g,
44894
44895 // which numbers in each path segment are x (or y) values
44896 // drawn is which param is a drawn point, as opposed to a
44897 // control point (which doesn't count toward autorange.
44898 // TODO: this means curved paths could extend beyond the
44899 // autorange bounds. This is a bit tricky to get right
44900 // unless we revert to bounding boxes, but perhaps there's
44901 // a calculation we could do...)
44902 paramIsX: {
44903 M: {0: true, drawn: 0},
44904 L: {0: true, drawn: 0},
44905 H: {0: true, drawn: 0},
44906 V: {},
44907 Q: {0: true, 2: true, drawn: 2},
44908 C: {0: true, 2: true, 4: true, drawn: 4},
44909 T: {0: true, drawn: 0},
44910 S: {0: true, 2: true, drawn: 2},
44911 // A: {0: true, 5: true},
44912 Z: {}
44913 },
44914
44915 paramIsY: {
44916 M: {1: true, drawn: 1},
44917 L: {1: true, drawn: 1},
44918 H: {},
44919 V: {0: true, drawn: 0},
44920 Q: {1: true, 3: true, drawn: 3},
44921 C: {1: true, 3: true, 5: true, drawn: 5},
44922 T: {1: true, drawn: 1},
44923 S: {1: true, 3: true, drawn: 5},
44924 // A: {1: true, 6: true},
44925 Z: {}
44926 },
44927
44928 numParams: {
44929 M: 2,
44930 L: 2,
44931 H: 1,
44932 V: 1,
44933 Q: 4,
44934 C: 6,
44935 T: 2,
44936 S: 4,
44937 // A: 7,
44938 Z: 0
44939 }
44940};
44941
44942},{}],240:[function(_dereq_,module,exports){
44943'use strict';
44944
44945var Lib = _dereq_('../../lib');
44946var Axes = _dereq_('../../plots/cartesian/axes');
44947var handleArrayContainerDefaults = _dereq_('../../plots/array_container_defaults');
44948
44949var attributes = _dereq_('./attributes');
44950var helpers = _dereq_('./helpers');
44951
44952
44953module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) {
44954 handleArrayContainerDefaults(layoutIn, layoutOut, {
44955 name: 'shapes',
44956 handleItemDefaults: handleShapeDefaults
44957 });
44958};
44959
44960function handleShapeDefaults(shapeIn, shapeOut, fullLayout) {
44961 function coerce(attr, dflt) {
44962 return Lib.coerce(shapeIn, shapeOut, attributes, attr, dflt);
44963 }
44964
44965 var visible = coerce('visible');
44966 if(!visible) return;
44967
44968 var path = coerce('path');
44969 var dfltType = path ? 'path' : 'rect';
44970 var shapeType = coerce('type', dfltType);
44971 if(shapeOut.type !== 'path') delete shapeOut.path;
44972
44973 coerce('editable');
44974 coerce('layer');
44975 coerce('opacity');
44976 coerce('fillcolor');
44977 coerce('fillrule');
44978 var lineWidth = coerce('line.width');
44979 if(lineWidth) {
44980 coerce('line.color');
44981 coerce('line.dash');
44982 }
44983
44984 var xSizeMode = coerce('xsizemode');
44985 var ySizeMode = coerce('ysizemode');
44986
44987 // positioning
44988 var axLetters = ['x', 'y'];
44989 for(var i = 0; i < 2; i++) {
44990 var axLetter = axLetters[i];
44991 var attrAnchor = axLetter + 'anchor';
44992 var sizeMode = axLetter === 'x' ? xSizeMode : ySizeMode;
44993 var gdMock = {_fullLayout: fullLayout};
44994 var ax;
44995 var pos2r;
44996 var r2pos;
44997
44998 // xref, yref
44999 var axRef = Axes.coerceRef(shapeIn, shapeOut, gdMock, axLetter, undefined,
45000 'paper');
45001 var axRefType = Axes.getRefType(axRef);
45002
45003 if(axRefType === 'range') {
45004 ax = Axes.getFromId(gdMock, axRef);
45005 ax._shapeIndices.push(shapeOut._index);
45006 r2pos = helpers.rangeToShapePosition(ax);
45007 pos2r = helpers.shapePositionToRange(ax);
45008 } else {
45009 pos2r = r2pos = Lib.identity;
45010 }
45011
45012 // Coerce x0, x1, y0, y1
45013 if(shapeType !== 'path') {
45014 var dflt0 = 0.25;
45015 var dflt1 = 0.75;
45016
45017 // hack until V3.0 when log has regular range behavior - make it look like other
45018 // ranges to send to coerce, then put it back after
45019 // this is all to give reasonable default position behavior on log axes, which is
45020 // a pretty unimportant edge case so we could just ignore this.
45021 var attr0 = axLetter + '0';
45022 var attr1 = axLetter + '1';
45023 var in0 = shapeIn[attr0];
45024 var in1 = shapeIn[attr1];
45025 shapeIn[attr0] = pos2r(shapeIn[attr0], true);
45026 shapeIn[attr1] = pos2r(shapeIn[attr1], true);
45027
45028 if(sizeMode === 'pixel') {
45029 coerce(attr0, 0);
45030 coerce(attr1, 10);
45031 } else {
45032 Axes.coercePosition(shapeOut, gdMock, coerce, axRef, attr0, dflt0);
45033 Axes.coercePosition(shapeOut, gdMock, coerce, axRef, attr1, dflt1);
45034 }
45035
45036 // hack part 2
45037 shapeOut[attr0] = r2pos(shapeOut[attr0]);
45038 shapeOut[attr1] = r2pos(shapeOut[attr1]);
45039 shapeIn[attr0] = in0;
45040 shapeIn[attr1] = in1;
45041 }
45042
45043 // Coerce xanchor and yanchor
45044 if(sizeMode === 'pixel') {
45045 // Hack for log axis described above
45046 var inAnchor = shapeIn[attrAnchor];
45047 shapeIn[attrAnchor] = pos2r(shapeIn[attrAnchor], true);
45048
45049 Axes.coercePosition(shapeOut, gdMock, coerce, axRef, attrAnchor, 0.25);
45050
45051 // Hack part 2
45052 shapeOut[attrAnchor] = r2pos(shapeOut[attrAnchor]);
45053 shapeIn[attrAnchor] = inAnchor;
45054 }
45055 }
45056
45057 if(shapeType === 'path') {
45058 coerce('path');
45059 } else {
45060 Lib.noneOrAll(shapeIn, shapeOut, ['x0', 'x1', 'y0', 'y1']);
45061 }
45062}
45063
45064},{"../../lib":287,"../../plots/array_container_defaults":329,"../../plots/cartesian/axes":334,"./attributes":237,"./helpers":248}],241:[function(_dereq_,module,exports){
45065'use strict';
45066
45067var Registry = _dereq_('../../registry');
45068var Lib = _dereq_('../../lib');
45069var Axes = _dereq_('../../plots/cartesian/axes');
45070
45071var readPaths = _dereq_('./draw_newshape/helpers').readPaths;
45072var displayOutlines = _dereq_('./draw_newshape/display_outlines');
45073
45074var clearOutlineControllers = _dereq_('../../plots/cartesian/handle_outline').clearOutlineControllers;
45075
45076var Color = _dereq_('../color');
45077var Drawing = _dereq_('../drawing');
45078var arrayEditor = _dereq_('../../plot_api/plot_template').arrayEditor;
45079
45080var dragElement = _dereq_('../dragelement');
45081var setCursor = _dereq_('../../lib/setcursor');
45082
45083var constants = _dereq_('./constants');
45084var helpers = _dereq_('./helpers');
45085
45086
45087// Shapes are stored in gd.layout.shapes, an array of objects
45088// index can point to one item in this array,
45089// or non-numeric to simply add a new one
45090// or -1 to modify all existing
45091// opt can be the full options object, or one key (to be set to value)
45092// or undefined to simply redraw
45093// if opt is blank, val can be 'add' or a full options object to add a new
45094// annotation at that point in the array, or 'remove' to delete this one
45095
45096module.exports = {
45097 draw: draw,
45098 drawOne: drawOne,
45099 eraseActiveShape: eraseActiveShape
45100};
45101
45102function draw(gd) {
45103 var fullLayout = gd._fullLayout;
45104
45105 // Remove previous shapes before drawing new in shapes in fullLayout.shapes
45106 fullLayout._shapeUpperLayer.selectAll('path').remove();
45107 fullLayout._shapeLowerLayer.selectAll('path').remove();
45108
45109 for(var k in fullLayout._plots) {
45110 var shapelayer = fullLayout._plots[k].shapelayer;
45111 if(shapelayer) shapelayer.selectAll('path').remove();
45112 }
45113
45114 for(var i = 0; i < fullLayout.shapes.length; i++) {
45115 if(fullLayout.shapes[i].visible) {
45116 drawOne(gd, i);
45117 }
45118 }
45119
45120 // may need to resurrect this if we put text (LaTeX) in shapes
45121 // return Plots.previousPromises(gd);
45122}
45123
45124function shouldSkipEdits(gd) {
45125 return !!gd._fullLayout._drawing;
45126}
45127
45128function couldHaveActiveShape(gd) {
45129 // for now keep config.editable: true as it was before shape-drawing PR
45130 return !gd._context.edits.shapePosition;
45131}
45132
45133function drawOne(gd, index) {
45134 // remove the existing shape if there is one.
45135 // because indices can change, we need to look in all shape layers
45136 gd._fullLayout._paperdiv
45137 .selectAll('.shapelayer [data-index="' + index + '"]')
45138 .remove();
45139
45140 var o = helpers.makeOptionsAndPlotinfo(gd, index);
45141 var options = o.options;
45142 var plotinfo = o.plotinfo;
45143
45144 // this shape is gone - quit now after deleting it
45145 // TODO: use d3 idioms instead of deleting and redrawing every time
45146 if(!options._input || options.visible === false) return;
45147
45148 if(options.layer !== 'below') {
45149 drawShape(gd._fullLayout._shapeUpperLayer);
45150 } else if(options.xref === 'paper' || options.yref === 'paper') {
45151 drawShape(gd._fullLayout._shapeLowerLayer);
45152 } else {
45153 if(plotinfo._hadPlotinfo) {
45154 var mainPlot = plotinfo.mainplotinfo || plotinfo;
45155 drawShape(mainPlot.shapelayer);
45156 } else {
45157 // Fall back to _shapeLowerLayer in case the requested subplot doesn't exist.
45158 // This can happen if you reference the shape to an x / y axis combination
45159 // that doesn't have any data on it (and layer is below)
45160 drawShape(gd._fullLayout._shapeLowerLayer);
45161 }
45162 }
45163
45164 function drawShape(shapeLayer) {
45165 var d = getPathString(gd, options);
45166 var attrs = {
45167 'data-index': index,
45168 'fill-rule': options.fillrule,
45169 d: d
45170 };
45171
45172 var opacity = options.opacity;
45173 var fillColor = options.fillcolor;
45174 var lineColor = options.line.width ? options.line.color : 'rgba(0,0,0,0)';
45175 var lineWidth = options.line.width;
45176 var lineDash = options.line.dash;
45177 if(!lineWidth && options.editable === true) {
45178 // ensure invisible border to activate the shape
45179 lineWidth = 5;
45180 lineDash = 'solid';
45181 }
45182
45183 var isOpen = d[d.length - 1] !== 'Z';
45184
45185 var isActiveShape = couldHaveActiveShape(gd) &&
45186 options.editable && gd._fullLayout._activeShapeIndex === index;
45187
45188 if(isActiveShape) {
45189 fillColor = isOpen ? 'rgba(0,0,0,0)' :
45190 gd._fullLayout.activeshape.fillcolor;
45191
45192 opacity = gd._fullLayout.activeshape.opacity;
45193 }
45194
45195 var path = shapeLayer.append('path')
45196 .attr(attrs)
45197 .style('opacity', opacity)
45198 .call(Color.stroke, lineColor)
45199 .call(Color.fill, fillColor)
45200 .call(Drawing.dashLine, lineDash, lineWidth);
45201
45202 setClipPath(path, gd, options);
45203
45204 var editHelpers;
45205 if(isActiveShape || gd._context.edits.shapePosition) editHelpers = arrayEditor(gd.layout, 'shapes', options);
45206
45207 if(isActiveShape) {
45208 path.style({
45209 'cursor': 'move',
45210 });
45211
45212 var dragOptions = {
45213 element: path.node(),
45214 plotinfo: plotinfo,
45215 gd: gd,
45216 editHelpers: editHelpers,
45217 isActiveShape: true // i.e. to enable controllers
45218 };
45219
45220 var polygons = readPaths(d, gd);
45221 // display polygons on the screen
45222 displayOutlines(polygons, path, dragOptions);
45223 } else {
45224 if(gd._context.edits.shapePosition) {
45225 setupDragElement(gd, path, options, index, shapeLayer, editHelpers);
45226 } else if(options.editable === true) {
45227 path.style('pointer-events',
45228 (isOpen || Color.opacity(fillColor) * opacity <= 0.5) ? 'stroke' : 'all'
45229 );
45230 }
45231 }
45232
45233 path.node().addEventListener('click', function() { return activateShape(gd, path); });
45234 }
45235}
45236
45237function setClipPath(shapePath, gd, shapeOptions) {
45238 // note that for layer="below" the clipAxes can be different from the
45239 // subplot we're drawing this in. This could cause problems if the shape
45240 // spans two subplots. See https://github.com/plotly/plotly.js/issues/1452
45241 //
45242 // if axis is 'paper' or an axis with " domain" appended, then there is no
45243 // clip axis
45244 var clipAxes = (shapeOptions.xref + shapeOptions.yref).replace(/paper/g, '').replace(/[xyz][1-9]* *domain/g, '');
45245
45246 Drawing.setClipUrl(
45247 shapePath,
45248 clipAxes ? 'clip' + gd._fullLayout._uid + clipAxes : null,
45249 gd
45250 );
45251}
45252
45253function setupDragElement(gd, shapePath, shapeOptions, index, shapeLayer, editHelpers) {
45254 var MINWIDTH = 10;
45255 var MINHEIGHT = 10;
45256
45257 var xPixelSized = shapeOptions.xsizemode === 'pixel';
45258 var yPixelSized = shapeOptions.ysizemode === 'pixel';
45259 var isLine = shapeOptions.type === 'line';
45260 var isPath = shapeOptions.type === 'path';
45261
45262 var modifyItem = editHelpers.modifyItem;
45263
45264 var x0, y0, x1, y1, xAnchor, yAnchor;
45265 var n0, s0, w0, e0, optN, optS, optW, optE;
45266 var pathIn;
45267
45268 // setup conversion functions
45269 var xa = Axes.getFromId(gd, shapeOptions.xref);
45270 var xRefType = Axes.getRefType(shapeOptions.xref);
45271 var ya = Axes.getFromId(gd, shapeOptions.yref);
45272 var yRefType = Axes.getRefType(shapeOptions.yref);
45273 var x2p = helpers.getDataToPixel(gd, xa, false, xRefType);
45274 var y2p = helpers.getDataToPixel(gd, ya, true, yRefType);
45275 var p2x = helpers.getPixelToData(gd, xa, false, xRefType);
45276 var p2y = helpers.getPixelToData(gd, ya, true, yRefType);
45277
45278 var sensoryElement = obtainSensoryElement();
45279 var dragOptions = {
45280 element: sensoryElement.node(),
45281 gd: gd,
45282 prepFn: startDrag,
45283 doneFn: endDrag,
45284 clickFn: abortDrag
45285 };
45286 var dragMode;
45287
45288 dragElement.init(dragOptions);
45289
45290 sensoryElement.node().onmousemove = updateDragMode;
45291
45292 function obtainSensoryElement() {
45293 return isLine ? createLineDragHandles() : shapePath;
45294 }
45295
45296 function createLineDragHandles() {
45297 var minSensoryWidth = 10;
45298 var sensoryWidth = Math.max(shapeOptions.line.width, minSensoryWidth);
45299
45300 // Helper shapes group
45301 // Note that by setting the `data-index` attr, it is ensured that
45302 // the helper group is purged in this modules `draw` function
45303 var g = shapeLayer.append('g')
45304 .attr('data-index', index);
45305
45306 // Helper path for moving
45307 g.append('path')
45308 .attr('d', shapePath.attr('d'))
45309 .style({
45310 'cursor': 'move',
45311 'stroke-width': sensoryWidth,
45312 'stroke-opacity': '0' // ensure not visible
45313 });
45314
45315 // Helper circles for resizing
45316 var circleStyle = {
45317 'fill-opacity': '0' // ensure not visible
45318 };
45319 var circleRadius = Math.max(sensoryWidth / 2, minSensoryWidth);
45320
45321 g.append('circle')
45322 .attr({
45323 'data-line-point': 'start-point',
45324 'cx': xPixelSized ? x2p(shapeOptions.xanchor) + shapeOptions.x0 : x2p(shapeOptions.x0),
45325 'cy': yPixelSized ? y2p(shapeOptions.yanchor) - shapeOptions.y0 : y2p(shapeOptions.y0),
45326 'r': circleRadius
45327 })
45328 .style(circleStyle)
45329 .classed('cursor-grab', true);
45330
45331 g.append('circle')
45332 .attr({
45333 'data-line-point': 'end-point',
45334 'cx': xPixelSized ? x2p(shapeOptions.xanchor) + shapeOptions.x1 : x2p(shapeOptions.x1),
45335 'cy': yPixelSized ? y2p(shapeOptions.yanchor) - shapeOptions.y1 : y2p(shapeOptions.y1),
45336 'r': circleRadius
45337 })
45338 .style(circleStyle)
45339 .classed('cursor-grab', true);
45340
45341 return g;
45342 }
45343
45344 function updateDragMode(evt) {
45345 if(shouldSkipEdits(gd)) {
45346 dragMode = null;
45347 return;
45348 }
45349
45350 if(isLine) {
45351 if(evt.target.tagName === 'path') {
45352 dragMode = 'move';
45353 } else {
45354 dragMode = evt.target.attributes['data-line-point'].value === 'start-point' ?
45355 'resize-over-start-point' : 'resize-over-end-point';
45356 }
45357 } else {
45358 // element might not be on screen at time of setup,
45359 // so obtain bounding box here
45360 var dragBBox = dragOptions.element.getBoundingClientRect();
45361
45362 // choose 'move' or 'resize'
45363 // based on initial position of cursor within the drag element
45364 var w = dragBBox.right - dragBBox.left;
45365 var h = dragBBox.bottom - dragBBox.top;
45366 var x = evt.clientX - dragBBox.left;
45367 var y = evt.clientY - dragBBox.top;
45368 var cursor = (!isPath && w > MINWIDTH && h > MINHEIGHT && !evt.shiftKey) ?
45369 dragElement.getCursor(x / w, 1 - y / h) :
45370 'move';
45371
45372 setCursor(shapePath, cursor);
45373
45374 // possible values 'move', 'sw', 'w', 'se', 'e', 'ne', 'n', 'nw' and 'w'
45375 dragMode = cursor.split('-')[0];
45376 }
45377 }
45378
45379 function startDrag(evt) {
45380 if(shouldSkipEdits(gd)) return;
45381
45382 // setup update strings and initial values
45383 if(xPixelSized) {
45384 xAnchor = x2p(shapeOptions.xanchor);
45385 }
45386 if(yPixelSized) {
45387 yAnchor = y2p(shapeOptions.yanchor);
45388 }
45389
45390 if(shapeOptions.type === 'path') {
45391 pathIn = shapeOptions.path;
45392 } else {
45393 x0 = xPixelSized ? shapeOptions.x0 : x2p(shapeOptions.x0);
45394 y0 = yPixelSized ? shapeOptions.y0 : y2p(shapeOptions.y0);
45395 x1 = xPixelSized ? shapeOptions.x1 : x2p(shapeOptions.x1);
45396 y1 = yPixelSized ? shapeOptions.y1 : y2p(shapeOptions.y1);
45397 }
45398
45399 if(x0 < x1) {
45400 w0 = x0;
45401 optW = 'x0';
45402 e0 = x1;
45403 optE = 'x1';
45404 } else {
45405 w0 = x1;
45406 optW = 'x1';
45407 e0 = x0;
45408 optE = 'x0';
45409 }
45410
45411 // For fixed size shapes take opposing direction of y-axis into account.
45412 // Hint: For data sized shapes this is done by the y2p function.
45413 if((!yPixelSized && y0 < y1) || (yPixelSized && y0 > y1)) {
45414 n0 = y0;
45415 optN = 'y0';
45416 s0 = y1;
45417 optS = 'y1';
45418 } else {
45419 n0 = y1;
45420 optN = 'y1';
45421 s0 = y0;
45422 optS = 'y0';
45423 }
45424
45425 // setup dragMode and the corresponding handler
45426 updateDragMode(evt);
45427 renderVisualCues(shapeLayer, shapeOptions);
45428 deactivateClipPathTemporarily(shapePath, shapeOptions, gd);
45429 dragOptions.moveFn = (dragMode === 'move') ? moveShape : resizeShape;
45430 dragOptions.altKey = evt.altKey;
45431 }
45432
45433 function endDrag() {
45434 if(shouldSkipEdits(gd)) return;
45435
45436 setCursor(shapePath);
45437 removeVisualCues(shapeLayer);
45438
45439 // Don't rely on clipPath being activated during re-layout
45440 setClipPath(shapePath, gd, shapeOptions);
45441 Registry.call('_guiRelayout', gd, editHelpers.getUpdateObj());
45442 }
45443
45444 function abortDrag() {
45445 if(shouldSkipEdits(gd)) return;
45446
45447 removeVisualCues(shapeLayer);
45448 }
45449
45450 function moveShape(dx, dy) {
45451 if(shapeOptions.type === 'path') {
45452 var noOp = function(coord) { return coord; };
45453 var moveX = noOp;
45454 var moveY = noOp;
45455
45456 if(xPixelSized) {
45457 modifyItem('xanchor', shapeOptions.xanchor = p2x(xAnchor + dx));
45458 } else {
45459 moveX = function moveX(x) { return p2x(x2p(x) + dx); };
45460 if(xa && xa.type === 'date') moveX = helpers.encodeDate(moveX);
45461 }
45462
45463 if(yPixelSized) {
45464 modifyItem('yanchor', shapeOptions.yanchor = p2y(yAnchor + dy));
45465 } else {
45466 moveY = function moveY(y) { return p2y(y2p(y) + dy); };
45467 if(ya && ya.type === 'date') moveY = helpers.encodeDate(moveY);
45468 }
45469
45470 modifyItem('path', shapeOptions.path = movePath(pathIn, moveX, moveY));
45471 } else {
45472 if(xPixelSized) {
45473 modifyItem('xanchor', shapeOptions.xanchor = p2x(xAnchor + dx));
45474 } else {
45475 modifyItem('x0', shapeOptions.x0 = p2x(x0 + dx));
45476 modifyItem('x1', shapeOptions.x1 = p2x(x1 + dx));
45477 }
45478
45479 if(yPixelSized) {
45480 modifyItem('yanchor', shapeOptions.yanchor = p2y(yAnchor + dy));
45481 } else {
45482 modifyItem('y0', shapeOptions.y0 = p2y(y0 + dy));
45483 modifyItem('y1', shapeOptions.y1 = p2y(y1 + dy));
45484 }
45485 }
45486
45487 shapePath.attr('d', getPathString(gd, shapeOptions));
45488 renderVisualCues(shapeLayer, shapeOptions);
45489 }
45490
45491 function resizeShape(dx, dy) {
45492 if(isPath) {
45493 // TODO: implement path resize, don't forget to update dragMode code
45494 var noOp = function(coord) { return coord; };
45495 var moveX = noOp;
45496 var moveY = noOp;
45497
45498 if(xPixelSized) {
45499 modifyItem('xanchor', shapeOptions.xanchor = p2x(xAnchor + dx));
45500 } else {
45501 moveX = function moveX(x) { return p2x(x2p(x) + dx); };
45502 if(xa && xa.type === 'date') moveX = helpers.encodeDate(moveX);
45503 }
45504
45505 if(yPixelSized) {
45506 modifyItem('yanchor', shapeOptions.yanchor = p2y(yAnchor + dy));
45507 } else {
45508 moveY = function moveY(y) { return p2y(y2p(y) + dy); };
45509 if(ya && ya.type === 'date') moveY = helpers.encodeDate(moveY);
45510 }
45511
45512 modifyItem('path', shapeOptions.path = movePath(pathIn, moveX, moveY));
45513 } else if(isLine) {
45514 if(dragMode === 'resize-over-start-point') {
45515 var newX0 = x0 + dx;
45516 var newY0 = yPixelSized ? y0 - dy : y0 + dy;
45517 modifyItem('x0', shapeOptions.x0 = xPixelSized ? newX0 : p2x(newX0));
45518 modifyItem('y0', shapeOptions.y0 = yPixelSized ? newY0 : p2y(newY0));
45519 } else if(dragMode === 'resize-over-end-point') {
45520 var newX1 = x1 + dx;
45521 var newY1 = yPixelSized ? y1 - dy : y1 + dy;
45522 modifyItem('x1', shapeOptions.x1 = xPixelSized ? newX1 : p2x(newX1));
45523 modifyItem('y1', shapeOptions.y1 = yPixelSized ? newY1 : p2y(newY1));
45524 }
45525 } else {
45526 var has = function(str) { return dragMode.indexOf(str) !== -1; };
45527 var hasN = has('n');
45528 var hasS = has('s');
45529 var hasW = has('w');
45530 var hasE = has('e');
45531
45532 var newN = hasN ? n0 + dy : n0;
45533 var newS = hasS ? s0 + dy : s0;
45534 var newW = hasW ? w0 + dx : w0;
45535 var newE = hasE ? e0 + dx : e0;
45536
45537 if(yPixelSized) {
45538 // Do things in opposing direction for y-axis.
45539 // Hint: for data-sized shapes the reversal of axis direction is done in p2y.
45540 if(hasN) newN = n0 - dy;
45541 if(hasS) newS = s0 - dy;
45542 }
45543
45544 // Update shape eventually. Again, be aware of the
45545 // opposing direction of the y-axis of fixed size shapes.
45546 if(
45547 (!yPixelSized && newS - newN > MINHEIGHT) ||
45548 (yPixelSized && newN - newS > MINHEIGHT)
45549 ) {
45550 modifyItem(optN, shapeOptions[optN] = yPixelSized ? newN : p2y(newN));
45551 modifyItem(optS, shapeOptions[optS] = yPixelSized ? newS : p2y(newS));
45552 }
45553 if(newE - newW > MINWIDTH) {
45554 modifyItem(optW, shapeOptions[optW] = xPixelSized ? newW : p2x(newW));
45555 modifyItem(optE, shapeOptions[optE] = xPixelSized ? newE : p2x(newE));
45556 }
45557 }
45558
45559 shapePath.attr('d', getPathString(gd, shapeOptions));
45560 renderVisualCues(shapeLayer, shapeOptions);
45561 }
45562
45563 function renderVisualCues(shapeLayer, shapeOptions) {
45564 if(xPixelSized || yPixelSized) {
45565 renderAnchor();
45566 }
45567
45568 function renderAnchor() {
45569 var isNotPath = shapeOptions.type !== 'path';
45570
45571 // d3 join with dummy data to satisfy d3 data-binding
45572 var visualCues = shapeLayer.selectAll('.visual-cue').data([0]);
45573
45574 // Enter
45575 var strokeWidth = 1;
45576 visualCues.enter()
45577 .append('path')
45578 .attr({
45579 'fill': '#fff',
45580 'fill-rule': 'evenodd',
45581 'stroke': '#000',
45582 'stroke-width': strokeWidth
45583 })
45584 .classed('visual-cue', true);
45585
45586 // Update
45587 var posX = x2p(
45588 xPixelSized ?
45589 shapeOptions.xanchor :
45590 Lib.midRange(
45591 isNotPath ?
45592 [shapeOptions.x0, shapeOptions.x1] :
45593 helpers.extractPathCoords(shapeOptions.path, constants.paramIsX))
45594 );
45595 var posY = y2p(
45596 yPixelSized ?
45597 shapeOptions.yanchor :
45598 Lib.midRange(
45599 isNotPath ?
45600 [shapeOptions.y0, shapeOptions.y1] :
45601 helpers.extractPathCoords(shapeOptions.path, constants.paramIsY))
45602 );
45603
45604 posX = helpers.roundPositionForSharpStrokeRendering(posX, strokeWidth);
45605 posY = helpers.roundPositionForSharpStrokeRendering(posY, strokeWidth);
45606
45607 if(xPixelSized && yPixelSized) {
45608 var crossPath = 'M' + (posX - 1 - strokeWidth) + ',' + (posY - 1 - strokeWidth) +
45609 'h-8v2h8 v8h2v-8 h8v-2h-8 v-8h-2 Z';
45610 visualCues.attr('d', crossPath);
45611 } else if(xPixelSized) {
45612 var vBarPath = 'M' + (posX - 1 - strokeWidth) + ',' + (posY - 9 - strokeWidth) +
45613 'v18 h2 v-18 Z';
45614 visualCues.attr('d', vBarPath);
45615 } else {
45616 var hBarPath = 'M' + (posX - 9 - strokeWidth) + ',' + (posY - 1 - strokeWidth) +
45617 'h18 v2 h-18 Z';
45618 visualCues.attr('d', hBarPath);
45619 }
45620 }
45621 }
45622
45623 function removeVisualCues(shapeLayer) {
45624 shapeLayer.selectAll('.visual-cue').remove();
45625 }
45626
45627 function deactivateClipPathTemporarily(shapePath, shapeOptions, gd) {
45628 var xref = shapeOptions.xref;
45629 var yref = shapeOptions.yref;
45630 var xa = Axes.getFromId(gd, xref);
45631 var ya = Axes.getFromId(gd, yref);
45632
45633 var clipAxes = '';
45634 if(xref !== 'paper' && !xa.autorange) clipAxes += xref;
45635 if(yref !== 'paper' && !ya.autorange) clipAxes += yref;
45636
45637 Drawing.setClipUrl(
45638 shapePath,
45639 clipAxes ? 'clip' + gd._fullLayout._uid + clipAxes : null,
45640 gd
45641 );
45642 }
45643}
45644
45645function getPathString(gd, options) {
45646 var type = options.type;
45647 var xRefType = Axes.getRefType(options.xref);
45648 var yRefType = Axes.getRefType(options.yref);
45649 var xa = Axes.getFromId(gd, options.xref);
45650 var ya = Axes.getFromId(gd, options.yref);
45651 var gs = gd._fullLayout._size;
45652 var x2r, x2p, y2r, y2p;
45653 var x0, x1, y0, y1;
45654
45655 if(xa) {
45656 if(xRefType === 'domain') {
45657 x2p = function(v) { return xa._offset + xa._length * v; };
45658 } else {
45659 x2r = helpers.shapePositionToRange(xa);
45660 x2p = function(v) { return xa._offset + xa.r2p(x2r(v, true)); };
45661 }
45662 } else {
45663 x2p = function(v) { return gs.l + gs.w * v; };
45664 }
45665
45666 if(ya) {
45667 if(yRefType === 'domain') {
45668 y2p = function(v) { return ya._offset + ya._length * (1 - v); };
45669 } else {
45670 y2r = helpers.shapePositionToRange(ya);
45671 y2p = function(v) { return ya._offset + ya.r2p(y2r(v, true)); };
45672 }
45673 } else {
45674 y2p = function(v) { return gs.t + gs.h * (1 - v); };
45675 }
45676
45677 if(type === 'path') {
45678 if(xa && xa.type === 'date') x2p = helpers.decodeDate(x2p);
45679 if(ya && ya.type === 'date') y2p = helpers.decodeDate(y2p);
45680 return convertPath(options, x2p, y2p);
45681 }
45682
45683 if(options.xsizemode === 'pixel') {
45684 var xAnchorPos = x2p(options.xanchor);
45685 x0 = xAnchorPos + options.x0;
45686 x1 = xAnchorPos + options.x1;
45687 } else {
45688 x0 = x2p(options.x0);
45689 x1 = x2p(options.x1);
45690 }
45691
45692 if(options.ysizemode === 'pixel') {
45693 var yAnchorPos = y2p(options.yanchor);
45694 y0 = yAnchorPos - options.y0;
45695 y1 = yAnchorPos - options.y1;
45696 } else {
45697 y0 = y2p(options.y0);
45698 y1 = y2p(options.y1);
45699 }
45700
45701 if(type === 'line') return 'M' + x0 + ',' + y0 + 'L' + x1 + ',' + y1;
45702 if(type === 'rect') return 'M' + x0 + ',' + y0 + 'H' + x1 + 'V' + y1 + 'H' + x0 + 'Z';
45703
45704 // circle
45705 var cx = (x0 + x1) / 2;
45706 var cy = (y0 + y1) / 2;
45707 var rx = Math.abs(cx - x0);
45708 var ry = Math.abs(cy - y0);
45709 var rArc = 'A' + rx + ',' + ry;
45710 var rightPt = (cx + rx) + ',' + cy;
45711 var topPt = cx + ',' + (cy - ry);
45712 return 'M' + rightPt + rArc + ' 0 1,1 ' + topPt +
45713 rArc + ' 0 0,1 ' + rightPt + 'Z';
45714}
45715
45716
45717function convertPath(options, x2p, y2p) {
45718 var pathIn = options.path;
45719 var xSizemode = options.xsizemode;
45720 var ySizemode = options.ysizemode;
45721 var xAnchor = options.xanchor;
45722 var yAnchor = options.yanchor;
45723
45724 return pathIn.replace(constants.segmentRE, function(segment) {
45725 var paramNumber = 0;
45726 var segmentType = segment.charAt(0);
45727 var xParams = constants.paramIsX[segmentType];
45728 var yParams = constants.paramIsY[segmentType];
45729 var nParams = constants.numParams[segmentType];
45730
45731 var paramString = segment.substr(1).replace(constants.paramRE, function(param) {
45732 if(xParams[paramNumber]) {
45733 if(xSizemode === 'pixel') param = x2p(xAnchor) + Number(param);
45734 else param = x2p(param);
45735 } else if(yParams[paramNumber]) {
45736 if(ySizemode === 'pixel') param = y2p(yAnchor) - Number(param);
45737 else param = y2p(param);
45738 }
45739 paramNumber++;
45740
45741 if(paramNumber > nParams) param = 'X';
45742 return param;
45743 });
45744
45745 if(paramNumber > nParams) {
45746 paramString = paramString.replace(/[\s,]*X.*/, '');
45747 Lib.log('Ignoring extra params in segment ' + segment);
45748 }
45749
45750 return segmentType + paramString;
45751 });
45752}
45753
45754function movePath(pathIn, moveX, moveY) {
45755 return pathIn.replace(constants.segmentRE, function(segment) {
45756 var paramNumber = 0;
45757 var segmentType = segment.charAt(0);
45758 var xParams = constants.paramIsX[segmentType];
45759 var yParams = constants.paramIsY[segmentType];
45760 var nParams = constants.numParams[segmentType];
45761
45762 var paramString = segment.substr(1).replace(constants.paramRE, function(param) {
45763 if(paramNumber >= nParams) return param;
45764
45765 if(xParams[paramNumber]) param = moveX(param);
45766 else if(yParams[paramNumber]) param = moveY(param);
45767
45768 paramNumber++;
45769
45770 return param;
45771 });
45772
45773 return segmentType + paramString;
45774 });
45775}
45776
45777function activateShape(gd, path) {
45778 if(!couldHaveActiveShape(gd)) return;
45779
45780 var element = path.node();
45781 var id = +element.getAttribute('data-index');
45782 if(id >= 0) {
45783 // deactivate if already active
45784 if(id === gd._fullLayout._activeShapeIndex) {
45785 deactivateShape(gd);
45786 return;
45787 }
45788
45789 gd._fullLayout._activeShapeIndex = id;
45790 gd._fullLayout._deactivateShape = deactivateShape;
45791 draw(gd);
45792 }
45793}
45794
45795function deactivateShape(gd) {
45796 if(!couldHaveActiveShape(gd)) return;
45797
45798 var id = gd._fullLayout._activeShapeIndex;
45799 if(id >= 0) {
45800 clearOutlineControllers(gd);
45801 delete gd._fullLayout._activeShapeIndex;
45802 draw(gd);
45803 }
45804}
45805
45806function eraseActiveShape(gd) {
45807 if(!couldHaveActiveShape(gd)) return;
45808
45809 clearOutlineControllers(gd);
45810
45811 var id = gd._fullLayout._activeShapeIndex;
45812 var shapes = (gd.layout || {}).shapes || [];
45813 if(id < shapes.length) {
45814 var newShapes = [];
45815 for(var q = 0; q < shapes.length; q++) {
45816 if(q !== id) {
45817 newShapes.push(shapes[q]);
45818 }
45819 }
45820
45821 delete gd._fullLayout._activeShapeIndex;
45822
45823 Registry.call('_guiRelayout', gd, {
45824 shapes: newShapes
45825 });
45826 }
45827}
45828
45829},{"../../lib":287,"../../lib/setcursor":307,"../../plot_api/plot_template":323,"../../plots/cartesian/axes":334,"../../plots/cartesian/handle_outline":345,"../../registry":376,"../color":157,"../dragelement":176,"../drawing":179,"./constants":239,"./draw_newshape/display_outlines":245,"./draw_newshape/helpers":246,"./helpers":248}],242:[function(_dereq_,module,exports){
45830'use strict';
45831
45832var dash = _dereq_('../../drawing/attributes').dash;
45833var extendFlat = _dereq_('../../../lib/extend').extendFlat;
45834
45835module.exports = {
45836 newshape: {
45837 line: {
45838 color: {
45839 valType: 'color',
45840 editType: 'none',
45841 },
45842 width: {
45843 valType: 'number',
45844 min: 0,
45845 dflt: 4,
45846 editType: 'none',
45847 },
45848 dash: extendFlat({}, dash, {
45849 dflt: 'solid',
45850 editType: 'none'
45851 }),
45852 editType: 'none'
45853 },
45854 fillcolor: {
45855 valType: 'color',
45856 dflt: 'rgba(0,0,0,0)',
45857 editType: 'none',
45858 },
45859 fillrule: {
45860 valType: 'enumerated',
45861 values: ['evenodd', 'nonzero'],
45862 dflt: 'evenodd',
45863 editType: 'none',
45864 },
45865 opacity: {
45866 valType: 'number',
45867 min: 0,
45868 max: 1,
45869 dflt: 1,
45870 editType: 'none',
45871 },
45872 layer: {
45873 valType: 'enumerated',
45874 values: ['below', 'above'],
45875 dflt: 'above',
45876 editType: 'none',
45877 },
45878 drawdirection: {
45879 valType: 'enumerated',
45880 values: ['ortho', 'horizontal', 'vertical', 'diagonal'],
45881 dflt: 'diagonal',
45882 editType: 'none',
45883 },
45884
45885 editType: 'none'
45886 },
45887
45888 activeshape: {
45889 fillcolor: {
45890 valType: 'color',
45891 dflt: 'rgb(255,0,255)',
45892 editType: 'none',
45893 },
45894 opacity: {
45895 valType: 'number',
45896 min: 0,
45897 max: 1,
45898 dflt: 0.5,
45899 editType: 'none',
45900 },
45901 editType: 'none'
45902 }
45903};
45904
45905},{"../../../lib/extend":281,"../../drawing/attributes":178}],243:[function(_dereq_,module,exports){
45906'use strict';
45907
45908var CIRCLE_SIDES = 32; // should be divisible by 4
45909
45910module.exports = {
45911 CIRCLE_SIDES: CIRCLE_SIDES,
45912 i000: 0,
45913 i090: CIRCLE_SIDES / 4,
45914 i180: CIRCLE_SIDES / 2,
45915 i270: CIRCLE_SIDES / 4 * 3,
45916 cos45: Math.cos(Math.PI / 4),
45917 sin45: Math.sin(Math.PI / 4),
45918 SQRT2: Math.sqrt(2)
45919};
45920
45921},{}],244:[function(_dereq_,module,exports){
45922'use strict';
45923
45924var Color = _dereq_('../../color');
45925
45926
45927module.exports = function supplyDrawNewShapeDefaults(layoutIn, layoutOut, coerce) {
45928 coerce('newshape.drawdirection');
45929 coerce('newshape.layer');
45930 coerce('newshape.fillcolor');
45931 coerce('newshape.fillrule');
45932 coerce('newshape.opacity');
45933 var newshapeLineWidth = coerce('newshape.line.width');
45934 if(newshapeLineWidth) {
45935 var bgcolor = (layoutIn || {}).plot_bgcolor || '#FFF';
45936 coerce('newshape.line.color', Color.contrast(bgcolor));
45937 coerce('newshape.line.dash');
45938 }
45939
45940 coerce('activeshape.fillcolor');
45941 coerce('activeshape.opacity');
45942};
45943
45944},{"../../color":157}],245:[function(_dereq_,module,exports){
45945'use strict';
45946
45947var dragElement = _dereq_('../../dragelement');
45948var dragHelpers = _dereq_('../../dragelement/helpers');
45949var drawMode = dragHelpers.drawMode;
45950
45951var Registry = _dereq_('../../../registry');
45952
45953var constants = _dereq_('./constants');
45954var i000 = constants.i000;
45955var i090 = constants.i090;
45956var i180 = constants.i180;
45957var i270 = constants.i270;
45958
45959var handleOutline = _dereq_('../../../plots/cartesian/handle_outline');
45960var clearOutlineControllers = handleOutline.clearOutlineControllers;
45961
45962var helpers = _dereq_('./helpers');
45963var pointsShapeRectangle = helpers.pointsShapeRectangle;
45964var pointsShapeEllipse = helpers.pointsShapeEllipse;
45965var writePaths = helpers.writePaths;
45966var newShapes = _dereq_('./newshapes');
45967
45968module.exports = function displayOutlines(polygons, outlines, dragOptions, nCalls) {
45969 if(!nCalls) nCalls = 0;
45970
45971 var gd = dragOptions.gd;
45972
45973 function redraw() {
45974 // recursive call
45975 displayOutlines(polygons, outlines, dragOptions, nCalls++);
45976
45977 if(pointsShapeEllipse(polygons[0])) {
45978 update({redrawing: true});
45979 }
45980 }
45981
45982 function update(opts) {
45983 dragOptions.isActiveShape = false; // i.e. to disable controllers
45984
45985 var updateObject = newShapes(outlines, dragOptions);
45986 if(Object.keys(updateObject).length) {
45987 Registry.call((opts || {}).redrawing ? 'relayout' : '_guiRelayout', gd, updateObject);
45988 }
45989 }
45990
45991
45992 var isActiveShape = dragOptions.isActiveShape;
45993 var fullLayout = gd._fullLayout;
45994 var zoomLayer = fullLayout._zoomlayer;
45995
45996 var dragmode = dragOptions.dragmode;
45997 var isDrawMode = drawMode(dragmode);
45998
45999 if(isDrawMode) gd._fullLayout._drawing = true;
46000 else if(gd._fullLayout._activeShapeIndex >= 0) clearOutlineControllers(gd);
46001
46002 // make outline
46003 outlines.attr('d', writePaths(polygons));
46004
46005 // add controllers
46006 var vertexDragOptions;
46007 var shapeDragOptions;
46008 var indexI; // cell index
46009 var indexJ; // vertex or cell-controller index
46010 var copyPolygons;
46011
46012 if(isActiveShape && !nCalls) {
46013 copyPolygons = recordPositions([], polygons);
46014
46015 var g = zoomLayer.append('g').attr('class', 'outline-controllers');
46016 addVertexControllers(g);
46017 addShapeControllers();
46018 }
46019
46020 function startDragVertex(evt) {
46021 indexI = +evt.srcElement.getAttribute('data-i');
46022 indexJ = +evt.srcElement.getAttribute('data-j');
46023
46024 vertexDragOptions[indexI][indexJ].moveFn = moveVertexController;
46025 }
46026
46027 function moveVertexController(dx, dy) {
46028 if(!polygons.length) return;
46029
46030 var x0 = copyPolygons[indexI][indexJ][1];
46031 var y0 = copyPolygons[indexI][indexJ][2];
46032
46033 var cell = polygons[indexI];
46034 var len = cell.length;
46035 if(pointsShapeRectangle(cell)) {
46036 for(var q = 0; q < len; q++) {
46037 if(q === indexJ) continue;
46038
46039 // move other corners of rectangle
46040 var pos = cell[q];
46041
46042 if(pos[1] === cell[indexJ][1]) {
46043 pos[1] = x0 + dx;
46044 }
46045
46046 if(pos[2] === cell[indexJ][2]) {
46047 pos[2] = y0 + dy;
46048 }
46049 }
46050 // move the corner
46051 cell[indexJ][1] = x0 + dx;
46052 cell[indexJ][2] = y0 + dy;
46053
46054 if(!pointsShapeRectangle(cell)) {
46055 // reject result to rectangles with ensure areas
46056 for(var j = 0; j < len; j++) {
46057 for(var k = 0; k < cell[j].length; k++) {
46058 cell[j][k] = copyPolygons[indexI][j][k];
46059 }
46060 }
46061 }
46062 } else { // other polylines
46063 cell[indexJ][1] = x0 + dx;
46064 cell[indexJ][2] = y0 + dy;
46065 }
46066
46067 redraw();
46068 }
46069
46070 function endDragVertexController() {
46071 update();
46072 }
46073
46074 function removeVertex() {
46075 if(!polygons.length) return;
46076 if(!polygons[indexI]) return;
46077 if(!polygons[indexI].length) return;
46078
46079 var newPolygon = [];
46080 for(var j = 0; j < polygons[indexI].length; j++) {
46081 if(j !== indexJ) {
46082 newPolygon.push(
46083 polygons[indexI][j]
46084 );
46085 }
46086 }
46087
46088 if(newPolygon.length > 1 && !(
46089 newPolygon.length === 2 && newPolygon[1][0] === 'Z')
46090 ) {
46091 if(indexJ === 0) {
46092 newPolygon[0][0] = 'M';
46093 }
46094
46095 polygons[indexI] = newPolygon;
46096
46097 redraw();
46098 update();
46099 }
46100 }
46101
46102 function clickVertexController(numClicks, evt) {
46103 if(numClicks === 2) {
46104 indexI = +evt.srcElement.getAttribute('data-i');
46105 indexJ = +evt.srcElement.getAttribute('data-j');
46106
46107 var cell = polygons[indexI];
46108 if(
46109 !pointsShapeRectangle(cell) &&
46110 !pointsShapeEllipse(cell)
46111 ) {
46112 removeVertex();
46113 }
46114 }
46115 }
46116
46117 function addVertexControllers(g) {
46118 vertexDragOptions = [];
46119
46120 for(var i = 0; i < polygons.length; i++) {
46121 var cell = polygons[i];
46122
46123 var onRect = pointsShapeRectangle(cell);
46124 var onEllipse = !onRect && pointsShapeEllipse(cell);
46125
46126 vertexDragOptions[i] = [];
46127 for(var j = 0; j < cell.length; j++) {
46128 if(cell[j][0] === 'Z') continue;
46129
46130 if(onEllipse &&
46131 j !== i000 &&
46132 j !== i090 &&
46133 j !== i180 &&
46134 j !== i270
46135 ) {
46136 continue;
46137 }
46138
46139 var x = cell[j][1];
46140 var y = cell[j][2];
46141
46142 var vertex = g.append('circle')
46143 .classed('cursor-grab', true)
46144 .attr('data-i', i)
46145 .attr('data-j', j)
46146 .attr('cx', x)
46147 .attr('cy', y)
46148 .attr('r', 4)
46149 .style({
46150 'mix-blend-mode': 'luminosity',
46151 fill: 'black',
46152 stroke: 'white',
46153 'stroke-width': 1
46154 });
46155
46156 vertexDragOptions[i][j] = {
46157 element: vertex.node(),
46158 gd: gd,
46159 prepFn: startDragVertex,
46160 doneFn: endDragVertexController,
46161 clickFn: clickVertexController
46162 };
46163
46164 dragElement.init(vertexDragOptions[i][j]);
46165 }
46166 }
46167 }
46168
46169 function moveShape(dx, dy) {
46170 if(!polygons.length) return;
46171
46172 for(var i = 0; i < polygons.length; i++) {
46173 for(var j = 0; j < polygons[i].length; j++) {
46174 for(var k = 0; k + 2 < polygons[i][j].length; k += 2) {
46175 polygons[i][j][k + 1] = copyPolygons[i][j][k + 1] + dx;
46176 polygons[i][j][k + 2] = copyPolygons[i][j][k + 2] + dy;
46177 }
46178 }
46179 }
46180 }
46181
46182 function moveShapeController(dx, dy) {
46183 moveShape(dx, dy);
46184
46185 redraw();
46186 }
46187
46188 function startDragShapeController(evt) {
46189 indexI = +evt.srcElement.getAttribute('data-i');
46190 if(!indexI) indexI = 0; // ensure non-existing move button get zero index
46191
46192 shapeDragOptions[indexI].moveFn = moveShapeController;
46193 }
46194
46195 function endDragShapeController() {
46196 update();
46197 }
46198
46199 function addShapeControllers() {
46200 shapeDragOptions = [];
46201
46202 if(!polygons.length) return;
46203
46204 var i = 0;
46205 shapeDragOptions[i] = {
46206 element: outlines[0][0],
46207 gd: gd,
46208 prepFn: startDragShapeController,
46209 doneFn: endDragShapeController
46210 };
46211
46212 dragElement.init(shapeDragOptions[i]);
46213 }
46214};
46215
46216function recordPositions(polygonsOut, polygonsIn) {
46217 for(var i = 0; i < polygonsIn.length; i++) {
46218 var cell = polygonsIn[i];
46219 polygonsOut[i] = [];
46220 for(var j = 0; j < cell.length; j++) {
46221 polygonsOut[i][j] = [];
46222 for(var k = 0; k < cell[j].length; k++) {
46223 polygonsOut[i][j][k] = cell[j][k];
46224 }
46225 }
46226 }
46227 return polygonsOut;
46228}
46229
46230},{"../../../plots/cartesian/handle_outline":345,"../../../registry":376,"../../dragelement":176,"../../dragelement/helpers":175,"./constants":243,"./helpers":246,"./newshapes":247}],246:[function(_dereq_,module,exports){
46231'use strict';
46232
46233var parseSvgPath = _dereq_('parse-svg-path');
46234
46235var constants = _dereq_('./constants');
46236var CIRCLE_SIDES = constants.CIRCLE_SIDES;
46237var SQRT2 = constants.SQRT2;
46238
46239var cartesianHelpers = _dereq_('../../../plots/cartesian/helpers');
46240var p2r = cartesianHelpers.p2r;
46241var r2p = cartesianHelpers.r2p;
46242
46243var iC = [0, 3, 4, 5, 6, 1, 2];
46244var iQS = [0, 3, 4, 1, 2];
46245
46246exports.writePaths = function(polygons) {
46247 var nI = polygons.length;
46248 if(!nI) return 'M0,0Z';
46249
46250 var str = '';
46251 for(var i = 0; i < nI; i++) {
46252 var nJ = polygons[i].length;
46253 for(var j = 0; j < nJ; j++) {
46254 var w = polygons[i][j][0];
46255 if(w === 'Z') {
46256 str += 'Z';
46257 } else {
46258 var nK = polygons[i][j].length;
46259 for(var k = 0; k < nK; k++) {
46260 var realK = k;
46261 if(w === 'Q' || w === 'S') {
46262 realK = iQS[k];
46263 } else if(w === 'C') {
46264 realK = iC[k];
46265 }
46266
46267 str += polygons[i][j][realK];
46268 if(k > 0 && k < nK - 1) {
46269 str += ',';
46270 }
46271 }
46272 }
46273 }
46274 }
46275
46276 return str;
46277};
46278
46279exports.readPaths = function(str, gd, plotinfo, isActiveShape) {
46280 var cmd = parseSvgPath(str);
46281
46282 var polys = [];
46283 var n = -1;
46284 var newPoly = function() {
46285 n++;
46286 polys[n] = [];
46287 };
46288
46289 var k;
46290 var x = 0;
46291 var y = 0;
46292 var initX;
46293 var initY;
46294 var recStart = function() {
46295 initX = x;
46296 initY = y;
46297 };
46298
46299 recStart();
46300 for(var i = 0; i < cmd.length; i++) {
46301 var newPos = [];
46302
46303 var x1, x2, y1, y2; // i.e. extra params for curves
46304
46305 var c = cmd[i][0];
46306 var w = c;
46307 switch(c) {
46308 case 'M':
46309 newPoly();
46310 x = +cmd[i][1];
46311 y = +cmd[i][2];
46312 newPos.push([w, x, y]);
46313
46314 recStart();
46315 break;
46316
46317 case 'Q':
46318 case 'S':
46319 x1 = +cmd[i][1];
46320 y1 = +cmd[i][2];
46321 x = +cmd[i][3];
46322 y = +cmd[i][4];
46323 newPos.push([w, x, y, x1, y1]); // -> iQS order
46324 break;
46325
46326 case 'C':
46327 x1 = +cmd[i][1];
46328 y1 = +cmd[i][2];
46329 x2 = +cmd[i][3];
46330 y2 = +cmd[i][4];
46331 x = +cmd[i][5];
46332 y = +cmd[i][6];
46333 newPos.push([w, x, y, x1, y1, x2, y2]); // -> iC order
46334 break;
46335
46336 case 'T':
46337 case 'L':
46338 x = +cmd[i][1];
46339 y = +cmd[i][2];
46340 newPos.push([w, x, y]);
46341 break;
46342
46343 case 'H':
46344 w = 'L'; // convert to line (for now)
46345 x = +cmd[i][1];
46346 newPos.push([w, x, y]);
46347 break;
46348
46349 case 'V':
46350 w = 'L'; // convert to line (for now)
46351 y = +cmd[i][1];
46352 newPos.push([w, x, y]);
46353 break;
46354
46355 case 'A':
46356 w = 'L'; // convert to line to handle circle
46357 var rx = +cmd[i][1];
46358 var ry = +cmd[i][2];
46359 if(!+cmd[i][4]) {
46360 rx = -rx;
46361 ry = -ry;
46362 }
46363
46364 var cenX = x - rx;
46365 var cenY = y;
46366 for(k = 1; k <= CIRCLE_SIDES / 2; k++) {
46367 var t = 2 * Math.PI * k / CIRCLE_SIDES;
46368 newPos.push([
46369 w,
46370 cenX + rx * Math.cos(t),
46371 cenY + ry * Math.sin(t)
46372 ]);
46373 }
46374 break;
46375
46376 case 'Z':
46377 if(x !== initX || y !== initY) {
46378 x = initX;
46379 y = initY;
46380 newPos.push([w, x, y]);
46381 }
46382 break;
46383 }
46384
46385 var domain = (plotinfo || {}).domain;
46386 var size = gd._fullLayout._size;
46387 var xPixelSized = plotinfo && plotinfo.xsizemode === 'pixel';
46388 var yPixelSized = plotinfo && plotinfo.ysizemode === 'pixel';
46389 var noOffset = isActiveShape === false;
46390
46391 for(var j = 0; j < newPos.length; j++) {
46392 for(k = 0; k + 2 < 7; k += 2) {
46393 var _x = newPos[j][k + 1];
46394 var _y = newPos[j][k + 2];
46395
46396 if(_x === undefined || _y === undefined) continue;
46397 // keep track of end point for Z
46398 x = _x;
46399 y = _y;
46400
46401 if(plotinfo) {
46402 if(plotinfo.xaxis && plotinfo.xaxis.p2r) {
46403 if(noOffset) _x -= plotinfo.xaxis._offset;
46404 if(xPixelSized) {
46405 _x = r2p(plotinfo.xaxis, plotinfo.xanchor) + _x;
46406 } else {
46407 _x = p2r(plotinfo.xaxis, _x);
46408 }
46409 } else {
46410 if(noOffset) _x -= size.l;
46411 if(domain) _x = domain.x[0] + _x / size.w;
46412 else _x = _x / size.w;
46413 }
46414
46415 if(plotinfo.yaxis && plotinfo.yaxis.p2r) {
46416 if(noOffset) _y -= plotinfo.yaxis._offset;
46417 if(yPixelSized) {
46418 _y = r2p(plotinfo.yaxis, plotinfo.yanchor) - _y;
46419 } else {
46420 _y = p2r(plotinfo.yaxis, _y);
46421 }
46422 } else {
46423 if(noOffset) _y -= size.t;
46424 if(domain) _y = domain.y[1] - _y / size.h;
46425 else _y = 1 - _y / size.h;
46426 }
46427 }
46428
46429 newPos[j][k + 1] = _x;
46430 newPos[j][k + 2] = _y;
46431 }
46432 polys[n].push(
46433 newPos[j].slice()
46434 );
46435 }
46436 }
46437
46438 return polys;
46439};
46440
46441function almostEq(a, b) {
46442 return Math.abs(a - b) <= 1e-6;
46443}
46444
46445function dist(a, b) {
46446 var dx = b[1] - a[1];
46447 var dy = b[2] - a[2];
46448 return Math.sqrt(
46449 dx * dx +
46450 dy * dy
46451 );
46452}
46453
46454exports.pointsShapeRectangle = function(cell) {
46455 var len = cell.length;
46456 if(len !== 5) return false;
46457
46458 for(var j = 1; j < 3; j++) {
46459 var e01 = cell[0][j] - cell[1][j];
46460 var e32 = cell[3][j] - cell[2][j];
46461
46462 if(!almostEq(e01, e32)) return false;
46463
46464 var e03 = cell[0][j] - cell[3][j];
46465 var e12 = cell[1][j] - cell[2][j];
46466 if(!almostEq(e03, e12)) return false;
46467 }
46468
46469 // N.B. rotated rectangles are not valid rects since rotation is not supported in shapes for now.
46470 if(
46471 !almostEq(cell[0][1], cell[1][1]) &&
46472 !almostEq(cell[0][1], cell[3][1])
46473 ) return false;
46474
46475 // reject cases with zero area
46476 return !!(
46477 dist(cell[0], cell[1]) *
46478 dist(cell[0], cell[3])
46479 );
46480};
46481
46482exports.pointsShapeEllipse = function(cell) {
46483 var len = cell.length;
46484 if(len !== CIRCLE_SIDES + 1) return false;
46485
46486 // opposite diagonals should be the same
46487 len = CIRCLE_SIDES;
46488 for(var i = 0; i < len; i++) {
46489 var k = (len * 2 - i) % len;
46490
46491 var k2 = (len / 2 + k) % len;
46492 var i2 = (len / 2 + i) % len;
46493
46494 if(!almostEq(
46495 dist(cell[i], cell[i2]),
46496 dist(cell[k], cell[k2])
46497 )) return false;
46498 }
46499 return true;
46500};
46501
46502exports.handleEllipse = function(isEllipse, start, end) {
46503 if(!isEllipse) return [start, end]; // i.e. case of line
46504
46505 var pos = exports.ellipseOver({
46506 x0: start[0],
46507 y0: start[1],
46508 x1: end[0],
46509 y1: end[1]
46510 });
46511
46512 var cx = (pos.x1 + pos.x0) / 2;
46513 var cy = (pos.y1 + pos.y0) / 2;
46514 var rx = (pos.x1 - pos.x0) / 2;
46515 var ry = (pos.y1 - pos.y0) / 2;
46516
46517 // make a circle when one dimension is zero
46518 if(!rx) rx = ry = ry / SQRT2;
46519 if(!ry) ry = rx = rx / SQRT2;
46520
46521 var cell = [];
46522 for(var i = 0; i < CIRCLE_SIDES; i++) {
46523 var t = i * 2 * Math.PI / CIRCLE_SIDES;
46524 cell.push([
46525 cx + rx * Math.cos(t),
46526 cy + ry * Math.sin(t),
46527 ]);
46528 }
46529 return cell;
46530};
46531
46532exports.ellipseOver = function(pos) {
46533 var x0 = pos.x0;
46534 var y0 = pos.y0;
46535 var x1 = pos.x1;
46536 var y1 = pos.y1;
46537
46538 var dx = x1 - x0;
46539 var dy = y1 - y0;
46540
46541 x0 -= dx;
46542 y0 -= dy;
46543
46544 var cx = (x0 + x1) / 2;
46545 var cy = (y0 + y1) / 2;
46546
46547 var scale = SQRT2;
46548 dx *= scale;
46549 dy *= scale;
46550
46551 return {
46552 x0: cx - dx,
46553 y0: cy - dy,
46554 x1: cx + dx,
46555 y1: cy + dy
46556 };
46557};
46558
46559},{"../../../plots/cartesian/helpers":346,"./constants":243,"parse-svg-path":74}],247:[function(_dereq_,module,exports){
46560'use strict';
46561
46562var dragHelpers = _dereq_('../../dragelement/helpers');
46563var drawMode = dragHelpers.drawMode;
46564var openMode = dragHelpers.openMode;
46565
46566var constants = _dereq_('./constants');
46567var i000 = constants.i000;
46568var i090 = constants.i090;
46569var i180 = constants.i180;
46570var i270 = constants.i270;
46571var cos45 = constants.cos45;
46572var sin45 = constants.sin45;
46573
46574var cartesianHelpers = _dereq_('../../../plots/cartesian/helpers');
46575var p2r = cartesianHelpers.p2r;
46576var r2p = cartesianHelpers.r2p;
46577
46578var handleOutline = _dereq_('../../../plots/cartesian/handle_outline');
46579var clearSelect = handleOutline.clearSelect;
46580
46581var helpers = _dereq_('./helpers');
46582var readPaths = helpers.readPaths;
46583var writePaths = helpers.writePaths;
46584var ellipseOver = helpers.ellipseOver;
46585
46586
46587module.exports = function newShapes(outlines, dragOptions) {
46588 if(!outlines.length) return;
46589 var e = outlines[0][0]; // pick first
46590 if(!e) return;
46591 var d = e.getAttribute('d');
46592
46593 var gd = dragOptions.gd;
46594 var drwStyle = gd._fullLayout.newshape;
46595
46596 var plotinfo = dragOptions.plotinfo;
46597 var xaxis = plotinfo.xaxis;
46598 var yaxis = plotinfo.yaxis;
46599 var xPaper = !!plotinfo.domain || !plotinfo.xaxis;
46600 var yPaper = !!plotinfo.domain || !plotinfo.yaxis;
46601
46602 var isActiveShape = dragOptions.isActiveShape;
46603 var dragmode = dragOptions.dragmode;
46604
46605 var shapes = (gd.layout || {}).shapes || [];
46606
46607 if(!drawMode(dragmode) && isActiveShape !== undefined) {
46608 var id = gd._fullLayout._activeShapeIndex;
46609 if(id < shapes.length) {
46610 switch(gd._fullLayout.shapes[id].type) {
46611 case 'rect':
46612 dragmode = 'drawrect';
46613 break;
46614 case 'circle':
46615 dragmode = 'drawcircle';
46616 break;
46617 case 'line':
46618 dragmode = 'drawline';
46619 break;
46620 case 'path':
46621 var path = shapes[id].path || '';
46622 if(path[path.length - 1] === 'Z') {
46623 dragmode = 'drawclosedpath';
46624 } else {
46625 dragmode = 'drawopenpath';
46626 }
46627 break;
46628 }
46629 }
46630 }
46631
46632 var isOpenMode = openMode(dragmode);
46633
46634 var polygons = readPaths(d, gd, plotinfo, isActiveShape);
46635
46636 var newShape = {
46637 editable: true,
46638
46639 xref: xPaper ? 'paper' : xaxis._id,
46640 yref: yPaper ? 'paper' : yaxis._id,
46641
46642 layer: drwStyle.layer,
46643 opacity: drwStyle.opacity,
46644 line: {
46645 color: drwStyle.line.color,
46646 width: drwStyle.line.width,
46647 dash: drwStyle.line.dash
46648 }
46649 };
46650
46651 if(!isOpenMode) {
46652 newShape.fillcolor = drwStyle.fillcolor;
46653 newShape.fillrule = drwStyle.fillrule;
46654 }
46655
46656 var cell;
46657 // line, rect and circle can be in one cell
46658 // only define cell if there is single cell
46659 if(polygons.length === 1) cell = polygons[0];
46660
46661 if(
46662 cell &&
46663 dragmode === 'drawrect'
46664 ) {
46665 newShape.type = 'rect';
46666 newShape.x0 = cell[0][1];
46667 newShape.y0 = cell[0][2];
46668 newShape.x1 = cell[2][1];
46669 newShape.y1 = cell[2][2];
46670 } else if(
46671 cell &&
46672 dragmode === 'drawline'
46673 ) {
46674 newShape.type = 'line';
46675 newShape.x0 = cell[0][1];
46676 newShape.y0 = cell[0][2];
46677 newShape.x1 = cell[1][1];
46678 newShape.y1 = cell[1][2];
46679 } else if(
46680 cell &&
46681 dragmode === 'drawcircle'
46682 ) {
46683 newShape.type = 'circle'; // an ellipse!
46684
46685 var xA = cell[i000][1];
46686 var xB = cell[i090][1];
46687 var xC = cell[i180][1];
46688 var xD = cell[i270][1];
46689
46690 var yA = cell[i000][2];
46691 var yB = cell[i090][2];
46692 var yC = cell[i180][2];
46693 var yD = cell[i270][2];
46694
46695 var xDateOrLog = plotinfo.xaxis && (
46696 plotinfo.xaxis.type === 'date' ||
46697 plotinfo.xaxis.type === 'log'
46698 );
46699
46700 var yDateOrLog = plotinfo.yaxis && (
46701 plotinfo.yaxis.type === 'date' ||
46702 plotinfo.yaxis.type === 'log'
46703 );
46704
46705 if(xDateOrLog) {
46706 xA = r2p(plotinfo.xaxis, xA);
46707 xB = r2p(plotinfo.xaxis, xB);
46708 xC = r2p(plotinfo.xaxis, xC);
46709 xD = r2p(plotinfo.xaxis, xD);
46710 }
46711
46712 if(yDateOrLog) {
46713 yA = r2p(plotinfo.yaxis, yA);
46714 yB = r2p(plotinfo.yaxis, yB);
46715 yC = r2p(plotinfo.yaxis, yC);
46716 yD = r2p(plotinfo.yaxis, yD);
46717 }
46718
46719 var x0 = (xB + xD) / 2;
46720 var y0 = (yA + yC) / 2;
46721 var rx = (xD - xB + xC - xA) / 2;
46722 var ry = (yD - yB + yC - yA) / 2;
46723 var pos = ellipseOver({
46724 x0: x0,
46725 y0: y0,
46726 x1: x0 + rx * cos45,
46727 y1: y0 + ry * sin45
46728 });
46729
46730 if(xDateOrLog) {
46731 pos.x0 = p2r(plotinfo.xaxis, pos.x0);
46732 pos.x1 = p2r(plotinfo.xaxis, pos.x1);
46733 }
46734
46735 if(yDateOrLog) {
46736 pos.y0 = p2r(plotinfo.yaxis, pos.y0);
46737 pos.y1 = p2r(plotinfo.yaxis, pos.y1);
46738 }
46739
46740 newShape.x0 = pos.x0;
46741 newShape.y0 = pos.y0;
46742 newShape.x1 = pos.x1;
46743 newShape.y1 = pos.y1;
46744 } else {
46745 newShape.type = 'path';
46746 if(xaxis && yaxis) fixDatesForPaths(polygons, xaxis, yaxis);
46747 newShape.path = writePaths(polygons);
46748 cell = null;
46749 }
46750
46751 clearSelect(gd);
46752
46753 var editHelpers = dragOptions.editHelpers;
46754 var modifyItem = (editHelpers || {}).modifyItem;
46755
46756 var allShapes = [];
46757 for(var q = 0; q < shapes.length; q++) {
46758 var beforeEdit = gd._fullLayout.shapes[q];
46759 allShapes[q] = beforeEdit._input;
46760
46761 if(
46762 isActiveShape !== undefined &&
46763 q === gd._fullLayout._activeShapeIndex
46764 ) {
46765 var afterEdit = newShape;
46766
46767 switch(beforeEdit.type) {
46768 case 'line':
46769 case 'rect':
46770 case 'circle':
46771 modifyItem('x0', afterEdit.x0);
46772 modifyItem('x1', afterEdit.x1);
46773 modifyItem('y0', afterEdit.y0);
46774 modifyItem('y1', afterEdit.y1);
46775 break;
46776
46777 case 'path':
46778 modifyItem('path', afterEdit.path);
46779 break;
46780 }
46781 }
46782 }
46783
46784 if(isActiveShape === undefined) {
46785 allShapes.push(newShape); // add new shape
46786 return allShapes;
46787 }
46788
46789 return editHelpers ? editHelpers.getUpdateObj() : {};
46790};
46791
46792function fixDatesForPaths(polygons, xaxis, yaxis) {
46793 var xIsDate = xaxis.type === 'date';
46794 var yIsDate = yaxis.type === 'date';
46795 if(!xIsDate && !yIsDate) return polygons;
46796
46797 for(var i = 0; i < polygons.length; i++) {
46798 for(var j = 0; j < polygons[i].length; j++) {
46799 for(var k = 0; k + 2 < polygons[i][j].length; k += 2) {
46800 if(xIsDate) polygons[i][j][k + 1] = polygons[i][j][k + 1].replace(' ', '_');
46801 if(yIsDate) polygons[i][j][k + 2] = polygons[i][j][k + 2].replace(' ', '_');
46802 }
46803 }
46804 }
46805
46806 return polygons;
46807}
46808
46809},{"../../../plots/cartesian/handle_outline":345,"../../../plots/cartesian/helpers":346,"../../dragelement/helpers":175,"./constants":243,"./helpers":246}],248:[function(_dereq_,module,exports){
46810'use strict';
46811
46812var constants = _dereq_('./constants');
46813
46814var Lib = _dereq_('../../lib');
46815
46816// special position conversion functions... category axis positions can't be
46817// specified by their data values, because they don't make a continuous mapping.
46818// so these have to be specified in terms of the category serial numbers,
46819// but can take fractional values. Other axis types we specify position based on
46820// the actual data values.
46821// TODO: in V3.0 (when log axis ranges are in data units) range and shape position
46822// will be identical, so rangeToShapePosition and shapePositionToRange can be
46823// removed entirely.
46824
46825exports.rangeToShapePosition = function(ax) {
46826 return (ax.type === 'log') ? ax.r2d : function(v) { return v; };
46827};
46828
46829exports.shapePositionToRange = function(ax) {
46830 return (ax.type === 'log') ? ax.d2r : function(v) { return v; };
46831};
46832
46833exports.decodeDate = function(convertToPx) {
46834 return function(v) {
46835 if(v.replace) v = v.replace('_', ' ');
46836 return convertToPx(v);
46837 };
46838};
46839
46840exports.encodeDate = function(convertToDate) {
46841 return function(v) { return convertToDate(v).replace(' ', '_'); };
46842};
46843
46844exports.extractPathCoords = function(path, paramsToUse) {
46845 var extractedCoordinates = [];
46846
46847 var segments = path.match(constants.segmentRE);
46848 segments.forEach(function(segment) {
46849 var relevantParamIdx = paramsToUse[segment.charAt(0)].drawn;
46850 if(relevantParamIdx === undefined) return;
46851
46852 var params = segment.substr(1).match(constants.paramRE);
46853 if(!params || params.length < relevantParamIdx) return;
46854
46855 extractedCoordinates.push(Lib.cleanNumber(params[relevantParamIdx]));
46856 });
46857
46858 return extractedCoordinates;
46859};
46860
46861exports.getDataToPixel = function(gd, axis, isVertical, refType) {
46862 var gs = gd._fullLayout._size;
46863 var dataToPixel;
46864
46865 if(axis) {
46866 if(refType === 'domain') {
46867 dataToPixel = function(v) {
46868 return axis._length * (isVertical ? (1 - v) : v) + axis._offset;
46869 };
46870 } else {
46871 var d2r = exports.shapePositionToRange(axis);
46872
46873 dataToPixel = function(v) {
46874 return axis._offset + axis.r2p(d2r(v, true));
46875 };
46876
46877 if(axis.type === 'date') dataToPixel = exports.decodeDate(dataToPixel);
46878 }
46879 } else if(isVertical) {
46880 dataToPixel = function(v) { return gs.t + gs.h * (1 - v); };
46881 } else {
46882 dataToPixel = function(v) { return gs.l + gs.w * v; };
46883 }
46884
46885 return dataToPixel;
46886};
46887
46888exports.getPixelToData = function(gd, axis, isVertical, opt) {
46889 var gs = gd._fullLayout._size;
46890 var pixelToData;
46891
46892 if(axis) {
46893 if(opt === 'domain') {
46894 pixelToData = function(p) {
46895 var q = (p - axis._offset) / axis._length;
46896 return isVertical ? 1 - q : q;
46897 };
46898 } else {
46899 var r2d = exports.rangeToShapePosition(axis);
46900 pixelToData = function(p) { return r2d(axis.p2r(p - axis._offset)); };
46901 }
46902 } else if(isVertical) {
46903 pixelToData = function(p) { return 1 - (p - gs.t) / gs.h; };
46904 } else {
46905 pixelToData = function(p) { return (p - gs.l) / gs.w; };
46906 }
46907
46908 return pixelToData;
46909};
46910
46911/**
46912 * Based on the given stroke width, rounds the passed
46913 * position value to represent either a full or half pixel.
46914 *
46915 * In case of an odd stroke width (e.g. 1), this measure ensures
46916 * that a stroke positioned at the returned position isn't rendered
46917 * blurry due to anti-aliasing.
46918 *
46919 * In case of an even stroke width (e.g. 2), this measure ensures
46920 * that the position value is transformed to a full pixel value
46921 * so that anti-aliasing doesn't take effect either.
46922 *
46923 * @param {number} pos The raw position value to be transformed
46924 * @param {number} strokeWidth The stroke width
46925 * @returns {number} either an integer or a .5 decimal number
46926 */
46927exports.roundPositionForSharpStrokeRendering = function(pos, strokeWidth) {
46928 var strokeWidthIsOdd = Math.round(strokeWidth % 2) === 1;
46929 var posValAsInt = Math.round(pos);
46930
46931 return strokeWidthIsOdd ? posValAsInt + 0.5 : posValAsInt;
46932};
46933
46934exports.makeOptionsAndPlotinfo = function(gd, index) {
46935 var options = gd._fullLayout.shapes[index] || {};
46936
46937 var plotinfo = gd._fullLayout._plots[options.xref + options.yref];
46938 var hasPlotinfo = !!plotinfo;
46939 if(hasPlotinfo) {
46940 plotinfo._hadPlotinfo = true;
46941 } else {
46942 plotinfo = {};
46943 if(options.xref && options.xref !== 'paper') plotinfo.xaxis = gd._fullLayout[options.xref + 'axis'];
46944 if(options.yref && options.yref !== 'paper') plotinfo.yaxis = gd._fullLayout[options.yref + 'axis'];
46945 }
46946
46947 plotinfo.xsizemode = options.xsizemode;
46948 plotinfo.ysizemode = options.ysizemode;
46949 plotinfo.xanchor = options.xanchor;
46950 plotinfo.yanchor = options.yanchor;
46951
46952 return {
46953 options: options,
46954 plotinfo: plotinfo
46955 };
46956};
46957
46958},{"../../lib":287,"./constants":239}],249:[function(_dereq_,module,exports){
46959'use strict';
46960
46961var drawModule = _dereq_('./draw');
46962
46963module.exports = {
46964 moduleType: 'component',
46965 name: 'shapes',
46966
46967 layoutAttributes: _dereq_('./attributes'),
46968 supplyLayoutDefaults: _dereq_('./defaults'),
46969 supplyDrawNewShapeDefaults: _dereq_('./draw_newshape/defaults'),
46970 includeBasePlot: _dereq_('../../plots/cartesian/include_components')('shapes'),
46971
46972 calcAutorange: _dereq_('./calc_autorange'),
46973 draw: drawModule.draw,
46974 drawOne: drawModule.drawOne
46975};
46976
46977},{"../../plots/cartesian/include_components":347,"./attributes":237,"./calc_autorange":238,"./defaults":240,"./draw":241,"./draw_newshape/defaults":244}],250:[function(_dereq_,module,exports){
46978'use strict';
46979
46980var fontAttrs = _dereq_('../../plots/font_attributes');
46981var padAttrs = _dereq_('../../plots/pad_attributes');
46982var extendDeepAll = _dereq_('../../lib/extend').extendDeepAll;
46983var overrideAll = _dereq_('../../plot_api/edit_types').overrideAll;
46984var animationAttrs = _dereq_('../../plots/animation_attributes');
46985var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray;
46986var constants = _dereq_('./constants');
46987
46988var stepsAttrs = templatedArray('step', {
46989 visible: {
46990 valType: 'boolean',
46991 dflt: true,
46992 },
46993 method: {
46994 valType: 'enumerated',
46995 values: ['restyle', 'relayout', 'animate', 'update', 'skip'],
46996 dflt: 'restyle',
46997 },
46998 args: {
46999 valType: 'info_array',
47000 freeLength: true,
47001 items: [
47002 { valType: 'any' },
47003 { valType: 'any' },
47004 { valType: 'any' }
47005 ],
47006 },
47007 label: {
47008 valType: 'string',
47009 },
47010 value: {
47011 valType: 'string',
47012 },
47013 execute: {
47014 valType: 'boolean',
47015 dflt: true,
47016 }
47017});
47018
47019module.exports = overrideAll(templatedArray('slider', {
47020 visible: {
47021 valType: 'boolean',
47022 dflt: true,
47023 },
47024
47025 active: {
47026 valType: 'number',
47027 min: 0,
47028 dflt: 0,
47029 },
47030
47031 steps: stepsAttrs,
47032
47033 lenmode: {
47034 valType: 'enumerated',
47035 values: ['fraction', 'pixels'],
47036 dflt: 'fraction',
47037 },
47038 len: {
47039 valType: 'number',
47040 min: 0,
47041 dflt: 1,
47042 },
47043 x: {
47044 valType: 'number',
47045 min: -2,
47046 max: 3,
47047 dflt: 0,
47048 },
47049 pad: extendDeepAll(padAttrs({editType: 'arraydraw'}), {
47050 }, {t: {dflt: 20}}),
47051 xanchor: {
47052 valType: 'enumerated',
47053 values: ['auto', 'left', 'center', 'right'],
47054 dflt: 'left',
47055 },
47056 y: {
47057 valType: 'number',
47058 min: -2,
47059 max: 3,
47060 dflt: 0,
47061 },
47062 yanchor: {
47063 valType: 'enumerated',
47064 values: ['auto', 'top', 'middle', 'bottom'],
47065 dflt: 'top',
47066 },
47067
47068 transition: {
47069 duration: {
47070 valType: 'number',
47071 min: 0,
47072 dflt: 150,
47073 },
47074 easing: {
47075 valType: 'enumerated',
47076 values: animationAttrs.transition.easing.values,
47077 dflt: 'cubic-in-out',
47078 }
47079 },
47080
47081 currentvalue: {
47082 visible: {
47083 valType: 'boolean',
47084 dflt: true,
47085 },
47086
47087 xanchor: {
47088 valType: 'enumerated',
47089 values: ['left', 'center', 'right'],
47090 dflt: 'left',
47091 },
47092
47093 offset: {
47094 valType: 'number',
47095 dflt: 10,
47096 },
47097
47098 prefix: {
47099 valType: 'string',
47100 },
47101
47102 suffix: {
47103 valType: 'string',
47104 },
47105
47106 font: fontAttrs({
47107 })
47108 },
47109
47110 font: fontAttrs({
47111 }),
47112
47113 activebgcolor: {
47114 valType: 'color',
47115 dflt: constants.gripBgActiveColor,
47116 },
47117 bgcolor: {
47118 valType: 'color',
47119 dflt: constants.railBgColor,
47120 },
47121 bordercolor: {
47122 valType: 'color',
47123 dflt: constants.railBorderColor,
47124 },
47125 borderwidth: {
47126 valType: 'number',
47127 min: 0,
47128 dflt: constants.railBorderWidth,
47129 },
47130 ticklen: {
47131 valType: 'number',
47132 min: 0,
47133 dflt: constants.tickLength,
47134 },
47135 tickcolor: {
47136 valType: 'color',
47137 dflt: constants.tickColor,
47138 },
47139 tickwidth: {
47140 valType: 'number',
47141 min: 0,
47142 dflt: 1,
47143 },
47144 minorticklen: {
47145 valType: 'number',
47146 min: 0,
47147 dflt: constants.minorTickLength,
47148 }
47149}), 'arraydraw', 'from-root');
47150
47151},{"../../lib/extend":281,"../../plot_api/edit_types":316,"../../plot_api/plot_template":323,"../../plots/animation_attributes":328,"../../plots/font_attributes":363,"../../plots/pad_attributes":368,"./constants":251}],251:[function(_dereq_,module,exports){
47152'use strict';
47153
47154
47155module.exports = {
47156
47157 // layout attribute name
47158 name: 'sliders',
47159
47160 // class names
47161 containerClassName: 'slider-container',
47162 groupClassName: 'slider-group',
47163 inputAreaClass: 'slider-input-area',
47164 railRectClass: 'slider-rail-rect',
47165 railTouchRectClass: 'slider-rail-touch-rect',
47166 gripRectClass: 'slider-grip-rect',
47167 tickRectClass: 'slider-tick-rect',
47168 inputProxyClass: 'slider-input-proxy',
47169 labelsClass: 'slider-labels',
47170 labelGroupClass: 'slider-label-group',
47171 labelClass: 'slider-label',
47172 currentValueClass: 'slider-current-value',
47173
47174 railHeight: 5,
47175
47176 // DOM attribute name in button group keeping track
47177 // of active update menu
47178 menuIndexAttrName: 'slider-active-index',
47179
47180 // id root pass to Plots.autoMargin
47181 autoMarginIdRoot: 'slider-',
47182
47183 // min item width / height
47184 minWidth: 30,
47185 minHeight: 30,
47186
47187 // padding around item text
47188 textPadX: 40,
47189
47190 // arrow offset off right edge
47191 arrowOffsetX: 4,
47192
47193 railRadius: 2,
47194 railWidth: 5,
47195 railBorder: 4,
47196 railBorderWidth: 1,
47197 railBorderColor: '#bec8d9',
47198 railBgColor: '#f8fafc',
47199
47200 // The distance of the rail from the edge of the touchable area
47201 // Slightly less than the step inset because of the curved edges
47202 // of the rail
47203 railInset: 8,
47204
47205 // The distance from the extremal tick marks to the edge of the
47206 // touchable area. This is basically the same as the grip radius,
47207 // but for other styles it wouldn't really need to be.
47208 stepInset: 10,
47209
47210 gripRadius: 10,
47211 gripWidth: 20,
47212 gripHeight: 20,
47213 gripBorder: 20,
47214 gripBorderWidth: 1,
47215 gripBorderColor: '#bec8d9',
47216 gripBgColor: '#f6f8fa',
47217 gripBgActiveColor: '#dbdde0',
47218
47219 labelPadding: 8,
47220 labelOffset: 0,
47221
47222 tickWidth: 1,
47223 tickColor: '#333',
47224 tickOffset: 25,
47225 tickLength: 7,
47226
47227 minorTickOffset: 25,
47228 minorTickColor: '#333',
47229 minorTickLength: 4,
47230
47231 // Extra space below the current value label:
47232 currentValuePadding: 8,
47233 currentValueInset: 0,
47234};
47235
47236},{}],252:[function(_dereq_,module,exports){
47237'use strict';
47238
47239var Lib = _dereq_('../../lib');
47240var handleArrayContainerDefaults = _dereq_('../../plots/array_container_defaults');
47241
47242var attributes = _dereq_('./attributes');
47243var constants = _dereq_('./constants');
47244
47245var name = constants.name;
47246var stepAttrs = attributes.steps;
47247
47248
47249module.exports = function slidersDefaults(layoutIn, layoutOut) {
47250 handleArrayContainerDefaults(layoutIn, layoutOut, {
47251 name: name,
47252 handleItemDefaults: sliderDefaults
47253 });
47254};
47255
47256function sliderDefaults(sliderIn, sliderOut, layoutOut) {
47257 function coerce(attr, dflt) {
47258 return Lib.coerce(sliderIn, sliderOut, attributes, attr, dflt);
47259 }
47260
47261 var steps = handleArrayContainerDefaults(sliderIn, sliderOut, {
47262 name: 'steps',
47263 handleItemDefaults: stepDefaults
47264 });
47265
47266 var stepCount = 0;
47267 for(var i = 0; i < steps.length; i++) {
47268 if(steps[i].visible) stepCount++;
47269 }
47270
47271 var visible;
47272 // If it has fewer than two options, it's not really a slider
47273 if(stepCount < 2) visible = sliderOut.visible = false;
47274 else visible = coerce('visible');
47275 if(!visible) return;
47276
47277 sliderOut._stepCount = stepCount;
47278 var visSteps = sliderOut._visibleSteps = Lib.filterVisible(steps);
47279
47280 var active = coerce('active');
47281 if(!(steps[active] || {}).visible) sliderOut.active = visSteps[0]._index;
47282
47283 coerce('x');
47284 coerce('y');
47285 Lib.noneOrAll(sliderIn, sliderOut, ['x', 'y']);
47286
47287 coerce('xanchor');
47288 coerce('yanchor');
47289
47290 coerce('len');
47291 coerce('lenmode');
47292
47293 coerce('pad.t');
47294 coerce('pad.r');
47295 coerce('pad.b');
47296 coerce('pad.l');
47297
47298 Lib.coerceFont(coerce, 'font', layoutOut.font);
47299
47300 var currentValueIsVisible = coerce('currentvalue.visible');
47301
47302 if(currentValueIsVisible) {
47303 coerce('currentvalue.xanchor');
47304 coerce('currentvalue.prefix');
47305 coerce('currentvalue.suffix');
47306 coerce('currentvalue.offset');
47307
47308 Lib.coerceFont(coerce, 'currentvalue.font', sliderOut.font);
47309 }
47310
47311 coerce('transition.duration');
47312 coerce('transition.easing');
47313
47314 coerce('bgcolor');
47315 coerce('activebgcolor');
47316 coerce('bordercolor');
47317 coerce('borderwidth');
47318 coerce('ticklen');
47319 coerce('tickwidth');
47320 coerce('tickcolor');
47321 coerce('minorticklen');
47322}
47323
47324function stepDefaults(valueIn, valueOut) {
47325 function coerce(attr, dflt) {
47326 return Lib.coerce(valueIn, valueOut, stepAttrs, attr, dflt);
47327 }
47328
47329 var visible;
47330 if(valueIn.method !== 'skip' && !Array.isArray(valueIn.args)) {
47331 visible = valueOut.visible = false;
47332 } else visible = coerce('visible');
47333
47334 if(visible) {
47335 coerce('method');
47336 coerce('args');
47337 var label = coerce('label', 'step-' + valueOut._index);
47338 coerce('value', label);
47339 coerce('execute');
47340 }
47341}
47342
47343},{"../../lib":287,"../../plots/array_container_defaults":329,"./attributes":250,"./constants":251}],253:[function(_dereq_,module,exports){
47344'use strict';
47345
47346var d3 = _dereq_('@plotly/d3');
47347
47348var Plots = _dereq_('../../plots/plots');
47349var Color = _dereq_('../color');
47350var Drawing = _dereq_('../drawing');
47351var Lib = _dereq_('../../lib');
47352var strTranslate = Lib.strTranslate;
47353var svgTextUtils = _dereq_('../../lib/svg_text_utils');
47354var arrayEditor = _dereq_('../../plot_api/plot_template').arrayEditor;
47355
47356var constants = _dereq_('./constants');
47357var alignmentConstants = _dereq_('../../constants/alignment');
47358var LINE_SPACING = alignmentConstants.LINE_SPACING;
47359var FROM_TL = alignmentConstants.FROM_TL;
47360var FROM_BR = alignmentConstants.FROM_BR;
47361
47362module.exports = function draw(gd) {
47363 var fullLayout = gd._fullLayout;
47364 var sliderData = makeSliderData(fullLayout, gd);
47365
47366 // draw a container for *all* sliders:
47367 var sliders = fullLayout._infolayer
47368 .selectAll('g.' + constants.containerClassName)
47369 .data(sliderData.length > 0 ? [0] : []);
47370
47371 sliders.enter().append('g')
47372 .classed(constants.containerClassName, true)
47373 .style('cursor', 'ew-resize');
47374
47375 function clearSlider(sliderOpts) {
47376 if(sliderOpts._commandObserver) {
47377 sliderOpts._commandObserver.remove();
47378 delete sliderOpts._commandObserver;
47379 }
47380
47381 // Most components don't need to explicitly remove autoMargin, because
47382 // marginPushers does this - but slider updates don't go through
47383 // a full replot so we need to explicitly remove it.
47384 Plots.autoMargin(gd, autoMarginId(sliderOpts));
47385 }
47386
47387 sliders.exit().each(function() {
47388 d3.select(this).selectAll('g.' + constants.groupClassName)
47389 .each(clearSlider);
47390 })
47391 .remove();
47392
47393 // Return early if no menus visible:
47394 if(sliderData.length === 0) return;
47395
47396 var sliderGroups = sliders.selectAll('g.' + constants.groupClassName)
47397 .data(sliderData, keyFunction);
47398
47399 sliderGroups.enter().append('g')
47400 .classed(constants.groupClassName, true);
47401
47402 sliderGroups.exit()
47403 .each(clearSlider)
47404 .remove();
47405
47406 // Find the dimensions of the sliders:
47407 for(var i = 0; i < sliderData.length; i++) {
47408 var sliderOpts = sliderData[i];
47409 findDimensions(gd, sliderOpts);
47410 }
47411
47412 sliderGroups.each(function(sliderOpts) {
47413 var gSlider = d3.select(this);
47414
47415 computeLabelSteps(sliderOpts);
47416
47417 Plots.manageCommandObserver(gd, sliderOpts, sliderOpts._visibleSteps, function(data) {
47418 // NB: Same as below. This is *not* always the same as sliderOpts since
47419 // if a new set of steps comes in, the reference in this callback would
47420 // be invalid. We need to refetch it from the slider group, which is
47421 // the join data that creates this slider. So if this slider still exists,
47422 // the group should be valid, *to the best of my knowledge.* If not,
47423 // we'd have to look it up by d3 data join index/key.
47424 var opts = gSlider.data()[0];
47425
47426 if(opts.active === data.index) return;
47427 if(opts._dragging) return;
47428
47429 setActive(gd, gSlider, opts, data.index, false, true);
47430 });
47431
47432 drawSlider(gd, d3.select(this), sliderOpts);
47433 });
47434};
47435
47436function autoMarginId(sliderOpts) {
47437 return constants.autoMarginIdRoot + sliderOpts._index;
47438}
47439
47440// This really only just filters by visibility:
47441function makeSliderData(fullLayout, gd) {
47442 var contOpts = fullLayout[constants.name];
47443 var sliderData = [];
47444
47445 for(var i = 0; i < contOpts.length; i++) {
47446 var item = contOpts[i];
47447 if(!item.visible) continue;
47448 item._gd = gd;
47449 sliderData.push(item);
47450 }
47451
47452 return sliderData;
47453}
47454
47455// This is set in the defaults step:
47456function keyFunction(opts) {
47457 return opts._index;
47458}
47459
47460// Compute the dimensions (mutates sliderOpts):
47461function findDimensions(gd, sliderOpts) {
47462 var sliderLabels = Drawing.tester.selectAll('g.' + constants.labelGroupClass)
47463 .data(sliderOpts._visibleSteps);
47464
47465 sliderLabels.enter().append('g')
47466 .classed(constants.labelGroupClass, true);
47467
47468 // loop over fake buttons to find width / height
47469 var maxLabelWidth = 0;
47470 var labelHeight = 0;
47471 sliderLabels.each(function(stepOpts) {
47472 var labelGroup = d3.select(this);
47473
47474 var text = drawLabel(labelGroup, {step: stepOpts}, sliderOpts);
47475
47476 var textNode = text.node();
47477 if(textNode) {
47478 var bBox = Drawing.bBox(textNode);
47479 labelHeight = Math.max(labelHeight, bBox.height);
47480 maxLabelWidth = Math.max(maxLabelWidth, bBox.width);
47481 }
47482 });
47483
47484 sliderLabels.remove();
47485
47486 var dims = sliderOpts._dims = {};
47487
47488 dims.inputAreaWidth = Math.max(
47489 constants.railWidth,
47490 constants.gripHeight
47491 );
47492
47493 // calculate some overall dimensions - some of these are needed for
47494 // calculating the currentValue dimensions
47495 var graphSize = gd._fullLayout._size;
47496 dims.lx = graphSize.l + graphSize.w * sliderOpts.x;
47497 dims.ly = graphSize.t + graphSize.h * (1 - sliderOpts.y);
47498
47499 if(sliderOpts.lenmode === 'fraction') {
47500 // fraction:
47501 dims.outerLength = Math.round(graphSize.w * sliderOpts.len);
47502 } else {
47503 // pixels:
47504 dims.outerLength = sliderOpts.len;
47505 }
47506
47507 // The length of the rail, *excluding* padding on either end:
47508 dims.inputAreaStart = 0;
47509 dims.inputAreaLength = Math.round(dims.outerLength - sliderOpts.pad.l - sliderOpts.pad.r);
47510
47511 var textableInputLength = dims.inputAreaLength - 2 * constants.stepInset;
47512 var availableSpacePerLabel = textableInputLength / (sliderOpts._stepCount - 1);
47513 var computedSpacePerLabel = maxLabelWidth + constants.labelPadding;
47514 dims.labelStride = Math.max(1, Math.ceil(computedSpacePerLabel / availableSpacePerLabel));
47515 dims.labelHeight = labelHeight;
47516
47517 // loop over all possible values for currentValue to find the
47518 // area we need for it
47519 dims.currentValueMaxWidth = 0;
47520 dims.currentValueHeight = 0;
47521 dims.currentValueTotalHeight = 0;
47522 dims.currentValueMaxLines = 1;
47523
47524 if(sliderOpts.currentvalue.visible) {
47525 // Get the dimensions of the current value label:
47526 var dummyGroup = Drawing.tester.append('g');
47527
47528 sliderLabels.each(function(stepOpts) {
47529 var curValPrefix = drawCurrentValue(dummyGroup, sliderOpts, stepOpts.label);
47530 var curValSize = (curValPrefix.node() && Drawing.bBox(curValPrefix.node())) || {width: 0, height: 0};
47531 var lines = svgTextUtils.lineCount(curValPrefix);
47532 dims.currentValueMaxWidth = Math.max(dims.currentValueMaxWidth, Math.ceil(curValSize.width));
47533 dims.currentValueHeight = Math.max(dims.currentValueHeight, Math.ceil(curValSize.height));
47534 dims.currentValueMaxLines = Math.max(dims.currentValueMaxLines, lines);
47535 });
47536
47537 dims.currentValueTotalHeight = dims.currentValueHeight + sliderOpts.currentvalue.offset;
47538
47539 dummyGroup.remove();
47540 }
47541
47542 dims.height = dims.currentValueTotalHeight + constants.tickOffset + sliderOpts.ticklen + constants.labelOffset + dims.labelHeight + sliderOpts.pad.t + sliderOpts.pad.b;
47543
47544 var xanchor = 'left';
47545 if(Lib.isRightAnchor(sliderOpts)) {
47546 dims.lx -= dims.outerLength;
47547 xanchor = 'right';
47548 }
47549 if(Lib.isCenterAnchor(sliderOpts)) {
47550 dims.lx -= dims.outerLength / 2;
47551 xanchor = 'center';
47552 }
47553
47554 var yanchor = 'top';
47555 if(Lib.isBottomAnchor(sliderOpts)) {
47556 dims.ly -= dims.height;
47557 yanchor = 'bottom';
47558 }
47559 if(Lib.isMiddleAnchor(sliderOpts)) {
47560 dims.ly -= dims.height / 2;
47561 yanchor = 'middle';
47562 }
47563
47564 dims.outerLength = Math.ceil(dims.outerLength);
47565 dims.height = Math.ceil(dims.height);
47566 dims.lx = Math.round(dims.lx);
47567 dims.ly = Math.round(dims.ly);
47568
47569 var marginOpts = {
47570 y: sliderOpts.y,
47571 b: dims.height * FROM_BR[yanchor],
47572 t: dims.height * FROM_TL[yanchor]
47573 };
47574
47575 if(sliderOpts.lenmode === 'fraction') {
47576 marginOpts.l = 0;
47577 marginOpts.xl = sliderOpts.x - sliderOpts.len * FROM_TL[xanchor];
47578 marginOpts.r = 0;
47579 marginOpts.xr = sliderOpts.x + sliderOpts.len * FROM_BR[xanchor];
47580 } else {
47581 marginOpts.x = sliderOpts.x;
47582 marginOpts.l = dims.outerLength * FROM_TL[xanchor];
47583 marginOpts.r = dims.outerLength * FROM_BR[xanchor];
47584 }
47585
47586 Plots.autoMargin(gd, autoMarginId(sliderOpts), marginOpts);
47587}
47588
47589function drawSlider(gd, sliderGroup, sliderOpts) {
47590 // This is related to the other long notes in this file regarding what happens
47591 // when slider steps disappear. This particular fix handles what happens when
47592 // the *current* slider step is removed. The drawing functions will error out
47593 // when they fail to find it, so the fix for now is that it will just draw the
47594 // slider in the first position but will not execute the command.
47595 if(!((sliderOpts.steps[sliderOpts.active] || {}).visible)) {
47596 sliderOpts.active = sliderOpts._visibleSteps[0]._index;
47597 }
47598
47599 // These are carefully ordered for proper z-ordering:
47600 sliderGroup
47601 .call(drawCurrentValue, sliderOpts)
47602 .call(drawRail, sliderOpts)
47603 .call(drawLabelGroup, sliderOpts)
47604 .call(drawTicks, sliderOpts)
47605 .call(drawTouchRect, gd, sliderOpts)
47606 .call(drawGrip, gd, sliderOpts);
47607
47608 var dims = sliderOpts._dims;
47609
47610 // Position the rectangle:
47611 Drawing.setTranslate(sliderGroup, dims.lx + sliderOpts.pad.l, dims.ly + sliderOpts.pad.t);
47612
47613 sliderGroup.call(setGripPosition, sliderOpts, false);
47614 sliderGroup.call(drawCurrentValue, sliderOpts);
47615}
47616
47617function drawCurrentValue(sliderGroup, sliderOpts, valueOverride) {
47618 if(!sliderOpts.currentvalue.visible) return;
47619
47620 var dims = sliderOpts._dims;
47621 var x0, textAnchor;
47622
47623 switch(sliderOpts.currentvalue.xanchor) {
47624 case 'right':
47625 // This is anchored left and adjusted by the width of the longest label
47626 // so that the prefix doesn't move. The goal of this is to emphasize
47627 // what's actually changing and make the update less distracting.
47628 x0 = dims.inputAreaLength - constants.currentValueInset - dims.currentValueMaxWidth;
47629 textAnchor = 'left';
47630 break;
47631 case 'center':
47632 x0 = dims.inputAreaLength * 0.5;
47633 textAnchor = 'middle';
47634 break;
47635 default:
47636 x0 = constants.currentValueInset;
47637 textAnchor = 'left';
47638 }
47639
47640 var text = Lib.ensureSingle(sliderGroup, 'text', constants.labelClass, function(s) {
47641 s.attr({
47642 'text-anchor': textAnchor,
47643 'data-notex': 1
47644 });
47645 });
47646
47647 var str = sliderOpts.currentvalue.prefix ? sliderOpts.currentvalue.prefix : '';
47648
47649 if(typeof valueOverride === 'string') {
47650 str += valueOverride;
47651 } else {
47652 var curVal = sliderOpts.steps[sliderOpts.active].label;
47653 var _meta = sliderOpts._gd._fullLayout._meta;
47654 if(_meta) curVal = Lib.templateString(curVal, _meta);
47655 str += curVal;
47656 }
47657
47658 if(sliderOpts.currentvalue.suffix) {
47659 str += sliderOpts.currentvalue.suffix;
47660 }
47661
47662 text.call(Drawing.font, sliderOpts.currentvalue.font)
47663 .text(str)
47664 .call(svgTextUtils.convertToTspans, sliderOpts._gd);
47665
47666 var lines = svgTextUtils.lineCount(text);
47667
47668 var y0 = (dims.currentValueMaxLines + 1 - lines) *
47669 sliderOpts.currentvalue.font.size * LINE_SPACING;
47670
47671 svgTextUtils.positionText(text, x0, y0);
47672
47673 return text;
47674}
47675
47676function drawGrip(sliderGroup, gd, sliderOpts) {
47677 var grip = Lib.ensureSingle(sliderGroup, 'rect', constants.gripRectClass, function(s) {
47678 s.call(attachGripEvents, gd, sliderGroup, sliderOpts)
47679 .style('pointer-events', 'all');
47680 });
47681
47682 grip.attr({
47683 width: constants.gripWidth,
47684 height: constants.gripHeight,
47685 rx: constants.gripRadius,
47686 ry: constants.gripRadius,
47687 })
47688 .call(Color.stroke, sliderOpts.bordercolor)
47689 .call(Color.fill, sliderOpts.bgcolor)
47690 .style('stroke-width', sliderOpts.borderwidth + 'px');
47691}
47692
47693function drawLabel(item, data, sliderOpts) {
47694 var text = Lib.ensureSingle(item, 'text', constants.labelClass, function(s) {
47695 s.attr({
47696 'text-anchor': 'middle',
47697 'data-notex': 1
47698 });
47699 });
47700
47701 var tx = data.step.label;
47702 var _meta = sliderOpts._gd._fullLayout._meta;
47703 if(_meta) tx = Lib.templateString(tx, _meta);
47704
47705 text.call(Drawing.font, sliderOpts.font)
47706 .text(tx)
47707 .call(svgTextUtils.convertToTspans, sliderOpts._gd);
47708
47709 return text;
47710}
47711
47712function drawLabelGroup(sliderGroup, sliderOpts) {
47713 var labels = Lib.ensureSingle(sliderGroup, 'g', constants.labelsClass);
47714 var dims = sliderOpts._dims;
47715
47716 var labelItems = labels.selectAll('g.' + constants.labelGroupClass)
47717 .data(dims.labelSteps);
47718
47719 labelItems.enter().append('g')
47720 .classed(constants.labelGroupClass, true);
47721
47722 labelItems.exit().remove();
47723
47724 labelItems.each(function(d) {
47725 var item = d3.select(this);
47726
47727 item.call(drawLabel, d, sliderOpts);
47728
47729 Drawing.setTranslate(item,
47730 normalizedValueToPosition(sliderOpts, d.fraction),
47731 constants.tickOffset +
47732 sliderOpts.ticklen +
47733 // position is the baseline of the top line of text only, even
47734 // if the label spans multiple lines
47735 sliderOpts.font.size * LINE_SPACING +
47736 constants.labelOffset +
47737 dims.currentValueTotalHeight
47738 );
47739 });
47740}
47741
47742function handleInput(gd, sliderGroup, sliderOpts, normalizedPosition, doTransition) {
47743 var quantizedPosition = Math.round(normalizedPosition * (sliderOpts._stepCount - 1));
47744 var quantizedIndex = sliderOpts._visibleSteps[quantizedPosition]._index;
47745
47746 if(quantizedIndex !== sliderOpts.active) {
47747 setActive(gd, sliderGroup, sliderOpts, quantizedIndex, true, doTransition);
47748 }
47749}
47750
47751function setActive(gd, sliderGroup, sliderOpts, index, doCallback, doTransition) {
47752 var previousActive = sliderOpts.active;
47753 sliderOpts.active = index;
47754
47755 // due to templating, it's possible this slider doesn't even exist yet
47756 arrayEditor(gd.layout, constants.name, sliderOpts)
47757 .applyUpdate('active', index);
47758
47759 var step = sliderOpts.steps[sliderOpts.active];
47760
47761 sliderGroup.call(setGripPosition, sliderOpts, doTransition);
47762 sliderGroup.call(drawCurrentValue, sliderOpts);
47763
47764 gd.emit('plotly_sliderchange', {
47765 slider: sliderOpts,
47766 step: sliderOpts.steps[sliderOpts.active],
47767 interaction: doCallback,
47768 previousActive: previousActive
47769 });
47770
47771 if(step && step.method && doCallback) {
47772 if(sliderGroup._nextMethod) {
47773 // If we've already queued up an update, just overwrite it with the most recent:
47774 sliderGroup._nextMethod.step = step;
47775 sliderGroup._nextMethod.doCallback = doCallback;
47776 sliderGroup._nextMethod.doTransition = doTransition;
47777 } else {
47778 sliderGroup._nextMethod = {step: step, doCallback: doCallback, doTransition: doTransition};
47779 sliderGroup._nextMethodRaf = window.requestAnimationFrame(function() {
47780 var _step = sliderGroup._nextMethod.step;
47781 if(!_step.method) return;
47782
47783 if(_step.execute) {
47784 Plots.executeAPICommand(gd, _step.method, _step.args);
47785 }
47786
47787 sliderGroup._nextMethod = null;
47788 sliderGroup._nextMethodRaf = null;
47789 });
47790 }
47791 }
47792}
47793
47794function attachGripEvents(item, gd, sliderGroup) {
47795 var node = sliderGroup.node();
47796 var $gd = d3.select(gd);
47797
47798 // NB: This is *not* the same as sliderOpts itself! These callbacks
47799 // are in a closure so this array won't actually be correct if the
47800 // steps have changed since this was initialized. The sliderGroup,
47801 // however, has not changed since that *is* the slider, so it must
47802 // be present to receive mouse events.
47803 function getSliderOpts() {
47804 return sliderGroup.data()[0];
47805 }
47806
47807 function mouseDownHandler() {
47808 var sliderOpts = getSliderOpts();
47809 gd.emit('plotly_sliderstart', {slider: sliderOpts});
47810
47811 var grip = sliderGroup.select('.' + constants.gripRectClass);
47812
47813 d3.event.stopPropagation();
47814 d3.event.preventDefault();
47815 grip.call(Color.fill, sliderOpts.activebgcolor);
47816
47817 var normalizedPosition = positionToNormalizedValue(sliderOpts, d3.mouse(node)[0]);
47818 handleInput(gd, sliderGroup, sliderOpts, normalizedPosition, true);
47819 sliderOpts._dragging = true;
47820
47821 function mouseMoveHandler() {
47822 var sliderOpts = getSliderOpts();
47823 var normalizedPosition = positionToNormalizedValue(sliderOpts, d3.mouse(node)[0]);
47824 handleInput(gd, sliderGroup, sliderOpts, normalizedPosition, false);
47825 }
47826
47827 $gd.on('mousemove', mouseMoveHandler);
47828 $gd.on('touchmove', mouseMoveHandler);
47829
47830 function mouseUpHandler() {
47831 var sliderOpts = getSliderOpts();
47832 sliderOpts._dragging = false;
47833 grip.call(Color.fill, sliderOpts.bgcolor);
47834 $gd.on('mouseup', null);
47835 $gd.on('mousemove', null);
47836 $gd.on('touchend', null);
47837 $gd.on('touchmove', null);
47838
47839 gd.emit('plotly_sliderend', {
47840 slider: sliderOpts,
47841 step: sliderOpts.steps[sliderOpts.active]
47842 });
47843 }
47844
47845 $gd.on('mouseup', mouseUpHandler);
47846 $gd.on('touchend', mouseUpHandler);
47847 }
47848
47849 item.on('mousedown', mouseDownHandler);
47850 item.on('touchstart', mouseDownHandler);
47851}
47852
47853function drawTicks(sliderGroup, sliderOpts) {
47854 var tick = sliderGroup.selectAll('rect.' + constants.tickRectClass)
47855 .data(sliderOpts._visibleSteps);
47856 var dims = sliderOpts._dims;
47857
47858 tick.enter().append('rect')
47859 .classed(constants.tickRectClass, true);
47860
47861 tick.exit().remove();
47862
47863 tick.attr({
47864 width: sliderOpts.tickwidth + 'px',
47865 'shape-rendering': 'crispEdges'
47866 });
47867
47868 tick.each(function(d, i) {
47869 var isMajor = i % dims.labelStride === 0;
47870 var item = d3.select(this);
47871
47872 item
47873 .attr({height: isMajor ? sliderOpts.ticklen : sliderOpts.minorticklen})
47874 .call(Color.fill, isMajor ? sliderOpts.tickcolor : sliderOpts.tickcolor);
47875
47876 Drawing.setTranslate(item,
47877 normalizedValueToPosition(sliderOpts, i / (sliderOpts._stepCount - 1)) - 0.5 * sliderOpts.tickwidth,
47878 (isMajor ? constants.tickOffset : constants.minorTickOffset) + dims.currentValueTotalHeight
47879 );
47880 });
47881}
47882
47883function computeLabelSteps(sliderOpts) {
47884 var dims = sliderOpts._dims;
47885 dims.labelSteps = [];
47886 var nsteps = sliderOpts._stepCount;
47887
47888 for(var i = 0; i < nsteps; i += dims.labelStride) {
47889 dims.labelSteps.push({
47890 fraction: i / (nsteps - 1),
47891 step: sliderOpts._visibleSteps[i]
47892 });
47893 }
47894}
47895
47896function setGripPosition(sliderGroup, sliderOpts, doTransition) {
47897 var grip = sliderGroup.select('rect.' + constants.gripRectClass);
47898
47899 var quantizedIndex = 0;
47900 for(var i = 0; i < sliderOpts._stepCount; i++) {
47901 if(sliderOpts._visibleSteps[i]._index === sliderOpts.active) {
47902 quantizedIndex = i;
47903 break;
47904 }
47905 }
47906
47907 var x = normalizedValueToPosition(sliderOpts, quantizedIndex / (sliderOpts._stepCount - 1));
47908
47909 // If this is true, then *this component* is already invoking its own command
47910 // and has triggered its own animation.
47911 if(sliderOpts._invokingCommand) return;
47912
47913 var el = grip;
47914 if(doTransition && sliderOpts.transition.duration > 0) {
47915 el = el.transition()
47916 .duration(sliderOpts.transition.duration)
47917 .ease(sliderOpts.transition.easing);
47918 }
47919
47920 // Drawing.setTranslate doesn't work here because of the transition duck-typing.
47921 // It's also not necessary because there are no other transitions to preserve.
47922 el.attr('transform', strTranslate(x - constants.gripWidth * 0.5, sliderOpts._dims.currentValueTotalHeight));
47923}
47924
47925// Convert a number from [0-1] to a pixel position relative to the slider group container:
47926function normalizedValueToPosition(sliderOpts, normalizedPosition) {
47927 var dims = sliderOpts._dims;
47928 return dims.inputAreaStart + constants.stepInset +
47929 (dims.inputAreaLength - 2 * constants.stepInset) * Math.min(1, Math.max(0, normalizedPosition));
47930}
47931
47932// Convert a position relative to the slider group to a nubmer in [0, 1]
47933function positionToNormalizedValue(sliderOpts, position) {
47934 var dims = sliderOpts._dims;
47935 return Math.min(1, Math.max(0, (position - constants.stepInset - dims.inputAreaStart) / (dims.inputAreaLength - 2 * constants.stepInset - 2 * dims.inputAreaStart)));
47936}
47937
47938function drawTouchRect(sliderGroup, gd, sliderOpts) {
47939 var dims = sliderOpts._dims;
47940 var rect = Lib.ensureSingle(sliderGroup, 'rect', constants.railTouchRectClass, function(s) {
47941 s.call(attachGripEvents, gd, sliderGroup, sliderOpts)
47942 .style('pointer-events', 'all');
47943 });
47944
47945 rect.attr({
47946 width: dims.inputAreaLength,
47947 height: Math.max(dims.inputAreaWidth, constants.tickOffset + sliderOpts.ticklen + dims.labelHeight)
47948 })
47949 .call(Color.fill, sliderOpts.bgcolor)
47950 .attr('opacity', 0);
47951
47952 Drawing.setTranslate(rect, 0, dims.currentValueTotalHeight);
47953}
47954
47955function drawRail(sliderGroup, sliderOpts) {
47956 var dims = sliderOpts._dims;
47957 var computedLength = dims.inputAreaLength - constants.railInset * 2;
47958 var rect = Lib.ensureSingle(sliderGroup, 'rect', constants.railRectClass);
47959
47960 rect.attr({
47961 width: computedLength,
47962 height: constants.railWidth,
47963 rx: constants.railRadius,
47964 ry: constants.railRadius,
47965 'shape-rendering': 'crispEdges'
47966 })
47967 .call(Color.stroke, sliderOpts.bordercolor)
47968 .call(Color.fill, sliderOpts.bgcolor)
47969 .style('stroke-width', sliderOpts.borderwidth + 'px');
47970
47971 Drawing.setTranslate(rect,
47972 constants.railInset,
47973 (dims.inputAreaWidth - constants.railWidth) * 0.5 + dims.currentValueTotalHeight
47974 );
47975}
47976
47977},{"../../constants/alignment":262,"../../lib":287,"../../lib/svg_text_utils":310,"../../plot_api/plot_template":323,"../../plots/plots":369,"../color":157,"../drawing":179,"./constants":251,"@plotly/d3":20}],254:[function(_dereq_,module,exports){
47978'use strict';
47979
47980var constants = _dereq_('./constants');
47981
47982module.exports = {
47983 moduleType: 'component',
47984 name: constants.name,
47985
47986 layoutAttributes: _dereq_('./attributes'),
47987 supplyLayoutDefaults: _dereq_('./defaults'),
47988
47989 draw: _dereq_('./draw')
47990};
47991
47992},{"./attributes":250,"./constants":251,"./defaults":252,"./draw":253}],255:[function(_dereq_,module,exports){
47993'use strict';
47994
47995var d3 = _dereq_('@plotly/d3');
47996var isNumeric = _dereq_('fast-isnumeric');
47997
47998var Plots = _dereq_('../../plots/plots');
47999var Registry = _dereq_('../../registry');
48000var Lib = _dereq_('../../lib');
48001var strTranslate = Lib.strTranslate;
48002var Drawing = _dereq_('../drawing');
48003var Color = _dereq_('../color');
48004var svgTextUtils = _dereq_('../../lib/svg_text_utils');
48005var interactConstants = _dereq_('../../constants/interactions');
48006
48007var OPPOSITE_SIDE = _dereq_('../../constants/alignment').OPPOSITE_SIDE;
48008var numStripRE = / [XY][0-9]* /;
48009
48010/**
48011 * Titles - (re)draw titles on the axes and plot:
48012 * @param {DOM element} gd - the graphDiv
48013 * @param {string} titleClass - the css class of this title
48014 * @param {object} options - how and what to draw
48015 * propContainer - the layout object containing `title` and `titlefont`
48016 * attributes that apply to this title
48017 * propName - the full name of the title property (for Plotly.relayout)
48018 * [traceIndex] - include only if this property applies to one trace
48019 * (such as a colorbar title) - then editing pipes to Plotly.restyle
48020 * instead of Plotly.relayout
48021 * placeholder - placeholder text for an empty editable title
48022 * [avoid] {object} - include if this title should move to avoid other elements
48023 * selection - d3 selection of elements to avoid
48024 * side - which direction to move if there is a conflict
48025 * [offsetLeft] - if these elements are subject to a translation
48026 * wrt the title element
48027 * [offsetTop]
48028 * attributes {object} - position and alignment attributes
48029 * x - pixels
48030 * y - pixels
48031 * text-anchor - start|middle|end
48032 * transform {object} - how to transform the title after positioning
48033 * rotate - degrees
48034 * offset - shift up/down in the rotated frame (unused?)
48035 * containerGroup - if an svg <g> element already exists to hold this
48036 * title, include here. Otherwise it will go in fullLayout._infolayer
48037 * _meta {object (optional} - meta key-value to for title with
48038 * Lib.templateString, default to fullLayout._meta, if not provided
48039 *
48040 * @return {selection} d3 selection of title container group
48041 */
48042function draw(gd, titleClass, options) {
48043 var cont = options.propContainer;
48044 var prop = options.propName;
48045 var placeholder = options.placeholder;
48046 var traceIndex = options.traceIndex;
48047 var avoid = options.avoid || {};
48048 var attributes = options.attributes;
48049 var transform = options.transform;
48050 var group = options.containerGroup;
48051
48052 var fullLayout = gd._fullLayout;
48053
48054 var opacity = 1;
48055 var isplaceholder = false;
48056 var title = cont.title;
48057 var txt = (title && title.text ? title.text : '').trim();
48058
48059 var font = title && title.font ? title.font : {};
48060 var fontFamily = font.family;
48061 var fontSize = font.size;
48062 var fontColor = font.color;
48063
48064 // only make this title editable if we positively identify its property
48065 // as one that has editing enabled.
48066 var editAttr;
48067 if(prop === 'title.text') editAttr = 'titleText';
48068 else if(prop.indexOf('axis') !== -1) editAttr = 'axisTitleText';
48069 else if(prop.indexOf('colorbar' !== -1)) editAttr = 'colorbarTitleText';
48070 var editable = gd._context.edits[editAttr];
48071
48072 if(txt === '') opacity = 0;
48073 // look for placeholder text while stripping out numbers from eg X2, Y3
48074 // this is just for backward compatibility with the old version that had
48075 // "Click to enter X2 title" and may have gotten saved in some old plots,
48076 // we don't want this to show up when these are displayed.
48077 else if(txt.replace(numStripRE, ' % ') === placeholder.replace(numStripRE, ' % ')) {
48078 opacity = 0.2;
48079 isplaceholder = true;
48080 if(!editable) txt = '';
48081 }
48082
48083 if(options._meta) {
48084 txt = Lib.templateString(txt, options._meta);
48085 } else if(fullLayout._meta) {
48086 txt = Lib.templateString(txt, fullLayout._meta);
48087 }
48088
48089 var elShouldExist = txt || editable;
48090
48091 if(!group) {
48092 group = Lib.ensureSingle(fullLayout._infolayer, 'g', 'g-' + titleClass);
48093 }
48094
48095 var el = group.selectAll('text')
48096 .data(elShouldExist ? [0] : []);
48097 el.enter().append('text');
48098 el.text(txt)
48099 // this is hacky, but convertToTspans uses the class
48100 // to determine whether to rotate mathJax...
48101 // so we need to clear out any old class and put the
48102 // correct one (only relevant for colorbars, at least
48103 // for now) - ie don't use .classed
48104 .attr('class', titleClass);
48105 el.exit().remove();
48106
48107 if(!elShouldExist) return group;
48108
48109 function titleLayout(titleEl) {
48110 Lib.syncOrAsync([drawTitle, scootTitle], titleEl);
48111 }
48112
48113 function drawTitle(titleEl) {
48114 var transformVal;
48115
48116 if(transform) {
48117 transformVal = '';
48118 if(transform.rotate) {
48119 transformVal += 'rotate(' + [transform.rotate, attributes.x, attributes.y] + ')';
48120 }
48121 if(transform.offset) {
48122 transformVal += strTranslate(0, transform.offset);
48123 }
48124 } else {
48125 transformVal = null;
48126 }
48127
48128 titleEl.attr('transform', transformVal);
48129
48130 titleEl.style({
48131 'font-family': fontFamily,
48132 'font-size': d3.round(fontSize, 2) + 'px',
48133 fill: Color.rgb(fontColor),
48134 opacity: opacity * Color.opacity(fontColor),
48135 'font-weight': Plots.fontWeight
48136 })
48137 .attr(attributes)
48138 .call(svgTextUtils.convertToTspans, gd);
48139
48140 return Plots.previousPromises(gd);
48141 }
48142
48143 function scootTitle(titleElIn) {
48144 var titleGroup = d3.select(titleElIn.node().parentNode);
48145
48146 if(avoid && avoid.selection && avoid.side && txt) {
48147 titleGroup.attr('transform', null);
48148
48149 // move toward avoid.side (= left, right, top, bottom) if needed
48150 // can include pad (pixels, default 2)
48151 var backside = OPPOSITE_SIDE[avoid.side];
48152 var shiftSign = (avoid.side === 'left' || avoid.side === 'top') ? -1 : 1;
48153 var pad = isNumeric(avoid.pad) ? avoid.pad : 2;
48154
48155 var titlebb = Drawing.bBox(titleGroup.node());
48156 var paperbb = {
48157 left: 0,
48158 top: 0,
48159 right: fullLayout.width,
48160 bottom: fullLayout.height
48161 };
48162
48163 var maxshift = avoid.maxShift ||
48164 shiftSign * (paperbb[avoid.side] - titlebb[avoid.side]);
48165 var shift = 0;
48166
48167 // Prevent the title going off the paper
48168 if(maxshift < 0) {
48169 shift = maxshift;
48170 } else {
48171 // so we don't have to offset each avoided element,
48172 // give the title the opposite offset
48173 var offsetLeft = avoid.offsetLeft || 0;
48174 var offsetTop = avoid.offsetTop || 0;
48175 titlebb.left -= offsetLeft;
48176 titlebb.right -= offsetLeft;
48177 titlebb.top -= offsetTop;
48178 titlebb.bottom -= offsetTop;
48179
48180 // iterate over a set of elements (avoid.selection)
48181 // to avoid collisions with
48182 avoid.selection.each(function() {
48183 var avoidbb = Drawing.bBox(this);
48184
48185 if(Lib.bBoxIntersect(titlebb, avoidbb, pad)) {
48186 shift = Math.max(shift, shiftSign * (
48187 avoidbb[avoid.side] - titlebb[backside]) + pad);
48188 }
48189 });
48190 shift = Math.min(maxshift, shift);
48191 }
48192
48193 if(shift > 0 || maxshift < 0) {
48194 var shiftTemplate = {
48195 left: [-shift, 0],
48196 right: [shift, 0],
48197 top: [0, -shift],
48198 bottom: [0, shift]
48199 }[avoid.side];
48200 titleGroup.attr('transform', strTranslate(shiftTemplate[0], shiftTemplate[1]));
48201 }
48202 }
48203 }
48204
48205 el.call(titleLayout);
48206
48207 function setPlaceholder() {
48208 opacity = 0;
48209 isplaceholder = true;
48210 el.text(placeholder)
48211 .on('mouseover.opacity', function() {
48212 d3.select(this).transition()
48213 .duration(interactConstants.SHOW_PLACEHOLDER).style('opacity', 1);
48214 })
48215 .on('mouseout.opacity', function() {
48216 d3.select(this).transition()
48217 .duration(interactConstants.HIDE_PLACEHOLDER).style('opacity', 0);
48218 });
48219 }
48220
48221 if(editable) {
48222 if(!txt) setPlaceholder();
48223 else el.on('.opacity', null);
48224
48225 el.call(svgTextUtils.makeEditable, {gd: gd})
48226 .on('edit', function(text) {
48227 if(traceIndex !== undefined) {
48228 Registry.call('_guiRestyle', gd, prop, text, traceIndex);
48229 } else {
48230 Registry.call('_guiRelayout', gd, prop, text);
48231 }
48232 })
48233 .on('cancel', function() {
48234 this.text(this.attr('data-unformatted'))
48235 .call(titleLayout);
48236 })
48237 .on('input', function(d) {
48238 this.text(d || ' ')
48239 .call(svgTextUtils.positionText, attributes.x, attributes.y);
48240 });
48241 }
48242 el.classed('js-placeholder', isplaceholder);
48243
48244 return group;
48245}
48246
48247module.exports = {
48248 draw: draw
48249};
48250
48251},{"../../constants/alignment":262,"../../constants/interactions":266,"../../lib":287,"../../lib/svg_text_utils":310,"../../plots/plots":369,"../../registry":376,"../color":157,"../drawing":179,"@plotly/d3":20,"fast-isnumeric":33}],256:[function(_dereq_,module,exports){
48252'use strict';
48253
48254var fontAttrs = _dereq_('../../plots/font_attributes');
48255var colorAttrs = _dereq_('../color/attributes');
48256var extendFlat = _dereq_('../../lib/extend').extendFlat;
48257var overrideAll = _dereq_('../../plot_api/edit_types').overrideAll;
48258var padAttrs = _dereq_('../../plots/pad_attributes');
48259var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray;
48260
48261var buttonsAttrs = templatedArray('button', {
48262 visible: {
48263 valType: 'boolean',
48264 },
48265 method: {
48266 valType: 'enumerated',
48267 values: ['restyle', 'relayout', 'animate', 'update', 'skip'],
48268 dflt: 'restyle',
48269 },
48270 args: {
48271 valType: 'info_array',
48272 freeLength: true,
48273 items: [
48274 {valType: 'any'},
48275 {valType: 'any'},
48276 {valType: 'any'}
48277 ],
48278 },
48279 args2: {
48280 valType: 'info_array',
48281 freeLength: true,
48282 items: [
48283 {valType: 'any'},
48284 {valType: 'any'},
48285 {valType: 'any'}
48286 ],
48287 },
48288 label: {
48289 valType: 'string',
48290 dflt: '',
48291 },
48292 execute: {
48293 valType: 'boolean',
48294 dflt: true,
48295 }
48296});
48297
48298module.exports = overrideAll(templatedArray('updatemenu', {
48299 _arrayAttrRegexps: [/^updatemenus\[(0|[1-9][0-9]+)\]\.buttons/],
48300
48301 visible: {
48302 valType: 'boolean',
48303 },
48304
48305 type: {
48306 valType: 'enumerated',
48307 values: ['dropdown', 'buttons'],
48308 dflt: 'dropdown',
48309 },
48310
48311 direction: {
48312 valType: 'enumerated',
48313 values: ['left', 'right', 'up', 'down'],
48314 dflt: 'down',
48315 },
48316
48317 active: {
48318 valType: 'integer',
48319 min: -1,
48320 dflt: 0,
48321 },
48322
48323 showactive: {
48324 valType: 'boolean',
48325 dflt: true,
48326 },
48327
48328 buttons: buttonsAttrs,
48329
48330 x: {
48331 valType: 'number',
48332 min: -2,
48333 max: 3,
48334 dflt: -0.05,
48335 },
48336 xanchor: {
48337 valType: 'enumerated',
48338 values: ['auto', 'left', 'center', 'right'],
48339 dflt: 'right',
48340 },
48341 y: {
48342 valType: 'number',
48343 min: -2,
48344 max: 3,
48345 dflt: 1,
48346 },
48347 yanchor: {
48348 valType: 'enumerated',
48349 values: ['auto', 'top', 'middle', 'bottom'],
48350 dflt: 'top',
48351 },
48352
48353 pad: extendFlat(padAttrs({editType: 'arraydraw'}), {
48354 }),
48355
48356 font: fontAttrs({
48357 }),
48358
48359 bgcolor: {
48360 valType: 'color',
48361 },
48362 bordercolor: {
48363 valType: 'color',
48364 dflt: colorAttrs.borderLine,
48365 },
48366 borderwidth: {
48367 valType: 'number',
48368 min: 0,
48369 dflt: 1,
48370 editType: 'arraydraw',
48371 }
48372}), 'arraydraw', 'from-root');
48373
48374},{"../../lib/extend":281,"../../plot_api/edit_types":316,"../../plot_api/plot_template":323,"../../plots/font_attributes":363,"../../plots/pad_attributes":368,"../color/attributes":156}],257:[function(_dereq_,module,exports){
48375'use strict';
48376
48377
48378module.exports = {
48379
48380 // layout attribute name
48381 name: 'updatemenus',
48382
48383 // class names
48384 containerClassName: 'updatemenu-container',
48385 headerGroupClassName: 'updatemenu-header-group',
48386 headerClassName: 'updatemenu-header',
48387 headerArrowClassName: 'updatemenu-header-arrow',
48388 dropdownButtonGroupClassName: 'updatemenu-dropdown-button-group',
48389 dropdownButtonClassName: 'updatemenu-dropdown-button',
48390 buttonClassName: 'updatemenu-button',
48391 itemRectClassName: 'updatemenu-item-rect',
48392 itemTextClassName: 'updatemenu-item-text',
48393
48394 // DOM attribute name in button group keeping track
48395 // of active update menu
48396 menuIndexAttrName: 'updatemenu-active-index',
48397
48398 // id root pass to Plots.autoMargin
48399 autoMarginIdRoot: 'updatemenu-',
48400
48401 // options when 'active: -1'
48402 blankHeaderOpts: { label: ' ' },
48403
48404 // min item width / height
48405 minWidth: 30,
48406 minHeight: 30,
48407
48408 // padding around item text
48409 textPadX: 24,
48410 arrowPadX: 16,
48411
48412 // item rect radii
48413 rx: 2,
48414 ry: 2,
48415
48416 // item text x offset off left edge
48417 textOffsetX: 12,
48418
48419 // item text y offset (w.r.t. middle)
48420 textOffsetY: 3,
48421
48422 // arrow offset off right edge
48423 arrowOffsetX: 4,
48424
48425 // gap between header and buttons
48426 gapButtonHeader: 5,
48427
48428 // gap between between buttons
48429 gapButton: 2,
48430
48431 // color given to active buttons
48432 activeColor: '#F4FAFF',
48433
48434 // color given to hovered buttons
48435 hoverColor: '#F4FAFF',
48436
48437 // symbol for menu open arrow
48438 arrowSymbol: {
48439 left: '◄',
48440 right: '►',
48441 up: '▲',
48442 down: '▼'
48443 }
48444};
48445
48446},{}],258:[function(_dereq_,module,exports){
48447'use strict';
48448
48449var Lib = _dereq_('../../lib');
48450var handleArrayContainerDefaults = _dereq_('../../plots/array_container_defaults');
48451
48452var attributes = _dereq_('./attributes');
48453var constants = _dereq_('./constants');
48454
48455var name = constants.name;
48456var buttonAttrs = attributes.buttons;
48457
48458
48459module.exports = function updateMenusDefaults(layoutIn, layoutOut) {
48460 var opts = {
48461 name: name,
48462 handleItemDefaults: menuDefaults
48463 };
48464
48465 handleArrayContainerDefaults(layoutIn, layoutOut, opts);
48466};
48467
48468function menuDefaults(menuIn, menuOut, layoutOut) {
48469 function coerce(attr, dflt) {
48470 return Lib.coerce(menuIn, menuOut, attributes, attr, dflt);
48471 }
48472
48473 var buttons = handleArrayContainerDefaults(menuIn, menuOut, {
48474 name: 'buttons',
48475 handleItemDefaults: buttonDefaults
48476 });
48477
48478 var visible = coerce('visible', buttons.length > 0);
48479 if(!visible) return;
48480
48481 coerce('active');
48482 coerce('direction');
48483 coerce('type');
48484 coerce('showactive');
48485
48486 coerce('x');
48487 coerce('y');
48488 Lib.noneOrAll(menuIn, menuOut, ['x', 'y']);
48489
48490 coerce('xanchor');
48491 coerce('yanchor');
48492
48493 coerce('pad.t');
48494 coerce('pad.r');
48495 coerce('pad.b');
48496 coerce('pad.l');
48497
48498 Lib.coerceFont(coerce, 'font', layoutOut.font);
48499
48500 coerce('bgcolor', layoutOut.paper_bgcolor);
48501 coerce('bordercolor');
48502 coerce('borderwidth');
48503}
48504
48505function buttonDefaults(buttonIn, buttonOut) {
48506 function coerce(attr, dflt) {
48507 return Lib.coerce(buttonIn, buttonOut, buttonAttrs, attr, dflt);
48508 }
48509
48510 var visible = coerce('visible',
48511 (buttonIn.method === 'skip' || Array.isArray(buttonIn.args)));
48512 if(visible) {
48513 coerce('method');
48514 coerce('args');
48515 coerce('args2');
48516 coerce('label');
48517 coerce('execute');
48518 }
48519}
48520
48521},{"../../lib":287,"../../plots/array_container_defaults":329,"./attributes":256,"./constants":257}],259:[function(_dereq_,module,exports){
48522'use strict';
48523
48524var d3 = _dereq_('@plotly/d3');
48525
48526var Plots = _dereq_('../../plots/plots');
48527var Color = _dereq_('../color');
48528var Drawing = _dereq_('../drawing');
48529var Lib = _dereq_('../../lib');
48530var svgTextUtils = _dereq_('../../lib/svg_text_utils');
48531var arrayEditor = _dereq_('../../plot_api/plot_template').arrayEditor;
48532
48533var LINE_SPACING = _dereq_('../../constants/alignment').LINE_SPACING;
48534
48535var constants = _dereq_('./constants');
48536var ScrollBox = _dereq_('./scrollbox');
48537
48538module.exports = function draw(gd) {
48539 var fullLayout = gd._fullLayout;
48540 var menuData = Lib.filterVisible(fullLayout[constants.name]);
48541
48542 /* Update menu data is bound to the header-group.
48543 * The items in the header group are always present.
48544 *
48545 * Upon clicking on a header its corresponding button
48546 * data is bound to the button-group.
48547 *
48548 * We draw all headers in one group before all buttons
48549 * so that the buttons *always* appear above the headers.
48550 *
48551 * Note that only one set of buttons are visible at once.
48552 *
48553 * <g container />
48554 *
48555 * <g header-group />
48556 * <g item header />
48557 * <text item header-arrow />
48558 * <g header-group />
48559 * <g item header />
48560 * <text item header-arrow />
48561 * ...
48562 *
48563 * <g button-group />
48564 * <g item button />
48565 * <g item button />
48566 * ...
48567 */
48568
48569 function clearAutoMargin(menuOpts) {
48570 Plots.autoMargin(gd, autoMarginId(menuOpts));
48571 }
48572
48573 // draw update menu container
48574 var menus = fullLayout._menulayer
48575 .selectAll('g.' + constants.containerClassName)
48576 .data(menuData.length > 0 ? [0] : []);
48577
48578 menus.enter().append('g')
48579 .classed(constants.containerClassName, true)
48580 .style('cursor', 'pointer');
48581
48582 menus.exit().each(function() {
48583 // Most components don't need to explicitly remove autoMargin, because
48584 // marginPushers does this - but updatemenu updates don't go through
48585 // a full replot so we need to explicitly remove it.
48586 // This is for removing *all* updatemenus, removing individuals is
48587 // handled below, in headerGroups.exit
48588 d3.select(this).selectAll('g.' + constants.headerGroupClassName)
48589 .each(clearAutoMargin);
48590 }).remove();
48591
48592 // return early if no update menus are visible
48593 if(menuData.length === 0) return;
48594
48595 // join header group
48596 var headerGroups = menus.selectAll('g.' + constants.headerGroupClassName)
48597 .data(menuData, keyFunction);
48598
48599 headerGroups.enter().append('g')
48600 .classed(constants.headerGroupClassName, true);
48601
48602 // draw dropdown button container
48603 var gButton = Lib.ensureSingle(menus, 'g', constants.dropdownButtonGroupClassName, function(s) {
48604 s.style('pointer-events', 'all');
48605 });
48606
48607 // find dimensions before plotting anything (this mutates menuOpts)
48608 for(var i = 0; i < menuData.length; i++) {
48609 var menuOpts = menuData[i];
48610 findDimensions(gd, menuOpts);
48611 }
48612
48613 // setup scrollbox
48614 var scrollBoxId = 'updatemenus' + fullLayout._uid;
48615 var scrollBox = new ScrollBox(gd, gButton, scrollBoxId);
48616
48617 // remove exiting header, remove dropped buttons and reset margins
48618 if(headerGroups.enter().size()) {
48619 // make sure gButton is on top of all headers
48620 gButton.node().parentNode.appendChild(gButton.node());
48621 gButton.call(removeAllButtons);
48622 }
48623
48624 headerGroups.exit().each(function(menuOpts) {
48625 gButton.call(removeAllButtons);
48626 clearAutoMargin(menuOpts);
48627 }).remove();
48628
48629 // draw headers!
48630 headerGroups.each(function(menuOpts) {
48631 var gHeader = d3.select(this);
48632
48633 var _gButton = menuOpts.type === 'dropdown' ? gButton : null;
48634
48635 Plots.manageCommandObserver(gd, menuOpts, menuOpts.buttons, function(data) {
48636 setActive(gd, menuOpts, menuOpts.buttons[data.index], gHeader, _gButton, scrollBox, data.index, true);
48637 });
48638
48639 if(menuOpts.type === 'dropdown') {
48640 drawHeader(gd, gHeader, gButton, scrollBox, menuOpts);
48641
48642 // if this menu is active, update the dropdown container
48643 if(isActive(gButton, menuOpts)) {
48644 drawButtons(gd, gHeader, gButton, scrollBox, menuOpts);
48645 }
48646 } else {
48647 drawButtons(gd, gHeader, null, null, menuOpts);
48648 }
48649 });
48650};
48651
48652// Note that '_index' is set at the default step,
48653// it corresponds to the menu index in the user layout update menu container.
48654// Because a menu can be set invisible,
48655// this is a more 'consistent' field than the index in the menuData.
48656function keyFunction(menuOpts) {
48657 return menuOpts._index;
48658}
48659
48660function isFolded(gButton) {
48661 return +gButton.attr(constants.menuIndexAttrName) === -1;
48662}
48663
48664function isActive(gButton, menuOpts) {
48665 return +gButton.attr(constants.menuIndexAttrName) === menuOpts._index;
48666}
48667
48668function setActive(gd, menuOpts, buttonOpts, gHeader, gButton, scrollBox, buttonIndex, isSilentUpdate) {
48669 // update 'active' attribute in menuOpts
48670 menuOpts.active = buttonIndex;
48671
48672 // due to templating, it's possible this slider doesn't even exist yet
48673 arrayEditor(gd.layout, constants.name, menuOpts)
48674 .applyUpdate('active', buttonIndex);
48675
48676 if(menuOpts.type === 'buttons') {
48677 drawButtons(gd, gHeader, null, null, menuOpts);
48678 } else if(menuOpts.type === 'dropdown') {
48679 // fold up buttons and redraw header
48680 gButton.attr(constants.menuIndexAttrName, '-1');
48681
48682 drawHeader(gd, gHeader, gButton, scrollBox, menuOpts);
48683
48684 if(!isSilentUpdate) {
48685 drawButtons(gd, gHeader, gButton, scrollBox, menuOpts);
48686 }
48687 }
48688}
48689
48690function drawHeader(gd, gHeader, gButton, scrollBox, menuOpts) {
48691 var header = Lib.ensureSingle(gHeader, 'g', constants.headerClassName, function(s) {
48692 s.style('pointer-events', 'all');
48693 });
48694
48695 var dims = menuOpts._dims;
48696 var active = menuOpts.active;
48697 var headerOpts = menuOpts.buttons[active] || constants.blankHeaderOpts;
48698 var posOpts = { y: menuOpts.pad.t, yPad: 0, x: menuOpts.pad.l, xPad: 0, index: 0 };
48699 var positionOverrides = {
48700 width: dims.headerWidth,
48701 height: dims.headerHeight
48702 };
48703
48704 header
48705 .call(drawItem, menuOpts, headerOpts, gd)
48706 .call(setItemPosition, menuOpts, posOpts, positionOverrides);
48707
48708 // draw drop arrow at the right edge
48709 var arrow = Lib.ensureSingle(gHeader, 'text', constants.headerArrowClassName, function(s) {
48710 s.attr('text-anchor', 'end')
48711 .call(Drawing.font, menuOpts.font)
48712 .text(constants.arrowSymbol[menuOpts.direction]);
48713 });
48714
48715 arrow.attr({
48716 x: dims.headerWidth - constants.arrowOffsetX + menuOpts.pad.l,
48717 y: dims.headerHeight / 2 + constants.textOffsetY + menuOpts.pad.t
48718 });
48719
48720 header.on('click', function() {
48721 gButton.call(removeAllButtons,
48722 String(isActive(gButton, menuOpts) ? -1 : menuOpts._index)
48723 );
48724
48725 drawButtons(gd, gHeader, gButton, scrollBox, menuOpts);
48726 });
48727
48728 header.on('mouseover', function() {
48729 header.call(styleOnMouseOver);
48730 });
48731
48732 header.on('mouseout', function() {
48733 header.call(styleOnMouseOut, menuOpts);
48734 });
48735
48736 // translate header group
48737 Drawing.setTranslate(gHeader, dims.lx, dims.ly);
48738}
48739
48740function drawButtons(gd, gHeader, gButton, scrollBox, menuOpts) {
48741 // If this is a set of buttons, set pointer events = all since we play
48742 // some minor games with which container is which in order to simplify
48743 // the drawing of *either* buttons or menus
48744 if(!gButton) {
48745 gButton = gHeader;
48746 gButton.attr('pointer-events', 'all');
48747 }
48748
48749 var buttonData = (!isFolded(gButton) || menuOpts.type === 'buttons') ?
48750 menuOpts.buttons :
48751 [];
48752
48753 var klass = menuOpts.type === 'dropdown' ? constants.dropdownButtonClassName : constants.buttonClassName;
48754
48755 var buttons = gButton.selectAll('g.' + klass)
48756 .data(Lib.filterVisible(buttonData));
48757
48758 var enter = buttons.enter().append('g')
48759 .classed(klass, true);
48760
48761 var exit = buttons.exit();
48762
48763 if(menuOpts.type === 'dropdown') {
48764 enter.attr('opacity', '0')
48765 .transition()
48766 .attr('opacity', '1');
48767
48768 exit.transition()
48769 .attr('opacity', '0')
48770 .remove();
48771 } else {
48772 exit.remove();
48773 }
48774
48775 var x0 = 0;
48776 var y0 = 0;
48777 var dims = menuOpts._dims;
48778
48779 var isVertical = ['up', 'down'].indexOf(menuOpts.direction) !== -1;
48780
48781 if(menuOpts.type === 'dropdown') {
48782 if(isVertical) {
48783 y0 = dims.headerHeight + constants.gapButtonHeader;
48784 } else {
48785 x0 = dims.headerWidth + constants.gapButtonHeader;
48786 }
48787 }
48788
48789 if(menuOpts.type === 'dropdown' && menuOpts.direction === 'up') {
48790 y0 = -constants.gapButtonHeader + constants.gapButton - dims.openHeight;
48791 }
48792
48793 if(menuOpts.type === 'dropdown' && menuOpts.direction === 'left') {
48794 x0 = -constants.gapButtonHeader + constants.gapButton - dims.openWidth;
48795 }
48796
48797 var posOpts = {
48798 x: dims.lx + x0 + menuOpts.pad.l,
48799 y: dims.ly + y0 + menuOpts.pad.t,
48800 yPad: constants.gapButton,
48801 xPad: constants.gapButton,
48802 index: 0,
48803 };
48804
48805 var scrollBoxPosition = {
48806 l: posOpts.x + menuOpts.borderwidth,
48807 t: posOpts.y + menuOpts.borderwidth
48808 };
48809
48810 buttons.each(function(buttonOpts, buttonIndex) {
48811 var button = d3.select(this);
48812
48813 button
48814 .call(drawItem, menuOpts, buttonOpts, gd)
48815 .call(setItemPosition, menuOpts, posOpts);
48816
48817 button.on('click', function() {
48818 // skip `dragend` events
48819 if(d3.event.defaultPrevented) return;
48820
48821 if(buttonOpts.execute) {
48822 if(buttonOpts.args2 && menuOpts.active === buttonIndex) {
48823 setActive(gd, menuOpts, buttonOpts, gHeader, gButton, scrollBox, -1);
48824 Plots.executeAPICommand(gd, buttonOpts.method, buttonOpts.args2);
48825 } else {
48826 setActive(gd, menuOpts, buttonOpts, gHeader, gButton, scrollBox, buttonIndex);
48827 Plots.executeAPICommand(gd, buttonOpts.method, buttonOpts.args);
48828 }
48829 }
48830
48831 gd.emit('plotly_buttonclicked', {menu: menuOpts, button: buttonOpts, active: menuOpts.active});
48832 });
48833
48834 button.on('mouseover', function() {
48835 button.call(styleOnMouseOver);
48836 });
48837
48838 button.on('mouseout', function() {
48839 button.call(styleOnMouseOut, menuOpts);
48840 buttons.call(styleButtons, menuOpts);
48841 });
48842 });
48843
48844 buttons.call(styleButtons, menuOpts);
48845
48846 if(isVertical) {
48847 scrollBoxPosition.w = Math.max(dims.openWidth, dims.headerWidth);
48848 scrollBoxPosition.h = posOpts.y - scrollBoxPosition.t;
48849 } else {
48850 scrollBoxPosition.w = posOpts.x - scrollBoxPosition.l;
48851 scrollBoxPosition.h = Math.max(dims.openHeight, dims.headerHeight);
48852 }
48853
48854 scrollBoxPosition.direction = menuOpts.direction;
48855
48856 if(scrollBox) {
48857 if(buttons.size()) {
48858 drawScrollBox(gd, gHeader, gButton, scrollBox, menuOpts, scrollBoxPosition);
48859 } else {
48860 hideScrollBox(scrollBox);
48861 }
48862 }
48863}
48864
48865function drawScrollBox(gd, gHeader, gButton, scrollBox, menuOpts, position) {
48866 // enable the scrollbox
48867 var direction = menuOpts.direction;
48868 var isVertical = (direction === 'up' || direction === 'down');
48869 var dims = menuOpts._dims;
48870
48871 var active = menuOpts.active;
48872 var translateX, translateY;
48873 var i;
48874 if(isVertical) {
48875 translateY = 0;
48876 for(i = 0; i < active; i++) {
48877 translateY += dims.heights[i] + constants.gapButton;
48878 }
48879 } else {
48880 translateX = 0;
48881 for(i = 0; i < active; i++) {
48882 translateX += dims.widths[i] + constants.gapButton;
48883 }
48884 }
48885
48886 scrollBox.enable(position, translateX, translateY);
48887
48888 if(scrollBox.hbar) {
48889 scrollBox.hbar
48890 .attr('opacity', '0')
48891 .transition()
48892 .attr('opacity', '1');
48893 }
48894
48895 if(scrollBox.vbar) {
48896 scrollBox.vbar
48897 .attr('opacity', '0')
48898 .transition()
48899 .attr('opacity', '1');
48900 }
48901}
48902
48903function hideScrollBox(scrollBox) {
48904 var hasHBar = !!scrollBox.hbar;
48905 var hasVBar = !!scrollBox.vbar;
48906
48907 if(hasHBar) {
48908 scrollBox.hbar
48909 .transition()
48910 .attr('opacity', '0')
48911 .each('end', function() {
48912 hasHBar = false;
48913 if(!hasVBar) scrollBox.disable();
48914 });
48915 }
48916
48917 if(hasVBar) {
48918 scrollBox.vbar
48919 .transition()
48920 .attr('opacity', '0')
48921 .each('end', function() {
48922 hasVBar = false;
48923 if(!hasHBar) scrollBox.disable();
48924 });
48925 }
48926}
48927
48928function drawItem(item, menuOpts, itemOpts, gd) {
48929 item.call(drawItemRect, menuOpts)
48930 .call(drawItemText, menuOpts, itemOpts, gd);
48931}
48932
48933function drawItemRect(item, menuOpts) {
48934 var rect = Lib.ensureSingle(item, 'rect', constants.itemRectClassName, function(s) {
48935 s.attr({
48936 rx: constants.rx,
48937 ry: constants.ry,
48938 'shape-rendering': 'crispEdges'
48939 });
48940 });
48941
48942 rect.call(Color.stroke, menuOpts.bordercolor)
48943 .call(Color.fill, menuOpts.bgcolor)
48944 .style('stroke-width', menuOpts.borderwidth + 'px');
48945}
48946
48947function drawItemText(item, menuOpts, itemOpts, gd) {
48948 var text = Lib.ensureSingle(item, 'text', constants.itemTextClassName, function(s) {
48949 s.attr({
48950 'text-anchor': 'start',
48951 'data-notex': 1
48952 });
48953 });
48954
48955 var tx = itemOpts.label;
48956 var _meta = gd._fullLayout._meta;
48957 if(_meta) tx = Lib.templateString(tx, _meta);
48958
48959 text.call(Drawing.font, menuOpts.font)
48960 .text(tx)
48961 .call(svgTextUtils.convertToTspans, gd);
48962}
48963
48964function styleButtons(buttons, menuOpts) {
48965 var active = menuOpts.active;
48966
48967 buttons.each(function(buttonOpts, i) {
48968 var button = d3.select(this);
48969
48970 if(i === active && menuOpts.showactive) {
48971 button.select('rect.' + constants.itemRectClassName)
48972 .call(Color.fill, constants.activeColor);
48973 }
48974 });
48975}
48976
48977function styleOnMouseOver(item) {
48978 item.select('rect.' + constants.itemRectClassName)
48979 .call(Color.fill, constants.hoverColor);
48980}
48981
48982function styleOnMouseOut(item, menuOpts) {
48983 item.select('rect.' + constants.itemRectClassName)
48984 .call(Color.fill, menuOpts.bgcolor);
48985}
48986
48987// find item dimensions (this mutates menuOpts)
48988function findDimensions(gd, menuOpts) {
48989 var dims = menuOpts._dims = {
48990 width1: 0,
48991 height1: 0,
48992 heights: [],
48993 widths: [],
48994 totalWidth: 0,
48995 totalHeight: 0,
48996 openWidth: 0,
48997 openHeight: 0,
48998 lx: 0,
48999 ly: 0
49000 };
49001
49002 var fakeButtons = Drawing.tester.selectAll('g.' + constants.dropdownButtonClassName)
49003 .data(Lib.filterVisible(menuOpts.buttons));
49004
49005 fakeButtons.enter().append('g')
49006 .classed(constants.dropdownButtonClassName, true);
49007
49008 var isVertical = ['up', 'down'].indexOf(menuOpts.direction) !== -1;
49009
49010 // loop over fake buttons to find width / height
49011 fakeButtons.each(function(buttonOpts, i) {
49012 var button = d3.select(this);
49013
49014 button.call(drawItem, menuOpts, buttonOpts, gd);
49015
49016 var text = button.select('.' + constants.itemTextClassName);
49017
49018 // width is given by max width of all buttons
49019 var tWidth = text.node() && Drawing.bBox(text.node()).width;
49020 var wEff = Math.max(tWidth + constants.textPadX, constants.minWidth);
49021
49022 // height is determined by item text
49023 var tHeight = menuOpts.font.size * LINE_SPACING;
49024 var tLines = svgTextUtils.lineCount(text);
49025 var hEff = Math.max(tHeight * tLines, constants.minHeight) + constants.textOffsetY;
49026
49027 hEff = Math.ceil(hEff);
49028 wEff = Math.ceil(wEff);
49029
49030 // Store per-item sizes since a row of horizontal buttons, for example,
49031 // don't all need to be the same width:
49032 dims.widths[i] = wEff;
49033 dims.heights[i] = hEff;
49034
49035 // Height and width of individual element:
49036 dims.height1 = Math.max(dims.height1, hEff);
49037 dims.width1 = Math.max(dims.width1, wEff);
49038
49039 if(isVertical) {
49040 dims.totalWidth = Math.max(dims.totalWidth, wEff);
49041 dims.openWidth = dims.totalWidth;
49042 dims.totalHeight += hEff + constants.gapButton;
49043 dims.openHeight += hEff + constants.gapButton;
49044 } else {
49045 dims.totalWidth += wEff + constants.gapButton;
49046 dims.openWidth += wEff + constants.gapButton;
49047 dims.totalHeight = Math.max(dims.totalHeight, hEff);
49048 dims.openHeight = dims.totalHeight;
49049 }
49050 });
49051
49052 if(isVertical) {
49053 dims.totalHeight -= constants.gapButton;
49054 } else {
49055 dims.totalWidth -= constants.gapButton;
49056 }
49057
49058
49059 dims.headerWidth = dims.width1 + constants.arrowPadX;
49060 dims.headerHeight = dims.height1;
49061
49062 if(menuOpts.type === 'dropdown') {
49063 if(isVertical) {
49064 dims.width1 += constants.arrowPadX;
49065 dims.totalHeight = dims.height1;
49066 } else {
49067 dims.totalWidth = dims.width1;
49068 }
49069 dims.totalWidth += constants.arrowPadX;
49070 }
49071
49072 fakeButtons.remove();
49073
49074 var paddedWidth = dims.totalWidth + menuOpts.pad.l + menuOpts.pad.r;
49075 var paddedHeight = dims.totalHeight + menuOpts.pad.t + menuOpts.pad.b;
49076
49077 var graphSize = gd._fullLayout._size;
49078 dims.lx = graphSize.l + graphSize.w * menuOpts.x;
49079 dims.ly = graphSize.t + graphSize.h * (1 - menuOpts.y);
49080
49081 var xanchor = 'left';
49082 if(Lib.isRightAnchor(menuOpts)) {
49083 dims.lx -= paddedWidth;
49084 xanchor = 'right';
49085 }
49086 if(Lib.isCenterAnchor(menuOpts)) {
49087 dims.lx -= paddedWidth / 2;
49088 xanchor = 'center';
49089 }
49090
49091 var yanchor = 'top';
49092 if(Lib.isBottomAnchor(menuOpts)) {
49093 dims.ly -= paddedHeight;
49094 yanchor = 'bottom';
49095 }
49096 if(Lib.isMiddleAnchor(menuOpts)) {
49097 dims.ly -= paddedHeight / 2;
49098 yanchor = 'middle';
49099 }
49100
49101 dims.totalWidth = Math.ceil(dims.totalWidth);
49102 dims.totalHeight = Math.ceil(dims.totalHeight);
49103 dims.lx = Math.round(dims.lx);
49104 dims.ly = Math.round(dims.ly);
49105
49106 Plots.autoMargin(gd, autoMarginId(menuOpts), {
49107 x: menuOpts.x,
49108 y: menuOpts.y,
49109 l: paddedWidth * ({right: 1, center: 0.5}[xanchor] || 0),
49110 r: paddedWidth * ({left: 1, center: 0.5}[xanchor] || 0),
49111 b: paddedHeight * ({top: 1, middle: 0.5}[yanchor] || 0),
49112 t: paddedHeight * ({bottom: 1, middle: 0.5}[yanchor] || 0)
49113 });
49114}
49115
49116function autoMarginId(menuOpts) {
49117 return constants.autoMarginIdRoot + menuOpts._index;
49118}
49119
49120// set item positions (mutates posOpts)
49121function setItemPosition(item, menuOpts, posOpts, overrideOpts) {
49122 overrideOpts = overrideOpts || {};
49123 var rect = item.select('.' + constants.itemRectClassName);
49124 var text = item.select('.' + constants.itemTextClassName);
49125 var borderWidth = menuOpts.borderwidth;
49126 var index = posOpts.index;
49127 var dims = menuOpts._dims;
49128
49129 Drawing.setTranslate(item, borderWidth + posOpts.x, borderWidth + posOpts.y);
49130
49131 var isVertical = ['up', 'down'].indexOf(menuOpts.direction) !== -1;
49132 var finalHeight = overrideOpts.height || (isVertical ? dims.heights[index] : dims.height1);
49133
49134 rect.attr({
49135 x: 0,
49136 y: 0,
49137 width: overrideOpts.width || (isVertical ? dims.width1 : dims.widths[index]),
49138 height: finalHeight
49139 });
49140
49141 var tHeight = menuOpts.font.size * LINE_SPACING;
49142 var tLines = svgTextUtils.lineCount(text);
49143 var spanOffset = ((tLines - 1) * tHeight / 2);
49144
49145 svgTextUtils.positionText(text, constants.textOffsetX,
49146 finalHeight / 2 - spanOffset + constants.textOffsetY);
49147
49148 if(isVertical) {
49149 posOpts.y += dims.heights[index] + posOpts.yPad;
49150 } else {
49151 posOpts.x += dims.widths[index] + posOpts.xPad;
49152 }
49153
49154 posOpts.index++;
49155}
49156
49157function removeAllButtons(gButton, newMenuIndexAttr) {
49158 gButton
49159 .attr(constants.menuIndexAttrName, newMenuIndexAttr || '-1')
49160 .selectAll('g.' + constants.dropdownButtonClassName).remove();
49161}
49162
49163},{"../../constants/alignment":262,"../../lib":287,"../../lib/svg_text_utils":310,"../../plot_api/plot_template":323,"../../plots/plots":369,"../color":157,"../drawing":179,"./constants":257,"./scrollbox":261,"@plotly/d3":20}],260:[function(_dereq_,module,exports){
49164arguments[4][254][0].apply(exports,arguments)
49165},{"./attributes":256,"./constants":257,"./defaults":258,"./draw":259,"dup":254}],261:[function(_dereq_,module,exports){
49166'use strict';
49167
49168module.exports = ScrollBox;
49169
49170var d3 = _dereq_('@plotly/d3');
49171
49172var Color = _dereq_('../color');
49173var Drawing = _dereq_('../drawing');
49174
49175var Lib = _dereq_('../../lib');
49176
49177/**
49178 * Helper class to setup a scroll box
49179 *
49180 * @class
49181 * @param gd Plotly's graph div
49182 * @param container Container to be scroll-boxed (as a D3 selection)
49183 * @param {string} id Id for the clip path to implement the scroll box
49184 */
49185function ScrollBox(gd, container, id) {
49186 this.gd = gd;
49187 this.container = container;
49188 this.id = id;
49189
49190 // See ScrollBox.prototype.enable for further definition
49191 this.position = null; // scrollbox position
49192 this.translateX = null; // scrollbox horizontal translation
49193 this.translateY = null; // scrollbox vertical translation
49194 this.hbar = null; // horizontal scrollbar D3 selection
49195 this.vbar = null; // vertical scrollbar D3 selection
49196
49197 // <rect> element to capture pointer events
49198 this.bg = this.container.selectAll('rect.scrollbox-bg').data([0]);
49199
49200 this.bg.exit()
49201 .on('.drag', null)
49202 .on('wheel', null)
49203 .remove();
49204
49205 this.bg.enter().append('rect')
49206 .classed('scrollbox-bg', true)
49207 .style('pointer-events', 'all')
49208 .attr({
49209 opacity: 0,
49210 x: 0,
49211 y: 0,
49212 width: 0,
49213 height: 0
49214 });
49215}
49216
49217// scroll bar dimensions
49218ScrollBox.barWidth = 2;
49219ScrollBox.barLength = 20;
49220ScrollBox.barRadius = 2;
49221ScrollBox.barPad = 1;
49222ScrollBox.barColor = '#808BA4';
49223
49224/**
49225 * If needed, setup a clip path and scrollbars
49226 *
49227 * @method
49228 * @param {Object} position
49229 * @param {number} position.l Left side position (in pixels)
49230 * @param {number} position.t Top side (in pixels)
49231 * @param {number} position.w Width (in pixels)
49232 * @param {number} position.h Height (in pixels)
49233 * @param {string} [position.direction='down']
49234 * Either 'down', 'left', 'right' or 'up'
49235 * @param {number} [translateX=0] Horizontal offset (in pixels)
49236 * @param {number} [translateY=0] Vertical offset (in pixels)
49237 */
49238ScrollBox.prototype.enable = function enable(position, translateX, translateY) {
49239 var fullLayout = this.gd._fullLayout;
49240 var fullWidth = fullLayout.width;
49241 var fullHeight = fullLayout.height;
49242
49243 // compute position of scrollbox
49244 this.position = position;
49245
49246 var l = this.position.l;
49247 var w = this.position.w;
49248 var t = this.position.t;
49249 var h = this.position.h;
49250 var direction = this.position.direction;
49251 var isDown = (direction === 'down');
49252 var isLeft = (direction === 'left');
49253 var isRight = (direction === 'right');
49254 var isUp = (direction === 'up');
49255 var boxW = w;
49256 var boxH = h;
49257 var boxL, boxR;
49258 var boxT, boxB;
49259
49260 if(!isDown && !isLeft && !isRight && !isUp) {
49261 this.position.direction = 'down';
49262 isDown = true;
49263 }
49264
49265 var isVertical = isDown || isUp;
49266 if(isVertical) {
49267 boxL = l;
49268 boxR = boxL + boxW;
49269
49270 if(isDown) {
49271 // anchor to top side
49272 boxT = t;
49273 boxB = Math.min(boxT + boxH, fullHeight);
49274 boxH = boxB - boxT;
49275 } else {
49276 // anchor to bottom side
49277 boxB = t + boxH;
49278 boxT = Math.max(boxB - boxH, 0);
49279 boxH = boxB - boxT;
49280 }
49281 } else {
49282 boxT = t;
49283 boxB = boxT + boxH;
49284
49285 if(isLeft) {
49286 // anchor to right side
49287 boxR = l + boxW;
49288 boxL = Math.max(boxR - boxW, 0);
49289 boxW = boxR - boxL;
49290 } else {
49291 // anchor to left side
49292 boxL = l;
49293 boxR = Math.min(boxL + boxW, fullWidth);
49294 boxW = boxR - boxL;
49295 }
49296 }
49297
49298 this._box = {
49299 l: boxL,
49300 t: boxT,
49301 w: boxW,
49302 h: boxH
49303 };
49304
49305 // compute position of horizontal scroll bar
49306 var needsHorizontalScrollBar = (w > boxW);
49307 var hbarW = ScrollBox.barLength + 2 * ScrollBox.barPad;
49308 var hbarH = ScrollBox.barWidth + 2 * ScrollBox.barPad;
49309 // draw horizontal scrollbar on the bottom side
49310 var hbarL = l;
49311 var hbarT = t + h;
49312
49313 if(hbarT + hbarH > fullHeight) hbarT = fullHeight - hbarH;
49314
49315 var hbar = this.container.selectAll('rect.scrollbar-horizontal').data(
49316 (needsHorizontalScrollBar) ? [0] : []);
49317
49318 hbar.exit()
49319 .on('.drag', null)
49320 .remove();
49321
49322 hbar.enter().append('rect')
49323 .classed('scrollbar-horizontal', true)
49324 .call(Color.fill, ScrollBox.barColor);
49325
49326 if(needsHorizontalScrollBar) {
49327 this.hbar = hbar.attr({
49328 'rx': ScrollBox.barRadius,
49329 'ry': ScrollBox.barRadius,
49330 'x': hbarL,
49331 'y': hbarT,
49332 'width': hbarW,
49333 'height': hbarH
49334 });
49335
49336 // hbar center moves between hbarXMin and hbarXMin + hbarTranslateMax
49337 this._hbarXMin = hbarL + hbarW / 2;
49338 this._hbarTranslateMax = boxW - hbarW;
49339 } else {
49340 delete this.hbar;
49341 delete this._hbarXMin;
49342 delete this._hbarTranslateMax;
49343 }
49344
49345 // compute position of vertical scroll bar
49346 var needsVerticalScrollBar = (h > boxH);
49347 var vbarW = ScrollBox.barWidth + 2 * ScrollBox.barPad;
49348 var vbarH = ScrollBox.barLength + 2 * ScrollBox.barPad;
49349 // draw vertical scrollbar on the right side
49350 var vbarL = l + w;
49351 var vbarT = t;
49352
49353 if(vbarL + vbarW > fullWidth) vbarL = fullWidth - vbarW;
49354
49355 var vbar = this.container.selectAll('rect.scrollbar-vertical').data(
49356 (needsVerticalScrollBar) ? [0] : []);
49357
49358 vbar.exit()
49359 .on('.drag', null)
49360 .remove();
49361
49362 vbar.enter().append('rect')
49363 .classed('scrollbar-vertical', true)
49364 .call(Color.fill, ScrollBox.barColor);
49365
49366 if(needsVerticalScrollBar) {
49367 this.vbar = vbar.attr({
49368 'rx': ScrollBox.barRadius,
49369 'ry': ScrollBox.barRadius,
49370 'x': vbarL,
49371 'y': vbarT,
49372 'width': vbarW,
49373 'height': vbarH
49374 });
49375
49376 // vbar center moves between vbarYMin and vbarYMin + vbarTranslateMax
49377 this._vbarYMin = vbarT + vbarH / 2;
49378 this._vbarTranslateMax = boxH - vbarH;
49379 } else {
49380 delete this.vbar;
49381 delete this._vbarYMin;
49382 delete this._vbarTranslateMax;
49383 }
49384
49385 // setup a clip path (if scroll bars are needed)
49386 var clipId = this.id;
49387 var clipL = boxL - 0.5;
49388 var clipR = (needsVerticalScrollBar) ? boxR + vbarW + 0.5 : boxR + 0.5;
49389 var clipT = boxT - 0.5;
49390 var clipB = (needsHorizontalScrollBar) ? boxB + hbarH + 0.5 : boxB + 0.5;
49391
49392 var clipPath = fullLayout._topdefs.selectAll('#' + clipId)
49393 .data((needsHorizontalScrollBar || needsVerticalScrollBar) ? [0] : []);
49394
49395 clipPath.exit().remove();
49396
49397 clipPath.enter()
49398 .append('clipPath').attr('id', clipId)
49399 .append('rect');
49400
49401 if(needsHorizontalScrollBar || needsVerticalScrollBar) {
49402 this._clipRect = clipPath.select('rect').attr({
49403 x: Math.floor(clipL),
49404 y: Math.floor(clipT),
49405 width: Math.ceil(clipR) - Math.floor(clipL),
49406 height: Math.ceil(clipB) - Math.floor(clipT)
49407 });
49408
49409 this.container.call(Drawing.setClipUrl, clipId, this.gd);
49410
49411 this.bg.attr({
49412 x: l,
49413 y: t,
49414 width: w,
49415 height: h
49416 });
49417 } else {
49418 this.bg.attr({
49419 width: 0,
49420 height: 0
49421 });
49422 this.container
49423 .on('wheel', null)
49424 .on('.drag', null)
49425 .call(Drawing.setClipUrl, null);
49426 delete this._clipRect;
49427 }
49428
49429 // set up drag listeners (if scroll bars are needed)
49430 if(needsHorizontalScrollBar || needsVerticalScrollBar) {
49431 var onBoxDrag = d3.behavior.drag()
49432 .on('dragstart', function() {
49433 d3.event.sourceEvent.preventDefault();
49434 })
49435 .on('drag', this._onBoxDrag.bind(this));
49436
49437 this.container
49438 .on('wheel', null)
49439 .on('wheel', this._onBoxWheel.bind(this))
49440 .on('.drag', null)
49441 .call(onBoxDrag);
49442
49443 var onBarDrag = d3.behavior.drag()
49444 .on('dragstart', function() {
49445 d3.event.sourceEvent.preventDefault();
49446 d3.event.sourceEvent.stopPropagation();
49447 })
49448 .on('drag', this._onBarDrag.bind(this));
49449
49450 if(needsHorizontalScrollBar) {
49451 this.hbar
49452 .on('.drag', null)
49453 .call(onBarDrag);
49454 }
49455
49456 if(needsVerticalScrollBar) {
49457 this.vbar
49458 .on('.drag', null)
49459 .call(onBarDrag);
49460 }
49461 }
49462
49463 // set scrollbox translation
49464 this.setTranslate(translateX, translateY);
49465};
49466
49467/**
49468 * If present, remove clip-path and scrollbars
49469 *
49470 * @method
49471 */
49472ScrollBox.prototype.disable = function disable() {
49473 if(this.hbar || this.vbar) {
49474 this.bg.attr({
49475 width: 0,
49476 height: 0
49477 });
49478 this.container
49479 .on('wheel', null)
49480 .on('.drag', null)
49481 .call(Drawing.setClipUrl, null);
49482 delete this._clipRect;
49483 }
49484
49485 if(this.hbar) {
49486 this.hbar.on('.drag', null);
49487 this.hbar.remove();
49488 delete this.hbar;
49489 delete this._hbarXMin;
49490 delete this._hbarTranslateMax;
49491 }
49492
49493 if(this.vbar) {
49494 this.vbar.on('.drag', null);
49495 this.vbar.remove();
49496 delete this.vbar;
49497 delete this._vbarYMin;
49498 delete this._vbarTranslateMax;
49499 }
49500};
49501
49502/**
49503 * Handles scroll box drag events
49504 *
49505 * @method
49506 */
49507ScrollBox.prototype._onBoxDrag = function _onBoxDrag() {
49508 var translateX = this.translateX;
49509 var translateY = this.translateY;
49510
49511 if(this.hbar) {
49512 translateX -= d3.event.dx;
49513 }
49514
49515 if(this.vbar) {
49516 translateY -= d3.event.dy;
49517 }
49518
49519 this.setTranslate(translateX, translateY);
49520};
49521
49522/**
49523 * Handles scroll box wheel events
49524 *
49525 * @method
49526 */
49527ScrollBox.prototype._onBoxWheel = function _onBoxWheel() {
49528 var translateX = this.translateX;
49529 var translateY = this.translateY;
49530
49531 if(this.hbar) {
49532 translateX += d3.event.deltaY;
49533 }
49534
49535 if(this.vbar) {
49536 translateY += d3.event.deltaY;
49537 }
49538
49539 this.setTranslate(translateX, translateY);
49540};
49541
49542/**
49543 * Handles scroll bar drag events
49544 *
49545 * @method
49546 */
49547ScrollBox.prototype._onBarDrag = function _onBarDrag() {
49548 var translateX = this.translateX;
49549 var translateY = this.translateY;
49550
49551 if(this.hbar) {
49552 var xMin = translateX + this._hbarXMin;
49553 var xMax = xMin + this._hbarTranslateMax;
49554 var x = Lib.constrain(d3.event.x, xMin, xMax);
49555 var xf = (x - xMin) / (xMax - xMin);
49556
49557 var translateXMax = this.position.w - this._box.w;
49558
49559 translateX = xf * translateXMax;
49560 }
49561
49562 if(this.vbar) {
49563 var yMin = translateY + this._vbarYMin;
49564 var yMax = yMin + this._vbarTranslateMax;
49565 var y = Lib.constrain(d3.event.y, yMin, yMax);
49566 var yf = (y - yMin) / (yMax - yMin);
49567
49568 var translateYMax = this.position.h - this._box.h;
49569
49570 translateY = yf * translateYMax;
49571 }
49572
49573 this.setTranslate(translateX, translateY);
49574};
49575
49576/**
49577 * Set clip path and scroll bar translate transform
49578 *
49579 * @method
49580 * @param {number} [translateX=0] Horizontal offset (in pixels)
49581 * @param {number} [translateY=0] Vertical offset (in pixels)
49582 */
49583ScrollBox.prototype.setTranslate = function setTranslate(translateX, translateY) {
49584 // store translateX and translateY (needed by mouse event handlers)
49585 var translateXMax = this.position.w - this._box.w;
49586 var translateYMax = this.position.h - this._box.h;
49587
49588 translateX = Lib.constrain(translateX || 0, 0, translateXMax);
49589 translateY = Lib.constrain(translateY || 0, 0, translateYMax);
49590
49591 this.translateX = translateX;
49592 this.translateY = translateY;
49593
49594 this.container.call(Drawing.setTranslate,
49595 this._box.l - this.position.l - translateX,
49596 this._box.t - this.position.t - translateY);
49597
49598 if(this._clipRect) {
49599 this._clipRect.attr({
49600 x: Math.floor(this.position.l + translateX - 0.5),
49601 y: Math.floor(this.position.t + translateY - 0.5)
49602 });
49603 }
49604
49605 if(this.hbar) {
49606 var xf = translateX / translateXMax;
49607
49608 this.hbar.call(Drawing.setTranslate,
49609 translateX + xf * this._hbarTranslateMax,
49610 translateY);
49611 }
49612
49613 if(this.vbar) {
49614 var yf = translateY / translateYMax;
49615
49616 this.vbar.call(Drawing.setTranslate,
49617 translateX,
49618 translateY + yf * this._vbarTranslateMax);
49619 }
49620};
49621
49622},{"../../lib":287,"../color":157,"../drawing":179,"@plotly/d3":20}],262:[function(_dereq_,module,exports){
49623'use strict';
49624
49625// fraction of some size to get to a named position
49626module.exports = {
49627 // from bottom left: this is the origin of our paper-reference
49628 // positioning system
49629 FROM_BL: {
49630 left: 0,
49631 center: 0.5,
49632 right: 1,
49633 bottom: 0,
49634 middle: 0.5,
49635 top: 1
49636 },
49637 // from top left: this is the screen pixel positioning origin
49638 FROM_TL: {
49639 left: 0,
49640 center: 0.5,
49641 right: 1,
49642 bottom: 1,
49643 middle: 0.5,
49644 top: 0
49645 },
49646 // from bottom right: sometimes you just need the opposite of ^^
49647 FROM_BR: {
49648 left: 1,
49649 center: 0.5,
49650 right: 0,
49651 bottom: 0,
49652 middle: 0.5,
49653 top: 1
49654 },
49655 // multiple of fontSize to get the vertical offset between lines
49656 LINE_SPACING: 1.3,
49657
49658 // multiple of fontSize to shift from the baseline
49659 // to the cap (captical letter) line
49660 // (to use when we don't calculate this shift from Drawing.bBox)
49661 // This is an approximation since in reality cap height can differ
49662 // from font to font. However, according to Wikipedia
49663 // an "average" font might have a cap height of 70% of the em
49664 // https://en.wikipedia.org/wiki/Em_(typography)#History
49665 CAP_SHIFT: 0.70,
49666
49667 // half the cap height (distance between baseline and cap line)
49668 // of an "average" font (for more info see above).
49669 MID_SHIFT: 0.35,
49670
49671 OPPOSITE_SIDE: {
49672 left: 'right',
49673 right: 'left',
49674 top: 'bottom',
49675 bottom: 'top'
49676 }
49677};
49678
49679},{}],263:[function(_dereq_,module,exports){
49680'use strict';
49681
49682module.exports = {
49683 axisRefDescription: function(axisname, lower, upper) {
49684 return [
49685 'If set to a', axisname, 'axis id (e.g. *' + axisname + '* or',
49686 '*' + axisname + '2*), the `' + axisname + '` position refers to a',
49687 axisname, 'coordinate. If set to *paper*, the `' + axisname + '`',
49688 'position refers to the distance from the', lower, 'of the plotting',
49689 'area in normalized coordinates where *0* (*1*) corresponds to the',
49690 lower, '(' + upper + '). If set to a', axisname, 'axis ID followed by',
49691 '*domain* (separated by a space), the position behaves like for',
49692 '*paper*, but refers to the distance in fractions of the domain',
49693 'length from the', lower, 'of the domain of that axis: e.g.,',
49694 '*' + axisname + '2 domain* refers to the domain of the second',
49695 axisname, ' axis and a', axisname, 'position of 0.5 refers to the',
49696 'point between the', lower, 'and the', upper, 'of the domain of the',
49697 'second', axisname, 'axis.',
49698 ].join(' ');
49699 }
49700};
49701
49702},{}],264:[function(_dereq_,module,exports){
49703'use strict';
49704
49705module.exports = {
49706 FORMAT_LINK: 'https://github.com/d3/d3-format/tree/v1.4.5#d3-format',
49707 DATE_FORMAT_LINK: 'https://github.com/d3/d3-time-format/tree/v2.2.3#locale_format'
49708};
49709
49710},{}],265:[function(_dereq_,module,exports){
49711'use strict';
49712
49713module.exports = {
49714 COMPARISON_OPS: ['=', '!=', '<', '>=', '>', '<='],
49715 COMPARISON_OPS2: ['=', '<', '>=', '>', '<='],
49716 INTERVAL_OPS: ['[]', '()', '[)', '(]', '][', ')(', '](', ')['],
49717 SET_OPS: ['{}', '}{'],
49718 CONSTRAINT_REDUCTION: {
49719 // for contour constraints, open/closed endpoints are equivalent
49720 '=': '=',
49721
49722 '<': '<',
49723 '<=': '<',
49724
49725 '>': '>',
49726 '>=': '>',
49727
49728 '[]': '[]',
49729 '()': '[]',
49730 '[)': '[]',
49731 '(]': '[]',
49732
49733 '][': '][',
49734 ')(': '][',
49735 '](': '][',
49736 ')[': ']['
49737 }
49738};
49739
49740},{}],266:[function(_dereq_,module,exports){
49741'use strict';
49742
49743
49744module.exports = {
49745 /**
49746 * Timing information for interactive elements
49747 */
49748 SHOW_PLACEHOLDER: 100,
49749 HIDE_PLACEHOLDER: 1000,
49750
49751 // opacity dimming fraction for points that are not in selection
49752 DESELECTDIM: 0.2
49753};
49754
49755},{}],267:[function(_dereq_,module,exports){
49756'use strict';
49757
49758
49759module.exports = {
49760 /**
49761 * Standardize all missing data in calcdata to use undefined
49762 * never null or NaN.
49763 * That way we can use !==undefined, or !== BADNUM,
49764 * to test for real data
49765 */
49766 BADNUM: undefined,
49767
49768 /*
49769 * Limit certain operations to well below floating point max value
49770 * to avoid glitches: Make sure that even when you multiply it by the
49771 * number of pixels on a giant screen it still works
49772 */
49773 FP_SAFE: Number.MAX_VALUE * 1e-4,
49774
49775 /*
49776 * conversion of date units to milliseconds
49777 * year and month constants are marked "AVG"
49778 * to remind us that not all years and months
49779 * have the same length
49780 */
49781 ONEMAXYEAR: 31622400000, // 366 * ONEDAY
49782 ONEAVGYEAR: 31557600000, // 365.25 days
49783 ONEMINYEAR: 31536000000, // 365 * ONEDAY
49784 ONEMAXQUARTER: 7948800000, // 92 * ONEDAY
49785 ONEAVGQUARTER: 7889400000, // 1/4 of ONEAVGYEAR
49786 ONEMINQUARTER: 7689600000, // 89 * ONEDAY
49787 ONEMAXMONTH: 2678400000, // 31 * ONEDAY
49788 ONEAVGMONTH: 2629800000, // 1/12 of ONEAVGYEAR
49789 ONEMINMONTH: 2419200000, // 28 * ONEDAY
49790 ONEWEEK: 604800000, // 7 * ONEDAY
49791 ONEDAY: 86400000, // 24 * ONEHOUR
49792 ONEHOUR: 3600000,
49793 ONEMIN: 60000,
49794 ONESEC: 1000,
49795
49796 /*
49797 * For fast conversion btwn world calendars and epoch ms, the Julian Day Number
49798 * of the unix epoch. From calendars.instance().newDate(1970, 1, 1).toJD()
49799 */
49800 EPOCHJD: 2440587.5,
49801
49802 /*
49803 * Are two values nearly equal? Compare to 1PPM
49804 */
49805 ALMOST_EQUAL: 1 - 1e-6,
49806
49807 /*
49808 * If we're asked to clip a non-positive log value, how far off-screen
49809 * do we put it?
49810 */
49811 LOG_CLIP: 10,
49812
49813 /*
49814 * not a number, but for displaying numbers: the "minus sign" symbol is
49815 * wider than the regular ascii dash "-"
49816 */
49817 MINUS_SIGN: '\u2212'
49818};
49819
49820},{}],268:[function(_dereq_,module,exports){
49821'use strict';
49822
49823
49824exports.xmlns = 'http://www.w3.org/2000/xmlns/';
49825exports.svg = 'http://www.w3.org/2000/svg';
49826exports.xlink = 'http://www.w3.org/1999/xlink';
49827
49828// the 'old' d3 quirk got fix in v3.5.7
49829// https://github.com/mbostock/d3/commit/a6f66e9dd37f764403fc7c1f26be09ab4af24fed
49830exports.svgAttrs = {
49831 xmlns: exports.svg,
49832 'xmlns:xlink': exports.xlink
49833};
49834
49835},{}],269:[function(_dereq_,module,exports){
49836'use strict';
49837
49838exports.version = _dereq_('./version').version;
49839
49840// inject promise polyfill
49841_dereq_('native-promise-only');
49842
49843// inject plot css
49844_dereq_('../build/plotcss');
49845
49846// include registry module and expose register method
49847var Registry = _dereq_('./registry');
49848var register = exports.register = Registry.register;
49849
49850// expose plot api methods
49851var plotApi = _dereq_('./plot_api');
49852var methodNames = Object.keys(plotApi);
49853for(var i = 0; i < methodNames.length; i++) {
49854 var name = methodNames[i];
49855 // _ -> private API methods, but still registered for internal use
49856 if(name.charAt(0) !== '_') exports[name] = plotApi[name];
49857 register({
49858 moduleType: 'apiMethod',
49859 name: name,
49860 fn: plotApi[name]
49861 });
49862}
49863
49864// scatter is the only trace included by default
49865register(_dereq_('./traces/scatter'));
49866
49867// register all registrable components modules
49868register([
49869 _dereq_('./components/legend'),
49870 _dereq_('./components/fx'), // fx needs to come after legend
49871 _dereq_('./components/annotations'),
49872 _dereq_('./components/annotations3d'),
49873 _dereq_('./components/shapes'),
49874 _dereq_('./components/images'),
49875 _dereq_('./components/updatemenus'),
49876 _dereq_('./components/sliders'),
49877 _dereq_('./components/rangeslider'),
49878 _dereq_('./components/rangeselector'),
49879 _dereq_('./components/grid'),
49880 _dereq_('./components/errorbars'),
49881 _dereq_('./components/colorscale'),
49882 _dereq_('./components/colorbar'),
49883 _dereq_('./components/modebar')
49884]);
49885
49886// locales en and en-US are required for default behavior
49887register([
49888 _dereq_('./locale-en'),
49889 _dereq_('./locale-en-us')
49890]);
49891
49892// locales that are present in the window should be loaded
49893if(window.PlotlyLocales && Array.isArray(window.PlotlyLocales)) {
49894 register(window.PlotlyLocales);
49895 delete window.PlotlyLocales;
49896}
49897
49898// plot icons
49899exports.Icons = _dereq_('./fonts/ploticon');
49900
49901// unofficial 'beta' plot methods, use at your own risk
49902var Fx = _dereq_('./components/fx');
49903var Plots = _dereq_('./plots/plots');
49904
49905exports.Plots = {
49906 resize: Plots.resize,
49907 graphJson: Plots.graphJson,
49908 sendDataToCloud: Plots.sendDataToCloud
49909};
49910exports.Fx = {
49911 hover: Fx.hover,
49912 unhover: Fx.unhover,
49913 loneHover: Fx.loneHover,
49914 loneUnhover: Fx.loneUnhover
49915};
49916exports.Snapshot = _dereq_('./snapshot');
49917exports.PlotSchema = _dereq_('./plot_api/plot_schema');
49918
49919},{"../build/plotcss":1,"./components/annotations":148,"./components/annotations3d":153,"./components/colorbar":163,"./components/colorscale":169,"./components/errorbars":185,"./components/fx":197,"./components/grid":201,"./components/images":206,"./components/legend":214,"./components/modebar":220,"./components/rangeselector":228,"./components/rangeslider":235,"./components/shapes":249,"./components/sliders":254,"./components/updatemenus":260,"./fonts/ploticon":270,"./locale-en":314,"./locale-en-us":313,"./plot_api":318,"./plot_api/plot_schema":322,"./plots/plots":369,"./registry":376,"./snapshot":381,"./traces/scatter":509,"./version":549,"native-promise-only":72}],270:[function(_dereq_,module,exports){
49920'use strict';
49921
49922module.exports = {
49923 'undo': {
49924 'width': 857.1,
49925 'height': 1000,
49926 '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',
49927 'transform': 'matrix(1 0 0 -1 0 850)'
49928 },
49929 'home': {
49930 'width': 928.6,
49931 'height': 1000,
49932 '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',
49933 'transform': 'matrix(1 0 0 -1 0 850)'
49934 },
49935 'camera-retro': {
49936 'width': 1000,
49937 'height': 1000,
49938 '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',
49939 'transform': 'matrix(1 0 0 -1 0 850)'
49940 },
49941 'zoombox': {
49942 'width': 1000,
49943 'height': 1000,
49944 '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',
49945 'transform': 'matrix(1 0 0 -1 0 850)'
49946 },
49947 'pan': {
49948 'width': 1000,
49949 'height': 1000,
49950 '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',
49951 'transform': 'matrix(1 0 0 -1 0 850)'
49952 },
49953 'zoom_plus': {
49954 'width': 875,
49955 'height': 1000,
49956 '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',
49957 'transform': 'matrix(1 0 0 -1 0 850)'
49958 },
49959 'zoom_minus': {
49960 'width': 875,
49961 'height': 1000,
49962 'path': 'm0 788l0-876 875 0 0 876-875 0z m688-500l-500 0 0 125 500 0 0-125z',
49963 'transform': 'matrix(1 0 0 -1 0 850)'
49964 },
49965 'autoscale': {
49966 'width': 1000,
49967 'height': 1000,
49968 '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',
49969 'transform': 'matrix(1 0 0 -1 0 850)'
49970 },
49971 'tooltip_basic': {
49972 'width': 1500,
49973 'height': 1000,
49974 'path': 'm375 725l0 0-375-375 375-374 0-1 1125 0 0 750-1125 0z',
49975 'transform': 'matrix(1 0 0 -1 0 850)'
49976 },
49977 'tooltip_compare': {
49978 'width': 1125,
49979 'height': 1000,
49980 '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',
49981 'transform': 'matrix(1 0 0 -1 0 850)'
49982 },
49983 'plotlylogo': {
49984 'width': 1542,
49985 'height': 1000,
49986 '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',
49987 'transform': 'matrix(1 0 0 -1 0 850)'
49988 },
49989 'z-axis': {
49990 'width': 1000,
49991 'height': 1000,
49992 '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',
49993 'transform': 'matrix(1 0 0 -1 0 850)'
49994 },
49995 '3d_rotate': {
49996 'width': 1000,
49997 'height': 1000,
49998 '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',
49999 'transform': 'matrix(1 0 0 -1 0 850)'
50000 },
50001 'camera': {
50002 'width': 1000,
50003 'height': 1000,
50004 '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',
50005 'transform': 'matrix(1 0 0 -1 0 850)'
50006 },
50007 'movie': {
50008 'width': 1000,
50009 'height': 1000,
50010 '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',
50011 'transform': 'matrix(1 0 0 -1 0 850)'
50012 },
50013 'question': {
50014 'width': 857.1,
50015 'height': 1000,
50016 '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',
50017 'transform': 'matrix(1 0 0 -1 0 850)'
50018 },
50019 'disk': {
50020 'width': 857.1,
50021 'height': 1000,
50022 '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',
50023 'transform': 'matrix(1 0 0 -1 0 850)'
50024 },
50025 'drawopenpath': {
50026 'width': 70,
50027 'height': 70,
50028 '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',
50029 'transform': 'matrix(1 0 0 1 -15 -15)'
50030 },
50031 'drawclosedpath': {
50032 'width': 90,
50033 'height': 90,
50034 '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',
50035 'transform': 'matrix(1 0 0 1 -5 -5)'
50036 },
50037 'lasso': {
50038 'width': 1031,
50039 'height': 1000,
50040 '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',
50041 'transform': 'matrix(1 0 0 -1 0 850)'
50042 },
50043 'selectbox': {
50044 'width': 1000,
50045 'height': 1000,
50046 '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',
50047 'transform': 'matrix(1 0 0 -1 0 850)'
50048 },
50049 'drawline': {
50050 'width': 70,
50051 'height': 70,
50052 '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',
50053 'transform': 'matrix(1 0 0 1 -15 -15)'
50054 },
50055 'drawrect': {
50056 'width': 80,
50057 'height': 80,
50058 'path': 'M78,22V79H21V22H78m9-9H12V88H87V13ZM68,46.22H31V54H68ZM53,32H45.22V69H53Z',
50059 'transform': 'matrix(1 0 0 1 -10 -10)'
50060 },
50061 'drawcircle': {
50062 'width': 80,
50063 'height': 80,
50064 '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',
50065 'transform': 'matrix(1 0 0 1 -10 -10)'
50066 },
50067 'eraseshape': {
50068 'width': 80,
50069 'height': 80,
50070 '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',
50071 'transform': 'matrix(1 0 0 1 -10 -10)'
50072 },
50073 'spikeline': {
50074 'width': 1000,
50075 'height': 1000,
50076 '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',
50077 'transform': 'matrix(1.5 0 0 -1.5 0 850)'
50078 },
50079 'pencil': {
50080 'width': 1792,
50081 'height': 1792,
50082 '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',
50083 'transform': 'matrix(1 0 0 1 0 1)'
50084 },
50085 'newplotlylogo': {
50086 'name': 'newplotlylogo',
50087 '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>'
50088 }
50089};
50090
50091},{}],271:[function(_dereq_,module,exports){
50092'use strict';
50093
50094
50095/**
50096 * Determine the position anchor property of x/y xanchor/yanchor components.
50097 *
50098 * - values < 1/3 align the low side at that fraction,
50099 * - values [1/3, 2/3] align the center at that fraction,
50100 * - values > 2/3 align the right at that fraction.
50101 */
50102
50103
50104exports.isLeftAnchor = function isLeftAnchor(opts) {
50105 return (
50106 opts.xanchor === 'left' ||
50107 (opts.xanchor === 'auto' && opts.x <= 1 / 3)
50108 );
50109};
50110
50111exports.isCenterAnchor = function isCenterAnchor(opts) {
50112 return (
50113 opts.xanchor === 'center' ||
50114 (opts.xanchor === 'auto' && opts.x > 1 / 3 && opts.x < 2 / 3)
50115 );
50116};
50117
50118exports.isRightAnchor = function isRightAnchor(opts) {
50119 return (
50120 opts.xanchor === 'right' ||
50121 (opts.xanchor === 'auto' && opts.x >= 2 / 3)
50122 );
50123};
50124
50125exports.isTopAnchor = function isTopAnchor(opts) {
50126 return (
50127 opts.yanchor === 'top' ||
50128 (opts.yanchor === 'auto' && opts.y >= 2 / 3)
50129 );
50130};
50131
50132exports.isMiddleAnchor = function isMiddleAnchor(opts) {
50133 return (
50134 opts.yanchor === 'middle' ||
50135 (opts.yanchor === 'auto' && opts.y > 1 / 3 && opts.y < 2 / 3)
50136 );
50137};
50138
50139exports.isBottomAnchor = function isBottomAnchor(opts) {
50140 return (
50141 opts.yanchor === 'bottom' ||
50142 (opts.yanchor === 'auto' && opts.y <= 1 / 3)
50143 );
50144};
50145
50146},{}],272:[function(_dereq_,module,exports){
50147'use strict';
50148
50149var modModule = _dereq_('./mod');
50150var mod = modModule.mod;
50151var modHalf = modModule.modHalf;
50152
50153var PI = Math.PI;
50154var twoPI = 2 * PI;
50155
50156function deg2rad(deg) { return deg / 180 * PI; }
50157
50158function rad2deg(rad) { return rad / PI * 180; }
50159
50160/**
50161 * is sector a full circle?
50162 * ... this comes up a lot in SVG path-drawing routines
50163 *
50164 * N.B. we consider all sectors that span more that 2pi 'full' circles
50165 *
50166 * @param {2-item array} aBnds : angular bounds in *radians*
50167 * @return {boolean}
50168 */
50169function isFullCircle(aBnds) {
50170 return Math.abs(aBnds[1] - aBnds[0]) > twoPI - 1e-14;
50171}
50172
50173/**
50174 * angular delta between angle 'a' and 'b'
50175 * solution taken from: https://stackoverflow.com/a/2007279
50176 *
50177 * @param {number} a : first angle in *radians*
50178 * @param {number} b : second angle in *radians*
50179 * @return {number} angular delta in *radians*
50180 */
50181function angleDelta(a, b) {
50182 return modHalf(b - a, twoPI);
50183}
50184
50185/**
50186 * angular distance between angle 'a' and 'b'
50187 *
50188 * @param {number} a : first angle in *radians*
50189 * @param {number} b : second angle in *radians*
50190 * @return {number} angular distance in *radians*
50191 */
50192function angleDist(a, b) {
50193 return Math.abs(angleDelta(a, b));
50194}
50195
50196/**
50197 * is angle inside sector?
50198 *
50199 * @param {number} a : angle to test in *radians*
50200 * @param {2-item array} aBnds : sector's angular bounds in *radians*
50201 * @param {boolean}
50202 */
50203function isAngleInsideSector(a, aBnds) {
50204 if(isFullCircle(aBnds)) return true;
50205
50206 var s0, s1;
50207
50208 if(aBnds[0] < aBnds[1]) {
50209 s0 = aBnds[0];
50210 s1 = aBnds[1];
50211 } else {
50212 s0 = aBnds[1];
50213 s1 = aBnds[0];
50214 }
50215
50216 s0 = mod(s0, twoPI);
50217 s1 = mod(s1, twoPI);
50218 if(s0 > s1) s1 += twoPI;
50219
50220 var a0 = mod(a, twoPI);
50221 var a1 = a0 + twoPI;
50222
50223 return (a0 >= s0 && a0 <= s1) || (a1 >= s0 && a1 <= s1);
50224}
50225
50226/**
50227 * is pt (r,a) inside sector?
50228 *
50229 * @param {number} r : pt's radial coordinate
50230 * @param {number} a : pt's angular coordinate in *radians*
50231 * @param {2-item array} rBnds : sector's radial bounds
50232 * @param {2-item array} aBnds : sector's angular bounds in *radians*
50233 * @return {boolean}
50234 */
50235function isPtInsideSector(r, a, rBnds, aBnds) {
50236 if(!isAngleInsideSector(a, aBnds)) return false;
50237
50238 var r0, r1;
50239
50240 if(rBnds[0] < rBnds[1]) {
50241 r0 = rBnds[0];
50242 r1 = rBnds[1];
50243 } else {
50244 r0 = rBnds[1];
50245 r1 = rBnds[0];
50246 }
50247
50248 return r >= r0 && r <= r1;
50249}
50250
50251// common to pathArc, pathSector and pathAnnulus
50252function _path(r0, r1, a0, a1, cx, cy, isClosed) {
50253 cx = cx || 0;
50254 cy = cy || 0;
50255
50256 var isCircle = isFullCircle([a0, a1]);
50257 var aStart, aMid, aEnd;
50258 var rStart, rEnd;
50259
50260 if(isCircle) {
50261 aStart = 0;
50262 aMid = PI;
50263 aEnd = twoPI;
50264 } else {
50265 if(a0 < a1) {
50266 aStart = a0;
50267 aEnd = a1;
50268 } else {
50269 aStart = a1;
50270 aEnd = a0;
50271 }
50272 }
50273
50274 if(r0 < r1) {
50275 rStart = r0;
50276 rEnd = r1;
50277 } else {
50278 rStart = r1;
50279 rEnd = r0;
50280 }
50281
50282 // N.B. svg coordinates here, where y increases downward
50283 function pt(r, a) {
50284 return [r * Math.cos(a) + cx, cy - r * Math.sin(a)];
50285 }
50286
50287 var largeArc = Math.abs(aEnd - aStart) <= PI ? 0 : 1;
50288 function arc(r, a, cw) {
50289 return 'A' + [r, r] + ' ' + [0, largeArc, cw] + ' ' + pt(r, a);
50290 }
50291
50292 var p;
50293
50294 if(isCircle) {
50295 if(rStart === null) {
50296 p = 'M' + pt(rEnd, aStart) +
50297 arc(rEnd, aMid, 0) +
50298 arc(rEnd, aEnd, 0) + 'Z';
50299 } else {
50300 p = 'M' + pt(rStart, aStart) +
50301 arc(rStart, aMid, 0) +
50302 arc(rStart, aEnd, 0) + 'Z' +
50303 'M' + pt(rEnd, aStart) +
50304 arc(rEnd, aMid, 1) +
50305 arc(rEnd, aEnd, 1) + 'Z';
50306 }
50307 } else {
50308 if(rStart === null) {
50309 p = 'M' + pt(rEnd, aStart) + arc(rEnd, aEnd, 0);
50310 if(isClosed) p += 'L0,0Z';
50311 } else {
50312 p = 'M' + pt(rStart, aStart) +
50313 'L' + pt(rEnd, aStart) +
50314 arc(rEnd, aEnd, 0) +
50315 'L' + pt(rStart, aEnd) +
50316 arc(rStart, aStart, 1) + 'Z';
50317 }
50318 }
50319
50320 return p;
50321}
50322
50323/**
50324 * path an arc
50325 *
50326 * @param {number} r : radius
50327 * @param {number} a0 : first angular coordinate in *radians*
50328 * @param {number} a1 : second angular coordinate in *radians*
50329 * @param {number (optional)} cx : x coordinate of center
50330 * @param {number (optional)} cy : y coordinate of center
50331 * @return {string} svg path
50332 */
50333function pathArc(r, a0, a1, cx, cy) {
50334 return _path(null, r, a0, a1, cx, cy, 0);
50335}
50336
50337/**
50338 * path a sector
50339 *
50340 * @param {number} r : radius
50341 * @param {number} a0 : first angular coordinate in *radians*
50342 * @param {number} a1 : second angular coordinate in *radians*
50343 * @param {number (optional)} cx : x coordinate of center
50344 * @param {number (optional)} cy : y coordinate of center
50345 * @return {string} svg path
50346 */
50347function pathSector(r, a0, a1, cx, cy) {
50348 return _path(null, r, a0, a1, cx, cy, 1);
50349}
50350
50351/**
50352 * path an annulus
50353 *
50354 * @param {number} r0 : first radial coordinate
50355 * @param {number} r1 : second radial coordinate
50356 * @param {number} a0 : first angular coordinate in *radians*
50357 * @param {number} a1 : second angular coordinate in *radians*
50358 * @param {number (optional)} cx : x coordinate of center
50359 * @param {number (optional)} cy : y coordinate of center
50360 * @return {string} svg path
50361 */
50362function pathAnnulus(r0, r1, a0, a1, cx, cy) {
50363 return _path(r0, r1, a0, a1, cx, cy, 1);
50364}
50365
50366module.exports = {
50367 deg2rad: deg2rad,
50368 rad2deg: rad2deg,
50369 angleDelta: angleDelta,
50370 angleDist: angleDist,
50371 isFullCircle: isFullCircle,
50372 isAngleInsideSector: isAngleInsideSector,
50373 isPtInsideSector: isPtInsideSector,
50374 pathArc: pathArc,
50375 pathSector: pathSector,
50376 pathAnnulus: pathAnnulus
50377};
50378
50379},{"./mod":294}],273:[function(_dereq_,module,exports){
50380'use strict';
50381
50382var isArray = Array.isArray;
50383
50384var ab = ArrayBuffer;
50385var dv = DataView;
50386
50387function isTypedArray(a) {
50388 return ab.isView(a) && !(a instanceof dv);
50389}
50390exports.isTypedArray = isTypedArray;
50391
50392function isArrayOrTypedArray(a) {
50393 return isArray(a) || isTypedArray(a);
50394}
50395exports.isArrayOrTypedArray = isArrayOrTypedArray;
50396
50397/*
50398 * Test whether an input object is 1D.
50399 *
50400 * Assumes we already know the object is an array.
50401 *
50402 * Looks only at the first element, if the dimensionality is
50403 * not consistent we won't figure that out here.
50404 */
50405function isArray1D(a) {
50406 return !isArrayOrTypedArray(a[0]);
50407}
50408exports.isArray1D = isArray1D;
50409
50410/*
50411 * Ensures an array has the right amount of storage space. If it doesn't
50412 * exist, it creates an array. If it does exist, it returns it if too
50413 * short or truncates it in-place.
50414 *
50415 * The goal is to just reuse memory to avoid a bit of excessive garbage
50416 * collection.
50417 */
50418exports.ensureArray = function(out, n) {
50419 // TODO: typed array support here? This is only used in
50420 // traces/carpet/compute_control_points
50421 if(!isArray(out)) out = [];
50422
50423 // If too long, truncate. (If too short, it will grow
50424 // automatically so we don't care about that case)
50425 out.length = n;
50426
50427 return out;
50428};
50429
50430/*
50431 * TypedArray-compatible concatenation of n arrays
50432 * if all arrays are the same type it will preserve that type,
50433 * otherwise it falls back on Array.
50434 * Also tries to avoid copying, in case one array has zero length
50435 * But never mutates an existing array
50436 */
50437exports.concat = function() {
50438 var args = [];
50439 var allArray = true;
50440 var totalLen = 0;
50441
50442 var _constructor, arg0, i, argi, posi, leni, out, j;
50443
50444 for(i = 0; i < arguments.length; i++) {
50445 argi = arguments[i];
50446 leni = argi.length;
50447 if(leni) {
50448 if(arg0) args.push(argi);
50449 else {
50450 arg0 = argi;
50451 posi = leni;
50452 }
50453
50454 if(isArray(argi)) {
50455 _constructor = false;
50456 } else {
50457 allArray = false;
50458 if(!totalLen) {
50459 _constructor = argi.constructor;
50460 } else if(_constructor !== argi.constructor) {
50461 // TODO: in principle we could upgrade here,
50462 // ie keep typed array but convert all to Float64Array?
50463 _constructor = false;
50464 }
50465 }
50466
50467 totalLen += leni;
50468 }
50469 }
50470
50471 if(!totalLen) return [];
50472 if(!args.length) return arg0;
50473
50474 if(allArray) return arg0.concat.apply(arg0, args);
50475 if(_constructor) {
50476 // matching typed arrays
50477 out = new _constructor(totalLen);
50478 out.set(arg0);
50479 for(i = 0; i < args.length; i++) {
50480 argi = args[i];
50481 out.set(argi, posi);
50482 posi += argi.length;
50483 }
50484 return out;
50485 }
50486
50487 // mismatched types or Array + typed
50488 out = new Array(totalLen);
50489 for(j = 0; j < arg0.length; j++) out[j] = arg0[j];
50490 for(i = 0; i < args.length; i++) {
50491 argi = args[i];
50492 for(j = 0; j < argi.length; j++) out[posi + j] = argi[j];
50493 posi += j;
50494 }
50495 return out;
50496};
50497
50498exports.maxRowLength = function(z) {
50499 return _rowLength(z, Math.max, 0);
50500};
50501
50502exports.minRowLength = function(z) {
50503 return _rowLength(z, Math.min, Infinity);
50504};
50505
50506function _rowLength(z, fn, len0) {
50507 if(isArrayOrTypedArray(z)) {
50508 if(isArrayOrTypedArray(z[0])) {
50509 var len = len0;
50510 for(var i = 0; i < z.length; i++) {
50511 len = fn(len, z[i].length);
50512 }
50513 return len;
50514 } else {
50515 return z.length;
50516 }
50517 }
50518 return 0;
50519}
50520
50521},{}],274:[function(_dereq_,module,exports){
50522'use strict';
50523
50524var isNumeric = _dereq_('fast-isnumeric');
50525
50526var BADNUM = _dereq_('../constants/numerical').BADNUM;
50527
50528// precompile for speed
50529var JUNK = /^['"%,$#\s']+|[, ]|['"%,$#\s']+$/g;
50530
50531/**
50532 * cleanNumber: remove common leading and trailing cruft
50533 * Always returns either a number or BADNUM.
50534 */
50535module.exports = function cleanNumber(v) {
50536 if(typeof v === 'string') {
50537 v = v.replace(JUNK, '');
50538 }
50539
50540 if(isNumeric(v)) return Number(v);
50541
50542 return BADNUM;
50543};
50544
50545},{"../constants/numerical":267,"fast-isnumeric":33}],275:[function(_dereq_,module,exports){
50546'use strict';
50547
50548/**
50549 * Clear gl frame (if any). This is a common pattern as
50550 * we usually set `preserveDrawingBuffer: true` during
50551 * gl context creation (e.g. via `reglUtils.prepare`).
50552 *
50553 * @param {DOM node or object} gd : graph div object
50554 */
50555module.exports = function clearGlCanvases(gd) {
50556 var fullLayout = gd._fullLayout;
50557
50558 if(fullLayout._glcanvas && fullLayout._glcanvas.size()) {
50559 fullLayout._glcanvas.each(function(d) {
50560 if(d.regl) d.regl.clear({color: true, depth: true});
50561 });
50562 }
50563};
50564
50565},{}],276:[function(_dereq_,module,exports){
50566'use strict';
50567
50568/**
50569 * Clear responsive handlers (if any).
50570 *
50571 * @param {DOM node or object} gd : graph div object
50572 */
50573module.exports = function clearResponsive(gd) {
50574 if(gd._responsiveChartHandler) {
50575 window.removeEventListener('resize', gd._responsiveChartHandler);
50576 delete gd._responsiveChartHandler;
50577 }
50578};
50579
50580},{}],277:[function(_dereq_,module,exports){
50581'use strict';
50582
50583var isNumeric = _dereq_('fast-isnumeric');
50584var tinycolor = _dereq_('tinycolor2');
50585
50586var baseTraceAttrs = _dereq_('../plots/attributes');
50587var colorscales = _dereq_('../components/colorscale/scales');
50588var Color = _dereq_('../components/color');
50589var DESELECTDIM = _dereq_('../constants/interactions').DESELECTDIM;
50590
50591var nestedProperty = _dereq_('./nested_property');
50592var counterRegex = _dereq_('./regex').counter;
50593var modHalf = _dereq_('./mod').modHalf;
50594var isArrayOrTypedArray = _dereq_('./array').isArrayOrTypedArray;
50595
50596exports.valObjectMeta = {
50597 data_array: {
50598 // You can use *dflt=[] to force said array to exist though.
50599 coerceFunction: function(v, propOut, dflt) {
50600 // TODO maybe `v: {type: 'float32', vals: [/* ... */]}` also
50601 if(isArrayOrTypedArray(v)) propOut.set(v);
50602 else if(dflt !== undefined) propOut.set(dflt);
50603 }
50604 },
50605 enumerated: {
50606 coerceFunction: function(v, propOut, dflt, opts) {
50607 if(opts.coerceNumber) v = +v;
50608 if(opts.values.indexOf(v) === -1) propOut.set(dflt);
50609 else propOut.set(v);
50610 },
50611 validateFunction: function(v, opts) {
50612 if(opts.coerceNumber) v = +v;
50613
50614 var values = opts.values;
50615 for(var i = 0; i < values.length; i++) {
50616 var k = String(values[i]);
50617
50618 if((k.charAt(0) === '/' && k.charAt(k.length - 1) === '/')) {
50619 var regex = new RegExp(k.substr(1, k.length - 2));
50620 if(regex.test(v)) return true;
50621 } else if(v === values[i]) return true;
50622 }
50623 return false;
50624 }
50625 },
50626 'boolean': {
50627 coerceFunction: function(v, propOut, dflt) {
50628 if(v === true || v === false) propOut.set(v);
50629 else propOut.set(dflt);
50630 }
50631 },
50632 number: {
50633 coerceFunction: function(v, propOut, dflt, opts) {
50634 if(!isNumeric(v) ||
50635 (opts.min !== undefined && v < opts.min) ||
50636 (opts.max !== undefined && v > opts.max)) {
50637 propOut.set(dflt);
50638 } else propOut.set(+v);
50639 }
50640 },
50641 integer: {
50642 coerceFunction: function(v, propOut, dflt, opts) {
50643 if(v % 1 || !isNumeric(v) ||
50644 (opts.min !== undefined && v < opts.min) ||
50645 (opts.max !== undefined && v > opts.max)) {
50646 propOut.set(dflt);
50647 } else propOut.set(+v);
50648 }
50649 },
50650 string: {
50651 // TODO 'values shouldn't be in there (edge case: 'dash' in Scatter)
50652 coerceFunction: function(v, propOut, dflt, opts) {
50653 if(typeof v !== 'string') {
50654 var okToCoerce = (typeof v === 'number');
50655
50656 if(opts.strict === true || !okToCoerce) propOut.set(dflt);
50657 else propOut.set(String(v));
50658 } else if(opts.noBlank && !v) propOut.set(dflt);
50659 else propOut.set(v);
50660 }
50661 },
50662 color: {
50663 coerceFunction: function(v, propOut, dflt) {
50664 if(tinycolor(v).isValid()) propOut.set(v);
50665 else propOut.set(dflt);
50666 }
50667 },
50668 colorlist: {
50669 coerceFunction: function(v, propOut, dflt) {
50670 function isColor(color) {
50671 return tinycolor(color).isValid();
50672 }
50673 if(!Array.isArray(v) || !v.length) propOut.set(dflt);
50674 else if(v.every(isColor)) propOut.set(v);
50675 else propOut.set(dflt);
50676 }
50677 },
50678 colorscale: {
50679 coerceFunction: function(v, propOut, dflt) {
50680 propOut.set(colorscales.get(v, dflt));
50681 }
50682 },
50683 angle: {
50684 coerceFunction: function(v, propOut, dflt) {
50685 if(v === 'auto') propOut.set('auto');
50686 else if(!isNumeric(v)) propOut.set(dflt);
50687 else propOut.set(modHalf(+v, 360));
50688 }
50689 },
50690 subplotid: {
50691 coerceFunction: function(v, propOut, dflt, opts) {
50692 var regex = opts.regex || counterRegex(dflt);
50693 if(typeof v === 'string' && regex.test(v)) {
50694 propOut.set(v);
50695 return;
50696 }
50697 propOut.set(dflt);
50698 },
50699 validateFunction: function(v, opts) {
50700 var dflt = opts.dflt;
50701
50702 if(v === dflt) return true;
50703 if(typeof v !== 'string') return false;
50704 if(counterRegex(dflt).test(v)) return true;
50705
50706 return false;
50707 }
50708 },
50709 flaglist: {
50710 coerceFunction: function(v, propOut, dflt, opts) {
50711 if(typeof v !== 'string') {
50712 propOut.set(dflt);
50713 return;
50714 }
50715 if((opts.extras || []).indexOf(v) !== -1) {
50716 propOut.set(v);
50717 return;
50718 }
50719 var vParts = v.split('+');
50720 var i = 0;
50721 while(i < vParts.length) {
50722 var vi = vParts[i];
50723 if(opts.flags.indexOf(vi) === -1 || vParts.indexOf(vi) < i) {
50724 vParts.splice(i, 1);
50725 } else i++;
50726 }
50727 if(!vParts.length) propOut.set(dflt);
50728 else propOut.set(vParts.join('+'));
50729 }
50730 },
50731 any: {
50732 coerceFunction: function(v, propOut, dflt) {
50733 if(v === undefined) propOut.set(dflt);
50734 else propOut.set(v);
50735 }
50736 },
50737 info_array: {
50738 // set `dimensions=2` for a 2D array or '1-2' for either
50739 // `items` may be a single object instead of an array, in which case
50740 // `freeLength` must be true.
50741 // if `dimensions='1-2'` and items is a 1D array, then the value can
50742 // either be a matching 1D array or an array of such matching 1D arrays
50743 coerceFunction: function(v, propOut, dflt, opts) {
50744 // simplified coerce function just for array items
50745 function coercePart(v, opts, dflt) {
50746 var out;
50747 var propPart = {set: function(v) { out = v; }};
50748
50749 if(dflt === undefined) dflt = opts.dflt;
50750
50751 exports.valObjectMeta[opts.valType].coerceFunction(v, propPart, dflt, opts);
50752
50753 return out;
50754 }
50755
50756 var twoD = opts.dimensions === 2 || (opts.dimensions === '1-2' && Array.isArray(v) && Array.isArray(v[0]));
50757
50758 if(!Array.isArray(v)) {
50759 propOut.set(dflt);
50760 return;
50761 }
50762
50763 var items = opts.items;
50764 var vOut = [];
50765 var arrayItems = Array.isArray(items);
50766 var arrayItems2D = arrayItems && twoD && Array.isArray(items[0]);
50767 var innerItemsOnly = twoD && arrayItems && !arrayItems2D;
50768 var len = (arrayItems && !innerItemsOnly) ? items.length : v.length;
50769
50770 var i, j, row, item, len2, vNew;
50771
50772 dflt = Array.isArray(dflt) ? dflt : [];
50773
50774 if(twoD) {
50775 for(i = 0; i < len; i++) {
50776 vOut[i] = [];
50777 row = Array.isArray(v[i]) ? v[i] : [];
50778 if(innerItemsOnly) len2 = items.length;
50779 else if(arrayItems) len2 = items[i].length;
50780 else len2 = row.length;
50781
50782 for(j = 0; j < len2; j++) {
50783 if(innerItemsOnly) item = items[j];
50784 else if(arrayItems) item = items[i][j];
50785 else item = items;
50786
50787 vNew = coercePart(row[j], item, (dflt[i] || [])[j]);
50788 if(vNew !== undefined) vOut[i][j] = vNew;
50789 }
50790 }
50791 } else {
50792 for(i = 0; i < len; i++) {
50793 vNew = coercePart(v[i], arrayItems ? items[i] : items, dflt[i]);
50794 if(vNew !== undefined) vOut[i] = vNew;
50795 }
50796 }
50797
50798 propOut.set(vOut);
50799 },
50800 validateFunction: function(v, opts) {
50801 if(!Array.isArray(v)) return false;
50802
50803 var items = opts.items;
50804 var arrayItems = Array.isArray(items);
50805 var twoD = opts.dimensions === 2;
50806
50807 // when free length is off, input and declared lengths must match
50808 if(!opts.freeLength && v.length !== items.length) return false;
50809
50810 // valid when all input items are valid
50811 for(var i = 0; i < v.length; i++) {
50812 if(twoD) {
50813 if(!Array.isArray(v[i]) || (!opts.freeLength && v[i].length !== items[i].length)) {
50814 return false;
50815 }
50816 for(var j = 0; j < v[i].length; j++) {
50817 if(!validate(v[i][j], arrayItems ? items[i][j] : items)) {
50818 return false;
50819 }
50820 }
50821 } else if(!validate(v[i], arrayItems ? items[i] : items)) return false;
50822 }
50823
50824 return true;
50825 }
50826 }
50827};
50828
50829/**
50830 * Ensures that container[attribute] has a valid value.
50831 *
50832 * attributes[attribute] is an object with possible keys:
50833 * - valType: data_array, enumerated, boolean, ... as in valObjectMeta
50834 * - values: (enumerated only) array of allowed vals
50835 * - min, max: (number, integer only) inclusive bounds on allowed vals
50836 * either or both may be omitted
50837 * - dflt: if attribute is invalid or missing, use this default
50838 * if dflt is provided as an argument to lib.coerce it takes precedence
50839 * as a convenience, returns the value it finally set
50840 */
50841exports.coerce = function(containerIn, containerOut, attributes, attribute, dflt) {
50842 var opts = nestedProperty(attributes, attribute).get();
50843 var propIn = nestedProperty(containerIn, attribute);
50844 var propOut = nestedProperty(containerOut, attribute);
50845 var v = propIn.get();
50846
50847 var template = containerOut._template;
50848 if(v === undefined && template) {
50849 v = nestedProperty(template, attribute).get();
50850 // already used the template value, so short-circuit the second check
50851 template = 0;
50852 }
50853
50854 if(dflt === undefined) dflt = opts.dflt;
50855
50856 /**
50857 * arrayOk: value MAY be an array, then we do no value checking
50858 * at this point, because it can be more complicated than the
50859 * individual form (eg. some array vals can be numbers, even if the
50860 * single values must be color strings)
50861 */
50862 if(opts.arrayOk && isArrayOrTypedArray(v)) {
50863 propOut.set(v);
50864 return v;
50865 }
50866
50867 var coerceFunction = exports.valObjectMeta[opts.valType].coerceFunction;
50868 coerceFunction(v, propOut, dflt, opts);
50869
50870 var out = propOut.get();
50871 // in case v was provided but invalid, try the template again so it still
50872 // overrides the regular default
50873 if(template && out === dflt && !validate(v, opts)) {
50874 v = nestedProperty(template, attribute).get();
50875 coerceFunction(v, propOut, dflt, opts);
50876 out = propOut.get();
50877 }
50878 return out;
50879};
50880
50881/**
50882 * Variation on coerce
50883 *
50884 * Uses coerce to get attribute value if user input is valid,
50885 * returns attribute default if user input it not valid or
50886 * returns false if there is no user input.
50887 */
50888exports.coerce2 = function(containerIn, containerOut, attributes, attribute, dflt) {
50889 var propIn = nestedProperty(containerIn, attribute);
50890 var propOut = exports.coerce(containerIn, containerOut, attributes, attribute, dflt);
50891 var valIn = propIn.get();
50892
50893 return (valIn !== undefined && valIn !== null) ? propOut : false;
50894};
50895
50896/*
50897 * Shortcut to coerce the three font attributes
50898 *
50899 * 'coerce' is a lib.coerce wrapper with implied first three arguments
50900 */
50901exports.coerceFont = function(coerce, attr, dfltObj) {
50902 var out = {};
50903
50904 dfltObj = dfltObj || {};
50905
50906 out.family = coerce(attr + '.family', dfltObj.family);
50907 out.size = coerce(attr + '.size', dfltObj.size);
50908 out.color = coerce(attr + '.color', dfltObj.color);
50909
50910 return out;
50911};
50912
50913/*
50914 * Shortcut to coerce the pattern attributes
50915 */
50916exports.coercePattern = function(coerce, attr, markerColor, hasMarkerColorscale) {
50917 var shape = coerce(attr + '.shape');
50918 if(shape) {
50919 coerce(attr + '.solidity');
50920 coerce(attr + '.size');
50921 var fillmode = coerce(attr + '.fillmode');
50922 var isOverlay = fillmode === 'overlay';
50923
50924 if(!hasMarkerColorscale) {
50925 var bgcolor = coerce(attr + '.bgcolor', isOverlay ?
50926 markerColor :
50927 undefined
50928 );
50929
50930 coerce(attr + '.fgcolor', isOverlay ?
50931 Color.contrast(bgcolor) :
50932 markerColor
50933 );
50934 }
50935
50936 coerce(attr + '.fgopacity', isOverlay ?
50937 0.5 :
50938 1
50939 );
50940 }
50941};
50942
50943/** Coerce shortcut for 'hoverinfo'
50944 * handling 1-vs-multi-trace dflt logic
50945 *
50946 * @param {object} traceIn : user trace object
50947 * @param {object} traceOut : full trace object (requires _module ref)
50948 * @param {object} layoutOut : full layout object (require _dataLength ref)
50949 * @return {any} : the coerced value
50950 */
50951exports.coerceHoverinfo = function(traceIn, traceOut, layoutOut) {
50952 var moduleAttrs = traceOut._module.attributes;
50953 var attrs = moduleAttrs.hoverinfo ? moduleAttrs : baseTraceAttrs;
50954
50955 var valObj = attrs.hoverinfo;
50956 var dflt;
50957
50958 if(layoutOut._dataLength === 1) {
50959 var flags = valObj.dflt === 'all' ?
50960 valObj.flags.slice() :
50961 valObj.dflt.split('+');
50962
50963 flags.splice(flags.indexOf('name'), 1);
50964 dflt = flags.join('+');
50965 }
50966
50967 return exports.coerce(traceIn, traceOut, attrs, 'hoverinfo', dflt);
50968};
50969
50970/** Coerce shortcut for [un]selected.marker.opacity,
50971 * which has special default logic, to ensure that it corresponds to the
50972 * default selection behavior while allowing to be overtaken by any other
50973 * [un]selected attribute.
50974 *
50975 * N.B. This must be called *after* coercing all the other [un]selected attrs,
50976 * to give the intended result.
50977 *
50978 * @param {object} traceOut : fullData item
50979 * @param {function} coerce : lib.coerce wrapper with implied first three arguments
50980 */
50981exports.coerceSelectionMarkerOpacity = function(traceOut, coerce) {
50982 if(!traceOut.marker) return;
50983
50984 var mo = traceOut.marker.opacity;
50985 // you can still have a `marker` container with no markers if there's text
50986 if(mo === undefined) return;
50987
50988 var smoDflt;
50989 var usmoDflt;
50990
50991 // Don't give [un]selected.marker.opacity a default value if
50992 // marker.opacity is an array: handle this during style step.
50993 //
50994 // Only give [un]selected.marker.opacity a default value if you don't
50995 // set any other [un]selected attributes.
50996 if(!isArrayOrTypedArray(mo) && !traceOut.selected && !traceOut.unselected) {
50997 smoDflt = mo;
50998 usmoDflt = DESELECTDIM * mo;
50999 }
51000
51001 coerce('selected.marker.opacity', smoDflt);
51002 coerce('unselected.marker.opacity', usmoDflt);
51003};
51004
51005function validate(value, opts) {
51006 var valObjectDef = exports.valObjectMeta[opts.valType];
51007
51008 if(opts.arrayOk && isArrayOrTypedArray(value)) return true;
51009
51010 if(valObjectDef.validateFunction) {
51011 return valObjectDef.validateFunction(value, opts);
51012 }
51013
51014 var failed = {};
51015 var out = failed;
51016 var propMock = { set: function(v) { out = v; } };
51017
51018 // 'failed' just something mutable that won't be === anything else
51019
51020 valObjectDef.coerceFunction(value, propMock, failed, opts);
51021 return out !== failed;
51022}
51023exports.validate = validate;
51024
51025},{"../components/color":157,"../components/colorscale/scales":172,"../constants/interactions":266,"../plots/attributes":330,"./array":273,"./mod":294,"./nested_property":295,"./regex":303,"fast-isnumeric":33,"tinycolor2":121}],278:[function(_dereq_,module,exports){
51026'use strict';
51027
51028var timeFormat = _dereq_('d3-time-format').timeFormat;
51029var isNumeric = _dereq_('fast-isnumeric');
51030
51031var Loggers = _dereq_('./loggers');
51032var mod = _dereq_('./mod').mod;
51033
51034var constants = _dereq_('../constants/numerical');
51035var BADNUM = constants.BADNUM;
51036var ONEDAY = constants.ONEDAY;
51037var ONEHOUR = constants.ONEHOUR;
51038var ONEMIN = constants.ONEMIN;
51039var ONESEC = constants.ONESEC;
51040var EPOCHJD = constants.EPOCHJD;
51041
51042var Registry = _dereq_('../registry');
51043
51044var utcFormat = _dereq_('d3-time-format').utcFormat;
51045
51046var 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;
51047// special regex for chinese calendars to support yyyy-mmi-dd etc for intercalary months
51048var 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;
51049
51050// for 2-digit years, the first year we map them onto
51051var YFIRST = new Date().getFullYear() - 70;
51052
51053function isWorldCalendar(calendar) {
51054 return (
51055 calendar &&
51056 Registry.componentsRegistry.calendars &&
51057 typeof calendar === 'string' && calendar !== 'gregorian'
51058 );
51059}
51060
51061/*
51062 * dateTick0: get the canonical tick for this calendar
51063 *
51064 * integer weekdays : Saturday: 0, Sunday: 1, Monday: 2, etc.
51065 */
51066exports.dateTick0 = function(calendar, dayOfWeek) {
51067 var tick0 = _dateTick0(calendar, !!dayOfWeek);
51068 if(dayOfWeek < 2) return tick0;
51069
51070 var v = exports.dateTime2ms(tick0, calendar);
51071 v += ONEDAY * (dayOfWeek - 1); // shift Sunday to Monday, etc.
51072 return exports.ms2DateTime(v, 0, calendar);
51073};
51074
51075/*
51076 * _dateTick0: get the canonical tick for this calendar
51077 *
51078 * bool sunday is for week ticks, shift it to a Sunday.
51079 */
51080function _dateTick0(calendar, sunday) {
51081 if(isWorldCalendar(calendar)) {
51082 return sunday ?
51083 Registry.getComponentMethod('calendars', 'CANONICAL_SUNDAY')[calendar] :
51084 Registry.getComponentMethod('calendars', 'CANONICAL_TICK')[calendar];
51085 } else {
51086 return sunday ? '2000-01-02' : '2000-01-01';
51087 }
51088}
51089
51090/*
51091 * dfltRange: for each calendar, give a valid default range
51092 */
51093exports.dfltRange = function(calendar) {
51094 if(isWorldCalendar(calendar)) {
51095 return Registry.getComponentMethod('calendars', 'DFLTRANGE')[calendar];
51096 } else {
51097 return ['2000-01-01', '2001-01-01'];
51098 }
51099};
51100
51101// is an object a javascript date?
51102exports.isJSDate = function(v) {
51103 return typeof v === 'object' && v !== null && typeof v.getTime === 'function';
51104};
51105
51106// The absolute limits of our date-time system
51107// This is a little weird: we use MIN_MS and MAX_MS in dateTime2ms
51108// but we use dateTime2ms to calculate them (after defining it!)
51109var MIN_MS, MAX_MS;
51110
51111/**
51112 * dateTime2ms - turn a date object or string s into milliseconds
51113 * (relative to 1970-01-01, per javascript standard)
51114 * optional calendar (string) to use a non-gregorian calendar
51115 *
51116 * Returns BADNUM if it doesn't find a date
51117 *
51118 * strings should have the form:
51119 *
51120 * -?YYYY-mm-dd<sep>HH:MM:SS.sss<tzInfo>?
51121 *
51122 * <sep>: space (our normal standard) or T or t (ISO-8601)
51123 * <tzInfo>: Z, z, [+\-]HH:?MM or [+\-]HH and we THROW IT AWAY
51124 * this format comes from https://tools.ietf.org/html/rfc3339#section-5.6
51125 * and 4.2.5.1 Difference between local time and UTC of day (ISO-8601)
51126 * but we allow it even with a space as the separator
51127 *
51128 * May truncate after any full field, and sss can be any length
51129 * even >3 digits, though javascript dates truncate to milliseconds,
51130 * we keep as much as javascript numeric precision can hold, but we only
51131 * report back up to 100 microsecond precision, because most dates support
51132 * this precision (close to 1970 support more, very far away support less)
51133 *
51134 * Expanded to support negative years to -9999 but you must always
51135 * give 4 digits, except for 2-digit positive years which we assume are
51136 * near the present time.
51137 * Note that we follow ISO 8601:2004: there *is* a year 0, which
51138 * is 1BC/BCE, and -1===2BC etc.
51139 *
51140 * World calendars: not all of these *have* agreed extensions to this full range,
51141 * if you have another calendar system but want a date range outside its validity,
51142 * you can use a gregorian date string prefixed with 'G' or 'g'.
51143 *
51144 * Where to cut off 2-digit years between 1900s and 2000s?
51145 * from https://docs.microsoft.com/en-us/office/troubleshoot/excel/two-digit-year-numbers#the-2029-rule:
51146 * 1930-2029 (the most retro of all...)
51147 * but in my mac chrome from eg. d=new Date(Date.parse('8/19/50')):
51148 * 1950-2049
51149 * by Java, from http://stackoverflow.com/questions/2024273/:
51150 * now-80 - now+19
51151 * or FileMaker Pro, from
51152 * https://fmhelp.filemaker.com/help/18/fmp/en/index.html#page/FMP_Help/dates-with-two-digit-years.html:
51153 * now-70 - now+29
51154 * but python strptime etc, via
51155 * http://docs.python.org/py3k/library/time.html:
51156 * 1969-2068 (super forward-looking, but static, not sliding!)
51157 *
51158 * lets go with now-70 to now+29, and if anyone runs into this problem
51159 * they can learn the hard way not to use 2-digit years, as no choice we
51160 * make now will cover all possibilities. mostly this will all be taken
51161 * care of in initial parsing, should only be an issue for hand-entered data
51162 * currently (2016) this range is:
51163 * 1946-2045
51164 */
51165exports.dateTime2ms = function(s, calendar) {
51166 // first check if s is a date object
51167 if(exports.isJSDate(s)) {
51168 // Convert to the UTC milliseconds that give the same
51169 // hours as this date has in the local timezone
51170 var tzOffset = s.getTimezoneOffset() * ONEMIN;
51171 var offsetTweak = (s.getUTCMinutes() - s.getMinutes()) * ONEMIN +
51172 (s.getUTCSeconds() - s.getSeconds()) * ONESEC +
51173 (s.getUTCMilliseconds() - s.getMilliseconds());
51174
51175 if(offsetTweak) {
51176 var comb = 3 * ONEMIN;
51177 tzOffset = tzOffset - comb / 2 + mod(offsetTweak - tzOffset + comb / 2, comb);
51178 }
51179 s = Number(s) - tzOffset;
51180 if(s >= MIN_MS && s <= MAX_MS) return s;
51181 return BADNUM;
51182 }
51183 // otherwise only accept strings and numbers
51184 if(typeof s !== 'string' && typeof s !== 'number') return BADNUM;
51185
51186 s = String(s);
51187
51188 var isWorld = isWorldCalendar(calendar);
51189
51190 // to handle out-of-range dates in international calendars, accept
51191 // 'G' as a prefix to force the built-in gregorian calendar.
51192 var s0 = s.charAt(0);
51193 if(isWorld && (s0 === 'G' || s0 === 'g')) {
51194 s = s.substr(1);
51195 calendar = '';
51196 }
51197
51198 var isChinese = isWorld && calendar.substr(0, 7) === 'chinese';
51199
51200 var match = s.match(isChinese ? DATETIME_REGEXP_CN : DATETIME_REGEXP);
51201 if(!match) return BADNUM;
51202 var y = match[1];
51203 var m = match[3] || '1';
51204 var d = Number(match[5] || 1);
51205 var H = Number(match[7] || 0);
51206 var M = Number(match[9] || 0);
51207 var S = Number(match[11] || 0);
51208
51209 if(isWorld) {
51210 // disallow 2-digit years for world calendars
51211 if(y.length === 2) return BADNUM;
51212 y = Number(y);
51213
51214 var cDate;
51215 try {
51216 var calInstance = Registry.getComponentMethod('calendars', 'getCal')(calendar);
51217 if(isChinese) {
51218 var isIntercalary = m.charAt(m.length - 1) === 'i';
51219 m = parseInt(m, 10);
51220 cDate = calInstance.newDate(y, calInstance.toMonthIndex(y, m, isIntercalary), d);
51221 } else {
51222 cDate = calInstance.newDate(y, Number(m), d);
51223 }
51224 } catch(e) { return BADNUM; } // Invalid ... date
51225
51226 if(!cDate) return BADNUM;
51227
51228 return ((cDate.toJD() - EPOCHJD) * ONEDAY) +
51229 (H * ONEHOUR) + (M * ONEMIN) + (S * ONESEC);
51230 }
51231
51232 if(y.length === 2) {
51233 y = (Number(y) + 2000 - YFIRST) % 100 + YFIRST;
51234 } else y = Number(y);
51235
51236 // new Date uses months from 0; subtract 1 here just so we
51237 // don't have to do it again during the validity test below
51238 m -= 1;
51239
51240 // javascript takes new Date(0..99,m,d) to mean 1900-1999, so
51241 // to support years 0-99 we need to use setFullYear explicitly
51242 // Note that 2000 is a leap year.
51243 var date = new Date(Date.UTC(2000, m, d, H, M));
51244 date.setUTCFullYear(y);
51245
51246 if(date.getUTCMonth() !== m) return BADNUM;
51247 if(date.getUTCDate() !== d) return BADNUM;
51248
51249 return date.getTime() + S * ONESEC;
51250};
51251
51252MIN_MS = exports.MIN_MS = exports.dateTime2ms('-9999');
51253MAX_MS = exports.MAX_MS = exports.dateTime2ms('9999-12-31 23:59:59.9999');
51254
51255// is string s a date? (see above)
51256exports.isDateTime = function(s, calendar) {
51257 return (exports.dateTime2ms(s, calendar) !== BADNUM);
51258};
51259
51260// pad a number with zeroes, to given # of digits before the decimal point
51261function lpad(val, digits) {
51262 return String(val + Math.pow(10, digits)).substr(1);
51263}
51264
51265/**
51266 * Turn ms into string of the form YYYY-mm-dd HH:MM:SS.ssss
51267 * Crop any trailing zeros in time, except never stop right after hours
51268 * (we could choose to crop '-01' from date too but for now we always
51269 * show the whole date)
51270 * Optional range r is the data range that applies, also in ms.
51271 * If rng is big, the later parts of time will be omitted
51272 */
51273var NINETYDAYS = 90 * ONEDAY;
51274var THREEHOURS = 3 * ONEHOUR;
51275var FIVEMIN = 5 * ONEMIN;
51276exports.ms2DateTime = function(ms, r, calendar) {
51277 if(typeof ms !== 'number' || !(ms >= MIN_MS && ms <= MAX_MS)) return BADNUM;
51278
51279 if(!r) r = 0;
51280
51281 var msecTenths = Math.floor(mod(ms + 0.05, 1) * 10);
51282 var msRounded = Math.round(ms - msecTenths / 10);
51283 var dateStr, h, m, s, msec10, d;
51284
51285 if(isWorldCalendar(calendar)) {
51286 var dateJD = Math.floor(msRounded / ONEDAY) + EPOCHJD;
51287 var timeMs = Math.floor(mod(ms, ONEDAY));
51288 try {
51289 dateStr = Registry.getComponentMethod('calendars', 'getCal')(calendar)
51290 .fromJD(dateJD).formatDate('yyyy-mm-dd');
51291 } catch(e) {
51292 // invalid date in this calendar - fall back to Gyyyy-mm-dd
51293 dateStr = utcFormat('G%Y-%m-%d')(new Date(msRounded));
51294 }
51295
51296 // yyyy does NOT guarantee 4-digit years. YYYY mostly does, but does
51297 // other things for a few calendars, so we can't trust it. Just pad
51298 // it manually (after the '-' if there is one)
51299 if(dateStr.charAt(0) === '-') {
51300 while(dateStr.length < 11) dateStr = '-0' + dateStr.substr(1);
51301 } else {
51302 while(dateStr.length < 10) dateStr = '0' + dateStr;
51303 }
51304
51305 // TODO: if this is faster, we could use this block for extracting
51306 // the time components of regular gregorian too
51307 h = (r < NINETYDAYS) ? Math.floor(timeMs / ONEHOUR) : 0;
51308 m = (r < NINETYDAYS) ? Math.floor((timeMs % ONEHOUR) / ONEMIN) : 0;
51309 s = (r < THREEHOURS) ? Math.floor((timeMs % ONEMIN) / ONESEC) : 0;
51310 msec10 = (r < FIVEMIN) ? (timeMs % ONESEC) * 10 + msecTenths : 0;
51311 } else {
51312 d = new Date(msRounded);
51313
51314 dateStr = utcFormat('%Y-%m-%d')(d);
51315
51316 // <90 days: add hours and minutes - never *only* add hours
51317 h = (r < NINETYDAYS) ? d.getUTCHours() : 0;
51318 m = (r < NINETYDAYS) ? d.getUTCMinutes() : 0;
51319 // <3 hours: add seconds
51320 s = (r < THREEHOURS) ? d.getUTCSeconds() : 0;
51321 // <5 minutes: add ms (plus one extra digit, this is msec*10)
51322 msec10 = (r < FIVEMIN) ? d.getUTCMilliseconds() * 10 + msecTenths : 0;
51323 }
51324
51325 return includeTime(dateStr, h, m, s, msec10);
51326};
51327
51328// For converting old-style milliseconds to date strings,
51329// we use the local timezone rather than UTC like we use
51330// everywhere else, both for backward compatibility and
51331// because that's how people mostly use javasript date objects.
51332// Clip one extra day off our date range though so we can't get
51333// thrown beyond the range by the timezone shift.
51334exports.ms2DateTimeLocal = function(ms) {
51335 if(!(ms >= MIN_MS + ONEDAY && ms <= MAX_MS - ONEDAY)) return BADNUM;
51336
51337 var msecTenths = Math.floor(mod(ms + 0.05, 1) * 10);
51338 var d = new Date(Math.round(ms - msecTenths / 10));
51339 var dateStr = timeFormat('%Y-%m-%d')(d);
51340 var h = d.getHours();
51341 var m = d.getMinutes();
51342 var s = d.getSeconds();
51343 var msec10 = d.getUTCMilliseconds() * 10 + msecTenths;
51344
51345 return includeTime(dateStr, h, m, s, msec10);
51346};
51347
51348function includeTime(dateStr, h, m, s, msec10) {
51349 // include each part that has nonzero data in or after it
51350 if(h || m || s || msec10) {
51351 dateStr += ' ' + lpad(h, 2) + ':' + lpad(m, 2);
51352 if(s || msec10) {
51353 dateStr += ':' + lpad(s, 2);
51354 if(msec10) {
51355 var digits = 4;
51356 while(msec10 % 10 === 0) {
51357 digits -= 1;
51358 msec10 /= 10;
51359 }
51360 dateStr += '.' + lpad(msec10, digits);
51361 }
51362 }
51363 }
51364 return dateStr;
51365}
51366
51367// normalize date format to date string, in case it starts as
51368// a Date object or milliseconds
51369// optional dflt is the return value if cleaning fails
51370exports.cleanDate = function(v, dflt, calendar) {
51371 // let us use cleanDate to provide a missing default without an error
51372 if(v === BADNUM) return dflt;
51373 if(exports.isJSDate(v) || (typeof v === 'number' && isFinite(v))) {
51374 // do not allow milliseconds (old) or jsdate objects (inherently
51375 // described as gregorian dates) with world calendars
51376 if(isWorldCalendar(calendar)) {
51377 Loggers.error('JS Dates and milliseconds are incompatible with world calendars', v);
51378 return dflt;
51379 }
51380
51381 // NOTE: if someone puts in a year as a number rather than a string,
51382 // this will mistakenly convert it thinking it's milliseconds from 1970
51383 // that is: '2012' -> Jan. 1, 2012, but 2012 -> 2012 epoch milliseconds
51384 v = exports.ms2DateTimeLocal(+v);
51385 if(!v && dflt !== undefined) return dflt;
51386 } else if(!exports.isDateTime(v, calendar)) {
51387 Loggers.error('unrecognized date', v);
51388 return dflt;
51389 }
51390 return v;
51391};
51392
51393/*
51394 * Date formatting for ticks and hovertext
51395 */
51396
51397/*
51398 * modDateFormat: Support world calendars, and add two items to
51399 * d3's vocabulary:
51400 * %{n}f where n is the max number of digits of fractional seconds
51401 * %h formats: half of the year as a decimal number [1,2]
51402 */
51403var fracMatch = /%\d?f/g;
51404var halfYearMatch = /%h/g;
51405var quarterToHalfYear = {
51406 '1': '1',
51407 '2': '1',
51408 '3': '2',
51409 '4': '2',
51410};
51411function modDateFormat(fmt, x, formatter, calendar) {
51412 fmt = fmt.replace(fracMatch, function(match) {
51413 var digits = Math.min(+(match.charAt(1)) || 6, 6);
51414 var fracSecs = ((x / 1000 % 1) + 2)
51415 .toFixed(digits)
51416 .substr(2).replace(/0+$/, '') || '0';
51417 return fracSecs;
51418 });
51419
51420 var d = new Date(Math.floor(x + 0.05));
51421
51422 fmt = fmt.replace(halfYearMatch, function() {
51423 return quarterToHalfYear[formatter('%q')(d)];
51424 });
51425
51426 if(isWorldCalendar(calendar)) {
51427 try {
51428 fmt = Registry.getComponentMethod('calendars', 'worldCalFmt')(fmt, x, calendar);
51429 } catch(e) {
51430 return 'Invalid';
51431 }
51432 }
51433 return formatter(fmt)(d);
51434}
51435
51436/*
51437 * formatTime: create a time string from:
51438 * x: milliseconds
51439 * tr: tickround ('M', 'S', or # digits)
51440 * only supports UTC times (where every day is 24 hours and 0 is at midnight)
51441 */
51442var MAXSECONDS = [59, 59.9, 59.99, 59.999, 59.9999];
51443function formatTime(x, tr) {
51444 var timePart = mod(x + 0.05, ONEDAY);
51445
51446 var timeStr = lpad(Math.floor(timePart / ONEHOUR), 2) + ':' +
51447 lpad(mod(Math.floor(timePart / ONEMIN), 60), 2);
51448
51449 if(tr !== 'M') {
51450 if(!isNumeric(tr)) tr = 0; // should only be 'S'
51451
51452 /*
51453 * this is a weird one - and shouldn't come up unless people
51454 * monkey with tick0 in weird ways, but we need to do something!
51455 * IN PARTICULAR we had better not display garbage (see below)
51456 * for numbers we always round to the nearest increment of the
51457 * precision we're showing, and this seems like the right way to
51458 * handle seconds and milliseconds, as they have a decimal point
51459 * and people will interpret that to mean rounding like numbers.
51460 * but for larger increments we floor the value: it's always
51461 * 2013 until the ball drops on the new year. We could argue about
51462 * which field it is where we start rounding (should 12:08:59
51463 * round to 12:09 if we're stopping at minutes?) but for now I'll
51464 * say we round seconds but floor everything else. BUT that means
51465 * we need to never round up to 60 seconds, ie 23:59:60
51466 */
51467 var sec = Math.min(mod(x / ONESEC, 60), MAXSECONDS[tr]);
51468
51469 var secStr = (100 + sec).toFixed(tr).substr(1);
51470 if(tr > 0) {
51471 secStr = secStr.replace(/0+$/, '').replace(/[\.]$/, '');
51472 }
51473
51474 timeStr += ':' + secStr;
51475 }
51476 return timeStr;
51477}
51478
51479/*
51480 * formatDate: turn a date into tick or hover label text.
51481 *
51482 * x: milliseconds, the value to convert
51483 * fmt: optional, an explicit format string (d3 format, even for world calendars)
51484 * tr: tickround ('y', 'm', 'd', 'M', 'S', or # digits)
51485 * used if no explicit fmt is provided
51486 * formatter: locale-aware d3 date formatter for standard gregorian calendars
51487 * should be the result of exports.getD3DateFormat(gd)
51488 * calendar: optional string, the world calendar system to use
51489 *
51490 * returns the date/time as a string, potentially with the leading portion
51491 * on a separate line (after '\n')
51492 * Note that this means if you provide an explicit format which includes '\n'
51493 * the axis may choose to strip things after it when they don't change from
51494 * one tick to the next (as it does with automatic formatting)
51495 */
51496exports.formatDate = function(x, fmt, tr, formatter, calendar, extraFormat) {
51497 calendar = isWorldCalendar(calendar) && calendar;
51498
51499 if(!fmt) {
51500 if(tr === 'y') fmt = extraFormat.year;
51501 else if(tr === 'm') fmt = extraFormat.month;
51502 else if(tr === 'd') {
51503 fmt = extraFormat.dayMonth + '\n' + extraFormat.year;
51504 } else {
51505 return formatTime(x, tr) + '\n' + modDateFormat(extraFormat.dayMonthYear, x, formatter, calendar);
51506 }
51507 }
51508
51509 return modDateFormat(fmt, x, formatter, calendar);
51510};
51511
51512/*
51513 * incrementMonth: make a new milliseconds value from the given one,
51514 * having changed the month
51515 *
51516 * special case for world calendars: multiples of 12 are treated as years,
51517 * even for calendar systems that don't have (always or ever) 12 months/year
51518 * TODO: perhaps we need a different code for year increments to support this?
51519 *
51520 * ms (number): the initial millisecond value
51521 * dMonth (int): the (signed) number of months to shift
51522 * calendar (string): the calendar system to use
51523 *
51524 * changing month does not (and CANNOT) always preserve day, since
51525 * months have different lengths. The worst example of this is:
51526 * d = new Date(1970,0,31); d.setMonth(1) -> Feb 31 turns into Mar 3
51527 *
51528 * But we want to be able to iterate over the last day of each month,
51529 * regardless of what its number is.
51530 * So shift 3 days forward, THEN set the new month, then unshift:
51531 * 1/31 -> 2/28 (or 29) -> 3/31 -> 4/30 -> ...
51532 *
51533 * Note that odd behavior still exists if you start from the 26th-28th:
51534 * 1/28 -> 2/28 -> 3/31
51535 * but at least you can't shift any dates into the wrong month,
51536 * and ticks on these days incrementing by month would be very unusual
51537 */
51538var THREEDAYS = 3 * ONEDAY;
51539exports.incrementMonth = function(ms, dMonth, calendar) {
51540 calendar = isWorldCalendar(calendar) && calendar;
51541
51542 // pull time out and operate on pure dates, then add time back at the end
51543 // this gives maximum precision - not that we *normally* care if we're
51544 // incrementing by month, but better to be safe!
51545 var timeMs = mod(ms, ONEDAY);
51546 ms = Math.round(ms - timeMs);
51547
51548 if(calendar) {
51549 try {
51550 var dateJD = Math.round(ms / ONEDAY) + EPOCHJD;
51551 var calInstance = Registry.getComponentMethod('calendars', 'getCal')(calendar);
51552 var cDate = calInstance.fromJD(dateJD);
51553
51554 if(dMonth % 12) calInstance.add(cDate, dMonth, 'm');
51555 else calInstance.add(cDate, dMonth / 12, 'y');
51556
51557 return (cDate.toJD() - EPOCHJD) * ONEDAY + timeMs;
51558 } catch(e) {
51559 Loggers.error('invalid ms ' + ms + ' in calendar ' + calendar);
51560 // then keep going in gregorian even though the result will be 'Invalid'
51561 }
51562 }
51563
51564 var y = new Date(ms + THREEDAYS);
51565 return y.setUTCMonth(y.getUTCMonth() + dMonth) + timeMs - THREEDAYS;
51566};
51567
51568/*
51569 * findExactDates: what fraction of data is exact days, months, or years?
51570 *
51571 * data: array of millisecond values
51572 * calendar (string) the calendar to test against
51573 */
51574exports.findExactDates = function(data, calendar) {
51575 var exactYears = 0;
51576 var exactMonths = 0;
51577 var exactDays = 0;
51578 var blankCount = 0;
51579 var d;
51580 var di;
51581
51582 var calInstance = (
51583 isWorldCalendar(calendar) &&
51584 Registry.getComponentMethod('calendars', 'getCal')(calendar)
51585 );
51586
51587 for(var i = 0; i < data.length; i++) {
51588 di = data[i];
51589
51590 // not date data at all
51591 if(!isNumeric(di)) {
51592 blankCount ++;
51593 continue;
51594 }
51595
51596 // not an exact date
51597 if(di % ONEDAY) continue;
51598
51599 if(calInstance) {
51600 try {
51601 d = calInstance.fromJD(di / ONEDAY + EPOCHJD);
51602 if(d.day() === 1) {
51603 if(d.month() === 1) exactYears++;
51604 else exactMonths++;
51605 } else exactDays++;
51606 } catch(e) {
51607 // invalid date in this calendar - ignore it here.
51608 }
51609 } else {
51610 d = new Date(di);
51611 if(d.getUTCDate() === 1) {
51612 if(d.getUTCMonth() === 0) exactYears++;
51613 else exactMonths++;
51614 } else exactDays++;
51615 }
51616 }
51617 exactMonths += exactYears;
51618 exactDays += exactMonths;
51619
51620 var dataCount = data.length - blankCount;
51621
51622 return {
51623 exactYears: exactYears / dataCount,
51624 exactMonths: exactMonths / dataCount,
51625 exactDays: exactDays / dataCount
51626 };
51627};
51628
51629},{"../constants/numerical":267,"../registry":376,"./loggers":291,"./mod":294,"d3-time-format":30,"fast-isnumeric":33}],279:[function(_dereq_,module,exports){
51630'use strict';
51631
51632var d3 = _dereq_('@plotly/d3');
51633var loggers = _dereq_('./loggers');
51634var matrix = _dereq_('./matrix');
51635var mat4X4 = _dereq_('gl-mat4');
51636
51637/**
51638 * Allow referencing a graph DOM element either directly
51639 * or by its id string
51640 *
51641 * @param {HTMLDivElement|string} gd: a graph element or its id
51642 *
51643 * @returns {HTMLDivElement} the DOM element of the graph
51644 */
51645function getGraphDiv(gd) {
51646 var gdElement;
51647
51648 if(typeof gd === 'string') {
51649 gdElement = document.getElementById(gd);
51650
51651 if(gdElement === null) {
51652 throw new Error('No DOM element with id \'' + gd + '\' exists on the page.');
51653 }
51654
51655 return gdElement;
51656 } else if(gd === null || gd === undefined) {
51657 throw new Error('DOM element provided is null or undefined');
51658 }
51659
51660 // otherwise assume that gd is a DOM element
51661 return gd;
51662}
51663
51664function isPlotDiv(el) {
51665 var el3 = d3.select(el);
51666 return el3.node() instanceof HTMLElement &&
51667 el3.size() &&
51668 el3.classed('js-plotly-plot');
51669}
51670
51671function removeElement(el) {
51672 var elParent = el && el.parentNode;
51673 if(elParent) elParent.removeChild(el);
51674}
51675
51676/**
51677 * for dynamically adding style rules
51678 * makes one stylesheet that contains all rules added
51679 * by all calls to this function
51680 */
51681function addStyleRule(selector, styleString) {
51682 addRelatedStyleRule('global', selector, styleString);
51683}
51684
51685/**
51686 * for dynamically adding style rules
51687 * to a stylesheet uniquely identified by a uid
51688 */
51689function addRelatedStyleRule(uid, selector, styleString) {
51690 var id = 'plotly.js-style-' + uid;
51691 var style = document.getElementById(id);
51692 if(!style) {
51693 style = document.createElement('style');
51694 style.setAttribute('id', id);
51695 // WebKit hack :(
51696 style.appendChild(document.createTextNode(''));
51697 document.head.appendChild(style);
51698 }
51699 var styleSheet = style.sheet;
51700
51701 if(styleSheet.insertRule) {
51702 styleSheet.insertRule(selector + '{' + styleString + '}', 0);
51703 } else if(styleSheet.addRule) {
51704 styleSheet.addRule(selector, styleString, 0);
51705 } else loggers.warn('addStyleRule failed');
51706}
51707
51708/**
51709 * to remove from the page a stylesheet identified by a given uid
51710 */
51711function deleteRelatedStyleRule(uid) {
51712 var id = 'plotly.js-style-' + uid;
51713 var style = document.getElementById(id);
51714 if(style) removeElement(style);
51715}
51716
51717function getFullTransformMatrix(element) {
51718 var allElements = getElementAndAncestors(element);
51719 // the identity matrix
51720 var out = [
51721 1, 0, 0, 0,
51722 0, 1, 0, 0,
51723 0, 0, 1, 0,
51724 0, 0, 0, 1
51725 ];
51726 allElements.forEach(function(e) {
51727 var t = getElementTransformMatrix(e);
51728 if(t) {
51729 var m = matrix.convertCssMatrix(t);
51730 out = mat4X4.multiply(out, out, m);
51731 }
51732 });
51733 return out;
51734}
51735
51736/**
51737 * extracts and parses the 2d css style transform matrix from some element
51738 */
51739function getElementTransformMatrix(element) {
51740 var style = window.getComputedStyle(element, null);
51741 var transform = (
51742 style.getPropertyValue('-webkit-transform') ||
51743 style.getPropertyValue('-moz-transform') ||
51744 style.getPropertyValue('-ms-transform') ||
51745 style.getPropertyValue('-o-transform') ||
51746 style.getPropertyValue('transform')
51747 );
51748
51749 if(transform === 'none') return null;
51750 // the transform is a string in the form of matrix(a, b, ...) or matrix3d(...)
51751 return transform
51752 .replace('matrix', '')
51753 .replace('3d', '')
51754 .slice(1, -1)
51755 .split(',')
51756 .map(function(n) { return +n; });
51757}
51758/**
51759 * retrieve all DOM elements that are ancestors of the specified one (including itself)
51760 */
51761function getElementAndAncestors(element) {
51762 var allElements = [];
51763 while(isTransformableElement(element)) {
51764 allElements.push(element);
51765 element = element.parentNode;
51766 }
51767 return allElements;
51768}
51769
51770function isTransformableElement(element) {
51771 return element && (element instanceof Element || element instanceof HTMLElement);
51772}
51773
51774function equalDomRects(a, b) {
51775 return (
51776 a && b &&
51777 a.x === b.x &&
51778 a.y === b.y &&
51779 a.top === b.top &&
51780 a.left === b.left &&
51781 a.right === b.right &&
51782 a.bottom === b.bottom
51783 );
51784}
51785
51786module.exports = {
51787 getGraphDiv: getGraphDiv,
51788 isPlotDiv: isPlotDiv,
51789 removeElement: removeElement,
51790 addStyleRule: addStyleRule,
51791 addRelatedStyleRule: addRelatedStyleRule,
51792 deleteRelatedStyleRule: deleteRelatedStyleRule,
51793 getFullTransformMatrix: getFullTransformMatrix,
51794 getElementTransformMatrix: getElementTransformMatrix,
51795 getElementAndAncestors: getElementAndAncestors,
51796 equalDomRects: equalDomRects
51797};
51798
51799},{"./loggers":291,"./matrix":293,"@plotly/d3":20,"gl-mat4":49}],280:[function(_dereq_,module,exports){
51800'use strict';
51801
51802/* global jQuery:false */
51803
51804var EventEmitter = _dereq_('events').EventEmitter;
51805
51806var Events = {
51807
51808 init: function(plotObj) {
51809 /*
51810 * If we have already instantiated an emitter for this plot
51811 * return early.
51812 */
51813 if(plotObj._ev instanceof EventEmitter) return plotObj;
51814
51815 var ev = new EventEmitter();
51816 var internalEv = new EventEmitter();
51817
51818 /*
51819 * Assign to plot._ev while we still live in a land
51820 * where plot is a DOM element with stuff attached to it.
51821 * In the future we can make plot the event emitter itself.
51822 */
51823 plotObj._ev = ev;
51824
51825 /*
51826 * Create a second event handler that will manage events *internally*.
51827 * This allows parts of plotly to respond to thing like relayout without
51828 * having to use the user-facing event handler. They cannot peacefully
51829 * coexist on the same handler because a user invoking
51830 * plotObj.removeAllListeners() would detach internal events, breaking
51831 * plotly.
51832 */
51833 plotObj._internalEv = internalEv;
51834
51835 /*
51836 * Assign bound methods from the ev to the plot object. These methods
51837 * will reference the 'this' of plot._ev even though they are methods
51838 * of plot. This will keep the event machinery away from the plot object
51839 * which currently is often a DOM element but presents an API that will
51840 * continue to function when plot becomes an emitter. Not all EventEmitter
51841 * methods have been bound to `plot` as some do not currently add value to
51842 * the Plotly event API.
51843 */
51844 plotObj.on = ev.on.bind(ev);
51845 plotObj.once = ev.once.bind(ev);
51846 plotObj.removeListener = ev.removeListener.bind(ev);
51847 plotObj.removeAllListeners = ev.removeAllListeners.bind(ev);
51848
51849 /*
51850 * Create functions for managing internal events. These are *only* triggered
51851 * by the mirroring of external events via the emit function.
51852 */
51853 plotObj._internalOn = internalEv.on.bind(internalEv);
51854 plotObj._internalOnce = internalEv.once.bind(internalEv);
51855 plotObj._removeInternalListener = internalEv.removeListener.bind(internalEv);
51856 plotObj._removeAllInternalListeners = internalEv.removeAllListeners.bind(internalEv);
51857
51858 /*
51859 * We must wrap emit to continue to support JQuery events. The idea
51860 * is to check to see if the user is using JQuery events, if they are
51861 * we emit JQuery events to trigger user handlers as well as the EventEmitter
51862 * events.
51863 */
51864 plotObj.emit = function(event, data) {
51865 if(typeof jQuery !== 'undefined') {
51866 jQuery(plotObj).trigger(event, data);
51867 }
51868
51869 ev.emit(event, data);
51870 internalEv.emit(event, data);
51871 };
51872
51873 return plotObj;
51874 },
51875
51876 /*
51877 * This function behaves like jQuery's triggerHandler. It calls
51878 * all handlers for a particular event and returns the return value
51879 * of the LAST handler. This function also triggers jQuery's
51880 * triggerHandler for backwards compatibility.
51881 */
51882 triggerHandler: function(plotObj, event, data) {
51883 var jQueryHandlerValue;
51884 var nodeEventHandlerValue;
51885
51886 /*
51887 * If jQuery exists run all its handlers for this event and
51888 * collect the return value of the LAST handler function
51889 */
51890 if(typeof jQuery !== 'undefined') {
51891 jQueryHandlerValue = jQuery(plotObj).triggerHandler(event, data);
51892 }
51893
51894 /*
51895 * Now run all the node style event handlers
51896 */
51897 var ev = plotObj._ev;
51898 if(!ev) return jQueryHandlerValue;
51899
51900 var handlers = ev._events[event];
51901 if(!handlers) return jQueryHandlerValue;
51902
51903 // making sure 'this' is the EventEmitter instance
51904 function apply(handler) {
51905 // The 'once' case, we can't just call handler() as we need
51906 // the return value here. So,
51907 // - remove handler
51908 // - call listener and grab return value!
51909 // - stash 'fired' key to not call handler twice
51910 if(handler.listener) {
51911 ev.removeListener(event, handler.listener);
51912 if(!handler.fired) {
51913 handler.fired = true;
51914 return handler.listener.apply(ev, [data]);
51915 }
51916 } else {
51917 return handler.apply(ev, [data]);
51918 }
51919 }
51920
51921 // handlers can be function or an array of functions
51922 handlers = Array.isArray(handlers) ? handlers : [handlers];
51923
51924 var i;
51925 for(i = 0; i < handlers.length - 1; i++) {
51926 apply(handlers[i]);
51927 }
51928 // now call the final handler and collect its value
51929 nodeEventHandlerValue = apply(handlers[i]);
51930
51931 /*
51932 * Return either the jQuery handler value if it exists or the
51933 * nodeEventHandler value. jQuery event value supersedes nodejs
51934 * events for backwards compatibility reasons.
51935 */
51936 return jQueryHandlerValue !== undefined ?
51937 jQueryHandlerValue :
51938 nodeEventHandlerValue;
51939 },
51940
51941 purge: function(plotObj) {
51942 delete plotObj._ev;
51943 delete plotObj.on;
51944 delete plotObj.once;
51945 delete plotObj.removeListener;
51946 delete plotObj.removeAllListeners;
51947 delete plotObj.emit;
51948
51949 delete plotObj._ev;
51950 delete plotObj._internalEv;
51951 delete plotObj._internalOn;
51952 delete plotObj._internalOnce;
51953 delete plotObj._removeInternalListener;
51954 delete plotObj._removeAllInternalListeners;
51955
51956 return plotObj;
51957 }
51958
51959};
51960
51961module.exports = Events;
51962
51963},{"events":27}],281:[function(_dereq_,module,exports){
51964'use strict';
51965
51966var isPlainObject = _dereq_('./is_plain_object.js');
51967var isArray = Array.isArray;
51968
51969function primitivesLoopSplice(source, target) {
51970 var i, value;
51971 for(i = 0; i < source.length; i++) {
51972 value = source[i];
51973 if(value !== null && typeof(value) === 'object') {
51974 return false;
51975 }
51976 if(value !== void(0)) {
51977 target[i] = value;
51978 }
51979 }
51980 return true;
51981}
51982
51983exports.extendFlat = function() {
51984 return _extend(arguments, false, false, false);
51985};
51986
51987exports.extendDeep = function() {
51988 return _extend(arguments, true, false, false);
51989};
51990
51991exports.extendDeepAll = function() {
51992 return _extend(arguments, true, true, false);
51993};
51994
51995exports.extendDeepNoArrays = function() {
51996 return _extend(arguments, true, false, true);
51997};
51998
51999/*
52000 * Inspired by https://github.com/justmoon/node-extend/blob/master/index.js
52001 * All credit to the jQuery authors for perfecting this amazing utility.
52002 *
52003 * API difference with jQuery version:
52004 * - No optional boolean (true -> deep extend) first argument,
52005 * use `extendFlat` for first-level only extend and
52006 * use `extendDeep` for a deep extend.
52007 *
52008 * Other differences with jQuery version:
52009 * - Uses a modern (and faster) isPlainObject routine.
52010 * - Expected to work with object {} and array [] arguments only.
52011 * - Does not check for circular structure.
52012 * FYI: jQuery only does a check across one level.
52013 * Warning: this might result in infinite loops.
52014 *
52015 */
52016function _extend(inputs, isDeep, keepAllKeys, noArrayCopies) {
52017 var target = inputs[0];
52018 var length = inputs.length;
52019
52020 var input, key, src, copy, copyIsArray, clone, allPrimitives;
52021
52022 // TODO does this do the right thing for typed arrays?
52023
52024 if(length === 2 && isArray(target) && isArray(inputs[1]) && target.length === 0) {
52025 allPrimitives = primitivesLoopSplice(inputs[1], target);
52026
52027 if(allPrimitives) {
52028 return target;
52029 } else {
52030 target.splice(0, target.length); // reset target and continue to next block
52031 }
52032 }
52033
52034 for(var i = 1; i < length; i++) {
52035 input = inputs[i];
52036
52037 for(key in input) {
52038 src = target[key];
52039 copy = input[key];
52040
52041 if(noArrayCopies && isArray(copy)) {
52042 // Stop early and just transfer the array if array copies are disallowed:
52043
52044 target[key] = copy;
52045 } else if(isDeep && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) {
52046 // recurse if we're merging plain objects or arrays
52047
52048 if(copyIsArray) {
52049 copyIsArray = false;
52050 clone = src && isArray(src) ? src : [];
52051 } else {
52052 clone = src && isPlainObject(src) ? src : {};
52053 }
52054
52055 // never move original objects, clone them
52056 target[key] = _extend([clone, copy], isDeep, keepAllKeys, noArrayCopies);
52057 } else if(typeof copy !== 'undefined' || keepAllKeys) {
52058 // don't bring in undefined values, except for extendDeepAll
52059
52060 target[key] = copy;
52061 }
52062 }
52063 }
52064
52065 return target;
52066}
52067
52068},{"./is_plain_object.js":288}],282:[function(_dereq_,module,exports){
52069'use strict';
52070
52071
52072/**
52073 * Return news array containing only the unique items
52074 * found in input array.
52075 *
52076 * IMPORTANT: Note that items are considered unique
52077 * if `String({})` is unique. For example;
52078 *
52079 * Lib.filterUnique([ { a: 1 }, { b: 2 } ])
52080 *
52081 * returns [{ a: 1 }]
52082 *
52083 * and
52084 *
52085 * Lib.filterUnique([ '1', 1 ])
52086 *
52087 * returns ['1']
52088 *
52089 *
52090 * @param {array} array base array
52091 * @return {array} new filtered array
52092 */
52093module.exports = function filterUnique(array) {
52094 var seen = {};
52095 var out = [];
52096 var j = 0;
52097
52098 for(var i = 0; i < array.length; i++) {
52099 var item = array[i];
52100
52101 if(seen[item] !== 1) {
52102 seen[item] = 1;
52103 out[j++] = item;
52104 }
52105 }
52106
52107 return out;
52108};
52109
52110},{}],283:[function(_dereq_,module,exports){
52111'use strict';
52112
52113/** Filter out object items with visible !== true
52114 * insider array container.
52115 *
52116 * @param {array of objects} container
52117 * @return {array of objects} of length <= container
52118 *
52119 */
52120module.exports = function filterVisible(container) {
52121 var filterFn = isCalcData(container) ? calcDataFilter : baseFilter;
52122 var out = [];
52123
52124 for(var i = 0; i < container.length; i++) {
52125 var item = container[i];
52126 if(filterFn(item)) out.push(item);
52127 }
52128
52129 return out;
52130};
52131
52132function baseFilter(item) {
52133 return item.visible === true;
52134}
52135
52136function calcDataFilter(item) {
52137 var trace = item[0].trace;
52138 return trace.visible === true && trace._length !== 0;
52139}
52140
52141function isCalcData(cont) {
52142 return (
52143 Array.isArray(cont) &&
52144 Array.isArray(cont[0]) &&
52145 cont[0][0] &&
52146 cont[0][0].trace
52147 );
52148}
52149
52150},{}],284:[function(_dereq_,module,exports){
52151'use strict';
52152
52153var mod = _dereq_('./mod').mod;
52154
52155/*
52156 * look for intersection of two line segments
52157 * (1->2 and 3->4) - returns array [x,y] if they do, null if not
52158 */
52159exports.segmentsIntersect = segmentsIntersect;
52160function segmentsIntersect(x1, y1, x2, y2, x3, y3, x4, y4) {
52161 var a = x2 - x1;
52162 var b = x3 - x1;
52163 var c = x4 - x3;
52164 var d = y2 - y1;
52165 var e = y3 - y1;
52166 var f = y4 - y3;
52167 var det = a * f - c * d;
52168 // parallel lines? intersection is undefined
52169 // ignore the case where they are colinear
52170 if(det === 0) return null;
52171 var t = (b * f - c * e) / det;
52172 var u = (b * d - a * e) / det;
52173 // segments do not intersect?
52174 if(u < 0 || u > 1 || t < 0 || t > 1) return null;
52175
52176 return {x: x1 + a * t, y: y1 + d * t};
52177}
52178
52179/*
52180 * find the minimum distance between two line segments (1->2 and 3->4)
52181 */
52182exports.segmentDistance = function segmentDistance(x1, y1, x2, y2, x3, y3, x4, y4) {
52183 if(segmentsIntersect(x1, y1, x2, y2, x3, y3, x4, y4)) return 0;
52184
52185 // the two segments and their lengths squared
52186 var x12 = x2 - x1;
52187 var y12 = y2 - y1;
52188 var x34 = x4 - x3;
52189 var y34 = y4 - y3;
52190 var ll12 = x12 * x12 + y12 * y12;
52191 var ll34 = x34 * x34 + y34 * y34;
52192
52193 // calculate distance squared, then take the sqrt at the very end
52194 var dist2 = Math.min(
52195 perpDistance2(x12, y12, ll12, x3 - x1, y3 - y1),
52196 perpDistance2(x12, y12, ll12, x4 - x1, y4 - y1),
52197 perpDistance2(x34, y34, ll34, x1 - x3, y1 - y3),
52198 perpDistance2(x34, y34, ll34, x2 - x3, y2 - y3)
52199 );
52200
52201 return Math.sqrt(dist2);
52202};
52203
52204/*
52205 * distance squared from segment ab to point c
52206 * [xab, yab] is the vector b-a
52207 * [xac, yac] is the vector c-a
52208 * llab is the length squared of (b-a), just to simplify calculation
52209 */
52210function perpDistance2(xab, yab, llab, xac, yac) {
52211 var fcAB = (xac * xab + yac * yab);
52212 if(fcAB < 0) {
52213 // point c is closer to point a
52214 return xac * xac + yac * yac;
52215 } else if(fcAB > llab) {
52216 // point c is closer to point b
52217 var xbc = xac - xab;
52218 var ybc = yac - yab;
52219 return xbc * xbc + ybc * ybc;
52220 } else {
52221 // perpendicular distance is the shortest
52222 var crossProduct = xac * yab - yac * xab;
52223 return crossProduct * crossProduct / llab;
52224 }
52225}
52226
52227// a very short-term cache for getTextLocation, just because
52228// we're often looping over the same locations multiple times
52229// invalidated as soon as we look at a different path
52230var locationCache, workingPath, workingTextWidth;
52231
52232// turn a path and position along it into x, y, and angle for the given text
52233exports.getTextLocation = function getTextLocation(path, totalPathLen, positionOnPath, textWidth) {
52234 if(path !== workingPath || textWidth !== workingTextWidth) {
52235 locationCache = {};
52236 workingPath = path;
52237 workingTextWidth = textWidth;
52238 }
52239 if(locationCache[positionOnPath]) {
52240 return locationCache[positionOnPath];
52241 }
52242
52243 // for the angle, use points on the path separated by the text width
52244 // even though due to curvature, the text will cover a bit more than that
52245 var p0 = path.getPointAtLength(mod(positionOnPath - textWidth / 2, totalPathLen));
52246 var p1 = path.getPointAtLength(mod(positionOnPath + textWidth / 2, totalPathLen));
52247 // note: atan handles 1/0 nicely
52248 var theta = Math.atan((p1.y - p0.y) / (p1.x - p0.x));
52249 // center the text at 2/3 of the center position plus 1/3 the p0/p1 midpoint
52250 // that's the average position of this segment, assuming it's roughly quadratic
52251 var pCenter = path.getPointAtLength(mod(positionOnPath, totalPathLen));
52252 var x = (pCenter.x * 4 + p0.x + p1.x) / 6;
52253 var y = (pCenter.y * 4 + p0.y + p1.y) / 6;
52254
52255 var out = {x: x, y: y, theta: theta};
52256 locationCache[positionOnPath] = out;
52257 return out;
52258};
52259
52260exports.clearLocationCache = function() {
52261 workingPath = null;
52262};
52263
52264/*
52265 * Find the segment of `path` that's within the visible area
52266 * given by `bounds` {left, right, top, bottom}, to within a
52267 * precision of `buffer` px
52268 *
52269 * returns: undefined if nothing is visible, else object:
52270 * {
52271 * min: position where the path first enters bounds, or 0 if it
52272 * starts within bounds
52273 * max: position where the path last exits bounds, or the path length
52274 * if it finishes within bounds
52275 * len: max - min, ie the length of visible path
52276 * total: the total path length - just included so the caller doesn't
52277 * need to call path.getTotalLength() again
52278 * isClosed: true iff the start and end points of the path are both visible
52279 * and are at the same point
52280 * }
52281 *
52282 * Works by starting from either end and repeatedly finding the distance from
52283 * that point to the plot area, and if it's outside the plot, moving along the
52284 * path by that distance (because the plot must be at least that far away on
52285 * the path). Note that if a path enters, exits, and re-enters the plot, we
52286 * will not capture this behavior.
52287 */
52288exports.getVisibleSegment = function getVisibleSegment(path, bounds, buffer) {
52289 var left = bounds.left;
52290 var right = bounds.right;
52291 var top = bounds.top;
52292 var bottom = bounds.bottom;
52293
52294 var pMin = 0;
52295 var pTotal = path.getTotalLength();
52296 var pMax = pTotal;
52297
52298 var pt0, ptTotal;
52299
52300 function getDistToPlot(len) {
52301 var pt = path.getPointAtLength(len);
52302
52303 // hold on to the start and end points for `closed`
52304 if(len === 0) pt0 = pt;
52305 else if(len === pTotal) ptTotal = pt;
52306
52307 var dx = (pt.x < left) ? left - pt.x : (pt.x > right ? pt.x - right : 0);
52308 var dy = (pt.y < top) ? top - pt.y : (pt.y > bottom ? pt.y - bottom : 0);
52309 return Math.sqrt(dx * dx + dy * dy);
52310 }
52311
52312 var distToPlot = getDistToPlot(pMin);
52313 while(distToPlot) {
52314 pMin += distToPlot + buffer;
52315 if(pMin > pMax) return;
52316 distToPlot = getDistToPlot(pMin);
52317 }
52318
52319 distToPlot = getDistToPlot(pMax);
52320 while(distToPlot) {
52321 pMax -= distToPlot + buffer;
52322 if(pMin > pMax) return;
52323 distToPlot = getDistToPlot(pMax);
52324 }
52325
52326 return {
52327 min: pMin,
52328 max: pMax,
52329 len: pMax - pMin,
52330 total: pTotal,
52331 isClosed: pMin === 0 && pMax === pTotal &&
52332 Math.abs(pt0.x - ptTotal.x) < 0.1 &&
52333 Math.abs(pt0.y - ptTotal.y) < 0.1
52334 };
52335};
52336
52337/**
52338 * Find point on SVG path corresponding to a given constraint coordinate
52339 *
52340 * @param {SVGPathElement} path
52341 * @param {Number} val : constraint coordinate value
52342 * @param {String} coord : 'x' or 'y' the constraint coordinate
52343 * @param {Object} opts :
52344 * - {Number} pathLength : supply total path length before hand
52345 * - {Number} tolerance
52346 * - {Number} iterationLimit
52347 * @return {SVGPoint}
52348 */
52349exports.findPointOnPath = function findPointOnPath(path, val, coord, opts) {
52350 opts = opts || {};
52351
52352 var pathLength = opts.pathLength || path.getTotalLength();
52353 var tolerance = opts.tolerance || 1e-3;
52354 var iterationLimit = opts.iterationLimit || 30;
52355
52356 // if path starts at a val greater than the path tail (like on vertical violins),
52357 // we must flip the sign of the computed diff.
52358 var mul = path.getPointAtLength(0)[coord] > path.getPointAtLength(pathLength)[coord] ? -1 : 1;
52359
52360 var i = 0;
52361 var b0 = 0;
52362 var b1 = pathLength;
52363 var mid;
52364 var pt;
52365 var diff;
52366
52367 while(i < iterationLimit) {
52368 mid = (b0 + b1) / 2;
52369 pt = path.getPointAtLength(mid);
52370 diff = pt[coord] - val;
52371
52372 if(Math.abs(diff) < tolerance) {
52373 return pt;
52374 } else {
52375 if(mul * diff > 0) {
52376 b1 = mid;
52377 } else {
52378 b0 = mid;
52379 }
52380 i++;
52381 }
52382 }
52383 return pt;
52384};
52385
52386},{"./mod":294}],285:[function(_dereq_,module,exports){
52387'use strict';
52388
52389// Simple helper functions
52390// none of these need any external deps
52391
52392module.exports = function identity(d) { return d; };
52393
52394},{}],286:[function(_dereq_,module,exports){
52395'use strict';
52396
52397module.exports = function incrementNumeric(x, delta) {
52398 if(!delta) return x;
52399
52400 // Note 1:
52401 // 0.3 != 0.1 + 0.2 == 0.30000000000000004
52402 // but 0.3 == (10 * 0.1 + 10 * 0.2) / 10
52403 // Attempt to use integer steps to increment
52404 var scale = 1 / Math.abs(delta);
52405 var newX = (scale > 1) ? (
52406 scale * x +
52407 scale * delta
52408 ) / scale : x + delta;
52409
52410 // Note 2:
52411 // now we may also consider rounding to cover few more edge cases
52412 // e.g. 0.3 * 3 = 0.8999999999999999
52413 var lenX1 = String(newX).length;
52414 if(lenX1 > 16) {
52415 var lenDt = String(delta).length;
52416 var lenX0 = String(x).length;
52417
52418 if(lenX1 >= lenX0 + lenDt) { // likely a rounding error!
52419 var s = parseFloat(newX).toPrecision(12);
52420 if(s.indexOf('e+') === -1) newX = +s;
52421 }
52422 }
52423
52424 return newX;
52425};
52426
52427},{}],287:[function(_dereq_,module,exports){
52428'use strict';
52429
52430var d3 = _dereq_('@plotly/d3');
52431var utcFormat = _dereq_('d3-time-format').utcFormat;
52432var d3Format = _dereq_('d3-format').format;
52433var isNumeric = _dereq_('fast-isnumeric');
52434
52435var numConstants = _dereq_('../constants/numerical');
52436var MAX_SAFE = numConstants.FP_SAFE;
52437var MIN_SAFE = -MAX_SAFE;
52438var BADNUM = numConstants.BADNUM;
52439
52440var lib = module.exports = {};
52441
52442lib.adjustFormat = function adjustFormat(formatStr) {
52443 if(
52444 !formatStr ||
52445 /^\d[.]\df/.test(formatStr) ||
52446 /[.]\d%/.test(formatStr)
52447 ) return formatStr;
52448
52449 if(formatStr === '0.f') return '~f';
52450 if(/^\d%/.test(formatStr)) return '~%';
52451 if(/^\ds/.test(formatStr)) return '~s';
52452
52453 // try adding tilde to the start of format in order to trim
52454 if(!(/^[~,.0$]/.test(formatStr)) && /[&fps]/.test(formatStr)) return '~' + formatStr;
52455
52456 return formatStr;
52457};
52458
52459var seenBadFormats = {};
52460lib.warnBadFormat = function(f) {
52461 var key = String(f);
52462 if(!seenBadFormats[key]) {
52463 seenBadFormats[key] = 1;
52464 lib.warn('encountered bad format: "' + key + '"');
52465 }
52466};
52467
52468lib.noFormat = function(value) {
52469 return String(value);
52470};
52471
52472lib.numberFormat = function(formatStr) {
52473 var fn;
52474 try {
52475 fn = d3Format(lib.adjustFormat(formatStr));
52476 } catch(e) {
52477 lib.warnBadFormat(formatStr);
52478 return lib.noFormat;
52479 }
52480
52481 return fn;
52482};
52483
52484lib.nestedProperty = _dereq_('./nested_property');
52485lib.keyedContainer = _dereq_('./keyed_container');
52486lib.relativeAttr = _dereq_('./relative_attr');
52487lib.isPlainObject = _dereq_('./is_plain_object');
52488lib.toLogRange = _dereq_('./to_log_range');
52489lib.relinkPrivateKeys = _dereq_('./relink_private');
52490
52491var arrayModule = _dereq_('./array');
52492lib.isTypedArray = arrayModule.isTypedArray;
52493lib.isArrayOrTypedArray = arrayModule.isArrayOrTypedArray;
52494lib.isArray1D = arrayModule.isArray1D;
52495lib.ensureArray = arrayModule.ensureArray;
52496lib.concat = arrayModule.concat;
52497lib.maxRowLength = arrayModule.maxRowLength;
52498lib.minRowLength = arrayModule.minRowLength;
52499
52500var modModule = _dereq_('./mod');
52501lib.mod = modModule.mod;
52502lib.modHalf = modModule.modHalf;
52503
52504var coerceModule = _dereq_('./coerce');
52505lib.valObjectMeta = coerceModule.valObjectMeta;
52506lib.coerce = coerceModule.coerce;
52507lib.coerce2 = coerceModule.coerce2;
52508lib.coerceFont = coerceModule.coerceFont;
52509lib.coercePattern = coerceModule.coercePattern;
52510lib.coerceHoverinfo = coerceModule.coerceHoverinfo;
52511lib.coerceSelectionMarkerOpacity = coerceModule.coerceSelectionMarkerOpacity;
52512lib.validate = coerceModule.validate;
52513
52514var datesModule = _dereq_('./dates');
52515lib.dateTime2ms = datesModule.dateTime2ms;
52516lib.isDateTime = datesModule.isDateTime;
52517lib.ms2DateTime = datesModule.ms2DateTime;
52518lib.ms2DateTimeLocal = datesModule.ms2DateTimeLocal;
52519lib.cleanDate = datesModule.cleanDate;
52520lib.isJSDate = datesModule.isJSDate;
52521lib.formatDate = datesModule.formatDate;
52522lib.incrementMonth = datesModule.incrementMonth;
52523lib.dateTick0 = datesModule.dateTick0;
52524lib.dfltRange = datesModule.dfltRange;
52525lib.findExactDates = datesModule.findExactDates;
52526lib.MIN_MS = datesModule.MIN_MS;
52527lib.MAX_MS = datesModule.MAX_MS;
52528
52529var searchModule = _dereq_('./search');
52530lib.findBin = searchModule.findBin;
52531lib.sorterAsc = searchModule.sorterAsc;
52532lib.sorterDes = searchModule.sorterDes;
52533lib.distinctVals = searchModule.distinctVals;
52534lib.roundUp = searchModule.roundUp;
52535lib.sort = searchModule.sort;
52536lib.findIndexOfMin = searchModule.findIndexOfMin;
52537
52538lib.sortObjectKeys = _dereq_('./sort_object_keys');
52539
52540var statsModule = _dereq_('./stats');
52541lib.aggNums = statsModule.aggNums;
52542lib.len = statsModule.len;
52543lib.mean = statsModule.mean;
52544lib.median = statsModule.median;
52545lib.midRange = statsModule.midRange;
52546lib.variance = statsModule.variance;
52547lib.stdev = statsModule.stdev;
52548lib.interp = statsModule.interp;
52549
52550var matrixModule = _dereq_('./matrix');
52551lib.init2dArray = matrixModule.init2dArray;
52552lib.transposeRagged = matrixModule.transposeRagged;
52553lib.dot = matrixModule.dot;
52554lib.translationMatrix = matrixModule.translationMatrix;
52555lib.rotationMatrix = matrixModule.rotationMatrix;
52556lib.rotationXYMatrix = matrixModule.rotationXYMatrix;
52557lib.apply3DTransform = matrixModule.apply3DTransform;
52558lib.apply2DTransform = matrixModule.apply2DTransform;
52559lib.apply2DTransform2 = matrixModule.apply2DTransform2;
52560lib.convertCssMatrix = matrixModule.convertCssMatrix;
52561lib.inverseTransformMatrix = matrixModule.inverseTransformMatrix;
52562
52563var anglesModule = _dereq_('./angles');
52564lib.deg2rad = anglesModule.deg2rad;
52565lib.rad2deg = anglesModule.rad2deg;
52566lib.angleDelta = anglesModule.angleDelta;
52567lib.angleDist = anglesModule.angleDist;
52568lib.isFullCircle = anglesModule.isFullCircle;
52569lib.isAngleInsideSector = anglesModule.isAngleInsideSector;
52570lib.isPtInsideSector = anglesModule.isPtInsideSector;
52571lib.pathArc = anglesModule.pathArc;
52572lib.pathSector = anglesModule.pathSector;
52573lib.pathAnnulus = anglesModule.pathAnnulus;
52574
52575var anchorUtils = _dereq_('./anchor_utils');
52576lib.isLeftAnchor = anchorUtils.isLeftAnchor;
52577lib.isCenterAnchor = anchorUtils.isCenterAnchor;
52578lib.isRightAnchor = anchorUtils.isRightAnchor;
52579lib.isTopAnchor = anchorUtils.isTopAnchor;
52580lib.isMiddleAnchor = anchorUtils.isMiddleAnchor;
52581lib.isBottomAnchor = anchorUtils.isBottomAnchor;
52582
52583var geom2dModule = _dereq_('./geometry2d');
52584lib.segmentsIntersect = geom2dModule.segmentsIntersect;
52585lib.segmentDistance = geom2dModule.segmentDistance;
52586lib.getTextLocation = geom2dModule.getTextLocation;
52587lib.clearLocationCache = geom2dModule.clearLocationCache;
52588lib.getVisibleSegment = geom2dModule.getVisibleSegment;
52589lib.findPointOnPath = geom2dModule.findPointOnPath;
52590
52591var extendModule = _dereq_('./extend');
52592lib.extendFlat = extendModule.extendFlat;
52593lib.extendDeep = extendModule.extendDeep;
52594lib.extendDeepAll = extendModule.extendDeepAll;
52595lib.extendDeepNoArrays = extendModule.extendDeepNoArrays;
52596
52597var loggersModule = _dereq_('./loggers');
52598lib.log = loggersModule.log;
52599lib.warn = loggersModule.warn;
52600lib.error = loggersModule.error;
52601
52602var regexModule = _dereq_('./regex');
52603lib.counterRegex = regexModule.counter;
52604
52605var throttleModule = _dereq_('./throttle');
52606lib.throttle = throttleModule.throttle;
52607lib.throttleDone = throttleModule.done;
52608lib.clearThrottle = throttleModule.clear;
52609
52610var domModule = _dereq_('./dom');
52611lib.getGraphDiv = domModule.getGraphDiv;
52612lib.isPlotDiv = domModule.isPlotDiv;
52613lib.removeElement = domModule.removeElement;
52614lib.addStyleRule = domModule.addStyleRule;
52615lib.addRelatedStyleRule = domModule.addRelatedStyleRule;
52616lib.deleteRelatedStyleRule = domModule.deleteRelatedStyleRule;
52617lib.getFullTransformMatrix = domModule.getFullTransformMatrix;
52618lib.getElementTransformMatrix = domModule.getElementTransformMatrix;
52619lib.getElementAndAncestors = domModule.getElementAndAncestors;
52620lib.equalDomRects = domModule.equalDomRects;
52621
52622lib.clearResponsive = _dereq_('./clear_responsive');
52623lib.preserveDrawingBuffer = _dereq_('./preserve_drawing_buffer');
52624
52625lib.makeTraceGroups = _dereq_('./make_trace_groups');
52626
52627lib._ = _dereq_('./localize');
52628
52629lib.notifier = _dereq_('./notifier');
52630
52631lib.filterUnique = _dereq_('./filter_unique');
52632lib.filterVisible = _dereq_('./filter_visible');
52633lib.pushUnique = _dereq_('./push_unique');
52634
52635lib.increment = _dereq_('./increment');
52636
52637lib.cleanNumber = _dereq_('./clean_number');
52638
52639lib.ensureNumber = function ensureNumber(v) {
52640 if(!isNumeric(v)) return BADNUM;
52641 v = Number(v);
52642 return (v > MAX_SAFE || v < MIN_SAFE) ? BADNUM : v;
52643};
52644
52645/**
52646 * Is v a valid array index? Accepts numeric strings as well as numbers.
52647 *
52648 * @param {any} v: the value to test
52649 * @param {Optional[integer]} len: the array length we are indexing
52650 *
52651 * @return {bool}: v is a valid array index
52652 */
52653lib.isIndex = function(v, len) {
52654 if(len !== undefined && v >= len) return false;
52655 return isNumeric(v) && (v >= 0) && (v % 1 === 0);
52656};
52657
52658lib.noop = _dereq_('./noop');
52659lib.identity = _dereq_('./identity');
52660
52661/**
52662 * create an array of length 'cnt' filled with 'v' at all indices
52663 *
52664 * @param {any} v
52665 * @param {number} cnt
52666 * @return {array}
52667 */
52668lib.repeat = function(v, cnt) {
52669 var out = new Array(cnt);
52670 for(var i = 0; i < cnt; i++) {
52671 out[i] = v;
52672 }
52673 return out;
52674};
52675
52676/**
52677 * swap x and y of the same attribute in container cont
52678 * specify attr with a ? in place of x/y
52679 * you can also swap other things than x/y by providing part1 and part2
52680 */
52681lib.swapAttrs = function(cont, attrList, part1, part2) {
52682 if(!part1) part1 = 'x';
52683 if(!part2) part2 = 'y';
52684 for(var i = 0; i < attrList.length; i++) {
52685 var attr = attrList[i];
52686 var xp = lib.nestedProperty(cont, attr.replace('?', part1));
52687 var yp = lib.nestedProperty(cont, attr.replace('?', part2));
52688 var temp = xp.get();
52689 xp.set(yp.get());
52690 yp.set(temp);
52691 }
52692};
52693
52694/**
52695 * SVG painter's algo worked around with reinsertion
52696 */
52697lib.raiseToTop = function raiseToTop(elem) {
52698 elem.parentNode.appendChild(elem);
52699};
52700
52701/**
52702 * cancel a possibly pending transition; returned selection may be used by caller
52703 */
52704lib.cancelTransition = function(selection) {
52705 return selection.transition().duration(0);
52706};
52707
52708// constrain - restrict a number v to be between v0 and v1
52709lib.constrain = function(v, v0, v1) {
52710 if(v0 > v1) return Math.max(v1, Math.min(v0, v));
52711 return Math.max(v0, Math.min(v1, v));
52712};
52713
52714/**
52715 * do two bounding boxes from getBoundingClientRect,
52716 * ie {left,right,top,bottom,width,height}, overlap?
52717 * takes optional padding pixels
52718 */
52719lib.bBoxIntersect = function(a, b, pad) {
52720 pad = pad || 0;
52721 return (a.left <= b.right + pad &&
52722 b.left <= a.right + pad &&
52723 a.top <= b.bottom + pad &&
52724 b.top <= a.bottom + pad);
52725};
52726
52727/*
52728 * simpleMap: alternative to Array.map that only
52729 * passes on the element and up to 2 extra args you
52730 * provide (but not the array index or the whole array)
52731 *
52732 * array: the array to map it to
52733 * func: the function to apply
52734 * x1, x2: optional extra args
52735 */
52736lib.simpleMap = function(array, func, x1, x2, opts) {
52737 var len = array.length;
52738 var out = new Array(len);
52739 for(var i = 0; i < len; i++) out[i] = func(array[i], x1, x2, opts);
52740 return out;
52741};
52742
52743/**
52744 * Random string generator
52745 *
52746 * @param {object} existing
52747 * pass in strings to avoid as keys with truthy values
52748 * @param {int} bits
52749 * bits of information in the output string, default 24
52750 * @param {int} base
52751 * base of string representation, default 16. Should be a power of 2.
52752 */
52753lib.randstr = function randstr(existing, bits, base, _recursion) {
52754 if(!base) base = 16;
52755 if(bits === undefined) bits = 24;
52756 if(bits <= 0) return '0';
52757
52758 var digits = Math.log(Math.pow(2, bits)) / Math.log(base);
52759 var res = '';
52760 var i, b, x;
52761
52762 for(i = 2; digits === Infinity; i *= 2) {
52763 digits = Math.log(Math.pow(2, bits / i)) / Math.log(base) * i;
52764 }
52765
52766 var rem = digits - Math.floor(digits);
52767
52768 for(i = 0; i < Math.floor(digits); i++) {
52769 x = Math.floor(Math.random() * base).toString(base);
52770 res = x + res;
52771 }
52772
52773 if(rem) {
52774 b = Math.pow(base, rem);
52775 x = Math.floor(Math.random() * b).toString(base);
52776 res = x + res;
52777 }
52778
52779 var parsed = parseInt(res, base);
52780 if((existing && existing[res]) ||
52781 (parsed !== Infinity && parsed >= Math.pow(2, bits))) {
52782 if(_recursion > 10) {
52783 lib.warn('randstr failed uniqueness');
52784 return res;
52785 }
52786 return randstr(existing, bits, base, (_recursion || 0) + 1);
52787 } else return res;
52788};
52789
52790lib.OptionControl = function(opt, optname) {
52791 /*
52792 * An environment to contain all option setters and
52793 * getters that collectively modify opts.
52794 *
52795 * You can call up opts from any function in new object
52796 * as this.optname || this.opt
52797 *
52798 * See FitOpts for example of usage
52799 */
52800 if(!opt) opt = {};
52801 if(!optname) optname = 'opt';
52802
52803 var self = {};
52804 self.optionList = [];
52805
52806 self._newoption = function(optObj) {
52807 optObj[optname] = opt;
52808 self[optObj.name] = optObj;
52809 self.optionList.push(optObj);
52810 };
52811
52812 self['_' + optname] = opt;
52813 return self;
52814};
52815
52816/**
52817 * lib.smooth: smooth arrayIn by convolving with
52818 * a hann window with given full width at half max
52819 * bounce the ends in, so the output has the same length as the input
52820 */
52821lib.smooth = function(arrayIn, FWHM) {
52822 FWHM = Math.round(FWHM) || 0; // only makes sense for integers
52823 if(FWHM < 2) return arrayIn;
52824
52825 var alen = arrayIn.length;
52826 var alen2 = 2 * alen;
52827 var wlen = 2 * FWHM - 1;
52828 var w = new Array(wlen);
52829 var arrayOut = new Array(alen);
52830 var i;
52831 var j;
52832 var k;
52833 var v;
52834
52835 // first make the window array
52836 for(i = 0; i < wlen; i++) {
52837 w[i] = (1 - Math.cos(Math.PI * (i + 1) / FWHM)) / (2 * FWHM);
52838 }
52839
52840 // now do the convolution
52841 for(i = 0; i < alen; i++) {
52842 v = 0;
52843 for(j = 0; j < wlen; j++) {
52844 k = i + j + 1 - FWHM;
52845
52846 // multibounce
52847 if(k < -alen) k -= alen2 * Math.round(k / alen2);
52848 else if(k >= alen2) k -= alen2 * Math.floor(k / alen2);
52849
52850 // single bounce
52851 if(k < 0) k = - 1 - k;
52852 else if(k >= alen) k = alen2 - 1 - k;
52853
52854 v += arrayIn[k] * w[j];
52855 }
52856 arrayOut[i] = v;
52857 }
52858
52859 return arrayOut;
52860};
52861
52862/**
52863 * syncOrAsync: run a sequence of functions synchronously
52864 * as long as its returns are not promises (ie have no .then)
52865 * includes one argument arg to send to all functions...
52866 * this is mainly just to prevent us having to make wrapper functions
52867 * when the only purpose of the wrapper is to reference gd
52868 * and a final step to be executed at the end
52869 * TODO: if there's an error and everything is sync,
52870 * this doesn't happen yet because we want to make sure
52871 * that it gets reported
52872 */
52873lib.syncOrAsync = function(sequence, arg, finalStep) {
52874 var ret, fni;
52875
52876 function continueAsync() {
52877 return lib.syncOrAsync(sequence, arg, finalStep);
52878 }
52879
52880 while(sequence.length) {
52881 fni = sequence.splice(0, 1)[0];
52882 ret = fni(arg);
52883
52884 if(ret && ret.then) {
52885 return ret.then(continueAsync);
52886 }
52887 }
52888
52889 return finalStep && finalStep(arg);
52890};
52891
52892
52893/**
52894 * Helper to strip trailing slash, from
52895 * http://stackoverflow.com/questions/6680825/return-string-without-trailing-slash
52896 */
52897lib.stripTrailingSlash = function(str) {
52898 if(str.substr(-1) === '/') return str.substr(0, str.length - 1);
52899 return str;
52900};
52901
52902lib.noneOrAll = function(containerIn, containerOut, attrList) {
52903 /**
52904 * some attributes come together, so if you have one of them
52905 * in the input, you should copy the default values of the others
52906 * to the input as well.
52907 */
52908 if(!containerIn) return;
52909
52910 var hasAny = false;
52911 var hasAll = true;
52912 var i;
52913 var val;
52914
52915 for(i = 0; i < attrList.length; i++) {
52916 val = containerIn[attrList[i]];
52917 if(val !== undefined && val !== null) hasAny = true;
52918 else hasAll = false;
52919 }
52920
52921 if(hasAny && !hasAll) {
52922 for(i = 0; i < attrList.length; i++) {
52923 containerIn[attrList[i]] = containerOut[attrList[i]];
52924 }
52925 }
52926};
52927
52928/** merges calcdata field (given by cdAttr) with traceAttr values
52929 *
52930 * N.B. Loop over minimum of cd.length and traceAttr.length
52931 * i.e. it does not try to fill in beyond traceAttr.length-1
52932 *
52933 * @param {array} traceAttr : trace attribute
52934 * @param {object} cd : calcdata trace
52935 * @param {string} cdAttr : calcdata key
52936 */
52937lib.mergeArray = function(traceAttr, cd, cdAttr, fn) {
52938 var hasFn = typeof fn === 'function';
52939 if(lib.isArrayOrTypedArray(traceAttr)) {
52940 var imax = Math.min(traceAttr.length, cd.length);
52941 for(var i = 0; i < imax; i++) {
52942 var v = traceAttr[i];
52943 cd[i][cdAttr] = hasFn ? fn(v) : v;
52944 }
52945 }
52946};
52947
52948// cast numbers to positive numbers, returns 0 if not greater than 0
52949lib.mergeArrayCastPositive = function(traceAttr, cd, cdAttr) {
52950 return lib.mergeArray(traceAttr, cd, cdAttr, function(v) {
52951 var w = +v;
52952 return !isFinite(w) ? 0 : w > 0 ? w : 0;
52953 });
52954};
52955
52956/** fills calcdata field (given by cdAttr) with traceAttr values
52957 * or function of traceAttr values (e.g. some fallback)
52958 *
52959 * N.B. Loops over all cd items.
52960 *
52961 * @param {array} traceAttr : trace attribute
52962 * @param {object} cd : calcdata trace
52963 * @param {string} cdAttr : calcdata key
52964 * @param {function} [fn] : optional function to apply to each array item
52965 */
52966lib.fillArray = function(traceAttr, cd, cdAttr, fn) {
52967 fn = fn || lib.identity;
52968
52969 if(lib.isArrayOrTypedArray(traceAttr)) {
52970 for(var i = 0; i < cd.length; i++) {
52971 cd[i][cdAttr] = fn(traceAttr[i]);
52972 }
52973 }
52974};
52975
52976/** Handler for trace-wide vs per-point options
52977 *
52978 * @param {object} trace : (full) trace object
52979 * @param {number} ptNumber : index of the point in question
52980 * @param {string} astr : attribute string
52981 * @param {function} [fn] : optional function to apply to each array item
52982 *
52983 * @return {any}
52984 */
52985lib.castOption = function(trace, ptNumber, astr, fn) {
52986 fn = fn || lib.identity;
52987
52988 var val = lib.nestedProperty(trace, astr).get();
52989
52990 if(lib.isArrayOrTypedArray(val)) {
52991 if(Array.isArray(ptNumber) && lib.isArrayOrTypedArray(val[ptNumber[0]])) {
52992 return fn(val[ptNumber[0]][ptNumber[1]]);
52993 } else {
52994 return fn(val[ptNumber]);
52995 }
52996 } else {
52997 return val;
52998 }
52999};
53000
53001/** Extract option from calcdata item, correctly falling back to
53002 * trace value if not found.
53003 *
53004 * @param {object} calcPt : calcdata[i][j] item
53005 * @param {object} trace : (full) trace object
53006 * @param {string} calcKey : calcdata key
53007 * @param {string} traceKey : aka trace attribute string
53008 * @return {any}
53009 */
53010lib.extractOption = function(calcPt, trace, calcKey, traceKey) {
53011 if(calcKey in calcPt) return calcPt[calcKey];
53012
53013 // fallback to trace value,
53014 // must check if value isn't itself an array
53015 // which means the trace attribute has a corresponding
53016 // calcdata key, but its value is falsy
53017 var traceVal = lib.nestedProperty(trace, traceKey).get();
53018 if(!Array.isArray(traceVal)) return traceVal;
53019};
53020
53021function makePtIndex2PtNumber(indexToPoints) {
53022 var ptIndex2ptNumber = {};
53023 for(var k in indexToPoints) {
53024 var pts = indexToPoints[k];
53025 for(var j = 0; j < pts.length; j++) {
53026 ptIndex2ptNumber[pts[j]] = +k;
53027 }
53028 }
53029 return ptIndex2ptNumber;
53030}
53031
53032/** Tag selected calcdata items
53033 *
53034 * N.B. note that point 'index' corresponds to input data array index
53035 * whereas 'number' is its post-transform version.
53036 *
53037 * @param {array} calcTrace
53038 * @param {object} trace
53039 * - selectedpoints {array}
53040 * - _indexToPoints {object}
53041 * @param {ptNumber2cdIndex} ptNumber2cdIndex (optional)
53042 * optional map object for trace types that do not have 1-to-1 point number to
53043 * calcdata item index correspondence (e.g. histogram)
53044 */
53045lib.tagSelected = function(calcTrace, trace, ptNumber2cdIndex) {
53046 var selectedpoints = trace.selectedpoints;
53047 var indexToPoints = trace._indexToPoints;
53048 var ptIndex2ptNumber;
53049
53050 // make pt index-to-number map object, which takes care of transformed traces
53051 if(indexToPoints) {
53052 ptIndex2ptNumber = makePtIndex2PtNumber(indexToPoints);
53053 }
53054
53055 function isCdIndexValid(v) {
53056 return v !== undefined && v < calcTrace.length;
53057 }
53058
53059 for(var i = 0; i < selectedpoints.length; i++) {
53060 var ptIndex = selectedpoints[i];
53061
53062 if(lib.isIndex(ptIndex) ||
53063 (lib.isArrayOrTypedArray(ptIndex) && lib.isIndex(ptIndex[0]) && lib.isIndex(ptIndex[1]))
53064 ) {
53065 var ptNumber = ptIndex2ptNumber ? ptIndex2ptNumber[ptIndex] : ptIndex;
53066 var cdIndex = ptNumber2cdIndex ? ptNumber2cdIndex[ptNumber] : ptNumber;
53067
53068 if(isCdIndexValid(cdIndex)) {
53069 calcTrace[cdIndex].selected = 1;
53070 }
53071 }
53072 }
53073};
53074
53075lib.selIndices2selPoints = function(trace) {
53076 var selectedpoints = trace.selectedpoints;
53077 var indexToPoints = trace._indexToPoints;
53078
53079 if(indexToPoints) {
53080 var ptIndex2ptNumber = makePtIndex2PtNumber(indexToPoints);
53081 var out = [];
53082
53083 for(var i = 0; i < selectedpoints.length; i++) {
53084 var ptIndex = selectedpoints[i];
53085 if(lib.isIndex(ptIndex)) {
53086 var ptNumber = ptIndex2ptNumber[ptIndex];
53087 if(lib.isIndex(ptNumber)) {
53088 out.push(ptNumber);
53089 }
53090 }
53091 }
53092
53093 return out;
53094 } else {
53095 return selectedpoints;
53096 }
53097};
53098
53099/** Returns target as set by 'target' transform attribute
53100 *
53101 * @param {object} trace : full trace object
53102 * @param {object} transformOpts : transform option object
53103 * - target (string} :
53104 * either an attribute string referencing an array in the trace object, or
53105 * a set array.
53106 *
53107 * @return {array or false} : the target array (NOT a copy!!) or false if invalid
53108 */
53109lib.getTargetArray = function(trace, transformOpts) {
53110 var target = transformOpts.target;
53111
53112 if(typeof target === 'string' && target) {
53113 var array = lib.nestedProperty(trace, target).get();
53114 return Array.isArray(array) ? array : false;
53115 } else if(Array.isArray(target)) {
53116 return target;
53117 }
53118
53119 return false;
53120};
53121
53122/**
53123 * modified version of jQuery's extend to strip out private objs and functions,
53124 * and cut arrays down to first <arraylen> or 1 elements
53125 * because extend-like algorithms are hella slow
53126 * obj2 is assumed to already be clean of these things (including no arrays)
53127 */
53128lib.minExtend = function(obj1, obj2) {
53129 var objOut = {};
53130 if(typeof obj2 !== 'object') obj2 = {};
53131 var arrayLen = 3;
53132 var keys = Object.keys(obj1);
53133 var i, k, v;
53134
53135 for(i = 0; i < keys.length; i++) {
53136 k = keys[i];
53137 v = obj1[k];
53138 if(k.charAt(0) === '_' || typeof v === 'function') continue;
53139 else if(k === 'module') objOut[k] = v;
53140 else if(Array.isArray(v)) {
53141 if(k === 'colorscale') {
53142 objOut[k] = v.slice();
53143 } else {
53144 objOut[k] = v.slice(0, arrayLen);
53145 }
53146 } else if(lib.isTypedArray(v)) {
53147 objOut[k] = v.subarray(0, arrayLen);
53148 } else if(v && (typeof v === 'object')) objOut[k] = lib.minExtend(obj1[k], obj2[k]);
53149 else objOut[k] = v;
53150 }
53151
53152 keys = Object.keys(obj2);
53153 for(i = 0; i < keys.length; i++) {
53154 k = keys[i];
53155 v = obj2[k];
53156 if(typeof v !== 'object' || !(k in objOut) || typeof objOut[k] !== 'object') {
53157 objOut[k] = v;
53158 }
53159 }
53160
53161 return objOut;
53162};
53163
53164lib.titleCase = function(s) {
53165 return s.charAt(0).toUpperCase() + s.substr(1);
53166};
53167
53168lib.containsAny = function(s, fragments) {
53169 for(var i = 0; i < fragments.length; i++) {
53170 if(s.indexOf(fragments[i]) !== -1) return true;
53171 }
53172 return false;
53173};
53174
53175lib.isIE = function() {
53176 return typeof window.navigator.msSaveBlob !== 'undefined';
53177};
53178
53179var IS_SAFARI_REGEX = /Version\/[\d\.]+.*Safari/;
53180lib.isSafari = function() {
53181 return IS_SAFARI_REGEX.test(window.navigator.userAgent);
53182};
53183
53184var IS_IOS_REGEX = /iPad|iPhone|iPod/;
53185lib.isIOS = function() {
53186 return IS_IOS_REGEX.test(window.navigator.userAgent);
53187};
53188
53189var FIREFOX_VERSION_REGEX = /Firefox\/(\d+)\.\d+/;
53190lib.getFirefoxVersion = function() {
53191 var match = FIREFOX_VERSION_REGEX.exec(window.navigator.userAgent);
53192 if(match && match.length === 2) {
53193 var versionInt = parseInt(match[1]);
53194 if(!isNaN(versionInt)) {
53195 return versionInt;
53196 }
53197 }
53198 return null;
53199};
53200
53201lib.isD3Selection = function(obj) {
53202 return obj instanceof d3.selection;
53203};
53204
53205/**
53206 * Append element to DOM only if not present.
53207 *
53208 * @param {d3 selection} parent : parent selection of the element in question
53209 * @param {string} nodeType : node type of element to append
53210 * @param {string} className (optional) : class name of element in question
53211 * @param {fn} enterFn (optional) : optional fn applied to entering elements only
53212 * @return {d3 selection} selection of new layer
53213 *
53214 * Previously, we were using the following pattern:
53215 *
53216 * ```
53217 * var sel = parent.selectAll('.' + className)
53218 * .data([0]);
53219 *
53220 * sel.enter().append(nodeType)
53221 * .classed(className, true);
53222 *
53223 * return sel;
53224 * ```
53225 *
53226 * in numerous places in our codebase to achieve the same behavior.
53227 *
53228 * The logic below performs much better, mostly as we are using
53229 * `.select` instead `.selectAll` that is `querySelector` instead of
53230 * `querySelectorAll`.
53231 *
53232 */
53233lib.ensureSingle = function(parent, nodeType, className, enterFn) {
53234 var sel = parent.select(nodeType + (className ? '.' + className : ''));
53235 if(sel.size()) return sel;
53236
53237 var layer = parent.append(nodeType);
53238 if(className) layer.classed(className, true);
53239 if(enterFn) layer.call(enterFn);
53240
53241 return layer;
53242};
53243
53244/**
53245 * Same as Lib.ensureSingle, but using id as selector.
53246 * This version is mostly used for clipPath nodes.
53247 *
53248 * @param {d3 selection} parent : parent selection of the element in question
53249 * @param {string} nodeType : node type of element to append
53250 * @param {string} id : id of element in question
53251 * @param {fn} enterFn (optional) : optional fn applied to entering elements only
53252 * @return {d3 selection} selection of new layer
53253 */
53254lib.ensureSingleById = function(parent, nodeType, id, enterFn) {
53255 var sel = parent.select(nodeType + '#' + id);
53256 if(sel.size()) return sel;
53257
53258 var layer = parent.append(nodeType).attr('id', id);
53259 if(enterFn) layer.call(enterFn);
53260
53261 return layer;
53262};
53263
53264/**
53265 * Converts a string path to an object.
53266 *
53267 * When given a string containing an array element, it will create a `null`
53268 * filled array of the given size.
53269 *
53270 * @example
53271 * lib.objectFromPath('nested.test[2].path', 'value');
53272 * // returns { nested: { test: [null, null, { path: 'value' }]}
53273 *
53274 * @param {string} path to nested value
53275 * @param {*} any value to be set
53276 *
53277 * @return {Object} the constructed object with a full nested path
53278 */
53279lib.objectFromPath = function(path, value) {
53280 var keys = path.split('.');
53281 var tmpObj;
53282 var obj = tmpObj = {};
53283
53284 for(var i = 0; i < keys.length; i++) {
53285 var key = keys[i];
53286 var el = null;
53287
53288 var parts = keys[i].match(/(.*)\[([0-9]+)\]/);
53289
53290 if(parts) {
53291 key = parts[1];
53292 el = parts[2];
53293
53294 tmpObj = tmpObj[key] = [];
53295
53296 if(i === keys.length - 1) {
53297 tmpObj[el] = value;
53298 } else {
53299 tmpObj[el] = {};
53300 }
53301
53302 tmpObj = tmpObj[el];
53303 } else {
53304 if(i === keys.length - 1) {
53305 tmpObj[key] = value;
53306 } else {
53307 tmpObj[key] = {};
53308 }
53309
53310 tmpObj = tmpObj[key];
53311 }
53312 }
53313
53314 return obj;
53315};
53316
53317/**
53318 * Iterate through an object in-place, converting dotted properties to objects.
53319 *
53320 * Examples:
53321 *
53322 * lib.expandObjectPaths({'nested.test.path': 'value'});
53323 * => { nested: { test: {path: 'value'}}}
53324 *
53325 * It also handles array notation, e.g.:
53326 *
53327 * lib.expandObjectPaths({'foo[1].bar': 'value'});
53328 * => { foo: [null, {bar: value}] }
53329 *
53330 * It handles merges the results when two properties are specified in parallel:
53331 *
53332 * lib.expandObjectPaths({'foo[1].bar': 10, 'foo[0].bar': 20});
53333 * => { foo: [{bar: 10}, {bar: 20}] }
53334 *
53335 * It does NOT, however, merge multiple multiply-nested arrays::
53336 *
53337 * lib.expandObjectPaths({'marker[1].range[1]': 5, 'marker[1].range[0]': 4})
53338 * => { marker: [null, {range: 4}] }
53339 */
53340
53341// Store this to avoid recompiling regex on *every* prop since this may happen many
53342// many times for animations. Could maybe be inside the function. Not sure about
53343// scoping vs. recompilation tradeoff, but at least it's not just inlining it into
53344// the inner loop.
53345var dottedPropertyRegex = /^([^\[\.]+)\.(.+)?/;
53346var indexedPropertyRegex = /^([^\.]+)\[([0-9]+)\](\.)?(.+)?/;
53347
53348lib.expandObjectPaths = function(data) {
53349 var match, key, prop, datum, idx, dest, trailingPath;
53350 if(typeof data === 'object' && !Array.isArray(data)) {
53351 for(key in data) {
53352 if(data.hasOwnProperty(key)) {
53353 if((match = key.match(dottedPropertyRegex))) {
53354 datum = data[key];
53355 prop = match[1];
53356
53357 delete data[key];
53358
53359 data[prop] = lib.extendDeepNoArrays(data[prop] || {}, lib.objectFromPath(key, lib.expandObjectPaths(datum))[prop]);
53360 } else if((match = key.match(indexedPropertyRegex))) {
53361 datum = data[key];
53362
53363 prop = match[1];
53364 idx = parseInt(match[2]);
53365
53366 delete data[key];
53367
53368 data[prop] = data[prop] || [];
53369
53370 if(match[3] === '.') {
53371 // This is the case where theere are subsequent properties into which
53372 // we must recurse, e.g. transforms[0].value
53373 trailingPath = match[4];
53374 dest = data[prop][idx] = data[prop][idx] || {};
53375
53376 // NB: Extend deep no arrays prevents this from working on multiple
53377 // nested properties in the same object, e.g.
53378 //
53379 // {
53380 // foo[0].bar[1].range
53381 // foo[0].bar[0].range
53382 // }
53383 //
53384 // In this case, the extendDeepNoArrays will overwrite one array with
53385 // the other, so that both properties *will not* be present in the
53386 // result. Fixing this would require a more intelligent tracking
53387 // of changes and merging than extendDeepNoArrays currently accomplishes.
53388 lib.extendDeepNoArrays(dest, lib.objectFromPath(trailingPath, lib.expandObjectPaths(datum)));
53389 } else {
53390 // This is the case where this property is the end of the line,
53391 // e.g. xaxis.range[0]
53392 data[prop][idx] = lib.expandObjectPaths(datum);
53393 }
53394 } else {
53395 data[key] = lib.expandObjectPaths(data[key]);
53396 }
53397 }
53398 }
53399 }
53400
53401 return data;
53402};
53403
53404/**
53405 * Converts value to string separated by the provided separators.
53406 *
53407 * @example
53408 * lib.numSeparate(2016, '.,');
53409 * // returns '2016'
53410 *
53411 * @example
53412 * lib.numSeparate(3000, '.,', true);
53413 * // returns '3,000'
53414 *
53415 * @example
53416 * lib.numSeparate(1234.56, '|,')
53417 * // returns '1,234|56'
53418 *
53419 * @param {string|number} value the value to be converted
53420 * @param {string} separators string of decimal, then thousands separators
53421 * @param {boolean} separatethousands boolean, 4-digit integers are separated if true
53422 *
53423 * @return {string} the value that has been separated
53424 */
53425lib.numSeparate = function(value, separators, separatethousands) {
53426 if(!separatethousands) separatethousands = false;
53427
53428 if(typeof separators !== 'string' || separators.length === 0) {
53429 throw new Error('Separator string required for formatting!');
53430 }
53431
53432 if(typeof value === 'number') {
53433 value = String(value);
53434 }
53435
53436 var thousandsRe = /(\d+)(\d{3})/;
53437 var decimalSep = separators.charAt(0);
53438 var thouSep = separators.charAt(1);
53439
53440 var x = value.split('.');
53441 var x1 = x[0];
53442 var x2 = x.length > 1 ? decimalSep + x[1] : '';
53443
53444 // Years are ignored for thousands separators
53445 if(thouSep && (x.length > 1 || x1.length > 4 || separatethousands)) {
53446 while(thousandsRe.test(x1)) {
53447 x1 = x1.replace(thousandsRe, '$1' + thouSep + '$2');
53448 }
53449 }
53450
53451 return x1 + x2;
53452};
53453
53454lib.TEMPLATE_STRING_REGEX = /%{([^\s%{}:]*)([:|\|][^}]*)?}/g;
53455var SIMPLE_PROPERTY_REGEX = /^\w*$/;
53456
53457/**
53458 * Substitute values from an object into a string
53459 *
53460 * Examples:
53461 * Lib.templateString('name: %{trace}', {trace: 'asdf'}) --> 'name: asdf'
53462 * Lib.templateString('name: %{trace[0].name}', {trace: [{name: 'asdf'}]}) --> 'name: asdf'
53463 *
53464 * @param {string} input string containing %{...} template strings
53465 * @param {obj} data object containing substitution values
53466 *
53467 * @return {string} templated string
53468 */
53469lib.templateString = function(string, obj) {
53470 // Not all that useful, but cache nestedProperty instantiation
53471 // just in case it speeds things up *slightly*:
53472 var getterCache = {};
53473
53474 return string.replace(lib.TEMPLATE_STRING_REGEX, function(dummy, key) {
53475 var v;
53476 if(SIMPLE_PROPERTY_REGEX.test(key)) {
53477 v = obj[key];
53478 } else {
53479 getterCache[key] = getterCache[key] || lib.nestedProperty(obj, key).get;
53480 v = getterCache[key]();
53481 }
53482 return lib.isValidTextValue(v) ? v : '';
53483 });
53484};
53485
53486var hovertemplateWarnings = {
53487 max: 10,
53488 count: 0,
53489 name: 'hovertemplate'
53490};
53491lib.hovertemplateString = function() {
53492 return templateFormatString.apply(hovertemplateWarnings, arguments);
53493};
53494
53495var texttemplateWarnings = {
53496 max: 10,
53497 count: 0,
53498 name: 'texttemplate'
53499};
53500lib.texttemplateString = function() {
53501 return templateFormatString.apply(texttemplateWarnings, arguments);
53502};
53503
53504var TEMPLATE_STRING_FORMAT_SEPARATOR = /^[:|\|]/;
53505/**
53506 * Substitute values from an object into a string and optionally formats them using d3-format,
53507 * or fallback to associated labels.
53508 *
53509 * Examples:
53510 * Lib.hovertemplateString('name: %{trace}', {trace: 'asdf'}) --> 'name: asdf'
53511 * Lib.hovertemplateString('name: %{trace[0].name}', {trace: [{name: 'asdf'}]}) --> 'name: asdf'
53512 * Lib.hovertemplateString('price: %{y:$.2f}', {y: 1}) --> 'price: $1.00'
53513 *
53514 * @param {string} input string containing %{...:...} template strings
53515 * @param {obj} data object containing fallback text when no formatting is specified, ex.: {yLabel: 'formattedYValue'}
53516 * @param {obj} d3 locale
53517 * @param {obj} data objects containing substitution values
53518 *
53519 * @return {string} templated string
53520 */
53521function templateFormatString(string, labels, d3locale) {
53522 var opts = this;
53523 var args = arguments;
53524 if(!labels) labels = {};
53525 // Not all that useful, but cache nestedProperty instantiation
53526 // just in case it speeds things up *slightly*:
53527 var getterCache = {};
53528
53529 return string.replace(lib.TEMPLATE_STRING_REGEX, function(match, rawKey, format) {
53530 var isOther =
53531 rawKey === 'xother' ||
53532 rawKey === 'yother';
53533
53534 var isSpaceOther =
53535 rawKey === '_xother' ||
53536 rawKey === '_yother';
53537
53538 var isSpaceOtherSpace =
53539 rawKey === '_xother_' ||
53540 rawKey === '_yother_';
53541
53542 var isOtherSpace =
53543 rawKey === 'xother_' ||
53544 rawKey === 'yother_';
53545
53546 var hasOther = isOther || isSpaceOther || isOtherSpace || isSpaceOtherSpace;
53547
53548 var key = rawKey;
53549 if(isSpaceOther || isSpaceOtherSpace) key = key.substring(1);
53550 if(isOtherSpace || isSpaceOtherSpace) key = key.substring(0, key.length - 1);
53551
53552 var value;
53553 if(hasOther) {
53554 value = labels[key];
53555 if(value === undefined) return '';
53556 } else {
53557 var obj, i;
53558 for(i = 3; i < args.length; i++) {
53559 obj = args[i];
53560 if(!obj) continue;
53561 if(obj.hasOwnProperty(key)) {
53562 value = obj[key];
53563 break;
53564 }
53565
53566 if(!SIMPLE_PROPERTY_REGEX.test(key)) {
53567 value = lib.nestedProperty(obj, key).get();
53568 value = getterCache[key] || lib.nestedProperty(obj, key).get();
53569 if(value) getterCache[key] = value;
53570 }
53571 if(value !== undefined) break;
53572 }
53573 }
53574
53575 if(value === undefined && opts) {
53576 if(opts.count < opts.max) {
53577 lib.warn('Variable \'' + key + '\' in ' + opts.name + ' could not be found!');
53578 value = match;
53579 }
53580
53581 if(opts.count === opts.max) {
53582 lib.warn('Too many ' + opts.name + ' warnings - additional warnings will be suppressed');
53583 }
53584 opts.count++;
53585
53586 return match;
53587 }
53588
53589 if(format) {
53590 var fmt;
53591 if(format[0] === ':') {
53592 fmt = d3locale ? d3locale.numberFormat : lib.numberFormat;
53593 value = fmt(format.replace(TEMPLATE_STRING_FORMAT_SEPARATOR, ''))(value);
53594 }
53595
53596 if(format[0] === '|') {
53597 fmt = d3locale ? d3locale.timeFormat : utcFormat;
53598 var ms = lib.dateTime2ms(value);
53599 value = lib.formatDate(ms, format.replace(TEMPLATE_STRING_FORMAT_SEPARATOR, ''), false, fmt);
53600 }
53601 } else {
53602 var keyLabel = key + 'Label';
53603 if(labels.hasOwnProperty(keyLabel)) value = labels[keyLabel];
53604 }
53605
53606 if(hasOther) {
53607 value = '(' + value + ')';
53608 if(isSpaceOther || isSpaceOtherSpace) value = ' ' + value;
53609 if(isOtherSpace || isSpaceOtherSpace) value = value + ' ';
53610 }
53611
53612 return value;
53613 });
53614}
53615
53616/*
53617 * alphanumeric string sort, tailored for subplot IDs like scene2, scene10, x10y13 etc
53618 */
53619var char0 = 48;
53620var char9 = 57;
53621lib.subplotSort = function(a, b) {
53622 var l = Math.min(a.length, b.length) + 1;
53623 var numA = 0;
53624 var numB = 0;
53625 for(var i = 0; i < l; i++) {
53626 var charA = a.charCodeAt(i) || 0;
53627 var charB = b.charCodeAt(i) || 0;
53628 var isNumA = charA >= char0 && charA <= char9;
53629 var isNumB = charB >= char0 && charB <= char9;
53630
53631 if(isNumA) numA = 10 * numA + charA - char0;
53632 if(isNumB) numB = 10 * numB + charB - char0;
53633
53634 if(!isNumA || !isNumB) {
53635 if(numA !== numB) return numA - numB;
53636 if(charA !== charB) return charA - charB;
53637 }
53638 }
53639 return numB - numA;
53640};
53641
53642// repeatable pseudorandom generator
53643var randSeed = 2000000000;
53644
53645lib.seedPseudoRandom = function() {
53646 randSeed = 2000000000;
53647};
53648
53649lib.pseudoRandom = function() {
53650 var lastVal = randSeed;
53651 randSeed = (69069 * randSeed + 1) % 4294967296;
53652 // don't let consecutive vals be too close together
53653 // gets away from really trying to be random, in favor of better local uniformity
53654 if(Math.abs(randSeed - lastVal) < 429496729) return lib.pseudoRandom();
53655 return randSeed / 4294967296;
53656};
53657
53658
53659/** Fill hover 'pointData' container with 'correct' hover text value
53660 *
53661 * - If trace hoverinfo contains a 'text' flag and hovertext is not set,
53662 * the text elements will be seen in the hover labels.
53663 *
53664 * - If trace hoverinfo contains a 'text' flag and hovertext is set,
53665 * hovertext takes precedence over text
53666 * i.e. the hoverinfo elements will be seen in the hover labels
53667 *
53668 * @param {object} calcPt
53669 * @param {object} trace
53670 * @param {object || array} contOut (mutated here)
53671 */
53672lib.fillText = function(calcPt, trace, contOut) {
53673 var fill = Array.isArray(contOut) ?
53674 function(v) { contOut.push(v); } :
53675 function(v) { contOut.text = v; };
53676
53677 var htx = lib.extractOption(calcPt, trace, 'htx', 'hovertext');
53678 if(lib.isValidTextValue(htx)) return fill(htx);
53679
53680 var tx = lib.extractOption(calcPt, trace, 'tx', 'text');
53681 if(lib.isValidTextValue(tx)) return fill(tx);
53682};
53683
53684// accept all truthy values and 0 (which gets cast to '0' in the hover labels)
53685lib.isValidTextValue = function(v) {
53686 return v || v === 0;
53687};
53688
53689/**
53690 * @param {number} ratio
53691 * @param {number} n (number of decimal places)
53692 */
53693lib.formatPercent = function(ratio, n) {
53694 n = n || 0;
53695 var str = (Math.round(100 * ratio * Math.pow(10, n)) * Math.pow(0.1, n)).toFixed(n) + '%';
53696 for(var i = 0; i < n; i++) {
53697 if(str.indexOf('.') !== -1) {
53698 str = str.replace('0%', '%');
53699 str = str.replace('.%', '%');
53700 }
53701 }
53702 return str;
53703};
53704
53705lib.isHidden = function(gd) {
53706 var display = window.getComputedStyle(gd).display;
53707 return !display || display === 'none';
53708};
53709
53710lib.strTranslate = function(x, y) {
53711 return (x || y) ? 'translate(' + x + ',' + y + ')' : '';
53712};
53713
53714lib.strRotate = function(a) {
53715 return a ? 'rotate(' + a + ')' : '';
53716};
53717
53718lib.strScale = function(s) {
53719 return s !== 1 ? 'scale(' + s + ')' : '';
53720};
53721
53722/** Return transform text for bar bar-like rectangles and pie-like slices
53723 * @param {object} transform
53724 * - targetX: desired position on the x-axis
53725 * - targetY: desired position on the y-axis
53726 * - textX: text middle position on the x-axis
53727 * - textY: text middle position on the y-axis
53728 * - anchorX: (optional) text anchor position on the x-axis (computed from textX), zero for middle anchor
53729 * - anchorY: (optional) text anchor position on the y-axis (computed from textY), zero for middle anchor
53730 * - scale: (optional) scale applied after translate
53731 * - rotate: (optional) rotation applied after scale
53732 * - noCenter: when defined no extra arguments needed in rotation
53733 */
53734lib.getTextTransform = function(transform) {
53735 var noCenter = transform.noCenter;
53736 var textX = transform.textX;
53737 var textY = transform.textY;
53738 var targetX = transform.targetX;
53739 var targetY = transform.targetY;
53740 var anchorX = transform.anchorX || 0;
53741 var anchorY = transform.anchorY || 0;
53742 var rotate = transform.rotate;
53743 var scale = transform.scale;
53744 if(!scale) scale = 0;
53745 else if(scale > 1) scale = 1;
53746
53747 return (
53748 lib.strTranslate(
53749 targetX - scale * (textX + anchorX),
53750 targetY - scale * (textY + anchorY)
53751 ) +
53752 lib.strScale(scale) +
53753 (rotate ?
53754 'rotate(' + rotate +
53755 (noCenter ? '' : ' ' + textX + ' ' + textY) +
53756 ')' : ''
53757 )
53758 );
53759};
53760
53761lib.ensureUniformFontSize = function(gd, baseFont) {
53762 var out = lib.extendFlat({}, baseFont);
53763 out.size = Math.max(
53764 baseFont.size,
53765 gd._fullLayout.uniformtext.minsize || 0
53766 );
53767 return out;
53768};
53769
53770/**
53771 * provide a human-readable list e.g. "A, B, C and D" with an ending separator
53772 *
53773 * @param {array} arr : the array to join
53774 * @param {string} mainSeparator : main separator
53775 * @param {string} lastSeparator : last separator
53776 *
53777 * @return {string} : joined list
53778 */
53779lib.join2 = function(arr, mainSeparator, lastSeparator) {
53780 var len = arr.length;
53781 if(len > 1) {
53782 return arr.slice(0, -1).join(mainSeparator) + lastSeparator + arr[len - 1];
53783 }
53784 return arr.join(mainSeparator);
53785};
53786
53787lib.bigFont = function(size) {
53788 return Math.round(1.2 * size);
53789};
53790
53791var firefoxVersion = lib.getFirefoxVersion();
53792// see https://bugzilla.mozilla.org/show_bug.cgi?id=1684973
53793var isProblematicFirefox = firefoxVersion !== null && firefoxVersion < 86;
53794
53795/**
53796 * Return the mouse position from the last event registered by D3.
53797 * @returns An array with two numbers, representing the x and y coordinates of the mouse pointer
53798 * at the event relative to the targeted node.
53799 */
53800lib.getPositionFromD3Event = function() {
53801 if(isProblematicFirefox) {
53802 // layerX and layerY are non-standard, so we only fallback to them when we have to:
53803 return [
53804 d3.event.layerX,
53805 d3.event.layerY
53806 ];
53807 } else {
53808 return [
53809 d3.event.offsetX,
53810 d3.event.offsetY
53811 ];
53812 }
53813};
53814
53815},{"../constants/numerical":267,"./anchor_utils":271,"./angles":272,"./array":273,"./clean_number":274,"./clear_responsive":276,"./coerce":277,"./dates":278,"./dom":279,"./extend":281,"./filter_unique":282,"./filter_visible":283,"./geometry2d":284,"./identity":285,"./increment":286,"./is_plain_object":288,"./keyed_container":289,"./localize":290,"./loggers":291,"./make_trace_groups":292,"./matrix":293,"./mod":294,"./nested_property":295,"./noop":296,"./notifier":297,"./preserve_drawing_buffer":300,"./push_unique":301,"./regex":303,"./relative_attr":304,"./relink_private":305,"./search":306,"./sort_object_keys":308,"./stats":309,"./throttle":311,"./to_log_range":312,"@plotly/d3":20,"d3-format":29,"d3-time-format":30,"fast-isnumeric":33}],288:[function(_dereq_,module,exports){
53816'use strict';
53817
53818// more info: http://stackoverflow.com/questions/18531624/isplainobject-thing
53819module.exports = function isPlainObject(obj) {
53820 // We need to be a little less strict in the `imagetest` container because
53821 // of how async image requests are handled.
53822 //
53823 // N.B. isPlainObject(new Constructor()) will return true in `imagetest`
53824 if(window && window.process && window.process.versions) {
53825 return Object.prototype.toString.call(obj) === '[object Object]';
53826 }
53827
53828 return (
53829 Object.prototype.toString.call(obj) === '[object Object]' &&
53830 Object.getPrototypeOf(obj).hasOwnProperty('hasOwnProperty')
53831 );
53832};
53833
53834},{}],289:[function(_dereq_,module,exports){
53835'use strict';
53836
53837var nestedProperty = _dereq_('./nested_property');
53838
53839var SIMPLE_PROPERTY_REGEX = /^\w*$/;
53840
53841// bitmask for deciding what's updated. Sometimes the name needs to be updated,
53842// sometimes the value needs to be updated, and sometimes both do. This is just
53843// a simple way to track what's updated such that it's a simple OR operation to
53844// assimilate new updates.
53845//
53846// The only exception is the UNSET bit that tracks when we need to explicitly
53847// unset and remove the property. This concrn arises because of the special
53848// way in which nestedProperty handles null/undefined. When you specify `null`,
53849// it prunes any unused items in the tree. I ran into some issues with it getting
53850// null vs undefined confused, so UNSET is just a bit that forces the property
53851// update to send `null`, removing the property explicitly rather than setting
53852// it to undefined.
53853var NONE = 0;
53854var NAME = 1;
53855var VALUE = 2;
53856var BOTH = 3;
53857var UNSET = 4;
53858
53859module.exports = function keyedContainer(baseObj, path, keyName, valueName) {
53860 keyName = keyName || 'name';
53861 valueName = valueName || 'value';
53862 var i, arr, baseProp;
53863 var changeTypes = {};
53864
53865 if(path && path.length) {
53866 baseProp = nestedProperty(baseObj, path);
53867 arr = baseProp.get();
53868 } else {
53869 arr = baseObj;
53870 }
53871
53872 path = path || '';
53873
53874 // Construct an index:
53875 var indexLookup = {};
53876 if(arr) {
53877 for(i = 0; i < arr.length; i++) {
53878 indexLookup[arr[i][keyName]] = i;
53879 }
53880 }
53881
53882 var isSimpleValueProp = SIMPLE_PROPERTY_REGEX.test(valueName);
53883
53884 var obj = {
53885 set: function(name, value) {
53886 var changeType = value === null ? UNSET : NONE;
53887
53888 // create the base array if necessary
53889 if(!arr) {
53890 if(!baseProp || changeType === UNSET) return;
53891
53892 arr = [];
53893 baseProp.set(arr);
53894 }
53895
53896 var idx = indexLookup[name];
53897 if(idx === undefined) {
53898 if(changeType === UNSET) return;
53899
53900 changeType = changeType | BOTH;
53901 idx = arr.length;
53902 indexLookup[name] = idx;
53903 } else if(value !== (isSimpleValueProp ? arr[idx][valueName] : nestedProperty(arr[idx], valueName).get())) {
53904 changeType = changeType | VALUE;
53905 }
53906
53907 var newValue = arr[idx] = arr[idx] || {};
53908 newValue[keyName] = name;
53909
53910 if(isSimpleValueProp) {
53911 newValue[valueName] = value;
53912 } else {
53913 nestedProperty(newValue, valueName).set(value);
53914 }
53915
53916 // If it's not an unset, force that bit to be unset. This is all related to the fact
53917 // that undefined and null are a bit specially implemented in nestedProperties.
53918 if(value !== null) {
53919 changeType = changeType & ~UNSET;
53920 }
53921
53922 changeTypes[idx] = changeTypes[idx] | changeType;
53923
53924 return obj;
53925 },
53926 get: function(name) {
53927 if(!arr) return;
53928
53929 var idx = indexLookup[name];
53930
53931 if(idx === undefined) {
53932 return undefined;
53933 } else if(isSimpleValueProp) {
53934 return arr[idx][valueName];
53935 } else {
53936 return nestedProperty(arr[idx], valueName).get();
53937 }
53938 },
53939 rename: function(name, newName) {
53940 var idx = indexLookup[name];
53941
53942 if(idx === undefined) return obj;
53943 changeTypes[idx] = changeTypes[idx] | NAME;
53944
53945 indexLookup[newName] = idx;
53946 delete indexLookup[name];
53947
53948 arr[idx][keyName] = newName;
53949
53950 return obj;
53951 },
53952 remove: function(name) {
53953 var idx = indexLookup[name];
53954
53955 if(idx === undefined) return obj;
53956
53957 var object = arr[idx];
53958 if(Object.keys(object).length > 2) {
53959 // This object contains more than just the key/value, so unset
53960 // the value without modifying the entry otherwise:
53961 changeTypes[idx] = changeTypes[idx] | VALUE;
53962 return obj.set(name, null);
53963 }
53964
53965 if(isSimpleValueProp) {
53966 for(i = idx; i < arr.length; i++) {
53967 changeTypes[i] = changeTypes[i] | BOTH;
53968 }
53969 for(i = idx; i < arr.length; i++) {
53970 indexLookup[arr[i][keyName]]--;
53971 }
53972 arr.splice(idx, 1);
53973 delete(indexLookup[name]);
53974 } else {
53975 // Perform this update *strictly* so we can check whether the result's
53976 // been pruned. If so, it's a removal. If not, it's a value unset only.
53977 nestedProperty(object, valueName).set(null);
53978
53979 // Now check if the top level nested property has any keys left. If so,
53980 // the object still has values so we only want to unset the key. If not,
53981 // the entire object can be removed since there's no other data.
53982 // var topLevelKeys = Object.keys(object[valueName.split('.')[0]] || []);
53983
53984 changeTypes[idx] = changeTypes[idx] | VALUE | UNSET;
53985 }
53986
53987 return obj;
53988 },
53989 constructUpdate: function() {
53990 var astr, idx;
53991 var update = {};
53992 var changed = Object.keys(changeTypes);
53993 for(var i = 0; i < changed.length; i++) {
53994 idx = changed[i];
53995 astr = path + '[' + idx + ']';
53996 if(arr[idx]) {
53997 if(changeTypes[idx] & NAME) {
53998 update[astr + '.' + keyName] = arr[idx][keyName];
53999 }
54000 if(changeTypes[idx] & VALUE) {
54001 if(isSimpleValueProp) {
54002 update[astr + '.' + valueName] = (changeTypes[idx] & UNSET) ? null : arr[idx][valueName];
54003 } else {
54004 update[astr + '.' + valueName] = (changeTypes[idx] & UNSET) ? null : nestedProperty(arr[idx], valueName).get();
54005 }
54006 }
54007 } else {
54008 update[astr] = null;
54009 }
54010 }
54011
54012 return update;
54013 }
54014 };
54015
54016 return obj;
54017};
54018
54019},{"./nested_property":295}],290:[function(_dereq_,module,exports){
54020'use strict';
54021
54022var Registry = _dereq_('../registry');
54023
54024/**
54025 * localize: translate a string for the current locale
54026 *
54027 * @param {object} gd: the graphDiv for context
54028 * gd._context.locale determines the language (& optional region/country)
54029 * the dictionary for each locale may either be supplied in
54030 * gd._context.locales or globally via Plotly.register
54031 * @param {string} s: the string to translate
54032 */
54033module.exports = function localize(gd, s) {
54034 var locale = gd._context.locale;
54035
54036 /*
54037 * Priority of lookup:
54038 * contextDicts[locale],
54039 * registeredDicts[locale],
54040 * contextDicts[baseLocale], (if baseLocale is distinct)
54041 * registeredDicts[baseLocale]
54042 * Return the first translation we find.
54043 * This way if you have a regionalization you are allowed to specify
54044 * only what's different from the base locale, everything else will
54045 * fall back on the base.
54046 */
54047 for(var i = 0; i < 2; i++) {
54048 var locales = gd._context.locales;
54049 for(var j = 0; j < 2; j++) {
54050 var dict = (locales[locale] || {}).dictionary;
54051 if(dict) {
54052 var out = dict[s];
54053 if(out) return out;
54054 }
54055 locales = Registry.localeRegistry;
54056 }
54057
54058 var baseLocale = locale.split('-')[0];
54059 if(baseLocale === locale) break;
54060 locale = baseLocale;
54061 }
54062
54063 return s;
54064};
54065
54066},{"../registry":376}],291:[function(_dereq_,module,exports){
54067'use strict';
54068
54069/* eslint-disable no-console */
54070
54071var dfltConfig = _dereq_('../plot_api/plot_config').dfltConfig;
54072
54073var notifier = _dereq_('./notifier');
54074
54075var loggers = module.exports = {};
54076
54077/**
54078 * ------------------------------------------
54079 * debugging tools
54080 * ------------------------------------------
54081 */
54082
54083loggers.log = function() {
54084 var i;
54085
54086 if(dfltConfig.logging > 1) {
54087 var messages = ['LOG:'];
54088 for(i = 0; i < arguments.length; i++) {
54089 messages.push(arguments[i]);
54090 }
54091 console.trace.apply(console, messages);
54092 }
54093
54094 if(dfltConfig.notifyOnLogging > 1) {
54095 var lines = [];
54096 for(i = 0; i < arguments.length; i++) {
54097 lines.push(arguments[i]);
54098 }
54099 notifier(lines.join('<br>'), 'long');
54100 }
54101};
54102
54103loggers.warn = function() {
54104 var i;
54105
54106 if(dfltConfig.logging > 0) {
54107 var messages = ['WARN:'];
54108 for(i = 0; i < arguments.length; i++) {
54109 messages.push(arguments[i]);
54110 }
54111 console.trace.apply(console, messages);
54112 }
54113
54114 if(dfltConfig.notifyOnLogging > 0) {
54115 var lines = [];
54116 for(i = 0; i < arguments.length; i++) {
54117 lines.push(arguments[i]);
54118 }
54119 notifier(lines.join('<br>'), 'stick');
54120 }
54121};
54122
54123loggers.error = function() {
54124 var i;
54125
54126 if(dfltConfig.logging > 0) {
54127 var messages = ['ERROR:'];
54128 for(i = 0; i < arguments.length; i++) {
54129 messages.push(arguments[i]);
54130 }
54131 console.error.apply(console, messages);
54132 }
54133
54134 if(dfltConfig.notifyOnLogging > 0) {
54135 var lines = [];
54136 for(i = 0; i < arguments.length; i++) {
54137 lines.push(arguments[i]);
54138 }
54139 notifier(lines.join('<br>'), 'stick');
54140 }
54141};
54142
54143},{"../plot_api/plot_config":321,"./notifier":297}],292:[function(_dereq_,module,exports){
54144'use strict';
54145
54146var d3 = _dereq_('@plotly/d3');
54147
54148/**
54149 * General helper to manage trace groups based on calcdata
54150 *
54151 * @param {d3.selection} traceLayer: a selection containing a single group
54152 * to draw these traces into
54153 * @param {array} cdModule: array of calcdata items for this
54154 * module and subplot combination. Assumes the calcdata item for each
54155 * trace is an array with the fullData trace attached to the first item.
54156 * @param {string} cls: the class attribute to give each trace group
54157 * so you can give multiple classes separated by spaces
54158 */
54159module.exports = function makeTraceGroups(traceLayer, cdModule, cls) {
54160 var traces = traceLayer.selectAll('g.' + cls.replace(/\s/g, '.'))
54161 .data(cdModule, function(cd) { return cd[0].trace.uid; });
54162
54163 traces.exit().remove();
54164
54165 traces.enter().append('g')
54166 .attr('class', cls);
54167
54168 traces.order();
54169
54170 // stash ref node to trace group in calcdata,
54171 // useful for (fast) styleOnSelect
54172 var k = traceLayer.classed('rangeplot') ? 'nodeRangePlot3' : 'node3';
54173 traces.each(function(cd) { cd[0][k] = d3.select(this); });
54174
54175 return traces;
54176};
54177
54178},{"@plotly/d3":20}],293:[function(_dereq_,module,exports){
54179'use strict';
54180
54181var mat4X4 = _dereq_('gl-mat4');
54182
54183exports.init2dArray = function(rowLength, colLength) {
54184 var array = new Array(rowLength);
54185 for(var i = 0; i < rowLength; i++) array[i] = new Array(colLength);
54186 return array;
54187};
54188
54189/**
54190 * transpose a (possibly ragged) 2d array z. inspired by
54191 * http://stackoverflow.com/questions/17428587/
54192 * transposing-a-2d-array-in-javascript
54193 */
54194exports.transposeRagged = function(z) {
54195 var maxlen = 0;
54196 var zlen = z.length;
54197 var i, j;
54198 // Maximum row length:
54199 for(i = 0; i < zlen; i++) maxlen = Math.max(maxlen, z[i].length);
54200
54201 var t = new Array(maxlen);
54202 for(i = 0; i < maxlen; i++) {
54203 t[i] = new Array(zlen);
54204 for(j = 0; j < zlen; j++) t[i][j] = z[j][i];
54205 }
54206
54207 return t;
54208};
54209
54210// our own dot function so that we don't need to include numeric
54211exports.dot = function(x, y) {
54212 if(!(x.length && y.length) || x.length !== y.length) return null;
54213
54214 var len = x.length;
54215 var out;
54216 var i;
54217
54218 if(x[0].length) {
54219 // mat-vec or mat-mat
54220 out = new Array(len);
54221 for(i = 0; i < len; i++) out[i] = exports.dot(x[i], y);
54222 } else if(y[0].length) {
54223 // vec-mat
54224 var yTranspose = exports.transposeRagged(y);
54225 out = new Array(yTranspose.length);
54226 for(i = 0; i < yTranspose.length; i++) out[i] = exports.dot(x, yTranspose[i]);
54227 } else {
54228 // vec-vec
54229 out = 0;
54230 for(i = 0; i < len; i++) out += x[i] * y[i];
54231 }
54232
54233 return out;
54234};
54235
54236// translate by (x,y)
54237exports.translationMatrix = function(x, y) {
54238 return [[1, 0, x], [0, 1, y], [0, 0, 1]];
54239};
54240
54241// rotate by alpha around (0,0)
54242exports.rotationMatrix = function(alpha) {
54243 var a = alpha * Math.PI / 180;
54244 return [[Math.cos(a), -Math.sin(a), 0],
54245 [Math.sin(a), Math.cos(a), 0],
54246 [0, 0, 1]];
54247};
54248
54249// rotate by alpha around (x,y)
54250exports.rotationXYMatrix = function(a, x, y) {
54251 return exports.dot(
54252 exports.dot(exports.translationMatrix(x, y),
54253 exports.rotationMatrix(a)),
54254 exports.translationMatrix(-x, -y));
54255};
54256
54257// applies a 3D transformation matrix to either x, y and z params
54258// Note: z is optional
54259exports.apply3DTransform = function(transform) {
54260 return function() {
54261 var args = arguments;
54262 var xyz = arguments.length === 1 ? args[0] : [args[0], args[1], args[2] || 0];
54263 return exports.dot(transform, [xyz[0], xyz[1], xyz[2], 1]).slice(0, 3);
54264 };
54265};
54266
54267// applies a 2D transformation matrix to either x and y params or an [x,y] array
54268exports.apply2DTransform = function(transform) {
54269 return function() {
54270 var args = arguments;
54271 if(args.length === 3) {
54272 args = args[0];
54273 } // from map
54274 var xy = arguments.length === 1 ? args[0] : [args[0], args[1]];
54275 return exports.dot(transform, [xy[0], xy[1], 1]).slice(0, 2);
54276 };
54277};
54278
54279// applies a 2D transformation matrix to an [x1,y1,x2,y2] array (to transform a segment)
54280exports.apply2DTransform2 = function(transform) {
54281 var at = exports.apply2DTransform(transform);
54282 return function(xys) {
54283 return at(xys.slice(0, 2)).concat(at(xys.slice(2, 4)));
54284 };
54285};
54286
54287exports.convertCssMatrix = function(m) {
54288 if(m) {
54289 var len = m.length;
54290 if(len === 16) return m;
54291 if(len === 6) {
54292 // converts a 2x3 css transform matrix to a 4x4 matrix see https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/matrix
54293 return [
54294 m[0], m[1], 0, 0,
54295 m[2], m[3], 0, 0,
54296 0, 0, 1, 0,
54297 m[4], m[5], 0, 1
54298 ];
54299 }
54300 }
54301 return [
54302 1, 0, 0, 0,
54303 0, 1, 0, 0,
54304 0, 0, 1, 0,
54305 0, 0, 0, 1
54306 ];
54307};
54308
54309// find the inverse for a 4x4 affine transform matrix
54310exports.inverseTransformMatrix = function(m) {
54311 var out = [];
54312 mat4X4.invert(out, m);
54313 return [
54314 [out[0], out[1], out[2], out[3]],
54315 [out[4], out[5], out[6], out[7]],
54316 [out[8], out[9], out[10], out[11]],
54317 [out[12], out[13], out[14], out[15]]
54318 ];
54319};
54320
54321},{"gl-mat4":49}],294:[function(_dereq_,module,exports){
54322'use strict';
54323
54324/**
54325 * sanitized modulus function that always returns in the range [0, d)
54326 * rather than (-d, 0] if v is negative
54327 */
54328function mod(v, d) {
54329 var out = v % d;
54330 return out < 0 ? out + d : out;
54331}
54332
54333/**
54334 * sanitized modulus function that always returns in the range [-d/2, d/2]
54335 * rather than (-d, 0] if v is negative
54336 */
54337function modHalf(v, d) {
54338 return Math.abs(v) > (d / 2) ?
54339 v - Math.round(v / d) * d :
54340 v;
54341}
54342
54343module.exports = {
54344 mod: mod,
54345 modHalf: modHalf
54346};
54347
54348},{}],295:[function(_dereq_,module,exports){
54349'use strict';
54350
54351var isNumeric = _dereq_('fast-isnumeric');
54352var isArrayOrTypedArray = _dereq_('./array').isArrayOrTypedArray;
54353
54354/**
54355 * convert a string s (such as 'xaxis.range[0]')
54356 * representing a property of nested object into set and get methods
54357 * also return the string and object so we don't have to keep track of them
54358 * allows [-1] for an array index, to set a property inside all elements
54359 * of an array
54360 * eg if obj = {arr: [{a: 1}, {a: 2}]}
54361 * you can do p = nestedProperty(obj, 'arr[-1].a')
54362 * but you cannot set the array itself this way, to do that
54363 * just set the whole array.
54364 * eg if obj = {arr: [1, 2, 3]}
54365 * you can't do nestedProperty(obj, 'arr[-1]').set(5)
54366 * but you can do nestedProperty(obj, 'arr').set([5, 5, 5])
54367 */
54368module.exports = function nestedProperty(container, propStr) {
54369 if(isNumeric(propStr)) propStr = String(propStr);
54370 else if(typeof propStr !== 'string' ||
54371 propStr.substr(propStr.length - 4) === '[-1]') {
54372 throw 'bad property string';
54373 }
54374
54375 var j = 0;
54376 var propParts = propStr.split('.');
54377 var indexed;
54378 var indices;
54379 var i;
54380
54381 // check for parts of the nesting hierarchy that are numbers (ie array elements)
54382 while(j < propParts.length) {
54383 // look for non-bracket chars, then any number of [##] blocks
54384 indexed = String(propParts[j]).match(/^([^\[\]]*)((\[\-?[0-9]*\])+)$/);
54385 if(indexed) {
54386 if(indexed[1]) propParts[j] = indexed[1];
54387 // allow propStr to start with bracketed array indices
54388 else if(j === 0) propParts.splice(0, 1);
54389 else throw 'bad property string';
54390
54391 indices = indexed[2]
54392 .substr(1, indexed[2].length - 2)
54393 .split('][');
54394
54395 for(i = 0; i < indices.length; i++) {
54396 j++;
54397 propParts.splice(j, 0, Number(indices[i]));
54398 }
54399 }
54400 j++;
54401 }
54402
54403 if(typeof container !== 'object') {
54404 return badContainer(container, propStr, propParts);
54405 }
54406
54407 return {
54408 set: npSet(container, propParts, propStr),
54409 get: npGet(container, propParts),
54410 astr: propStr,
54411 parts: propParts,
54412 obj: container
54413 };
54414};
54415
54416function npGet(cont, parts) {
54417 return function() {
54418 var curCont = cont;
54419 var curPart;
54420 var allSame;
54421 var out;
54422 var i;
54423 var j;
54424
54425 for(i = 0; i < parts.length - 1; i++) {
54426 curPart = parts[i];
54427 if(curPart === -1) {
54428 allSame = true;
54429 out = [];
54430 for(j = 0; j < curCont.length; j++) {
54431 out[j] = npGet(curCont[j], parts.slice(i + 1))();
54432 if(out[j] !== out[0]) allSame = false;
54433 }
54434 return allSame ? out[0] : out;
54435 }
54436 if(typeof curPart === 'number' && !isArrayOrTypedArray(curCont)) {
54437 return undefined;
54438 }
54439 curCont = curCont[curPart];
54440 if(typeof curCont !== 'object' || curCont === null) {
54441 return undefined;
54442 }
54443 }
54444
54445 // only hit this if parts.length === 1
54446 if(typeof curCont !== 'object' || curCont === null) return undefined;
54447
54448 out = curCont[parts[i]];
54449 if(out === null) return undefined;
54450 return out;
54451 };
54452}
54453
54454/*
54455 * Can this value be deleted? We can delete `undefined`, and `null` except INSIDE an
54456 * *args* array.
54457 *
54458 * Previously we also deleted some `{}` and `[]`, in order to try and make set/unset
54459 * a net noop; but this causes far more complication than it's worth, and still had
54460 * lots of exceptions. See https://github.com/plotly/plotly.js/issues/1410
54461 *
54462 * *args* arrays get passed directly to API methods and we should respect null if
54463 * the user put it there, but otherwise null is deleted as we use it as code
54464 * in restyle/relayout/update for "delete this value" whereas undefined means
54465 * "ignore this edit"
54466 */
54467var ARGS_PATTERN = /(^|\.)args\[/;
54468function isDeletable(val, propStr) {
54469 return (val === undefined) || (val === null && !propStr.match(ARGS_PATTERN));
54470}
54471
54472function npSet(cont, parts, propStr) {
54473 return function(val) {
54474 var curCont = cont;
54475 var propPart = '';
54476 var containerLevels = [[cont, propPart]];
54477 var toDelete = isDeletable(val, propStr);
54478 var curPart;
54479 var i;
54480
54481 for(i = 0; i < parts.length - 1; i++) {
54482 curPart = parts[i];
54483
54484 if(typeof curPart === 'number' && !isArrayOrTypedArray(curCont)) {
54485 throw 'array index but container is not an array';
54486 }
54487
54488 // handle special -1 array index
54489 if(curPart === -1) {
54490 toDelete = !setArrayAll(curCont, parts.slice(i + 1), val, propStr);
54491 if(toDelete) break;
54492 else return;
54493 }
54494
54495 if(!checkNewContainer(curCont, curPart, parts[i + 1], toDelete)) {
54496 break;
54497 }
54498
54499 curCont = curCont[curPart];
54500
54501 if(typeof curCont !== 'object' || curCont === null) {
54502 throw 'container is not an object';
54503 }
54504
54505 propPart = joinPropStr(propPart, curPart);
54506
54507 containerLevels.push([curCont, propPart]);
54508 }
54509
54510 if(toDelete) {
54511 if(i === parts.length - 1) {
54512 delete curCont[parts[i]];
54513
54514 // The one bit of pruning we still do: drop `undefined` from the end of arrays.
54515 // In case someone has already unset previous items, continue until we hit a
54516 // non-undefined value.
54517 if(Array.isArray(curCont) && +parts[i] === curCont.length - 1) {
54518 while(curCont.length && curCont[curCont.length - 1] === undefined) {
54519 curCont.pop();
54520 }
54521 }
54522 }
54523 } else curCont[parts[i]] = val;
54524 };
54525}
54526
54527function joinPropStr(propStr, newPart) {
54528 var toAdd = newPart;
54529 if(isNumeric(newPart)) toAdd = '[' + newPart + ']';
54530 else if(propStr) toAdd = '.' + newPart;
54531
54532 return propStr + toAdd;
54533}
54534
54535// handle special -1 array index
54536function setArrayAll(containerArray, innerParts, val, propStr) {
54537 var arrayVal = isArrayOrTypedArray(val);
54538 var allSet = true;
54539 var thisVal = val;
54540 var thisPropStr = propStr.replace('-1', 0);
54541 var deleteThis = arrayVal ? false : isDeletable(val, thisPropStr);
54542 var firstPart = innerParts[0];
54543 var i;
54544
54545 for(i = 0; i < containerArray.length; i++) {
54546 thisPropStr = propStr.replace('-1', i);
54547 if(arrayVal) {
54548 thisVal = val[i % val.length];
54549 deleteThis = isDeletable(thisVal, thisPropStr);
54550 }
54551 if(deleteThis) allSet = false;
54552 if(!checkNewContainer(containerArray, i, firstPart, deleteThis)) {
54553 continue;
54554 }
54555 npSet(containerArray[i], innerParts, propStr.replace('-1', i))(thisVal);
54556 }
54557 return allSet;
54558}
54559
54560/**
54561 * make new sub-container as needed.
54562 * returns false if there's no container and none is needed
54563 * because we're only deleting an attribute
54564 */
54565function checkNewContainer(container, part, nextPart, toDelete) {
54566 if(container[part] === undefined) {
54567 if(toDelete) return false;
54568
54569 if(typeof nextPart === 'number') container[part] = [];
54570 else container[part] = {};
54571 }
54572 return true;
54573}
54574
54575function badContainer(container, propStr, propParts) {
54576 return {
54577 set: function() { throw 'bad container'; },
54578 get: function() {},
54579 astr: propStr,
54580 parts: propParts,
54581 obj: container
54582 };
54583}
54584
54585},{"./array":273,"fast-isnumeric":33}],296:[function(_dereq_,module,exports){
54586'use strict';
54587
54588// Simple helper functions
54589// none of these need any external deps
54590
54591module.exports = function noop() {};
54592
54593},{}],297:[function(_dereq_,module,exports){
54594'use strict';
54595
54596var d3 = _dereq_('@plotly/d3');
54597var isNumeric = _dereq_('fast-isnumeric');
54598
54599var NOTEDATA = [];
54600
54601/**
54602 * notifier
54603 * @param {String} text The person's user name
54604 * @param {Number} [delay=1000] The delay time in milliseconds
54605 * or 'long' which provides 2000 ms delay time.
54606 * @return {undefined} this function does not return a value
54607 */
54608module.exports = function(text, displayLength) {
54609 if(NOTEDATA.indexOf(text) !== -1) return;
54610
54611 NOTEDATA.push(text);
54612
54613 var ts = 1000;
54614 if(isNumeric(displayLength)) ts = displayLength;
54615 else if(displayLength === 'long') ts = 3000;
54616
54617 var notifierContainer = d3.select('body')
54618 .selectAll('.plotly-notifier')
54619 .data([0]);
54620 notifierContainer.enter()
54621 .append('div')
54622 .classed('plotly-notifier', true);
54623
54624 var notes = notifierContainer.selectAll('.notifier-note').data(NOTEDATA);
54625
54626 function killNote(transition) {
54627 transition
54628 .duration(700)
54629 .style('opacity', 0)
54630 .each('end', function(thisText) {
54631 var thisIndex = NOTEDATA.indexOf(thisText);
54632 if(thisIndex !== -1) NOTEDATA.splice(thisIndex, 1);
54633 d3.select(this).remove();
54634 });
54635 }
54636
54637 notes.enter().append('div')
54638 .classed('notifier-note', true)
54639 .style('opacity', 0)
54640 .each(function(thisText) {
54641 var note = d3.select(this);
54642
54643 note.append('button')
54644 .classed('notifier-close', true)
54645 .html('&times;')
54646 .on('click', function() {
54647 note.transition().call(killNote);
54648 });
54649
54650 var p = note.append('p');
54651 var lines = thisText.split(/<br\s*\/?>/g);
54652 for(var i = 0; i < lines.length; i++) {
54653 if(i) p.append('br');
54654 p.append('span').text(lines[i]);
54655 }
54656
54657 if(displayLength === 'stick') {
54658 note.transition()
54659 .duration(350)
54660 .style('opacity', 1);
54661 } else {
54662 note.transition()
54663 .duration(700)
54664 .style('opacity', 1)
54665 .transition()
54666 .delay(ts)
54667 .call(killNote);
54668 }
54669 });
54670};
54671
54672},{"@plotly/d3":20,"fast-isnumeric":33}],298:[function(_dereq_,module,exports){
54673'use strict';
54674
54675var setCursor = _dereq_('./setcursor');
54676
54677var STASHATTR = 'data-savedcursor';
54678var NO_CURSOR = '!!';
54679
54680/*
54681 * works with our CSS cursor classes (see css/_cursor.scss)
54682 * to override a previous cursor set on d3 single-element selections,
54683 * by moving the name of the original cursor to the data-savedcursor attr.
54684 * omit cursor to revert to the previously set value.
54685 */
54686module.exports = function overrideCursor(el3, csr) {
54687 var savedCursor = el3.attr(STASHATTR);
54688 if(csr) {
54689 if(!savedCursor) {
54690 var classes = (el3.attr('class') || '').split(' ');
54691 for(var i = 0; i < classes.length; i++) {
54692 var cls = classes[i];
54693 if(cls.indexOf('cursor-') === 0) {
54694 el3.attr(STASHATTR, cls.substr(7))
54695 .classed(cls, false);
54696 }
54697 }
54698 if(!el3.attr(STASHATTR)) {
54699 el3.attr(STASHATTR, NO_CURSOR);
54700 }
54701 }
54702 setCursor(el3, csr);
54703 } else if(savedCursor) {
54704 el3.attr(STASHATTR, null);
54705
54706 if(savedCursor === NO_CURSOR) setCursor(el3);
54707 else setCursor(el3, savedCursor);
54708 }
54709};
54710
54711},{"./setcursor":307}],299:[function(_dereq_,module,exports){
54712'use strict';
54713
54714var dot = _dereq_('./matrix').dot;
54715var BADNUM = _dereq_('../constants/numerical').BADNUM;
54716
54717var polygon = module.exports = {};
54718
54719/**
54720 * Turn an array of [x, y] pairs into a polygon object
54721 * that can test if points are inside it
54722 *
54723 * @param ptsIn Array of [x, y] pairs
54724 *
54725 * @returns polygon Object {xmin, xmax, ymin, ymax, pts, contains}
54726 * (x|y)(min|max) are the bounding rect of the polygon
54727 * pts is the original array, with the first pair repeated at the end
54728 * contains is a function: (pt, omitFirstEdge)
54729 * pt is the [x, y] pair to test
54730 * omitFirstEdge truthy means points exactly on the first edge don't
54731 * count. This is for use adding one polygon to another so we
54732 * don't double-count the edge where they meet.
54733 * returns boolean: is pt inside the polygon (including on its edges)
54734 */
54735polygon.tester = function tester(ptsIn) {
54736 var pts = ptsIn.slice();
54737 var xmin = pts[0][0];
54738 var xmax = xmin;
54739 var ymin = pts[0][1];
54740 var ymax = ymin;
54741 var i;
54742
54743 pts.push(pts[0]);
54744 for(i = 1; i < pts.length; i++) {
54745 xmin = Math.min(xmin, pts[i][0]);
54746 xmax = Math.max(xmax, pts[i][0]);
54747 ymin = Math.min(ymin, pts[i][1]);
54748 ymax = Math.max(ymax, pts[i][1]);
54749 }
54750
54751 // do we have a rectangle? Handle this here, so we can use the same
54752 // tester for the rectangular case without sacrificing speed
54753
54754 var isRect = false;
54755 var rectFirstEdgeTest;
54756
54757 if(pts.length === 5) {
54758 if(pts[0][0] === pts[1][0]) { // vert, horz, vert, horz
54759 if(pts[2][0] === pts[3][0] &&
54760 pts[0][1] === pts[3][1] &&
54761 pts[1][1] === pts[2][1]) {
54762 isRect = true;
54763 rectFirstEdgeTest = function(pt) { return pt[0] === pts[0][0]; };
54764 }
54765 } else if(pts[0][1] === pts[1][1]) { // horz, vert, horz, vert
54766 if(pts[2][1] === pts[3][1] &&
54767 pts[0][0] === pts[3][0] &&
54768 pts[1][0] === pts[2][0]) {
54769 isRect = true;
54770 rectFirstEdgeTest = function(pt) { return pt[1] === pts[0][1]; };
54771 }
54772 }
54773 }
54774
54775 function rectContains(pt, omitFirstEdge) {
54776 var x = pt[0];
54777 var y = pt[1];
54778
54779 if(x === BADNUM || x < xmin || x > xmax || y === BADNUM || y < ymin || y > ymax) {
54780 // pt is outside the bounding box of polygon
54781 return false;
54782 }
54783 if(omitFirstEdge && rectFirstEdgeTest(pt)) return false;
54784
54785 return true;
54786 }
54787
54788 function contains(pt, omitFirstEdge) {
54789 var x = pt[0];
54790 var y = pt[1];
54791
54792 if(x === BADNUM || x < xmin || x > xmax || y === BADNUM || y < ymin || y > ymax) {
54793 // pt is outside the bounding box of polygon
54794 return false;
54795 }
54796
54797 var imax = pts.length;
54798 var x1 = pts[0][0];
54799 var y1 = pts[0][1];
54800 var crossings = 0;
54801 var i;
54802 var x0;
54803 var y0;
54804 var xmini;
54805 var ycross;
54806
54807 for(i = 1; i < imax; i++) {
54808 // find all crossings of a vertical line upward from pt with
54809 // polygon segments
54810 // crossings exactly at xmax don't count, unless the point is
54811 // exactly on the segment, then it counts as inside.
54812 x0 = x1;
54813 y0 = y1;
54814 x1 = pts[i][0];
54815 y1 = pts[i][1];
54816 xmini = Math.min(x0, x1);
54817
54818 if(x < xmini || x > Math.max(x0, x1) || y > Math.max(y0, y1)) {
54819 // outside the bounding box of this segment, it's only a crossing
54820 // if it's below the box.
54821
54822 continue;
54823 } else if(y < Math.min(y0, y1)) {
54824 // don't count the left-most point of the segment as a crossing
54825 // because we don't want to double-count adjacent crossings
54826 // UNLESS the polygon turns past vertical at exactly this x
54827 // Note that this is repeated below, but we can't factor it out
54828 // because
54829 if(x !== xmini) crossings++;
54830 } else {
54831 // inside the bounding box, check the actual line intercept
54832
54833 // vertical segment - we know already that the point is exactly
54834 // on the segment, so mark the crossing as exactly at the point.
54835 if(x1 === x0) ycross = y;
54836 // any other angle
54837 else ycross = y0 + (x - x0) * (y1 - y0) / (x1 - x0);
54838
54839 // exactly on the edge: counts as inside the polygon, unless it's the
54840 // first edge and we're omitting it.
54841 if(y === ycross) {
54842 if(i === 1 && omitFirstEdge) return false;
54843 return true;
54844 }
54845
54846 if(y <= ycross && x !== xmini) crossings++;
54847 }
54848 }
54849
54850 // if we've gotten this far, odd crossings means inside, even is outside
54851 return crossings % 2 === 1;
54852 }
54853
54854 // detect if poly is degenerate
54855 var degenerate = true;
54856 var lastPt = pts[0];
54857 for(i = 1; i < pts.length; i++) {
54858 if(lastPt[0] !== pts[i][0] || lastPt[1] !== pts[i][1]) {
54859 degenerate = false;
54860 break;
54861 }
54862 }
54863
54864 return {
54865 xmin: xmin,
54866 xmax: xmax,
54867 ymin: ymin,
54868 ymax: ymax,
54869 pts: pts,
54870 contains: isRect ? rectContains : contains,
54871 isRect: isRect,
54872 degenerate: degenerate
54873 };
54874};
54875
54876/**
54877 * Test if a segment of a points array is bent or straight
54878 *
54879 * @param pts Array of [x, y] pairs
54880 * @param start the index of the proposed start of the straight section
54881 * @param end the index of the proposed end point
54882 * @param tolerance the max distance off the line connecting start and end
54883 * before the line counts as bent
54884 * @returns boolean: true means this segment is bent, false means straight
54885 */
54886polygon.isSegmentBent = function isSegmentBent(pts, start, end, tolerance) {
54887 var startPt = pts[start];
54888 var segment = [pts[end][0] - startPt[0], pts[end][1] - startPt[1]];
54889 var segmentSquared = dot(segment, segment);
54890 var segmentLen = Math.sqrt(segmentSquared);
54891 var unitPerp = [-segment[1] / segmentLen, segment[0] / segmentLen];
54892 var i;
54893 var part;
54894 var partParallel;
54895
54896 for(i = start + 1; i < end; i++) {
54897 part = [pts[i][0] - startPt[0], pts[i][1] - startPt[1]];
54898 partParallel = dot(part, segment);
54899
54900 if(partParallel < 0 || partParallel > segmentSquared ||
54901 Math.abs(dot(part, unitPerp)) > tolerance) return true;
54902 }
54903 return false;
54904};
54905
54906/**
54907 * Make a filtering polygon, to minimize the number of segments
54908 *
54909 * @param pts Array of [x, y] pairs (must start with at least 1 pair)
54910 * @param tolerance the maximum deviation from straight allowed for
54911 * removing points to simplify the polygon
54912 *
54913 * @returns Object {addPt, raw, filtered}
54914 * addPt is a function(pt: [x, y] pair) to add a raw point and
54915 * continue filtering
54916 * raw is all the input points
54917 * filtered is the resulting filtered Array of [x, y] pairs
54918 */
54919polygon.filter = function filter(pts, tolerance) {
54920 var ptsFiltered = [pts[0]];
54921 var doneRawIndex = 0;
54922 var doneFilteredIndex = 0;
54923
54924 function addPt(pt) {
54925 pts.push(pt);
54926 var prevFilterLen = ptsFiltered.length;
54927 var iLast = doneRawIndex;
54928 ptsFiltered.splice(doneFilteredIndex + 1);
54929
54930 for(var i = iLast + 1; i < pts.length; i++) {
54931 if(i === pts.length - 1 || polygon.isSegmentBent(pts, iLast, i + 1, tolerance)) {
54932 ptsFiltered.push(pts[i]);
54933 if(ptsFiltered.length < prevFilterLen - 2) {
54934 doneRawIndex = i;
54935 doneFilteredIndex = ptsFiltered.length - 1;
54936 }
54937 iLast = i;
54938 }
54939 }
54940 }
54941
54942 if(pts.length > 1) {
54943 var lastPt = pts.pop();
54944 addPt(lastPt);
54945 }
54946
54947 return {
54948 addPt: addPt,
54949 raw: pts,
54950 filtered: ptsFiltered
54951 };
54952};
54953
54954},{"../constants/numerical":267,"./matrix":293}],300:[function(_dereq_,module,exports){
54955'use strict';
54956
54957var isNumeric = _dereq_('fast-isnumeric');
54958var isMobileOrTablet = _dereq_('is-mobile');
54959
54960module.exports = function preserveDrawingBuffer(opts) {
54961 var ua;
54962
54963 if(opts && opts.hasOwnProperty('userAgent')) {
54964 ua = opts.userAgent;
54965 } else {
54966 ua = getUserAgent();
54967 }
54968
54969 if(typeof ua !== 'string') return true;
54970
54971 var enable = isMobileOrTablet({
54972 ua: { headers: {'user-agent': ua }},
54973 tablet: true,
54974 featureDetect: false
54975 });
54976
54977 if(!enable) {
54978 var allParts = ua.split(' ');
54979 for(var i = 1; i < allParts.length; i++) {
54980 var part = allParts[i];
54981 if(part.indexOf('Safari') !== -1) {
54982 // find Safari version
54983 for(var k = i - 1; k > -1; k--) {
54984 var prevPart = allParts[k];
54985 if(prevPart.substr(0, 8) === 'Version/') {
54986 var v = prevPart.substr(8).split('.')[0];
54987 if(isNumeric(v)) v = +v;
54988 if(v >= 13) return true;
54989 }
54990 }
54991 }
54992 }
54993 }
54994
54995 return enable;
54996};
54997
54998function getUserAgent() {
54999 // similar to https://github.com/juliangruber/is-mobile/blob/91ca39ccdd4cfc5edfb5391e2515b923a730fbea/index.js#L14-L17
55000 var ua;
55001 if(typeof navigator !== 'undefined') {
55002 ua = navigator.userAgent;
55003 }
55004
55005 if(
55006 ua &&
55007 ua.headers &&
55008 typeof ua.headers['user-agent'] === 'string'
55009 ) {
55010 ua = ua.headers['user-agent'];
55011 }
55012
55013 return ua;
55014}
55015
55016},{"fast-isnumeric":33,"is-mobile":69}],301:[function(_dereq_,module,exports){
55017'use strict';
55018
55019/**
55020 * Push array with unique items
55021 *
55022 * Ignores falsy items, except 0 so we can use it to construct arrays of indices.
55023 *
55024 * @param {array} array
55025 * array to be filled
55026 * @param {any} item
55027 * item to be or not to be inserted
55028 * @return {array}
55029 * ref to array (now possibly containing one more item)
55030 *
55031 */
55032module.exports = function pushUnique(array, item) {
55033 if(item instanceof RegExp) {
55034 var itemStr = item.toString();
55035 for(var i = 0; i < array.length; i++) {
55036 if(array[i] instanceof RegExp && array[i].toString() === itemStr) {
55037 return array;
55038 }
55039 }
55040 array.push(item);
55041 } else if((item || item === 0) && array.indexOf(item) === -1) array.push(item);
55042
55043 return array;
55044};
55045
55046},{}],302:[function(_dereq_,module,exports){
55047'use strict';
55048
55049var Lib = _dereq_('../lib');
55050var dfltConfig = _dereq_('../plot_api/plot_config').dfltConfig;
55051
55052/**
55053 * Copy arg array *without* removing `undefined` values from objects.
55054 *
55055 * @param gd
55056 * @param args
55057 * @returns {Array}
55058 */
55059function copyArgArray(gd, args) {
55060 var copy = [];
55061 var arg;
55062
55063 for(var i = 0; i < args.length; i++) {
55064 arg = args[i];
55065
55066 if(arg === gd) copy[i] = arg;
55067 else if(typeof arg === 'object') {
55068 copy[i] = Array.isArray(arg) ?
55069 Lib.extendDeep([], arg) :
55070 Lib.extendDeepAll({}, arg);
55071 } else copy[i] = arg;
55072 }
55073
55074 return copy;
55075}
55076
55077
55078// -----------------------------------------------------
55079// Undo/Redo queue for plots
55080// -----------------------------------------------------
55081
55082
55083var queue = {};
55084
55085// TODO: disable/enable undo and redo buttons appropriately
55086
55087/**
55088 * Add an item to the undoQueue for a graphDiv
55089 *
55090 * @param gd
55091 * @param undoFunc Function undo this operation
55092 * @param undoArgs Args to supply undoFunc with
55093 * @param redoFunc Function to redo this operation
55094 * @param redoArgs Args to supply redoFunc with
55095 */
55096queue.add = function(gd, undoFunc, undoArgs, redoFunc, redoArgs) {
55097 var queueObj,
55098 queueIndex;
55099
55100 // make sure we have the queue and our position in it
55101 gd.undoQueue = gd.undoQueue || {index: 0, queue: [], sequence: false};
55102 queueIndex = gd.undoQueue.index;
55103
55104 // if we're already playing an undo or redo, or if this is an auto operation
55105 // (like pane resize... any others?) then we don't save this to the undo queue
55106 if(gd.autoplay) {
55107 if(!gd.undoQueue.inSequence) gd.autoplay = false;
55108 return;
55109 }
55110
55111 // if we're not in a sequence or are just starting, we need a new queue item
55112 if(!gd.undoQueue.sequence || gd.undoQueue.beginSequence) {
55113 queueObj = {undo: {calls: [], args: []}, redo: {calls: [], args: []}};
55114 gd.undoQueue.queue.splice(queueIndex, gd.undoQueue.queue.length - queueIndex, queueObj);
55115 gd.undoQueue.index += 1;
55116 } else {
55117 queueObj = gd.undoQueue.queue[queueIndex - 1];
55118 }
55119 gd.undoQueue.beginSequence = false;
55120
55121 // we unshift to handle calls for undo in a forward for loop later
55122 if(queueObj) {
55123 queueObj.undo.calls.unshift(undoFunc);
55124 queueObj.undo.args.unshift(undoArgs);
55125 queueObj.redo.calls.push(redoFunc);
55126 queueObj.redo.args.push(redoArgs);
55127 }
55128
55129 if(gd.undoQueue.queue.length > dfltConfig.queueLength) {
55130 gd.undoQueue.queue.shift();
55131 gd.undoQueue.index--;
55132 }
55133};
55134
55135/**
55136 * Begin a sequence of undoQueue changes
55137 *
55138 * @param gd
55139 */
55140queue.startSequence = function(gd) {
55141 gd.undoQueue = gd.undoQueue || {index: 0, queue: [], sequence: false};
55142 gd.undoQueue.sequence = true;
55143 gd.undoQueue.beginSequence = true;
55144};
55145
55146/**
55147 * Stop a sequence of undoQueue changes
55148 *
55149 * Call this *after* you're sure your undo chain has ended
55150 *
55151 * @param gd
55152 */
55153queue.stopSequence = function(gd) {
55154 gd.undoQueue = gd.undoQueue || {index: 0, queue: [], sequence: false};
55155 gd.undoQueue.sequence = false;
55156 gd.undoQueue.beginSequence = false;
55157};
55158
55159/**
55160 * Move one step back in the undo queue, and undo the object there.
55161 *
55162 * @param gd
55163 */
55164queue.undo = function undo(gd) {
55165 var queueObj, i;
55166
55167 if(gd.undoQueue === undefined ||
55168 isNaN(gd.undoQueue.index) ||
55169 gd.undoQueue.index <= 0) {
55170 return;
55171 }
55172
55173 // index is pointing to next *forward* queueObj, point to the one we're undoing
55174 gd.undoQueue.index--;
55175
55176 // get the queueObj for instructions on how to undo
55177 queueObj = gd.undoQueue.queue[gd.undoQueue.index];
55178
55179 // this sequence keeps things from adding to the queue during undo/redo
55180 gd.undoQueue.inSequence = true;
55181 for(i = 0; i < queueObj.undo.calls.length; i++) {
55182 queue.plotDo(gd, queueObj.undo.calls[i], queueObj.undo.args[i]);
55183 }
55184 gd.undoQueue.inSequence = false;
55185 gd.autoplay = false;
55186};
55187
55188/**
55189 * Redo the current object in the undo, then move forward in the queue.
55190 *
55191 * @param gd
55192 */
55193queue.redo = function redo(gd) {
55194 var queueObj, i;
55195
55196 if(gd.undoQueue === undefined ||
55197 isNaN(gd.undoQueue.index) ||
55198 gd.undoQueue.index >= gd.undoQueue.queue.length) {
55199 return;
55200 }
55201
55202 // get the queueObj for instructions on how to undo
55203 queueObj = gd.undoQueue.queue[gd.undoQueue.index];
55204
55205 // this sequence keeps things from adding to the queue during undo/redo
55206 gd.undoQueue.inSequence = true;
55207 for(i = 0; i < queueObj.redo.calls.length; i++) {
55208 queue.plotDo(gd, queueObj.redo.calls[i], queueObj.redo.args[i]);
55209 }
55210 gd.undoQueue.inSequence = false;
55211 gd.autoplay = false;
55212
55213 // index is pointing to the thing we just redid, move it
55214 gd.undoQueue.index++;
55215};
55216
55217/**
55218 * Called by undo/redo to make the actual changes.
55219 *
55220 * Not meant to be called publically, but included for mocking out in tests.
55221 *
55222 * @param gd
55223 * @param func
55224 * @param args
55225 */
55226queue.plotDo = function(gd, func, args) {
55227 gd.autoplay = true;
55228
55229 // this *won't* copy gd and it preserves `undefined` properties!
55230 args = copyArgArray(gd, args);
55231
55232 // call the supplied function
55233 func.apply(null, args);
55234};
55235
55236module.exports = queue;
55237
55238},{"../lib":287,"../plot_api/plot_config":321}],303:[function(_dereq_,module,exports){
55239'use strict';
55240
55241/*
55242 * make a regex for matching counter ids/names ie xaxis, xaxis2, xaxis10...
55243 *
55244 * @param {string} head: the head of the pattern, eg 'x' matches 'x', 'x2', 'x10' etc.
55245 * 'xy' is a special case for cartesian subplots: it matches 'x2y3' etc
55246 * @param {Optional(string)} tail: a fixed piece after the id
55247 * eg counterRegex('scene', '.annotations') for scene2.annotations etc.
55248 * @param {boolean} openEnded: if true, the string may continue past the match.
55249 * @param {boolean} matchBeginning: if false, the string may start before the match.
55250 */
55251exports.counter = function(head, tail, openEnded, matchBeginning) {
55252 var fullTail = (tail || '') + (openEnded ? '' : '$');
55253 var startWithPrefix = matchBeginning === false ? '' : '^';
55254 if(head === 'xy') {
55255 return new RegExp(startWithPrefix + 'x([2-9]|[1-9][0-9]+)?y([2-9]|[1-9][0-9]+)?' + fullTail);
55256 }
55257 return new RegExp(startWithPrefix + head + '([2-9]|[1-9][0-9]+)?' + fullTail);
55258};
55259
55260},{}],304:[function(_dereq_,module,exports){
55261'use strict';
55262
55263// ASCEND: chop off the last nesting level - either [<n>] or .<key> - to ascend
55264// the attribute tree. the remaining attrString is in match[1]
55265var ASCEND = /^(.*)(\.[^\.\[\]]+|\[\d\])$/;
55266
55267// SIMPLEATTR: is this an un-nested attribute? (no dots or brackets)
55268var SIMPLEATTR = /^[^\.\[\]]+$/;
55269
55270/*
55271 * calculate a relative attribute string, similar to a relative path
55272 *
55273 * @param {string} baseAttr:
55274 * an attribute string, such as 'annotations[3].x'. The "current location"
55275 * is the attribute string minus the last component ('annotations[3]')
55276 * @param {string} relativeAttr:
55277 * a route to the desired attribute string, using '^' to ascend
55278 *
55279 * @return {string} attrString:
55280 * for example:
55281 * relativeAttr('annotations[3].x', 'y') = 'annotations[3].y'
55282 * relativeAttr('annotations[3].x', '^[2].z') = 'annotations[2].z'
55283 * relativeAttr('annotations[3].x', '^^margin') = 'margin'
55284 * relativeAttr('annotations[3].x', '^^margin.r') = 'margin.r'
55285 */
55286module.exports = function(baseAttr, relativeAttr) {
55287 while(relativeAttr) {
55288 var match = baseAttr.match(ASCEND);
55289
55290 if(match) baseAttr = match[1];
55291 else if(baseAttr.match(SIMPLEATTR)) baseAttr = '';
55292 else throw new Error('bad relativeAttr call:' + [baseAttr, relativeAttr]);
55293
55294 if(relativeAttr.charAt(0) === '^') relativeAttr = relativeAttr.slice(1);
55295 else break;
55296 }
55297
55298 if(baseAttr && relativeAttr.charAt(0) !== '[') {
55299 return baseAttr + '.' + relativeAttr;
55300 }
55301 return baseAttr + relativeAttr;
55302};
55303
55304},{}],305:[function(_dereq_,module,exports){
55305'use strict';
55306
55307var isArrayOrTypedArray = _dereq_('./array').isArrayOrTypedArray;
55308var isPlainObject = _dereq_('./is_plain_object');
55309
55310/**
55311 * Relink private _keys and keys with a function value from one container
55312 * to the new container.
55313 * Relink means copying if object is pass-by-value and adding a reference
55314 * if object is pass-by-ref.
55315 * This prevents deepCopying massive structures like a webgl context.
55316 */
55317module.exports = function relinkPrivateKeys(toContainer, fromContainer) {
55318 for(var k in fromContainer) {
55319 var fromVal = fromContainer[k];
55320 var toVal = toContainer[k];
55321
55322 if(toVal === fromVal) continue;
55323
55324 if(k.charAt(0) === '_' || typeof fromVal === 'function') {
55325 // if it already exists at this point, it's something
55326 // that we recreate each time around, so ignore it
55327 if(k in toContainer) continue;
55328
55329 toContainer[k] = fromVal;
55330 } else if(isArrayOrTypedArray(fromVal) && isArrayOrTypedArray(toVal) && isPlainObject(fromVal[0])) {
55331 // filter out data_array items that can contain user objects
55332 // most of the time the toVal === fromVal check will catch these early
55333 // but if the user makes new ones we also don't want to recurse in.
55334 if(k === 'customdata' || k === 'ids') continue;
55335
55336 // recurse into arrays containers
55337 var minLen = Math.min(fromVal.length, toVal.length);
55338 for(var j = 0; j < minLen; j++) {
55339 if((toVal[j] !== fromVal[j]) && isPlainObject(fromVal[j]) && isPlainObject(toVal[j])) {
55340 relinkPrivateKeys(toVal[j], fromVal[j]);
55341 }
55342 }
55343 } else if(isPlainObject(fromVal) && isPlainObject(toVal)) {
55344 // recurse into objects, but only if they still exist
55345 relinkPrivateKeys(toVal, fromVal);
55346
55347 if(!Object.keys(toVal).length) delete toContainer[k];
55348 }
55349 }
55350};
55351
55352},{"./array":273,"./is_plain_object":288}],306:[function(_dereq_,module,exports){
55353'use strict';
55354
55355var isNumeric = _dereq_('fast-isnumeric');
55356var loggers = _dereq_('./loggers');
55357var identity = _dereq_('./identity');
55358var BADNUM = _dereq_('../constants/numerical').BADNUM;
55359
55360// don't trust floating point equality - fraction of bin size to call
55361// "on the line" and ensure that they go the right way specified by
55362// linelow
55363var roundingError = 1e-9;
55364
55365
55366/**
55367 * findBin - find the bin for val - note that it can return outside the
55368 * bin range any pos. or neg. integer for linear bins, or -1 or
55369 * bins.length-1 for explicit.
55370 * bins is either an object {start,size,end} or an array length #bins+1
55371 * bins can be either increasing or decreasing but must be monotonic
55372 * for linear bins, we can just calculate. For listed bins, run a binary
55373 * search linelow (truthy) says the bin boundary should be attributed to
55374 * the lower bin rather than the default upper bin
55375 */
55376exports.findBin = function(val, bins, linelow) {
55377 if(isNumeric(bins.start)) {
55378 return linelow ?
55379 Math.ceil((val - bins.start) / bins.size - roundingError) - 1 :
55380 Math.floor((val - bins.start) / bins.size + roundingError);
55381 } else {
55382 var n1 = 0;
55383 var n2 = bins.length;
55384 var c = 0;
55385 var binSize = (n2 > 1) ? (bins[n2 - 1] - bins[0]) / (n2 - 1) : 1;
55386 var n, test;
55387 if(binSize >= 0) {
55388 test = linelow ? lessThan : lessOrEqual;
55389 } else {
55390 test = linelow ? greaterOrEqual : greaterThan;
55391 }
55392 val += binSize * roundingError * (linelow ? -1 : 1) * (binSize >= 0 ? 1 : -1);
55393 // c is just to avoid infinite loops if there's an error
55394 while(n1 < n2 && c++ < 100) {
55395 n = Math.floor((n1 + n2) / 2);
55396 if(test(bins[n], val)) n1 = n + 1;
55397 else n2 = n;
55398 }
55399 if(c > 90) loggers.log('Long binary search...');
55400 return n1 - 1;
55401 }
55402};
55403
55404function lessThan(a, b) { return a < b; }
55405function lessOrEqual(a, b) { return a <= b; }
55406function greaterThan(a, b) { return a > b; }
55407function greaterOrEqual(a, b) { return a >= b; }
55408
55409exports.sorterAsc = function(a, b) { return a - b; };
55410exports.sorterDes = function(a, b) { return b - a; };
55411
55412/**
55413 * find distinct values in an array, lumping together ones that appear to
55414 * just be off by a rounding error
55415 * return the distinct values and the minimum difference between any two
55416 */
55417exports.distinctVals = function(valsIn) {
55418 var vals = valsIn.slice(); // otherwise we sort the original array...
55419 vals.sort(exports.sorterAsc); // undefined listed in the end - also works on IE11
55420
55421 var last;
55422 for(last = vals.length - 1; last > -1; last--) {
55423 if(vals[last] !== BADNUM) break;
55424 }
55425
55426 var minDiff = (vals[last] - vals[0]) || 1;
55427 var errDiff = minDiff / (last || 1) / 10000;
55428 var newVals = [];
55429 var preV;
55430 for(var i = 0; i <= last; i++) {
55431 var v = vals[i];
55432
55433 // make sure values aren't just off by a rounding error
55434 var diff = v - preV;
55435
55436 if(preV === undefined) {
55437 newVals.push(v);
55438 preV = v;
55439 } else if(diff > errDiff) {
55440 minDiff = Math.min(minDiff, diff);
55441
55442 newVals.push(v);
55443 preV = v;
55444 }
55445 }
55446
55447 return {vals: newVals, minDiff: minDiff};
55448};
55449
55450/**
55451 * return the smallest element from (sorted) array arrayIn that's bigger than val,
55452 * or (reverse) the largest element smaller than val
55453 * used to find the best tick given the minimum (non-rounded) tick
55454 * particularly useful for date/time where things are not powers of 10
55455 * binary search is probably overkill here...
55456 */
55457exports.roundUp = function(val, arrayIn, reverse) {
55458 var low = 0;
55459 var high = arrayIn.length - 1;
55460 var mid;
55461 var c = 0;
55462 var dlow = reverse ? 0 : 1;
55463 var dhigh = reverse ? 1 : 0;
55464 var rounded = reverse ? Math.ceil : Math.floor;
55465 // c is just to avoid infinite loops if there's an error
55466 while(low < high && c++ < 100) {
55467 mid = rounded((low + high) / 2);
55468 if(arrayIn[mid] <= val) low = mid + dlow;
55469 else high = mid - dhigh;
55470 }
55471 return arrayIn[low];
55472};
55473
55474/**
55475 * Tweak to Array.sort(sortFn) that improves performance for pre-sorted arrays
55476 *
55477 * Note that newer browsers (such as Chrome v70+) are starting to pick up
55478 * on pre-sorted arrays which may render the following optimization unnecessary
55479 * in the future.
55480 *
55481 * Motivation: sometimes we need to sort arrays but the input is likely to
55482 * already be sorted. Browsers don't seem to pick up on pre-sorted arrays,
55483 * and in fact Chrome is actually *slower* sorting pre-sorted arrays than purely
55484 * random arrays. FF is at least faster if the array is pre-sorted, but still
55485 * not as fast as it could be.
55486 * Here's how this plays out sorting a length-1e6 array:
55487 *
55488 * Calls to Sort FN | Chrome bare | FF bare | Chrome tweak | FF tweak
55489 * | v68.0 Mac | v61.0 Mac| |
55490 * ------------------+---------------+-----------+----------------+------------
55491 * ordered | 30.4e6 | 10.1e6 | 1e6 | 1e6
55492 * reversed | 29.4e6 | 9.9e6 | 1e6 + reverse | 1e6 + reverse
55493 * random | ~21e6 | ~18.7e6 | ~21e6 | ~18.7e6
55494 *
55495 * So this is a substantial win for pre-sorted (ordered or exactly reversed)
55496 * arrays. Including this wrapper on an unsorted array adds a penalty that will
55497 * in general be only a few calls to the sort function. The only case this
55498 * penalty will be significant is if the array is mostly sorted but there are
55499 * a few unsorted items near the end, but the penalty is still at most N calls
55500 * out of (for N=1e6) ~20N total calls
55501 *
55502 * @param {Array} array: the array, to be sorted in place
55503 * @param {function} sortFn: As in Array.sort, function(a, b) that puts
55504 * item a before item b if the return is negative, a after b if positive,
55505 * and no change if zero.
55506 * @return {Array}: the original array, sorted in place.
55507 */
55508exports.sort = function(array, sortFn) {
55509 var notOrdered = 0;
55510 var notReversed = 0;
55511 for(var i = 1; i < array.length; i++) {
55512 var pairOrder = sortFn(array[i], array[i - 1]);
55513 if(pairOrder < 0) notOrdered = 1;
55514 else if(pairOrder > 0) notReversed = 1;
55515 if(notOrdered && notReversed) return array.sort(sortFn);
55516 }
55517 return notReversed ? array : array.reverse();
55518};
55519
55520/**
55521 * find index in array 'arr' that minimizes 'fn'
55522 *
55523 * @param {array} arr : array where to search
55524 * @param {fn (optional)} fn : function to minimize,
55525 * if not given, fn is the identity function
55526 * @return {integer}
55527 */
55528exports.findIndexOfMin = function(arr, fn) {
55529 fn = fn || identity;
55530
55531 var min = Infinity;
55532 var ind;
55533
55534 for(var i = 0; i < arr.length; i++) {
55535 var v = fn(arr[i]);
55536 if(v < min) {
55537 min = v;
55538 ind = i;
55539 }
55540 }
55541 return ind;
55542};
55543
55544},{"../constants/numerical":267,"./identity":285,"./loggers":291,"fast-isnumeric":33}],307:[function(_dereq_,module,exports){
55545'use strict';
55546
55547// works with our CSS cursor classes (see css/_cursor.scss)
55548// to apply cursors to d3 single-element selections.
55549// omit cursor to revert to the default.
55550module.exports = function setCursor(el3, csr) {
55551 (el3.attr('class') || '').split(' ').forEach(function(cls) {
55552 if(cls.indexOf('cursor-') === 0) el3.classed(cls, false);
55553 });
55554
55555 if(csr) el3.classed('cursor-' + csr, true);
55556};
55557
55558},{}],308:[function(_dereq_,module,exports){
55559'use strict';
55560
55561module.exports = function sortObjectKeys(obj) {
55562 return Object.keys(obj).sort();
55563};
55564
55565},{}],309:[function(_dereq_,module,exports){
55566'use strict';
55567
55568var isNumeric = _dereq_('fast-isnumeric');
55569var isArrayOrTypedArray = _dereq_('./array').isArrayOrTypedArray;
55570
55571/**
55572 * aggNums() returns the result of an aggregate function applied to an array of
55573 * values, where non-numerical values have been tossed out.
55574 *
55575 * @param {function} f - aggregation function (e.g., Math.min)
55576 * @param {Number} v - initial value (continuing from previous calls)
55577 * if there's no continuing value, use null for selector-type
55578 * functions (max,min), or 0 for summations
55579 * @param {Array} a - array to aggregate (may be nested, we will recurse,
55580 * but all elements must have the same dimension)
55581 * @param {Number} len - maximum length of a to aggregate
55582 * @return {Number} - result of f applied to a starting from v
55583 */
55584exports.aggNums = function(f, v, a, len) {
55585 var i,
55586 b;
55587 if(!len || len > a.length) len = a.length;
55588 if(!isNumeric(v)) v = false;
55589 if(isArrayOrTypedArray(a[0])) {
55590 b = new Array(len);
55591 for(i = 0; i < len; i++) b[i] = exports.aggNums(f, v, a[i]);
55592 a = b;
55593 }
55594
55595 for(i = 0; i < len; i++) {
55596 if(!isNumeric(v)) v = a[i];
55597 else if(isNumeric(a[i])) v = f(+v, +a[i]);
55598 }
55599 return v;
55600};
55601
55602/**
55603 * mean & std dev functions using aggNums, so it handles non-numerics nicely
55604 * even need to use aggNums instead of .length, to toss out non-numerics
55605 */
55606exports.len = function(data) {
55607 return exports.aggNums(function(a) { return a + 1; }, 0, data);
55608};
55609
55610exports.mean = function(data, len) {
55611 if(!len) len = exports.len(data);
55612 return exports.aggNums(function(a, b) { return a + b; }, 0, data) / len;
55613};
55614
55615exports.midRange = function(numArr) {
55616 if(numArr === undefined || numArr.length === 0) return undefined;
55617 return (exports.aggNums(Math.max, null, numArr) + exports.aggNums(Math.min, null, numArr)) / 2;
55618};
55619
55620exports.variance = function(data, len, mean) {
55621 if(!len) len = exports.len(data);
55622 if(!isNumeric(mean)) mean = exports.mean(data, len);
55623
55624 return exports.aggNums(function(a, b) {
55625 return a + Math.pow(b - mean, 2);
55626 }, 0, data) / len;
55627};
55628
55629exports.stdev = function(data, len, mean) {
55630 return Math.sqrt(exports.variance(data, len, mean));
55631};
55632
55633/**
55634 * median of a finite set of numbers
55635 * reference page: https://en.wikipedia.org/wiki/Median#Finite_set_of_numbers
55636**/
55637exports.median = function(data) {
55638 var b = data.slice().sort();
55639 return exports.interp(b, 0.5);
55640};
55641
55642/**
55643 * interp() computes a percentile (quantile) for a given distribution.
55644 * We interpolate the distribution (to compute quantiles, we follow method #10 here:
55645 * http://www.amstat.org/publications/jse/v14n3/langford.html).
55646 * Typically the index or rank (n * arr.length) may be non-integer.
55647 * For reference: ends are clipped to the extreme values in the array;
55648 * For box plots: index you get is half a point too high (see
55649 * http://en.wikipedia.org/wiki/Percentile#Nearest_rank) but note that this definition
55650 * indexes from 1 rather than 0, so we subtract 1/2 (instead of add).
55651 *
55652 * @param {Array} arr - This array contains the values that make up the distribution.
55653 * @param {Number} n - Between 0 and 1, n = p/100 is such that we compute the p^th percentile.
55654 * For example, the 50th percentile (or median) corresponds to n = 0.5
55655 * @return {Number} - percentile
55656 */
55657exports.interp = function(arr, n) {
55658 if(!isNumeric(n)) throw 'n should be a finite number';
55659 n = n * arr.length - 0.5;
55660 if(n < 0) return arr[0];
55661 if(n > arr.length - 1) return arr[arr.length - 1];
55662 var frac = n % 1;
55663 return frac * arr[Math.ceil(n)] + (1 - frac) * arr[Math.floor(n)];
55664};
55665
55666},{"./array":273,"fast-isnumeric":33}],310:[function(_dereq_,module,exports){
55667'use strict';
55668
55669/* global MathJax:false */
55670
55671var d3 = _dereq_('@plotly/d3');
55672
55673var Lib = _dereq_('../lib');
55674var strTranslate = Lib.strTranslate;
55675var xmlnsNamespaces = _dereq_('../constants/xmlns_namespaces');
55676var LINE_SPACING = _dereq_('../constants/alignment').LINE_SPACING;
55677
55678// text converter
55679
55680function getSize(_selection, _dimension) {
55681 return _selection.node().getBoundingClientRect()[_dimension];
55682}
55683
55684var FIND_TEX = /([^$]*)([$]+[^$]*[$]+)([^$]*)/;
55685
55686exports.convertToTspans = function(_context, gd, _callback) {
55687 var str = _context.text();
55688
55689 // Until we get tex integrated more fully (so it can be used along with non-tex)
55690 // allow some elements to prohibit it by attaching 'data-notex' to the original
55691 var tex = (!_context.attr('data-notex')) &&
55692 (typeof MathJax !== 'undefined') &&
55693 str.match(FIND_TEX);
55694
55695 var parent = d3.select(_context.node().parentNode);
55696 if(parent.empty()) return;
55697 var svgClass = (_context.attr('class')) ? _context.attr('class').split(' ')[0] : 'text';
55698 svgClass += '-math';
55699 parent.selectAll('svg.' + svgClass).remove();
55700 parent.selectAll('g.' + svgClass + '-group').remove();
55701 _context.style('display', null)
55702 .attr({
55703 // some callers use data-unformatted *from the <text> element* in 'cancel'
55704 // so we need it here even if we're going to turn it into math
55705 // these two (plus style and text-anchor attributes) form the key we're
55706 // going to use for Drawing.bBox
55707 'data-unformatted': str,
55708 'data-math': 'N'
55709 });
55710
55711 function showText() {
55712 if(!parent.empty()) {
55713 svgClass = _context.attr('class') + '-math';
55714 parent.select('svg.' + svgClass).remove();
55715 }
55716 _context.text('')
55717 .style('white-space', 'pre');
55718
55719 var hasLink = buildSVGText(_context.node(), str);
55720
55721 if(hasLink) {
55722 // at least in Chrome, pointer-events does not seem
55723 // to be honored in children of <text> elements
55724 // so if we have an anchor, we have to make the
55725 // whole element respond
55726 _context.style('pointer-events', 'all');
55727 }
55728
55729 exports.positionText(_context);
55730
55731 if(_callback) _callback.call(_context);
55732 }
55733
55734 if(tex) {
55735 ((gd && gd._promises) || []).push(new Promise(function(resolve) {
55736 _context.style('display', 'none');
55737 var fontSize = parseInt(_context.node().style.fontSize, 10);
55738 var config = {fontSize: fontSize};
55739
55740 texToSVG(tex[2], config, function(_svgEl, _glyphDefs, _svgBBox) {
55741 parent.selectAll('svg.' + svgClass).remove();
55742 parent.selectAll('g.' + svgClass + '-group').remove();
55743
55744 var newSvg = _svgEl && _svgEl.select('svg');
55745 if(!newSvg || !newSvg.node()) {
55746 showText();
55747 resolve();
55748 return;
55749 }
55750
55751 var mathjaxGroup = parent.append('g')
55752 .classed(svgClass + '-group', true)
55753 .attr({
55754 'pointer-events': 'none',
55755 'data-unformatted': str,
55756 'data-math': 'Y'
55757 });
55758
55759 mathjaxGroup.node().appendChild(newSvg.node());
55760
55761 // stitch the glyph defs
55762 if(_glyphDefs && _glyphDefs.node()) {
55763 newSvg.node().insertBefore(_glyphDefs.node().cloneNode(true),
55764 newSvg.node().firstChild);
55765 }
55766
55767 newSvg.attr({
55768 'class': svgClass,
55769 height: _svgBBox.height,
55770 preserveAspectRatio: 'xMinYMin meet'
55771 })
55772 .style({overflow: 'visible', 'pointer-events': 'none'});
55773
55774 var fill = _context.node().style.fill || 'black';
55775 var g = newSvg.select('g');
55776 g.attr({fill: fill, stroke: fill});
55777
55778 var newSvgW = getSize(g, 'width');
55779 var newSvgH = getSize(g, 'height');
55780 var newX = +_context.attr('x') - newSvgW *
55781 {start: 0, middle: 0.5, end: 1}[_context.attr('text-anchor') || 'start'];
55782 // font baseline is about 1/4 fontSize below centerline
55783 var textHeight = fontSize || getSize(_context, 'height');
55784 var dy = -textHeight / 4;
55785
55786 if(svgClass[0] === 'y') {
55787 mathjaxGroup.attr({
55788 transform: 'rotate(' + [-90, +_context.attr('x'), +_context.attr('y')] +
55789 ')' + strTranslate(-newSvgW / 2, dy - newSvgH / 2)
55790 });
55791 newSvg.attr({x: +_context.attr('x'), y: +_context.attr('y')});
55792 } else if(svgClass[0] === 'l') {
55793 newSvg.attr({x: _context.attr('x'), y: dy - (newSvgH / 2)});
55794 } else if(svgClass[0] === 'a' && svgClass.indexOf('atitle') !== 0) {
55795 newSvg.attr({x: 0, y: dy});
55796 } else {
55797 newSvg.attr({x: newX, y: (+_context.attr('y') + dy - newSvgH / 2)});
55798 }
55799
55800 if(_callback) _callback.call(_context, mathjaxGroup);
55801 resolve(mathjaxGroup);
55802 });
55803 }));
55804 } else showText();
55805
55806 return _context;
55807};
55808
55809
55810// MathJax
55811
55812var LT_MATCH = /(<|&lt;|&#60;)/g;
55813var GT_MATCH = /(>|&gt;|&#62;)/g;
55814
55815function cleanEscapesForTex(s) {
55816 return s.replace(LT_MATCH, '\\lt ')
55817 .replace(GT_MATCH, '\\gt ');
55818}
55819
55820function texToSVG(_texString, _config, _callback) {
55821 var originalRenderer,
55822 originalConfig,
55823 originalProcessSectionDelay,
55824 tmpDiv;
55825
55826 MathJax.Hub.Queue(
55827 function() {
55828 originalConfig = Lib.extendDeepAll({}, MathJax.Hub.config);
55829
55830 originalProcessSectionDelay = MathJax.Hub.processSectionDelay;
55831 if(MathJax.Hub.processSectionDelay !== undefined) {
55832 // MathJax 2.5+
55833 MathJax.Hub.processSectionDelay = 0;
55834 }
55835
55836 return MathJax.Hub.Config({
55837 messageStyle: 'none',
55838 tex2jax: {
55839 inlineMath: [['$', '$'], ['\\(', '\\)']]
55840 },
55841 displayAlign: 'left',
55842 });
55843 },
55844 function() {
55845 // Get original renderer
55846 originalRenderer = MathJax.Hub.config.menuSettings.renderer;
55847 if(originalRenderer !== 'SVG') {
55848 return MathJax.Hub.setRenderer('SVG');
55849 }
55850 },
55851 function() {
55852 var randomID = 'math-output-' + Lib.randstr({}, 64);
55853 tmpDiv = d3.select('body').append('div')
55854 .attr({id: randomID})
55855 .style({visibility: 'hidden', position: 'absolute'})
55856 .style({'font-size': _config.fontSize + 'px'})
55857 .text(cleanEscapesForTex(_texString));
55858
55859 return MathJax.Hub.Typeset(tmpDiv.node());
55860 },
55861 function() {
55862 var glyphDefs = d3.select('body').select('#MathJax_SVG_glyphs');
55863
55864 if(tmpDiv.select('.MathJax_SVG').empty() || !tmpDiv.select('svg').node()) {
55865 Lib.log('There was an error in the tex syntax.', _texString);
55866 _callback();
55867 } else {
55868 var svgBBox = tmpDiv.select('svg').node().getBoundingClientRect();
55869 _callback(tmpDiv.select('.MathJax_SVG'), glyphDefs, svgBBox);
55870 }
55871
55872 tmpDiv.remove();
55873
55874 if(originalRenderer !== 'SVG') {
55875 return MathJax.Hub.setRenderer(originalRenderer);
55876 }
55877 },
55878 function() {
55879 if(originalProcessSectionDelay !== undefined) {
55880 MathJax.Hub.processSectionDelay = originalProcessSectionDelay;
55881 }
55882 return MathJax.Hub.Config(originalConfig);
55883 });
55884}
55885
55886var TAG_STYLES = {
55887 // would like to use baseline-shift for sub/sup but FF doesn't support it
55888 // so we need to use dy along with the uber hacky shift-back-to
55889 // baseline below
55890 sup: 'font-size:70%',
55891 sub: 'font-size:70%',
55892 b: 'font-weight:bold',
55893 i: 'font-style:italic',
55894 a: 'cursor:pointer',
55895 span: '',
55896 em: 'font-style:italic;font-weight:bold'
55897};
55898
55899// baseline shifts for sub and sup
55900var SHIFT_DY = {
55901 sub: '0.3em',
55902 sup: '-0.6em'
55903};
55904// reset baseline by adding a tspan (empty except for a zero-width space)
55905// with dy of -70% * SHIFT_DY (because font-size=70%)
55906var RESET_DY = {
55907 sub: '-0.21em',
55908 sup: '0.42em'
55909};
55910var ZERO_WIDTH_SPACE = '\u200b';
55911
55912/*
55913 * Whitelist of protocols in user-supplied urls. Mostly we want to avoid javascript
55914 * and related attack vectors. The empty items are there for IE, that in various
55915 * versions treats relative paths as having different flavors of no protocol, while
55916 * other browsers have these explicitly inherit the protocol of the page they're in.
55917 */
55918var PROTOCOLS = ['http:', 'https:', 'mailto:', '', undefined, ':'];
55919
55920var NEWLINES = exports.NEWLINES = /(\r\n?|\n)/g;
55921
55922var SPLIT_TAGS = /(<[^<>]*>)/;
55923
55924var ONE_TAG = /<(\/?)([^ >]*)(\s+(.*))?>/i;
55925
55926var BR_TAG = /<br(\s+.*)?>/i;
55927exports.BR_TAG_ALL = /<br(\s+.*)?>/gi;
55928
55929/*
55930 * style and href: pull them out of either single or double quotes. Also
55931 * - target: (_blank|_self|_parent|_top|framename)
55932 * note that you can't use target to get a popup but if you use popup,
55933 * a `framename` will be passed along as the name of the popup window.
55934 * per the spec, cannot contain whitespace.
55935 * for backward compatibility we default to '_blank'
55936 * - popup: a custom one for us to enable popup (new window) links. String
55937 * for window.open -> strWindowFeatures, like 'menubar=yes,width=500,height=550'
55938 * note that at least in Chrome, you need to give at least one property
55939 * in this string or the page will open in a new tab anyway. We follow this
55940 * convention and will not make a popup if this string is empty.
55941 * per the spec, cannot contain whitespace.
55942 *
55943 * Because we hack in other attributes with style (sub & sup), drop any trailing
55944 * semicolon in user-supplied styles so we can consistently append the tag-dependent style
55945 *
55946 * These are for tag attributes; Chrome anyway will convert entities in
55947 * attribute values, but not in attribute names
55948 * you can test this by for example:
55949 * > p = document.createElement('p')
55950 * > p.innerHTML = '<span styl&#x65;="font-color:r&#x65;d;">Hi</span>'
55951 * > p.innerHTML
55952 * <- '<span styl&#x65;="font-color:red;">Hi</span>'
55953 */
55954var STYLEMATCH = /(^|[\s"'])style\s*=\s*("([^"]*);?"|'([^']*);?')/i;
55955var HREFMATCH = /(^|[\s"'])href\s*=\s*("([^"]*)"|'([^']*)')/i;
55956var TARGETMATCH = /(^|[\s"'])target\s*=\s*("([^"\s]*)"|'([^'\s]*)')/i;
55957var POPUPMATCH = /(^|[\s"'])popup\s*=\s*("([\w=,]*)"|'([\w=,]*)')/i;
55958
55959// dedicated matcher for these quoted regexes, that can return their results
55960// in two different places
55961function getQuotedMatch(_str, re) {
55962 if(!_str) return null;
55963 var match = _str.match(re);
55964 var result = match && (match[3] || match[4]);
55965 return result && convertEntities(result);
55966}
55967
55968var COLORMATCH = /(^|;)\s*color:/;
55969
55970/**
55971 * Strip string of tags
55972 *
55973 * @param {string} _str : input string
55974 * @param {object} opts :
55975 * - len {number} max length of output string
55976 * - allowedTags {array} list of pseudo-html tags to NOT strip
55977 * @return {string}
55978 */
55979exports.plainText = function(_str, opts) {
55980 opts = opts || {};
55981
55982 var len = (opts.len !== undefined && opts.len !== -1) ? opts.len : Infinity;
55983 var allowedTags = opts.allowedTags !== undefined ? opts.allowedTags : ['br'];
55984
55985 var ellipsis = '...';
55986 var eLen = ellipsis.length;
55987
55988 var oldParts = _str.split(SPLIT_TAGS);
55989 var newParts = [];
55990 var prevTag = '';
55991 var l = 0;
55992
55993 for(var i = 0; i < oldParts.length; i++) {
55994 var p = oldParts[i];
55995 var match = p.match(ONE_TAG);
55996 var tagType = match && match[2].toLowerCase();
55997
55998 if(tagType) {
55999 // N.B. tags do not count towards string length
56000 if(allowedTags.indexOf(tagType) !== -1) {
56001 newParts.push(p);
56002 prevTag = tagType;
56003 }
56004 } else {
56005 var pLen = p.length;
56006
56007 if((l + pLen) < len) {
56008 newParts.push(p);
56009 l += pLen;
56010 } else if(l < len) {
56011 var pLen2 = len - l;
56012
56013 if(prevTag && (prevTag !== 'br' || pLen2 <= eLen || pLen <= eLen)) {
56014 newParts.pop();
56015 }
56016
56017 if(len > eLen) {
56018 newParts.push(p.substr(0, pLen2 - eLen) + ellipsis);
56019 } else {
56020 newParts.push(p.substr(0, pLen2));
56021 }
56022 break;
56023 }
56024
56025 prevTag = '';
56026 }
56027 }
56028
56029 return newParts.join('');
56030};
56031
56032/*
56033 * N.B. HTML entities are listed without the leading '&' and trailing ';'
56034 * https://www.freeformatter.com/html-entities.html
56035 *
56036 * FWIW if we wanted to support the full set, it has 2261 entries:
56037 * https://www.w3.org/TR/html5/entities.json
56038 * though I notice that some of these are duplicates and/or are missing ";"
56039 * eg: "&amp;", "&amp", "&AMP;", and "&AMP" all map to "&"
56040 * We no longer need to include numeric entities here, these are now handled
56041 * by String.fromCodePoint/fromCharCode
56042 *
56043 * Anyway the only ones that are really important to allow are the HTML special
56044 * chars <, >, and &, because these ones can trigger special processing if not
56045 * replaced by the corresponding entity.
56046 */
56047var entityToUnicode = {
56048 mu: 'μ',
56049 amp: '&',
56050 lt: '<',
56051 gt: '>',
56052 nbsp: ' ',
56053 times: '×',
56054 plusmn: '±',
56055 deg: '°'
56056};
56057
56058// NOTE: in general entities can contain uppercase too (so [a-zA-Z]) but all the
56059// ones we support use only lowercase. If we ever change that, update the regex.
56060var ENTITY_MATCH = /&(#\d+|#x[\da-fA-F]+|[a-z]+);/g;
56061function convertEntities(_str) {
56062 return _str.replace(ENTITY_MATCH, function(fullMatch, innerMatch) {
56063 var outChar;
56064 if(innerMatch.charAt(0) === '#') {
56065 // cannot use String.fromCodePoint in IE
56066 outChar = fromCodePoint(
56067 innerMatch.charAt(1) === 'x' ?
56068 parseInt(innerMatch.substr(2), 16) :
56069 parseInt(innerMatch.substr(1), 10)
56070 );
56071 } else outChar = entityToUnicode[innerMatch];
56072
56073 // as in regular HTML, if we didn't decode the entity just
56074 // leave the raw text in place.
56075 return outChar || fullMatch;
56076 });
56077}
56078exports.convertEntities = convertEntities;
56079
56080function fromCodePoint(code) {
56081 // Don't allow overflow. In Chrome this turns into � but I feel like it's
56082 // more useful to just not convert it at all.
56083 if(code > 0x10FFFF) return;
56084 var stringFromCodePoint = String.fromCodePoint;
56085 if(stringFromCodePoint) return stringFromCodePoint(code);
56086
56087 // IE doesn't have String.fromCodePoint
56088 // see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint
56089 var stringFromCharCode = String.fromCharCode;
56090 if(code <= 0xFFFF) return stringFromCharCode(code);
56091 return stringFromCharCode(
56092 (code >> 10) + 0xD7C0,
56093 (code % 0x400) + 0xDC00
56094 );
56095}
56096
56097/*
56098 * buildSVGText: convert our pseudo-html into SVG tspan elements, and attach these
56099 * to containerNode
56100 *
56101 * @param {svg text element} containerNode: the <text> node to insert this text into
56102 * @param {string} str: the pseudo-html string to convert to svg
56103 *
56104 * @returns {bool}: does the result contain any links? We need to handle the text element
56105 * somewhat differently if it does, so just keep track of this when it happens.
56106 */
56107function buildSVGText(containerNode, str) {
56108 /*
56109 * Normalize behavior between IE and others wrt newlines and whitespace:pre
56110 * this combination makes IE barf https://github.com/plotly/plotly.js/issues/746
56111 * Chrome and FF display \n, \r, or \r\n as a space in this mode.
56112 * I feel like at some point we turned these into <br> but currently we don't so
56113 * I'm just going to cement what we do now in Chrome and FF
56114 */
56115 str = str.replace(NEWLINES, ' ');
56116
56117 var hasLink = false;
56118
56119 // as we're building the text, keep track of what elements we're nested inside
56120 // nodeStack will be an array of {node, type, style, href, target, popup}
56121 // where only type: 'a' gets the last 3 and node is only added when it's created
56122 var nodeStack = [];
56123 var currentNode;
56124 var currentLine = -1;
56125
56126 function newLine() {
56127 currentLine++;
56128
56129 var lineNode = document.createElementNS(xmlnsNamespaces.svg, 'tspan');
56130 d3.select(lineNode).attr({
56131 class: 'line',
56132 dy: (currentLine * LINE_SPACING) + 'em'
56133 });
56134 containerNode.appendChild(lineNode);
56135
56136 currentNode = lineNode;
56137
56138 var oldNodeStack = nodeStack;
56139 nodeStack = [{node: lineNode}];
56140
56141 if(oldNodeStack.length > 1) {
56142 for(var i = 1; i < oldNodeStack.length; i++) {
56143 enterNode(oldNodeStack[i]);
56144 }
56145 }
56146 }
56147
56148 function enterNode(nodeSpec) {
56149 var type = nodeSpec.type;
56150 var nodeAttrs = {};
56151 var nodeType;
56152
56153 if(type === 'a') {
56154 nodeType = 'a';
56155 var target = nodeSpec.target;
56156 var href = nodeSpec.href;
56157 var popup = nodeSpec.popup;
56158 if(href) {
56159 nodeAttrs = {
56160 'xlink:xlink:show': (target === '_blank' || target.charAt(0) !== '_') ? 'new' : 'replace',
56161 target: target,
56162 'xlink:xlink:href': href
56163 };
56164 if(popup) {
56165 // security: href and target are not inserted as code but
56166 // as attributes. popup is, but limited to /[A-Za-z0-9_=,]/
56167 nodeAttrs.onclick = 'window.open(this.href.baseVal,this.target.baseVal,"' +
56168 popup + '");return false;';
56169 }
56170 }
56171 } else nodeType = 'tspan';
56172
56173 if(nodeSpec.style) nodeAttrs.style = nodeSpec.style;
56174
56175 var newNode = document.createElementNS(xmlnsNamespaces.svg, nodeType);
56176
56177 if(type === 'sup' || type === 'sub') {
56178 addTextNode(currentNode, ZERO_WIDTH_SPACE);
56179 currentNode.appendChild(newNode);
56180
56181 var resetter = document.createElementNS(xmlnsNamespaces.svg, 'tspan');
56182 addTextNode(resetter, ZERO_WIDTH_SPACE);
56183 d3.select(resetter).attr('dy', RESET_DY[type]);
56184 nodeAttrs.dy = SHIFT_DY[type];
56185
56186 currentNode.appendChild(newNode);
56187 currentNode.appendChild(resetter);
56188 } else {
56189 currentNode.appendChild(newNode);
56190 }
56191
56192 d3.select(newNode).attr(nodeAttrs);
56193
56194 currentNode = nodeSpec.node = newNode;
56195 nodeStack.push(nodeSpec);
56196 }
56197
56198 function addTextNode(node, text) {
56199 node.appendChild(document.createTextNode(text));
56200 }
56201
56202 function exitNode(type) {
56203 // A bare closing tag can't close the root node. If we encounter this it
56204 // means there's an extra closing tag that can just be ignored:
56205 if(nodeStack.length === 1) {
56206 Lib.log('Ignoring unexpected end tag </' + type + '>.', str);
56207 return;
56208 }
56209
56210 var innerNode = nodeStack.pop();
56211
56212 if(type !== innerNode.type) {
56213 Lib.log('Start tag <' + innerNode.type + '> doesnt match end tag <' +
56214 type + '>. Pretending it did match.', str);
56215 }
56216 currentNode = nodeStack[nodeStack.length - 1].node;
56217 }
56218
56219 var hasLines = BR_TAG.test(str);
56220
56221 if(hasLines) newLine();
56222 else {
56223 currentNode = containerNode;
56224 nodeStack = [{node: containerNode}];
56225 }
56226
56227 var parts = str.split(SPLIT_TAGS);
56228 for(var i = 0; i < parts.length; i++) {
56229 var parti = parts[i];
56230 var match = parti.match(ONE_TAG);
56231 var tagType = match && match[2].toLowerCase();
56232 var tagStyle = TAG_STYLES[tagType];
56233
56234 if(tagType === 'br') {
56235 newLine();
56236 } else if(tagStyle === undefined) {
56237 addTextNode(currentNode, convertEntities(parti));
56238 } else {
56239 // tag - open or close
56240 if(match[1]) {
56241 exitNode(tagType);
56242 } else {
56243 var extra = match[4];
56244
56245 var nodeSpec = {type: tagType};
56246
56247 // now add style, from both the tag name and any extra css
56248 // Most of the svg css that users will care about is just like html,
56249 // but font color is different (uses fill). Let our users ignore this.
56250 var css = getQuotedMatch(extra, STYLEMATCH);
56251 if(css) {
56252 css = css.replace(COLORMATCH, '$1 fill:');
56253 if(tagStyle) css += ';' + tagStyle;
56254 } else if(tagStyle) css = tagStyle;
56255
56256 if(css) nodeSpec.style = css;
56257
56258 if(tagType === 'a') {
56259 hasLink = true;
56260
56261 var href = getQuotedMatch(extra, HREFMATCH);
56262
56263 if(href) {
56264 var safeHref = sanitizeHref(href);
56265 if(safeHref) {
56266 nodeSpec.href = safeHref;
56267 nodeSpec.target = getQuotedMatch(extra, TARGETMATCH) || '_blank';
56268 nodeSpec.popup = getQuotedMatch(extra, POPUPMATCH);
56269 }
56270 }
56271 }
56272
56273 enterNode(nodeSpec);
56274 }
56275 }
56276 }
56277
56278 return hasLink;
56279}
56280
56281function sanitizeHref(href) {
56282 var decodedHref = encodeURI(decodeURI(href));
56283 var dummyAnchor1 = document.createElement('a');
56284 var dummyAnchor2 = document.createElement('a');
56285 dummyAnchor1.href = href;
56286 dummyAnchor2.href = decodedHref;
56287
56288 var p1 = dummyAnchor1.protocol;
56289 var p2 = dummyAnchor2.protocol;
56290
56291 // check safe protocols
56292 if(
56293 PROTOCOLS.indexOf(p1) !== -1 &&
56294 PROTOCOLS.indexOf(p2) !== -1
56295 ) {
56296 return decodedHref;
56297 } else {
56298 return '';
56299 }
56300}
56301
56302/*
56303 * sanitizeHTML: port of buildSVGText aimed at providing a clean subset of HTML
56304 * @param {string} str: the html string to clean
56305 * @returns {string}: a cleaned and normalized version of the input,
56306 * supporting only a small subset of html
56307 */
56308exports.sanitizeHTML = function sanitizeHTML(str) {
56309 str = str.replace(NEWLINES, ' ');
56310
56311 var rootNode = document.createElement('p');
56312 var currentNode = rootNode;
56313 var nodeStack = [];
56314
56315 var parts = str.split(SPLIT_TAGS);
56316 for(var i = 0; i < parts.length; i++) {
56317 var parti = parts[i];
56318 var match = parti.match(ONE_TAG);
56319 var tagType = match && match[2].toLowerCase();
56320
56321 if(tagType in TAG_STYLES) {
56322 if(match[1]) {
56323 if(nodeStack.length) {
56324 currentNode = nodeStack.pop();
56325 }
56326 } else {
56327 var extra = match[4];
56328
56329 var css = getQuotedMatch(extra, STYLEMATCH);
56330 var nodeAttrs = css ? {style: css} : {};
56331
56332 if(tagType === 'a') {
56333 var href = getQuotedMatch(extra, HREFMATCH);
56334
56335 if(href) {
56336 var safeHref = sanitizeHref(href);
56337 if(safeHref) {
56338 nodeAttrs.href = safeHref;
56339 var target = getQuotedMatch(extra, TARGETMATCH);
56340 if(target) {
56341 nodeAttrs.target = target;
56342 }
56343 }
56344 }
56345 }
56346
56347 var newNode = document.createElement(tagType);
56348 currentNode.appendChild(newNode);
56349 d3.select(newNode).attr(nodeAttrs);
56350
56351 currentNode = newNode;
56352 nodeStack.push(newNode);
56353 }
56354 } else {
56355 currentNode.appendChild(
56356 document.createTextNode(convertEntities(parti))
56357 );
56358 }
56359 }
56360 var key = 'innerHTML'; // i.e. to avoid pass test-syntax
56361 return rootNode[key];
56362};
56363
56364exports.lineCount = function lineCount(s) {
56365 return s.selectAll('tspan.line').size() || 1;
56366};
56367
56368exports.positionText = function positionText(s, x, y) {
56369 return s.each(function() {
56370 var text = d3.select(this);
56371
56372 function setOrGet(attr, val) {
56373 if(val === undefined) {
56374 val = text.attr(attr);
56375 if(val === null) {
56376 text.attr(attr, 0);
56377 val = 0;
56378 }
56379 } else text.attr(attr, val);
56380 return val;
56381 }
56382
56383 var thisX = setOrGet('x', x);
56384 var thisY = setOrGet('y', y);
56385
56386 if(this.nodeName === 'text') {
56387 text.selectAll('tspan.line').attr({x: thisX, y: thisY});
56388 }
56389 });
56390};
56391
56392function alignHTMLWith(_base, container, options) {
56393 var alignH = options.horizontalAlign;
56394 var alignV = options.verticalAlign || 'top';
56395 var bRect = _base.node().getBoundingClientRect();
56396 var cRect = container.node().getBoundingClientRect();
56397 var thisRect;
56398 var getTop;
56399 var getLeft;
56400
56401 if(alignV === 'bottom') {
56402 getTop = function() { return bRect.bottom - thisRect.height; };
56403 } else if(alignV === 'middle') {
56404 getTop = function() { return bRect.top + (bRect.height - thisRect.height) / 2; };
56405 } else { // default: top
56406 getTop = function() { return bRect.top; };
56407 }
56408
56409 if(alignH === 'right') {
56410 getLeft = function() { return bRect.right - thisRect.width; };
56411 } else if(alignH === 'center') {
56412 getLeft = function() { return bRect.left + (bRect.width - thisRect.width) / 2; };
56413 } else { // default: left
56414 getLeft = function() { return bRect.left; };
56415 }
56416
56417 return function() {
56418 thisRect = this.node().getBoundingClientRect();
56419
56420 var x0 = getLeft() - cRect.left;
56421 var y0 = getTop() - cRect.top;
56422 var gd = options.gd || {};
56423 if(options.gd) {
56424 gd._fullLayout._calcInverseTransform(gd);
56425 var transformedCoords = Lib.apply3DTransform(gd._fullLayout._invTransform)(x0, y0);
56426 x0 = transformedCoords[0];
56427 y0 = transformedCoords[1];
56428 }
56429
56430 this.style({
56431 top: y0 + 'px',
56432 left: x0 + 'px',
56433 'z-index': 1000
56434 });
56435 return this;
56436 };
56437}
56438
56439var onePx = '1px ';
56440
56441exports.makeTextShadow = function(color) {
56442 var x = onePx;
56443 var y = onePx;
56444 var b = onePx;
56445 return x + y + b + color + ', ' +
56446 '-' + x + '-' + y + b + color + ', ' +
56447 x + '-' + y + b + color + ', ' +
56448 '-' + x + y + b + color;
56449};
56450
56451/*
56452 * Editable title
56453 * @param {d3.selection} context: the element being edited. Normally text,
56454 * but if it isn't, you should provide the styling options
56455 * @param {object} options:
56456 * @param {div} options.gd: graphDiv
56457 * @param {d3.selection} options.delegate: item to bind events to if not this
56458 * @param {boolean} options.immediate: start editing now (true) or on click (false, default)
56459 * @param {string} options.fill: font color if not as shown
56460 * @param {string} options.background: background color if not as shown
56461 * @param {string} options.text: initial text, if not as shown
56462 * @param {string} options.horizontalAlign: alignment of the edit box wrt. the bound element
56463 * @param {string} options.verticalAlign: alignment of the edit box wrt. the bound element
56464 */
56465
56466exports.makeEditable = function(context, options) {
56467 var gd = options.gd;
56468 var _delegate = options.delegate;
56469 var dispatch = d3.dispatch('edit', 'input', 'cancel');
56470 var handlerElement = _delegate || context;
56471
56472 context.style({'pointer-events': _delegate ? 'none' : 'all'});
56473
56474 if(context.size() !== 1) throw new Error('boo');
56475
56476 function handleClick() {
56477 appendEditable();
56478 context.style({opacity: 0});
56479 // also hide any mathjax svg
56480 var svgClass = handlerElement.attr('class');
56481 var mathjaxClass;
56482 if(svgClass) mathjaxClass = '.' + svgClass.split(' ')[0] + '-math-group';
56483 else mathjaxClass = '[class*=-math-group]';
56484 if(mathjaxClass) {
56485 d3.select(context.node().parentNode).select(mathjaxClass).style({opacity: 0});
56486 }
56487 }
56488
56489 function selectElementContents(_el) {
56490 var el = _el.node();
56491 var range = document.createRange();
56492 range.selectNodeContents(el);
56493 var sel = window.getSelection();
56494 sel.removeAllRanges();
56495 sel.addRange(range);
56496 el.focus();
56497 }
56498
56499 function appendEditable() {
56500 var plotDiv = d3.select(gd);
56501 var container = plotDiv.select('.svg-container');
56502 var div = container.append('div');
56503 var cStyle = context.node().style;
56504 var fontSize = parseFloat(cStyle.fontSize || 12);
56505
56506 var initialText = options.text;
56507 if(initialText === undefined) initialText = context.attr('data-unformatted');
56508
56509 div.classed('plugin-editable editable', true)
56510 .style({
56511 position: 'absolute',
56512 'font-family': cStyle.fontFamily || 'Arial',
56513 'font-size': fontSize,
56514 color: options.fill || cStyle.fill || 'black',
56515 opacity: 1,
56516 'background-color': options.background || 'transparent',
56517 outline: '#ffffff33 1px solid',
56518 margin: [-fontSize / 8 + 1, 0, 0, -1].join('px ') + 'px',
56519 padding: '0',
56520 'box-sizing': 'border-box'
56521 })
56522 .attr({contenteditable: true})
56523 .text(initialText)
56524 .call(alignHTMLWith(context, container, options))
56525 .on('blur', function() {
56526 gd._editing = false;
56527 context.text(this.textContent)
56528 .style({opacity: 1});
56529 var svgClass = d3.select(this).attr('class');
56530 var mathjaxClass;
56531 if(svgClass) mathjaxClass = '.' + svgClass.split(' ')[0] + '-math-group';
56532 else mathjaxClass = '[class*=-math-group]';
56533 if(mathjaxClass) {
56534 d3.select(context.node().parentNode).select(mathjaxClass).style({opacity: 0});
56535 }
56536 var text = this.textContent;
56537 d3.select(this).transition().duration(0).remove();
56538 d3.select(document).on('mouseup', null);
56539 dispatch.edit.call(context, text);
56540 })
56541 .on('focus', function() {
56542 var editDiv = this;
56543 gd._editing = true;
56544 d3.select(document).on('mouseup', function() {
56545 if(d3.event.target === editDiv) return false;
56546 if(document.activeElement === div.node()) div.node().blur();
56547 });
56548 })
56549 .on('keyup', function() {
56550 if(d3.event.which === 27) {
56551 gd._editing = false;
56552 context.style({opacity: 1});
56553 d3.select(this)
56554 .style({opacity: 0})
56555 .on('blur', function() { return false; })
56556 .transition().remove();
56557 dispatch.cancel.call(context, this.textContent);
56558 } else {
56559 dispatch.input.call(context, this.textContent);
56560 d3.select(this).call(alignHTMLWith(context, container, options));
56561 }
56562 })
56563 .on('keydown', function() {
56564 if(d3.event.which === 13) this.blur();
56565 })
56566 .call(selectElementContents);
56567 }
56568
56569 if(options.immediate) handleClick();
56570 else handlerElement.on('click', handleClick);
56571
56572 return d3.rebind(context, dispatch, 'on');
56573};
56574
56575},{"../constants/alignment":262,"../constants/xmlns_namespaces":268,"../lib":287,"@plotly/d3":20}],311:[function(_dereq_,module,exports){
56576'use strict';
56577
56578var timerCache = {};
56579
56580/**
56581 * Throttle a callback. `callback` executes synchronously only if
56582 * more than `minInterval` milliseconds have already elapsed since the latest
56583 * call (if any). Otherwise we wait until `minInterval` is over and execute the
56584 * last callback received while waiting.
56585 * So the first and last events in a train are always executed (eventually)
56586 * but some of the events in the middle can be dropped.
56587 *
56588 * @param {string} id: an identifier to mark events to throttle together
56589 * @param {number} minInterval: minimum time, in milliseconds, between
56590 * invocations of `callback`
56591 * @param {function} callback: the function to throttle. `callback` itself
56592 * should be a purely synchronous function.
56593 */
56594exports.throttle = function throttle(id, minInterval, callback) {
56595 var cache = timerCache[id];
56596 var now = Date.now();
56597
56598 if(!cache) {
56599 /*
56600 * Throw out old items before making a new one, to prevent the cache
56601 * getting overgrown, for example from old plots that have been replaced.
56602 * 1 minute age is arbitrary.
56603 */
56604 for(var idi in timerCache) {
56605 if(timerCache[idi].ts < now - 60000) {
56606 delete timerCache[idi];
56607 }
56608 }
56609 cache = timerCache[id] = {ts: 0, timer: null};
56610 }
56611
56612 _clearTimeout(cache);
56613
56614 function exec() {
56615 callback();
56616 cache.ts = Date.now();
56617 if(cache.onDone) {
56618 cache.onDone();
56619 cache.onDone = null;
56620 }
56621 }
56622
56623 if(now > cache.ts + minInterval) {
56624 exec();
56625 return;
56626 }
56627
56628 cache.timer = setTimeout(function() {
56629 exec();
56630 cache.timer = null;
56631 }, minInterval);
56632};
56633
56634exports.done = function(id) {
56635 var cache = timerCache[id];
56636 if(!cache || !cache.timer) return Promise.resolve();
56637
56638 return new Promise(function(resolve) {
56639 var previousOnDone = cache.onDone;
56640 cache.onDone = function onDone() {
56641 if(previousOnDone) previousOnDone();
56642 resolve();
56643 cache.onDone = null;
56644 };
56645 });
56646};
56647
56648/**
56649 * Clear the throttle cache for one or all timers
56650 * @param {optional string} id:
56651 * if provided, clear just this timer
56652 * if omitted, clear all timers (mainly useful for testing)
56653 */
56654exports.clear = function(id) {
56655 if(id) {
56656 _clearTimeout(timerCache[id]);
56657 delete timerCache[id];
56658 } else {
56659 for(var idi in timerCache) exports.clear(idi);
56660 }
56661};
56662
56663function _clearTimeout(cache) {
56664 if(cache && cache.timer !== null) {
56665 clearTimeout(cache.timer);
56666 cache.timer = null;
56667 }
56668}
56669
56670},{}],312:[function(_dereq_,module,exports){
56671'use strict';
56672
56673var isNumeric = _dereq_('fast-isnumeric');
56674
56675/**
56676 * convert a linear value into a logged value, folding negative numbers into
56677 * the given range
56678 */
56679module.exports = function toLogRange(val, range) {
56680 if(val > 0) return Math.log(val) / Math.LN10;
56681
56682 // move a negative value reference to a log axis - just put the
56683 // result at the lowest range value on the plot (or if the range also went negative,
56684 // one millionth of the top of the range)
56685 var newVal = Math.log(Math.min(range[0], range[1])) / Math.LN10;
56686 if(!isNumeric(newVal)) newVal = Math.log(Math.max(range[0], range[1])) / Math.LN10 - 6;
56687 return newVal;
56688};
56689
56690},{"fast-isnumeric":33}],313:[function(_dereq_,module,exports){
56691'use strict';
56692
56693module.exports = {
56694 moduleType: 'locale',
56695 name: 'en-US',
56696 dictionary: {
56697 'Click to enter Colorscale title': 'Click to enter Colorscale title'
56698 },
56699 format: {
56700 date: '%m/%d/%Y'
56701 }
56702};
56703
56704},{}],314:[function(_dereq_,module,exports){
56705'use strict';
56706
56707module.exports = {
56708 moduleType: 'locale',
56709 name: 'en',
56710 dictionary: {
56711 'Click to enter Colorscale title': 'Click to enter Colourscale title'
56712 },
56713 format: {
56714 days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
56715 shortDays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
56716 months: [
56717 'January', 'February', 'March', 'April', 'May', 'June',
56718 'July', 'August', 'September', 'October', 'November', 'December'
56719 ],
56720 shortMonths: [
56721 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
56722 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
56723 ],
56724 periods: ['AM', 'PM'],
56725 dateTime: '%a %b %e %X %Y',
56726 date: '%d/%m/%Y',
56727 time: '%H:%M:%S',
56728 decimal: '.',
56729 thousands: ',',
56730 grouping: [3],
56731 currency: ['$', ''],
56732 year: '%Y',
56733 month: '%b %Y',
56734 dayMonth: '%b %-d',
56735 dayMonthYear: '%b %-d, %Y'
56736 }
56737};
56738
56739},{}],315:[function(_dereq_,module,exports){
56740'use strict';
56741
56742var Registry = _dereq_('../registry');
56743
56744/*
56745 * containerArrayMatch: does this attribute string point into a
56746 * layout container array?
56747 *
56748 * @param {String} astr: an attribute string, like *annotations[2].text*
56749 *
56750 * @returns {Object | false} Returns false if `astr` doesn't match a container
56751 * array. If it does, returns:
56752 * {array: {String}, index: {Number}, property: {String}}
56753 * ie the attribute string for the array, the index within the array (or ''
56754 * if the whole array) and the property within that (or '' if the whole array
56755 * or the whole object)
56756 */
56757module.exports = function containerArrayMatch(astr) {
56758 var rootContainers = Registry.layoutArrayContainers;
56759 var regexpContainers = Registry.layoutArrayRegexes;
56760 var rootPart = astr.split('[')[0];
56761 var arrayStr;
56762 var match;
56763
56764 // look for regexp matches first, because they may be nested inside root matches
56765 // eg updatemenus[i].buttons is nested inside updatemenus
56766 for(var i = 0; i < regexpContainers.length; i++) {
56767 match = astr.match(regexpContainers[i]);
56768 if(match && match.index === 0) {
56769 arrayStr = match[0];
56770 break;
56771 }
56772 }
56773
56774 // now look for root matches
56775 if(!arrayStr) arrayStr = rootContainers[rootContainers.indexOf(rootPart)];
56776
56777 if(!arrayStr) return false;
56778
56779 var tail = astr.substr(arrayStr.length);
56780 if(!tail) return {array: arrayStr, index: '', property: ''};
56781
56782 match = tail.match(/^\[(0|[1-9][0-9]*)\](\.(.+))?$/);
56783 if(!match) return false;
56784
56785 return {array: arrayStr, index: Number(match[1]), property: match[3] || ''};
56786};
56787
56788},{"../registry":376}],316:[function(_dereq_,module,exports){
56789'use strict';
56790
56791var Lib = _dereq_('../lib');
56792var extendFlat = Lib.extendFlat;
56793var isPlainObject = Lib.isPlainObject;
56794
56795var traceOpts = {
56796 valType: 'flaglist',
56797 extras: ['none'],
56798 flags: ['calc', 'clearAxisTypes', 'plot', 'style', 'markerSize', 'colorbars'],
56799};
56800
56801var layoutOpts = {
56802 valType: 'flaglist',
56803 extras: ['none'],
56804 flags: [
56805 'calc', 'plot', 'legend', 'ticks', 'axrange',
56806 'layoutstyle', 'modebar', 'camera', 'arraydraw', 'colorbars'
56807 ],
56808};
56809
56810// flags for inside restyle/relayout include a few extras
56811// that shouldn't be used in attributes, to deal with certain
56812// combinations and conditionals efficiently
56813var traceEditTypeFlags = traceOpts.flags.slice()
56814 .concat(['fullReplot']);
56815
56816var layoutEditTypeFlags = layoutOpts.flags.slice()
56817 .concat('layoutReplot');
56818
56819module.exports = {
56820 traces: traceOpts,
56821 layout: layoutOpts,
56822 /*
56823 * default (all false) edit flags for restyle (traces)
56824 * creates a new object each call, so the caller can mutate freely
56825 */
56826 traceFlags: function() { return falseObj(traceEditTypeFlags); },
56827
56828 /*
56829 * default (all false) edit flags for relayout
56830 * creates a new object each call, so the caller can mutate freely
56831 */
56832 layoutFlags: function() { return falseObj(layoutEditTypeFlags); },
56833
56834 /*
56835 * update `flags` with the `editType` values found in `attr`
56836 */
56837 update: function(flags, attr) {
56838 var editType = attr.editType;
56839 if(editType && editType !== 'none') {
56840 var editTypeParts = editType.split('+');
56841 for(var i = 0; i < editTypeParts.length; i++) {
56842 flags[editTypeParts[i]] = true;
56843 }
56844 }
56845 },
56846
56847 overrideAll: overrideAll
56848};
56849
56850function falseObj(keys) {
56851 var out = {};
56852 for(var i = 0; i < keys.length; i++) out[keys[i]] = false;
56853 return out;
56854}
56855
56856/**
56857 * For attributes that are largely copied from elsewhere into a plot type that doesn't
56858 * support partial redraws - overrides the editType field of all attributes in the object
56859 *
56860 * @param {object} attrs: the attributes to override. Will not be mutated.
56861 * @param {string} editTypeOverride: the new editType to use
56862 * @param {'nested'|'from-root'} overrideContainers:
56863 * - 'nested' will override editType for nested containers but not the root.
56864 * - 'from-root' will also override editType of the root container.
56865 * Containers below the absolute top level (trace or layout root) DO need an
56866 * editType even if they are not `valObject`s themselves (eg `scatter.marker`)
56867 * to handle the case where you edit the whole container.
56868 *
56869 * @return {object} a new attributes object with `editType` modified as directed
56870 */
56871function overrideAll(attrs, editTypeOverride, overrideContainers) {
56872 var out = extendFlat({}, attrs);
56873 for(var key in out) {
56874 var attr = out[key];
56875 if(isPlainObject(attr)) {
56876 out[key] = overrideOne(attr, editTypeOverride, overrideContainers, key);
56877 }
56878 }
56879 if(overrideContainers === 'from-root') out.editType = editTypeOverride;
56880
56881 return out;
56882}
56883
56884function overrideOne(attr, editTypeOverride, overrideContainers, key) {
56885 if(attr.valType) {
56886 var out = extendFlat({}, attr);
56887 out.editType = editTypeOverride;
56888
56889 if(Array.isArray(attr.items)) {
56890 out.items = new Array(attr.items.length);
56891 for(var i = 0; i < attr.items.length; i++) {
56892 out.items[i] = overrideOne(attr.items[i], editTypeOverride, 'from-root');
56893 }
56894 }
56895 return out;
56896 } else {
56897 // don't provide an editType for the _deprecated container
56898 return overrideAll(attr, editTypeOverride,
56899 (key.charAt(0) === '_') ? 'nested' : 'from-root');
56900 }
56901}
56902
56903},{"../lib":287}],317:[function(_dereq_,module,exports){
56904'use strict';
56905
56906var isNumeric = _dereq_('fast-isnumeric');
56907var m4FromQuat = _dereq_('gl-mat4/fromQuat');
56908
56909var Registry = _dereq_('../registry');
56910var Lib = _dereq_('../lib');
56911var Plots = _dereq_('../plots/plots');
56912var AxisIds = _dereq_('../plots/cartesian/axis_ids');
56913var Color = _dereq_('../components/color');
56914
56915var cleanId = AxisIds.cleanId;
56916var getFromTrace = AxisIds.getFromTrace;
56917var traceIs = Registry.traceIs;
56918
56919// clear the promise queue if one of them got rejected
56920exports.clearPromiseQueue = function(gd) {
56921 if(Array.isArray(gd._promises) && gd._promises.length > 0) {
56922 Lib.log('Clearing previous rejected promises from queue.');
56923 }
56924
56925 gd._promises = [];
56926};
56927
56928// make a few changes to the layout right away
56929// before it gets used for anything
56930// backward compatibility and cleanup of nonstandard options
56931exports.cleanLayout = function(layout) {
56932 var i, j;
56933
56934 if(!layout) layout = {};
56935
56936 // cannot have (x|y)axis1, numbering goes axis, axis2, axis3...
56937 if(layout.xaxis1) {
56938 if(!layout.xaxis) layout.xaxis = layout.xaxis1;
56939 delete layout.xaxis1;
56940 }
56941 if(layout.yaxis1) {
56942 if(!layout.yaxis) layout.yaxis = layout.yaxis1;
56943 delete layout.yaxis1;
56944 }
56945 if(layout.scene1) {
56946 if(!layout.scene) layout.scene = layout.scene1;
56947 delete layout.scene1;
56948 }
56949
56950 var axisAttrRegex = (Plots.subplotsRegistry.cartesian || {}).attrRegex;
56951 var polarAttrRegex = (Plots.subplotsRegistry.polar || {}).attrRegex;
56952 var ternaryAttrRegex = (Plots.subplotsRegistry.ternary || {}).attrRegex;
56953 var sceneAttrRegex = (Plots.subplotsRegistry.gl3d || {}).attrRegex;
56954
56955 var keys = Object.keys(layout);
56956 for(i = 0; i < keys.length; i++) {
56957 var key = keys[i];
56958
56959 if(axisAttrRegex && axisAttrRegex.test(key)) {
56960 // modifications to cartesian axes
56961
56962 var ax = layout[key];
56963 if(ax.anchor && ax.anchor !== 'free') {
56964 ax.anchor = cleanId(ax.anchor);
56965 }
56966 if(ax.overlaying) ax.overlaying = cleanId(ax.overlaying);
56967
56968 // old method of axis type - isdate and islog (before category existed)
56969 if(!ax.type) {
56970 if(ax.isdate) ax.type = 'date';
56971 else if(ax.islog) ax.type = 'log';
56972 else if(ax.isdate === false && ax.islog === false) ax.type = 'linear';
56973 }
56974 if(ax.autorange === 'withzero' || ax.autorange === 'tozero') {
56975 ax.autorange = true;
56976 ax.rangemode = 'tozero';
56977 }
56978 delete ax.islog;
56979 delete ax.isdate;
56980 delete ax.categories; // replaced by _categories
56981
56982 // prune empty domain arrays made before the new nestedProperty
56983 if(emptyContainer(ax, 'domain')) delete ax.domain;
56984
56985 // autotick -> tickmode
56986 if(ax.autotick !== undefined) {
56987 if(ax.tickmode === undefined) {
56988 ax.tickmode = ax.autotick ? 'auto' : 'linear';
56989 }
56990 delete ax.autotick;
56991 }
56992
56993 cleanTitle(ax);
56994 } else if(polarAttrRegex && polarAttrRegex.test(key)) {
56995 // modifications for polar
56996
56997 var polar = layout[key];
56998 cleanTitle(polar.radialaxis);
56999 } else if(ternaryAttrRegex && ternaryAttrRegex.test(key)) {
57000 // modifications for ternary
57001
57002 var ternary = layout[key];
57003 cleanTitle(ternary.aaxis);
57004 cleanTitle(ternary.baxis);
57005 cleanTitle(ternary.caxis);
57006 } else if(sceneAttrRegex && sceneAttrRegex.test(key)) {
57007 // modifications for 3D scenes
57008
57009 var scene = layout[key];
57010
57011 // clean old Camera coords
57012 var cameraposition = scene.cameraposition;
57013
57014 if(Array.isArray(cameraposition) && cameraposition[0].length === 4) {
57015 var rotation = cameraposition[0];
57016 var center = cameraposition[1];
57017 var radius = cameraposition[2];
57018 var mat = m4FromQuat([], rotation);
57019 var eye = [];
57020
57021 for(j = 0; j < 3; ++j) {
57022 eye[j] = center[j] + radius * mat[2 + 4 * j];
57023 }
57024
57025 scene.camera = {
57026 eye: {x: eye[0], y: eye[1], z: eye[2]},
57027 center: {x: center[0], y: center[1], z: center[2]},
57028 up: {x: 0, y: 0, z: 1} // we just ignore calculating camera z up in this case
57029 };
57030
57031 delete scene.cameraposition;
57032 }
57033
57034 // clean axis titles
57035 cleanTitle(scene.xaxis);
57036 cleanTitle(scene.yaxis);
57037 cleanTitle(scene.zaxis);
57038 }
57039 }
57040
57041 var annotationsLen = Array.isArray(layout.annotations) ? layout.annotations.length : 0;
57042 for(i = 0; i < annotationsLen; i++) {
57043 var ann = layout.annotations[i];
57044
57045 if(!Lib.isPlainObject(ann)) continue;
57046
57047 if(ann.ref) {
57048 if(ann.ref === 'paper') {
57049 ann.xref = 'paper';
57050 ann.yref = 'paper';
57051 } else if(ann.ref === 'data') {
57052 ann.xref = 'x';
57053 ann.yref = 'y';
57054 }
57055 delete ann.ref;
57056 }
57057
57058 cleanAxRef(ann, 'xref');
57059 cleanAxRef(ann, 'yref');
57060 }
57061
57062 var shapesLen = Array.isArray(layout.shapes) ? layout.shapes.length : 0;
57063 for(i = 0; i < shapesLen; i++) {
57064 var shape = layout.shapes[i];
57065
57066 if(!Lib.isPlainObject(shape)) continue;
57067
57068 cleanAxRef(shape, 'xref');
57069 cleanAxRef(shape, 'yref');
57070 }
57071
57072 var imagesLen = Array.isArray(layout.images) ? layout.images.length : 0;
57073 for(i = 0; i < imagesLen; i++) {
57074 var image = layout.images[i];
57075
57076 if(!Lib.isPlainObject(image)) continue;
57077
57078 cleanAxRef(image, 'xref');
57079 cleanAxRef(image, 'yref');
57080 }
57081
57082 var legend = layout.legend;
57083 if(legend) {
57084 // check for old-style legend positioning (x or y is +/- 100)
57085 if(legend.x > 3) {
57086 legend.x = 1.02;
57087 legend.xanchor = 'left';
57088 } else if(legend.x < -2) {
57089 legend.x = -0.02;
57090 legend.xanchor = 'right';
57091 }
57092
57093 if(legend.y > 3) {
57094 legend.y = 1.02;
57095 legend.yanchor = 'bottom';
57096 } else if(legend.y < -2) {
57097 legend.y = -0.02;
57098 legend.yanchor = 'top';
57099 }
57100 }
57101
57102 // clean plot title
57103 cleanTitle(layout);
57104
57105 /*
57106 * Moved from rotate -> orbit for dragmode
57107 */
57108 if(layout.dragmode === 'rotate') layout.dragmode = 'orbit';
57109
57110 // sanitize rgb(fractions) and rgba(fractions) that old tinycolor
57111 // supported, but new tinycolor does not because they're not valid css
57112 Color.clean(layout);
57113
57114 // clean the layout container in layout.template
57115 if(layout.template && layout.template.layout) {
57116 exports.cleanLayout(layout.template.layout);
57117 }
57118
57119 return layout;
57120};
57121
57122function cleanAxRef(container, attr) {
57123 var valIn = container[attr];
57124 var axLetter = attr.charAt(0);
57125 if(valIn && valIn !== 'paper') {
57126 container[attr] = cleanId(valIn, axLetter, true);
57127 }
57128}
57129
57130/**
57131 * Cleans up old title attribute structure (flat) in favor of the new one (nested).
57132 *
57133 * @param {Object} titleContainer - an object potentially including deprecated title attributes
57134 */
57135function cleanTitle(titleContainer) {
57136 if(titleContainer) {
57137 // title -> title.text
57138 // (although title used to be a string attribute,
57139 // numbers are accepted as well)
57140 if(typeof titleContainer.title === 'string' || typeof titleContainer.title === 'number') {
57141 titleContainer.title = {
57142 text: titleContainer.title
57143 };
57144 }
57145
57146 rewireAttr('titlefont', 'font');
57147 rewireAttr('titleposition', 'position');
57148 rewireAttr('titleside', 'side');
57149 rewireAttr('titleoffset', 'offset');
57150 }
57151
57152 function rewireAttr(oldAttrName, newAttrName) {
57153 var oldAttrSet = titleContainer[oldAttrName];
57154 var newAttrSet = titleContainer.title && titleContainer.title[newAttrName];
57155
57156 if(oldAttrSet && !newAttrSet) {
57157 // Ensure title object exists
57158 if(!titleContainer.title) {
57159 titleContainer.title = {};
57160 }
57161
57162 titleContainer.title[newAttrName] = titleContainer[oldAttrName];
57163 delete titleContainer[oldAttrName];
57164 }
57165 }
57166}
57167
57168/*
57169 * cleanData: Make a few changes to the data for backward compatibility
57170 * before it gets used for anything. Modifies the data traces users provide.
57171 *
57172 * Important: if you're going to add something here that modifies a data array,
57173 * update it in place so the new array === the old one.
57174 */
57175exports.cleanData = function(data) {
57176 for(var tracei = 0; tracei < data.length; tracei++) {
57177 var trace = data[tracei];
57178 var i;
57179
57180 // use xbins to bin data in x, and ybins to bin data in y
57181 if(trace.type === 'histogramy' && 'xbins' in trace && !('ybins' in trace)) {
57182 trace.ybins = trace.xbins;
57183 delete trace.xbins;
57184 }
57185
57186 // error_y.opacity is obsolete - merge into color
57187 if(trace.error_y && 'opacity' in trace.error_y) {
57188 var dc = Color.defaults;
57189 var yeColor = trace.error_y.color || (traceIs(trace, 'bar') ?
57190 Color.defaultLine :
57191 dc[tracei % dc.length]);
57192 trace.error_y.color = Color.addOpacity(
57193 Color.rgb(yeColor),
57194 Color.opacity(yeColor) * trace.error_y.opacity);
57195 delete trace.error_y.opacity;
57196 }
57197
57198 // convert bardir to orientation, and put the data into
57199 // the axes it's eventually going to be used with
57200 if('bardir' in trace) {
57201 if(trace.bardir === 'h' && (traceIs(trace, 'bar') ||
57202 trace.type.substr(0, 9) === 'histogram')) {
57203 trace.orientation = 'h';
57204 exports.swapXYData(trace);
57205 }
57206 delete trace.bardir;
57207 }
57208
57209 // now we have only one 1D histogram type, and whether
57210 // it uses x or y data depends on trace.orientation
57211 if(trace.type === 'histogramy') exports.swapXYData(trace);
57212 if(trace.type === 'histogramx' || trace.type === 'histogramy') {
57213 trace.type = 'histogram';
57214 }
57215
57216 // scl->scale, reversescl->reversescale
57217 if('scl' in trace && !('colorscale' in trace)) {
57218 trace.colorscale = trace.scl;
57219 delete trace.scl;
57220 }
57221 if('reversescl' in trace && !('reversescale' in trace)) {
57222 trace.reversescale = trace.reversescl;
57223 delete trace.reversescl;
57224 }
57225
57226 // axis ids x1 -> x, y1-> y
57227 if(trace.xaxis) trace.xaxis = cleanId(trace.xaxis, 'x');
57228 if(trace.yaxis) trace.yaxis = cleanId(trace.yaxis, 'y');
57229
57230 // scene ids scene1 -> scene
57231 if(traceIs(trace, 'gl3d') && trace.scene) {
57232 trace.scene = Plots.subplotsRegistry.gl3d.cleanId(trace.scene);
57233 }
57234
57235 if(!traceIs(trace, 'pie-like') && !traceIs(trace, 'bar-like')) {
57236 if(Array.isArray(trace.textposition)) {
57237 for(i = 0; i < trace.textposition.length; i++) {
57238 trace.textposition[i] = cleanTextPosition(trace.textposition[i]);
57239 }
57240 } else if(trace.textposition) {
57241 trace.textposition = cleanTextPosition(trace.textposition);
57242 }
57243 }
57244
57245 // fix typo in colorscale definition
57246 var _module = Registry.getModule(trace);
57247 if(_module && _module.colorbar) {
57248 var containerName = _module.colorbar.container;
57249 var container = containerName ? trace[containerName] : trace;
57250 if(container && container.colorscale) {
57251 if(container.colorscale === 'YIGnBu') container.colorscale = 'YlGnBu';
57252 if(container.colorscale === 'YIOrRd') container.colorscale = 'YlOrRd';
57253 }
57254 }
57255
57256 // fix typo in surface 'highlight*' definitions
57257 if(trace.type === 'surface' && Lib.isPlainObject(trace.contours)) {
57258 var dims = ['x', 'y', 'z'];
57259
57260 for(i = 0; i < dims.length; i++) {
57261 var opts = trace.contours[dims[i]];
57262
57263 if(!Lib.isPlainObject(opts)) continue;
57264
57265 if(opts.highlightColor) {
57266 opts.highlightcolor = opts.highlightColor;
57267 delete opts.highlightColor;
57268 }
57269
57270 if(opts.highlightWidth) {
57271 opts.highlightwidth = opts.highlightWidth;
57272 delete opts.highlightWidth;
57273 }
57274 }
57275 }
57276
57277 // fixes from converting finance from transforms to real trace types
57278 if(trace.type === 'candlestick' || trace.type === 'ohlc') {
57279 var increasingShowlegend = (trace.increasing || {}).showlegend !== false;
57280 var decreasingShowlegend = (trace.decreasing || {}).showlegend !== false;
57281 var increasingName = cleanFinanceDir(trace.increasing);
57282 var decreasingName = cleanFinanceDir(trace.decreasing);
57283
57284 // now figure out something smart to do with the separate direction
57285 // names we removed
57286 if((increasingName !== false) && (decreasingName !== false)) {
57287 // both sub-names existed: base name previously had no effect
57288 // so ignore it and try to find a shared part of the sub-names
57289
57290 var newName = commonPrefix(
57291 increasingName, decreasingName,
57292 increasingShowlegend, decreasingShowlegend
57293 );
57294 // if no common part, leave whatever name was (or wasn't) there
57295 if(newName) trace.name = newName;
57296 } else if((increasingName || decreasingName) && !trace.name) {
57297 // one sub-name existed but not the base name - just use the sub-name
57298 trace.name = increasingName || decreasingName;
57299 }
57300 }
57301
57302 // transforms backward compatibility fixes
57303 if(Array.isArray(trace.transforms)) {
57304 var transforms = trace.transforms;
57305
57306 for(i = 0; i < transforms.length; i++) {
57307 var transform = transforms[i];
57308
57309 if(!Lib.isPlainObject(transform)) continue;
57310
57311 switch(transform.type) {
57312 case 'filter':
57313 if(transform.filtersrc) {
57314 transform.target = transform.filtersrc;
57315 delete transform.filtersrc;
57316 }
57317
57318 if(transform.calendar) {
57319 if(!transform.valuecalendar) {
57320 transform.valuecalendar = transform.calendar;
57321 }
57322 delete transform.calendar;
57323 }
57324 break;
57325
57326 case 'groupby':
57327 // Name has changed from `style` to `styles`, so use `style` but prefer `styles`:
57328 transform.styles = transform.styles || transform.style;
57329
57330 if(transform.styles && !Array.isArray(transform.styles)) {
57331 var prevStyles = transform.styles;
57332 var styleKeys = Object.keys(prevStyles);
57333
57334 transform.styles = [];
57335 for(var j = 0; j < styleKeys.length; j++) {
57336 transform.styles.push({
57337 target: styleKeys[j],
57338 value: prevStyles[styleKeys[j]]
57339 });
57340 }
57341 }
57342 break;
57343 }
57344 }
57345 }
57346
57347 // prune empty containers made before the new nestedProperty
57348 if(emptyContainer(trace, 'line')) delete trace.line;
57349 if('marker' in trace) {
57350 if(emptyContainer(trace.marker, 'line')) delete trace.marker.line;
57351 if(emptyContainer(trace, 'marker')) delete trace.marker;
57352 }
57353
57354 // sanitize rgb(fractions) and rgba(fractions) that old tinycolor
57355 // supported, but new tinycolor does not because they're not valid css
57356 Color.clean(trace);
57357
57358 // remove obsolete autobin(x|y) attributes, but only if true
57359 // if false, this needs to happen in Histogram.calc because it
57360 // can be a one-time autobin so we need to know the results before
57361 // we can push them back into the trace.
57362 if(trace.autobinx) {
57363 delete trace.autobinx;
57364 delete trace.xbins;
57365 }
57366 if(trace.autobiny) {
57367 delete trace.autobiny;
57368 delete trace.ybins;
57369 }
57370
57371 cleanTitle(trace);
57372 if(trace.colorbar) cleanTitle(trace.colorbar);
57373 if(trace.marker && trace.marker.colorbar) cleanTitle(trace.marker.colorbar);
57374 if(trace.line && trace.line.colorbar) cleanTitle(trace.line.colorbar);
57375 if(trace.aaxis) cleanTitle(trace.aaxis);
57376 if(trace.baxis) cleanTitle(trace.baxis);
57377 }
57378};
57379
57380function cleanFinanceDir(dirContainer) {
57381 if(!Lib.isPlainObject(dirContainer)) return false;
57382
57383 var dirName = dirContainer.name;
57384
57385 delete dirContainer.name;
57386 delete dirContainer.showlegend;
57387
57388 return (typeof dirName === 'string' || typeof dirName === 'number') && String(dirName);
57389}
57390
57391function commonPrefix(name1, name2, show1, show2) {
57392 // if only one is shown in the legend, use that
57393 if(show1 && !show2) return name1;
57394 if(show2 && !show1) return name2;
57395
57396 // if both or neither are in the legend, check if one is blank (or whitespace)
57397 // and use the other one
57398 // note that hover labels can still use the name even if the legend doesn't
57399 if(!name1.trim()) return name2;
57400 if(!name2.trim()) return name1;
57401
57402 var minLen = Math.min(name1.length, name2.length);
57403 var i;
57404 for(i = 0; i < minLen; i++) {
57405 if(name1.charAt(i) !== name2.charAt(i)) break;
57406 }
57407
57408 var out = name1.substr(0, i);
57409 return out.trim();
57410}
57411
57412// textposition - support partial attributes (ie just 'top')
57413// and incorrect use of middle / center etc.
57414function cleanTextPosition(textposition) {
57415 var posY = 'middle';
57416 var posX = 'center';
57417
57418 if(typeof textposition === 'string') {
57419 if(textposition.indexOf('top') !== -1) posY = 'top';
57420 else if(textposition.indexOf('bottom') !== -1) posY = 'bottom';
57421
57422 if(textposition.indexOf('left') !== -1) posX = 'left';
57423 else if(textposition.indexOf('right') !== -1) posX = 'right';
57424 }
57425
57426 return posY + ' ' + posX;
57427}
57428
57429function emptyContainer(outer, innerStr) {
57430 return (innerStr in outer) &&
57431 (typeof outer[innerStr] === 'object') &&
57432 (Object.keys(outer[innerStr]).length === 0);
57433}
57434
57435
57436// swap all the data and data attributes associated with x and y
57437exports.swapXYData = function(trace) {
57438 var i;
57439 Lib.swapAttrs(trace, ['?', '?0', 'd?', '?bins', 'nbins?', 'autobin?', '?src', 'error_?']);
57440 if(Array.isArray(trace.z) && Array.isArray(trace.z[0])) {
57441 if(trace.transpose) delete trace.transpose;
57442 else trace.transpose = true;
57443 }
57444 if(trace.error_x && trace.error_y) {
57445 var errorY = trace.error_y;
57446 var copyYstyle = ('copy_ystyle' in errorY) ?
57447 errorY.copy_ystyle :
57448 !(errorY.color || errorY.thickness || errorY.width);
57449 Lib.swapAttrs(trace, ['error_?.copy_ystyle']);
57450 if(copyYstyle) {
57451 Lib.swapAttrs(trace, ['error_?.color', 'error_?.thickness', 'error_?.width']);
57452 }
57453 }
57454 if(typeof trace.hoverinfo === 'string') {
57455 var hoverInfoParts = trace.hoverinfo.split('+');
57456 for(i = 0; i < hoverInfoParts.length; i++) {
57457 if(hoverInfoParts[i] === 'x') hoverInfoParts[i] = 'y';
57458 else if(hoverInfoParts[i] === 'y') hoverInfoParts[i] = 'x';
57459 }
57460 trace.hoverinfo = hoverInfoParts.join('+');
57461 }
57462};
57463
57464// coerce traceIndices input to array of trace indices
57465exports.coerceTraceIndices = function(gd, traceIndices) {
57466 if(isNumeric(traceIndices)) {
57467 return [traceIndices];
57468 } else if(!Array.isArray(traceIndices) || !traceIndices.length) {
57469 return gd.data.map(function(_, i) { return i; });
57470 } else if(Array.isArray(traceIndices)) {
57471 var traceIndicesOut = [];
57472 for(var i = 0; i < traceIndices.length; i++) {
57473 if(Lib.isIndex(traceIndices[i], gd.data.length)) {
57474 traceIndicesOut.push(traceIndices[i]);
57475 } else {
57476 Lib.warn('trace index (', traceIndices[i], ') is not a number or is out of bounds');
57477 }
57478 }
57479 return traceIndicesOut;
57480 }
57481
57482 return traceIndices;
57483};
57484
57485/**
57486 * Manages logic around array container item creation / deletion / update
57487 * that nested property alone can't handle.
57488 *
57489 * @param {Object} np
57490 * nested property of update attribute string about trace or layout object
57491 * @param {*} newVal
57492 * update value passed to restyle / relayout / update
57493 * @param {Object} undoit
57494 * undo hash (N.B. undoit may be mutated here).
57495 *
57496 */
57497exports.manageArrayContainers = function(np, newVal, undoit) {
57498 var obj = np.obj;
57499 var parts = np.parts;
57500 var pLength = parts.length;
57501 var pLast = parts[pLength - 1];
57502
57503 var pLastIsNumber = isNumeric(pLast);
57504
57505 if(pLastIsNumber && newVal === null) {
57506 // delete item
57507
57508 // Clear item in array container when new value is null
57509 var contPath = parts.slice(0, pLength - 1).join('.');
57510 var cont = Lib.nestedProperty(obj, contPath).get();
57511 cont.splice(pLast, 1);
57512
57513 // Note that nested property clears null / undefined at end of
57514 // array container, but not within them.
57515 } else if(pLastIsNumber && np.get() === undefined) {
57516 // create item
57517
57518 // When adding a new item, make sure undo command will remove it
57519 if(np.get() === undefined) undoit[np.astr] = null;
57520
57521 np.set(newVal);
57522 } else {
57523 // update item
57524
57525 // If the last part of attribute string isn't a number,
57526 // np.set is all we need.
57527 np.set(newVal);
57528 }
57529};
57530
57531/*
57532 * Match the part to strip off to turn an attribute into its parent
57533 * really it should be either '.some_characters' or '[number]'
57534 * but we're a little more permissive here and match either
57535 * '.not_brackets_or_dot' or '[not_brackets_or_dot]'
57536 */
57537var ATTR_TAIL_RE = /(\.[^\[\]\.]+|\[[^\[\]\.]+\])$/;
57538
57539function getParent(attr) {
57540 var tail = attr.search(ATTR_TAIL_RE);
57541 if(tail > 0) return attr.substr(0, tail);
57542}
57543
57544/*
57545 * hasParent: does an attribute object contain a parent of the given attribute?
57546 * for example, given 'images[2].x' do we also have 'images' or 'images[2]'?
57547 *
57548 * @param {Object} aobj
57549 * update object, whose keys are attribute strings and values are their new settings
57550 * @param {string} attr
57551 * the attribute string to test against
57552 * @returns {Boolean}
57553 * is a parent of attr present in aobj?
57554 */
57555exports.hasParent = function(aobj, attr) {
57556 var attrParent = getParent(attr);
57557 while(attrParent) {
57558 if(attrParent in aobj) return true;
57559 attrParent = getParent(attrParent);
57560 }
57561 return false;
57562};
57563
57564/**
57565 * Empty out types for all axes containing these traces so we auto-set them again
57566 *
57567 * @param {object} gd
57568 * @param {[integer]} traces: trace indices to search for axes to clear the types of
57569 * @param {object} layoutUpdate: any update being done concurrently to the layout,
57570 * which may supercede clearing the axis types
57571 */
57572var axLetters = ['x', 'y', 'z'];
57573exports.clearAxisTypes = function(gd, traces, layoutUpdate) {
57574 for(var i = 0; i < traces.length; i++) {
57575 var trace = gd._fullData[i];
57576 for(var j = 0; j < 3; j++) {
57577 var ax = getFromTrace(gd, trace, axLetters[j]);
57578
57579 // do not clear log type - that's never an auto result so must have been intentional
57580 if(ax && ax.type !== 'log') {
57581 var axAttr = ax._name;
57582 var sceneName = ax._id.substr(1);
57583 if(sceneName.substr(0, 5) === 'scene') {
57584 if(layoutUpdate[sceneName] !== undefined) continue;
57585 axAttr = sceneName + '.' + axAttr;
57586 }
57587 var typeAttr = axAttr + '.type';
57588
57589 if(layoutUpdate[axAttr] === undefined && layoutUpdate[typeAttr] === undefined) {
57590 Lib.nestedProperty(gd.layout, typeAttr).set(null);
57591 }
57592 }
57593 }
57594 }
57595};
57596
57597},{"../components/color":157,"../lib":287,"../plots/cartesian/axis_ids":338,"../plots/plots":369,"../registry":376,"fast-isnumeric":33,"gl-mat4/fromQuat":39}],318:[function(_dereq_,module,exports){
57598'use strict';
57599
57600var main = _dereq_('./plot_api');
57601
57602exports._doPlot = main._doPlot;
57603exports.newPlot = main.newPlot;
57604exports.restyle = main.restyle;
57605exports.relayout = main.relayout;
57606exports.redraw = main.redraw;
57607exports.update = main.update;
57608exports._guiRestyle = main._guiRestyle;
57609exports._guiRelayout = main._guiRelayout;
57610exports._guiUpdate = main._guiUpdate;
57611exports._storeDirectGUIEdit = main._storeDirectGUIEdit;
57612exports.react = main.react;
57613exports.extendTraces = main.extendTraces;
57614exports.prependTraces = main.prependTraces;
57615exports.addTraces = main.addTraces;
57616exports.deleteTraces = main.deleteTraces;
57617exports.moveTraces = main.moveTraces;
57618exports.purge = main.purge;
57619exports.addFrames = main.addFrames;
57620exports.deleteFrames = main.deleteFrames;
57621exports.animate = main.animate;
57622exports.setPlotConfig = main.setPlotConfig;
57623
57624exports.toImage = _dereq_('./to_image');
57625exports.validate = _dereq_('./validate');
57626exports.downloadImage = _dereq_('../snapshot/download');
57627
57628var templateApi = _dereq_('./template_api');
57629exports.makeTemplate = templateApi.makeTemplate;
57630exports.validateTemplate = templateApi.validateTemplate;
57631
57632},{"../snapshot/download":378,"./plot_api":320,"./template_api":325,"./to_image":326,"./validate":327}],319:[function(_dereq_,module,exports){
57633'use strict';
57634
57635var isPlainObject = _dereq_('../lib/is_plain_object');
57636var noop = _dereq_('../lib/noop');
57637var Loggers = _dereq_('../lib/loggers');
57638var sorterAsc = _dereq_('../lib/search').sorterAsc;
57639var Registry = _dereq_('../registry');
57640
57641
57642exports.containerArrayMatch = _dereq_('./container_array_match');
57643
57644var isAddVal = exports.isAddVal = function isAddVal(val) {
57645 return val === 'add' || isPlainObject(val);
57646};
57647
57648var isRemoveVal = exports.isRemoveVal = function isRemoveVal(val) {
57649 return val === null || val === 'remove';
57650};
57651
57652/*
57653 * applyContainerArrayChanges: for managing arrays of layout components in relayout
57654 * handles them all with a consistent interface.
57655 *
57656 * Here are the supported actions -> relayout calls -> edits we get here
57657 * (as prepared in _relayout):
57658 *
57659 * add an empty obj -> {'annotations[2]': 'add'} -> {2: {'': 'add'}}
57660 * add a specific obj -> {'annotations[2]': {attrs}} -> {2: {'': {attrs}}}
57661 * delete an obj -> {'annotations[2]': 'remove'} -> {2: {'': 'remove'}}
57662 * -> {'annotations[2]': null} -> {2: {'': null}}
57663 * delete the whole array -> {'annotations': 'remove'} -> {'': {'': 'remove'}}
57664 * -> {'annotations': null} -> {'': {'': null}}
57665 * edit an object -> {'annotations[2].text': 'boo'} -> {2: {'text': 'boo'}}
57666 *
57667 * You can combine many edits to different objects. Objects are added and edited
57668 * in ascending order, then removed in descending order.
57669 * For example, starting with [a, b, c], if you want to:
57670 * - replace b with d:
57671 * {'annotations[1]': d, 'annotations[2]': null} (b is item 2 after adding d)
57672 * - add a new item d between a and b, and edit b:
57673 * {'annotations[1]': d, 'annotations[2].x': newX} (b is item 2 after adding d)
57674 * - delete b and edit c:
57675 * {'annotations[1]': null, 'annotations[2].x': newX} (c is edited before b is removed)
57676 *
57677 * You CANNOT combine adding/deleting an item at index `i` with edits to the same index `i`
57678 * You CANNOT combine replacing/deleting the whole array with anything else (for the same array).
57679 *
57680 * @param {HTMLDivElement} gd
57681 * the DOM element of the graph container div
57682 * @param {Lib.nestedProperty} componentType: the array we are editing
57683 * @param {Object} edits
57684 * the changes to make; keys are indices to edit, values are themselves objects:
57685 * {attr: newValue} of changes to make to that index (with add/remove behavior
57686 * in special values of the empty attr)
57687 * @param {Object} flags
57688 * the flags for which actions we're going to perform to display these (and
57689 * any other) changes. If we're already `recalc`ing, we don't need to redraw
57690 * individual items
57691 * @param {function} _nestedProperty
57692 * a (possibly modified for gui edits) nestedProperty constructor
57693 * The modified version takes a 3rd argument, for a prefix to the attribute
57694 * string necessary for storing GUI edits
57695 *
57696 * @returns {bool} `true` if it managed to complete drawing of the changes
57697 * `false` would mean the parent should replot.
57698 */
57699exports.applyContainerArrayChanges = function applyContainerArrayChanges(gd, np, edits, flags, _nestedProperty) {
57700 var componentType = np.astr;
57701 var supplyComponentDefaults = Registry.getComponentMethod(componentType, 'supplyLayoutDefaults');
57702 var draw = Registry.getComponentMethod(componentType, 'draw');
57703 var drawOne = Registry.getComponentMethod(componentType, 'drawOne');
57704 var replotLater = flags.replot || flags.recalc || (supplyComponentDefaults === noop) || (draw === noop);
57705 var layout = gd.layout;
57706 var fullLayout = gd._fullLayout;
57707
57708 if(edits['']) {
57709 if(Object.keys(edits).length > 1) {
57710 Loggers.warn('Full array edits are incompatible with other edits',
57711 componentType);
57712 }
57713
57714 var fullVal = edits[''][''];
57715
57716 if(isRemoveVal(fullVal)) np.set(null);
57717 else if(Array.isArray(fullVal)) np.set(fullVal);
57718 else {
57719 Loggers.warn('Unrecognized full array edit value', componentType, fullVal);
57720 return true;
57721 }
57722
57723 if(replotLater) return false;
57724
57725 supplyComponentDefaults(layout, fullLayout);
57726 draw(gd);
57727 return true;
57728 }
57729
57730 var componentNums = Object.keys(edits).map(Number).sort(sorterAsc);
57731 var componentArrayIn = np.get();
57732 var componentArray = componentArrayIn || [];
57733 // componentArrayFull is used just to keep splices in line between
57734 // full and input arrays, so private keys can be copied over after
57735 // redoing supplyDefaults
57736 // TODO: this assumes componentArray is in gd.layout - which will not be
57737 // true after we extend this to restyle
57738 var componentArrayFull = _nestedProperty(fullLayout, componentType).get();
57739
57740 var deletes = [];
57741 var firstIndexChange = -1;
57742 var maxIndex = componentArray.length;
57743 var i;
57744 var j;
57745 var componentNum;
57746 var objEdits;
57747 var objKeys;
57748 var objVal;
57749 var adding, prefix;
57750
57751 // first make the add and edit changes
57752 for(i = 0; i < componentNums.length; i++) {
57753 componentNum = componentNums[i];
57754 objEdits = edits[componentNum];
57755 objKeys = Object.keys(objEdits);
57756 objVal = objEdits[''],
57757 adding = isAddVal(objVal);
57758
57759 if(componentNum < 0 || componentNum > componentArray.length - (adding ? 0 : 1)) {
57760 Loggers.warn('index out of range', componentType, componentNum);
57761 continue;
57762 }
57763
57764 if(objVal !== undefined) {
57765 if(objKeys.length > 1) {
57766 Loggers.warn(
57767 'Insertion & removal are incompatible with edits to the same index.',
57768 componentType, componentNum);
57769 }
57770
57771 if(isRemoveVal(objVal)) {
57772 deletes.push(componentNum);
57773 } else if(adding) {
57774 if(objVal === 'add') objVal = {};
57775 componentArray.splice(componentNum, 0, objVal);
57776 if(componentArrayFull) componentArrayFull.splice(componentNum, 0, {});
57777 } else {
57778 Loggers.warn('Unrecognized full object edit value',
57779 componentType, componentNum, objVal);
57780 }
57781
57782 if(firstIndexChange === -1) firstIndexChange = componentNum;
57783 } else {
57784 for(j = 0; j < objKeys.length; j++) {
57785 prefix = componentType + '[' + componentNum + '].';
57786 _nestedProperty(componentArray[componentNum], objKeys[j], prefix)
57787 .set(objEdits[objKeys[j]]);
57788 }
57789 }
57790 }
57791
57792 // now do deletes
57793 for(i = deletes.length - 1; i >= 0; i--) {
57794 componentArray.splice(deletes[i], 1);
57795 // TODO: this drops private keys that had been stored in componentArrayFull
57796 // does this have any ill effects?
57797 if(componentArrayFull) componentArrayFull.splice(deletes[i], 1);
57798 }
57799
57800 if(!componentArray.length) np.set(null);
57801 else if(!componentArrayIn) np.set(componentArray);
57802
57803 if(replotLater) return false;
57804
57805 supplyComponentDefaults(layout, fullLayout);
57806
57807 // finally draw all the components we need to
57808 // if we added or removed any, redraw all after it
57809 if(drawOne !== noop) {
57810 var indicesToDraw;
57811 if(firstIndexChange === -1) {
57812 // there's no re-indexing to do, so only redraw components that changed
57813 indicesToDraw = componentNums;
57814 } else {
57815 // in case the component array was shortened, we still need do call
57816 // drawOne on the latter items so they get properly removed
57817 maxIndex = Math.max(componentArray.length, maxIndex);
57818 indicesToDraw = [];
57819 for(i = 0; i < componentNums.length; i++) {
57820 componentNum = componentNums[i];
57821 if(componentNum >= firstIndexChange) break;
57822 indicesToDraw.push(componentNum);
57823 }
57824 for(i = firstIndexChange; i < maxIndex; i++) {
57825 indicesToDraw.push(i);
57826 }
57827 }
57828 for(i = 0; i < indicesToDraw.length; i++) {
57829 drawOne(gd, indicesToDraw[i]);
57830 }
57831 } else draw(gd);
57832
57833 return true;
57834};
57835
57836},{"../lib/is_plain_object":288,"../lib/loggers":291,"../lib/noop":296,"../lib/search":306,"../registry":376,"./container_array_match":315}],320:[function(_dereq_,module,exports){
57837'use strict';
57838
57839var d3 = _dereq_('@plotly/d3');
57840var isNumeric = _dereq_('fast-isnumeric');
57841var hasHover = _dereq_('has-hover');
57842
57843var Lib = _dereq_('../lib');
57844var nestedProperty = Lib.nestedProperty;
57845
57846var Events = _dereq_('../lib/events');
57847var Queue = _dereq_('../lib/queue');
57848
57849var Registry = _dereq_('../registry');
57850var PlotSchema = _dereq_('./plot_schema');
57851var Plots = _dereq_('../plots/plots');
57852
57853var Axes = _dereq_('../plots/cartesian/axes');
57854var Drawing = _dereq_('../components/drawing');
57855var Color = _dereq_('../components/color');
57856var initInteractions = _dereq_('../plots/cartesian/graph_interact').initInteractions;
57857var xmlnsNamespaces = _dereq_('../constants/xmlns_namespaces');
57858var clearSelect = _dereq_('../plots/cartesian/select').clearSelect;
57859
57860var dfltConfig = _dereq_('./plot_config').dfltConfig;
57861var manageArrays = _dereq_('./manage_arrays');
57862var helpers = _dereq_('./helpers');
57863var subroutines = _dereq_('./subroutines');
57864var editTypes = _dereq_('./edit_types');
57865
57866var AX_NAME_PATTERN = _dereq_('../plots/cartesian/constants').AX_NAME_PATTERN;
57867
57868var numericNameWarningCount = 0;
57869var numericNameWarningCountLimit = 5;
57870
57871/**
57872 * Internal plot-creation function
57873 *
57874 * @param {string id or DOM element} gd
57875 * the id or DOM element of the graph container div
57876 * @param {array of objects} data
57877 * array of traces, containing the data and display information for each trace
57878 * @param {object} layout
57879 * object describing the overall display of the plot,
57880 * all the stuff that doesn't pertain to any individual trace
57881 * @param {object} config
57882 * configuration options (see ./plot_config.js for more info)
57883 *
57884 * OR
57885 *
57886 * @param {string id or DOM element} gd
57887 * the id or DOM element of the graph container div
57888 * @param {object} figure
57889 * object containing `data`, `layout`, `config`, and `frames` members
57890 *
57891 */
57892function _doPlot(gd, data, layout, config) {
57893 var frames;
57894
57895 gd = Lib.getGraphDiv(gd);
57896
57897 // Events.init is idempotent and bails early if gd has already been init'd
57898 Events.init(gd);
57899
57900 if(Lib.isPlainObject(data)) {
57901 var obj = data;
57902 data = obj.data;
57903 layout = obj.layout;
57904 config = obj.config;
57905 frames = obj.frames;
57906 }
57907
57908 var okToPlot = Events.triggerHandler(gd, 'plotly_beforeplot', [data, layout, config]);
57909 if(okToPlot === false) return Promise.reject();
57910
57911 // if there's no data or layout, and this isn't yet a plotly plot
57912 // container, log a warning to help plotly.js users debug
57913 if(!data && !layout && !Lib.isPlotDiv(gd)) {
57914 Lib.warn('Calling _doPlot as if redrawing ' +
57915 'but this container doesn\'t yet have a plot.', gd);
57916 }
57917
57918 function addFrames() {
57919 if(frames) {
57920 return exports.addFrames(gd, frames);
57921 }
57922 }
57923
57924 // transfer configuration options to gd until we move over to
57925 // a more OO like model
57926 setPlotContext(gd, config);
57927
57928 if(!layout) layout = {};
57929
57930 // hook class for plots main container (in case of plotly.js
57931 // this won't be #embedded-graph or .js-tab-contents)
57932 d3.select(gd).classed('js-plotly-plot', true);
57933
57934 // off-screen getBoundingClientRect testing space,
57935 // in #js-plotly-tester (and stored as Drawing.tester)
57936 // so we can share cached text across tabs
57937 Drawing.makeTester();
57938
57939 // collect promises for any async actions during plotting
57940 // any part of the plotting code can push to gd._promises, then
57941 // before we move to the next step, we check that they're all
57942 // complete, and empty out the promise list again.
57943 if(!Array.isArray(gd._promises)) gd._promises = [];
57944
57945 var graphWasEmpty = ((gd.data || []).length === 0 && Array.isArray(data));
57946
57947 // if there is already data on the graph, append the new data
57948 // if you only want to redraw, pass a non-array for data
57949 if(Array.isArray(data)) {
57950 helpers.cleanData(data);
57951
57952 if(graphWasEmpty) gd.data = data;
57953 else gd.data.push.apply(gd.data, data);
57954
57955 // for routines outside graph_obj that want a clean tab
57956 // (rather than appending to an existing one) gd.empty
57957 // is used to determine whether to make a new tab
57958 gd.empty = false;
57959 }
57960
57961 if(!gd.layout || graphWasEmpty) {
57962 gd.layout = helpers.cleanLayout(layout);
57963 }
57964
57965 Plots.supplyDefaults(gd);
57966
57967 var fullLayout = gd._fullLayout;
57968 var hasCartesian = fullLayout._has('cartesian');
57969
57970 // so we don't try to re-call _doPlot from inside
57971 // legend and colorbar, if margins changed
57972 fullLayout._replotting = true;
57973
57974 // make or remake the framework if we need to
57975 if(graphWasEmpty || fullLayout._shouldCreateBgLayer) {
57976 makePlotFramework(gd);
57977
57978 if(fullLayout._shouldCreateBgLayer) {
57979 delete fullLayout._shouldCreateBgLayer;
57980 }
57981 }
57982
57983 // clear gradient and pattern defs on each .plot call, because we know we'll loop through all traces
57984 Drawing.initGradients(gd);
57985 Drawing.initPatterns(gd);
57986
57987 // save initial show spikes once per graph
57988 if(graphWasEmpty) Axes.saveShowSpikeInitial(gd);
57989
57990 // prepare the data and find the autorange
57991
57992 // generate calcdata, if we need to
57993 // to force redoing calcdata, just delete it before calling _doPlot
57994 var recalc = !gd.calcdata || gd.calcdata.length !== (gd._fullData || []).length;
57995 if(recalc) Plots.doCalcdata(gd);
57996
57997 // in case it has changed, attach fullData traces to calcdata
57998 for(var i = 0; i < gd.calcdata.length; i++) {
57999 gd.calcdata[i][0].trace = gd._fullData[i];
58000 }
58001
58002 // make the figure responsive
58003 if(gd._context.responsive) {
58004 if(!gd._responsiveChartHandler) {
58005 // Keep a reference to the resize handler to purge it down the road
58006 gd._responsiveChartHandler = function() { if(!Lib.isHidden(gd)) Plots.resize(gd); };
58007
58008 // Listen to window resize
58009 window.addEventListener('resize', gd._responsiveChartHandler);
58010 }
58011 } else {
58012 Lib.clearResponsive(gd);
58013 }
58014
58015 /*
58016 * start async-friendly code - now we're actually drawing things
58017 */
58018
58019 var oldMargins = Lib.extendFlat({}, fullLayout._size);
58020
58021 // draw framework first so that margin-pushing
58022 // components can position themselves correctly
58023 var drawFrameworkCalls = 0;
58024 function drawFramework() {
58025 var basePlotModules = fullLayout._basePlotModules;
58026
58027 for(var i = 0; i < basePlotModules.length; i++) {
58028 if(basePlotModules[i].drawFramework) {
58029 basePlotModules[i].drawFramework(gd);
58030 }
58031 }
58032
58033 if(!fullLayout._glcanvas && fullLayout._has('gl')) {
58034 fullLayout._glcanvas = fullLayout._glcontainer.selectAll('.gl-canvas').data([{
58035 key: 'contextLayer',
58036 context: true,
58037 pick: false
58038 }, {
58039 key: 'focusLayer',
58040 context: false,
58041 pick: false
58042 }, {
58043 key: 'pickLayer',
58044 context: false,
58045 pick: true
58046 }], function(d) { return d.key; });
58047
58048 fullLayout._glcanvas.enter().append('canvas')
58049 .attr('class', function(d) {
58050 return 'gl-canvas gl-canvas-' + d.key.replace('Layer', '');
58051 })
58052 .style({
58053 position: 'absolute',
58054 top: 0,
58055 left: 0,
58056 overflow: 'visible',
58057 'pointer-events': 'none'
58058 });
58059 }
58060
58061 var plotGlPixelRatio = gd._context.plotGlPixelRatio;
58062 if(fullLayout._glcanvas) {
58063 fullLayout._glcanvas
58064 .attr('width', fullLayout.width * plotGlPixelRatio)
58065 .attr('height', fullLayout.height * plotGlPixelRatio)
58066 .style('width', fullLayout.width + 'px')
58067 .style('height', fullLayout.height + 'px');
58068
58069 var regl = fullLayout._glcanvas.data()[0].regl;
58070 if(regl) {
58071 // Unfortunately, this can happen when relayouting to large
58072 // width/height on some browsers.
58073 if(Math.floor(fullLayout.width * plotGlPixelRatio) !== regl._gl.drawingBufferWidth ||
58074 Math.floor(fullLayout.height * plotGlPixelRatio) !== regl._gl.drawingBufferHeight
58075 ) {
58076 var msg = 'WebGL context buffer and canvas dimensions do not match due to browser/WebGL bug.';
58077 if(drawFrameworkCalls) {
58078 Lib.error(msg);
58079 } else {
58080 Lib.log(msg + ' Clearing graph and plotting again.');
58081 Plots.cleanPlot([], {}, gd._fullData, fullLayout);
58082 Plots.supplyDefaults(gd);
58083 fullLayout = gd._fullLayout;
58084 Plots.doCalcdata(gd);
58085 drawFrameworkCalls++;
58086 return drawFramework();
58087 }
58088 }
58089 }
58090 }
58091
58092 if(fullLayout.modebar.orientation === 'h') {
58093 fullLayout._modebardiv
58094 .style('height', null)
58095 .style('width', '100%');
58096 } else {
58097 fullLayout._modebardiv
58098 .style('width', null)
58099 .style('height', fullLayout.height + 'px');
58100 }
58101
58102 return Plots.previousPromises(gd);
58103 }
58104
58105 // draw anything that can affect margins.
58106 function marginPushers() {
58107 // First reset the list of things that are allowed to change the margins
58108 // So any deleted traces or components will be wiped out of the
58109 // automargin calculation.
58110 // This means *every* margin pusher must be listed here, even if it
58111 // doesn't actually try to push the margins until later.
58112 Plots.clearAutoMarginIds(gd);
58113
58114 subroutines.drawMarginPushers(gd);
58115 Axes.allowAutoMargin(gd);
58116
58117 // TODO can this be moved elsewhere?
58118 if(fullLayout._has('pie')) {
58119 var fullData = gd._fullData;
58120 for(var i = 0; i < fullData.length; i++) {
58121 var trace = fullData[i];
58122 if(trace.type === 'pie' && trace.automargin) {
58123 Plots.allowAutoMargin(gd, 'pie.' + trace.uid + '.automargin');
58124 }
58125 }
58126 }
58127
58128 Plots.doAutoMargin(gd);
58129 return Plots.previousPromises(gd);
58130 }
58131
58132 // in case the margins changed, draw margin pushers again
58133 function marginPushersAgain() {
58134 if(!Plots.didMarginChange(oldMargins, fullLayout._size)) return;
58135
58136 return Lib.syncOrAsync([
58137 marginPushers,
58138 subroutines.layoutStyles
58139 ], gd);
58140 }
58141
58142 function positionAndAutorange() {
58143 if(!recalc) {
58144 doAutoRangeAndConstraints();
58145 return;
58146 }
58147
58148 // TODO: autosize extra for text markers and images
58149 // see https://github.com/plotly/plotly.js/issues/1111
58150 return Lib.syncOrAsync([
58151 Registry.getComponentMethod('shapes', 'calcAutorange'),
58152 Registry.getComponentMethod('annotations', 'calcAutorange'),
58153 doAutoRangeAndConstraints
58154 ], gd);
58155 }
58156
58157 function doAutoRangeAndConstraints() {
58158 if(gd._transitioning) return;
58159
58160 subroutines.doAutoRangeAndConstraints(gd);
58161
58162 // store initial ranges *after* enforcing constraints, otherwise
58163 // we will never look like we're at the initial ranges
58164 if(graphWasEmpty) Axes.saveRangeInitial(gd);
58165
58166 // this one is different from shapes/annotations calcAutorange
58167 // the others incorporate those components into ax._extremes,
58168 // this one actually sets the ranges in rangesliders.
58169 Registry.getComponentMethod('rangeslider', 'calcAutorange')(gd);
58170 }
58171
58172 // draw ticks, titles, and calculate axis scaling (._b, ._m)
58173 function drawAxes() {
58174 return Axes.draw(gd, graphWasEmpty ? '' : 'redraw');
58175 }
58176
58177 var seq = [
58178 Plots.previousPromises,
58179 addFrames,
58180 drawFramework,
58181 marginPushers,
58182 marginPushersAgain
58183 ];
58184
58185 if(hasCartesian) seq.push(positionAndAutorange);
58186
58187 seq.push(subroutines.layoutStyles);
58188 if(hasCartesian) {
58189 seq.push(
58190 drawAxes,
58191 function insideTickLabelsAutorange(gd) {
58192 if(gd._fullLayout._insideTickLabelsAutorange) {
58193 relayout(gd, gd._fullLayout._insideTickLabelsAutorange).then(function() {
58194 gd._fullLayout._insideTickLabelsAutorange = undefined;
58195 });
58196 }
58197 }
58198 );
58199 }
58200
58201 seq.push(
58202 subroutines.drawData,
58203 subroutines.finalDraw,
58204 initInteractions,
58205 Plots.addLinks,
58206 Plots.rehover,
58207 Plots.redrag,
58208 // TODO: doAutoMargin is only needed here for axis automargin, which
58209 // happens outside of marginPushers where all the other automargins are
58210 // calculated. Would be much better to separate margin calculations from
58211 // component drawing - see https://github.com/plotly/plotly.js/issues/2704
58212 Plots.doAutoMargin,
58213 saveRangeInitialForInsideTickLabels,
58214 Plots.previousPromises
58215 );
58216
58217 function saveRangeInitialForInsideTickLabels(gd) {
58218 if(gd._fullLayout._insideTickLabelsAutorange) {
58219 if(graphWasEmpty) Axes.saveRangeInitial(gd, true);
58220 }
58221 }
58222
58223 // even if everything we did was synchronous, return a promise
58224 // so that the caller doesn't care which route we took
58225 var plotDone = Lib.syncOrAsync(seq, gd);
58226 if(!plotDone || !plotDone.then) plotDone = Promise.resolve();
58227
58228 return plotDone.then(function() {
58229 emitAfterPlot(gd);
58230 return gd;
58231 });
58232}
58233
58234function emitAfterPlot(gd) {
58235 var fullLayout = gd._fullLayout;
58236
58237 if(fullLayout._redrawFromAutoMarginCount) {
58238 fullLayout._redrawFromAutoMarginCount--;
58239 } else {
58240 gd.emit('plotly_afterplot');
58241 }
58242}
58243
58244function setPlotConfig(obj) {
58245 return Lib.extendFlat(dfltConfig, obj);
58246}
58247
58248function setBackground(gd, bgColor) {
58249 try {
58250 gd._fullLayout._paper.style('background', bgColor);
58251 } catch(e) {
58252 Lib.error(e);
58253 }
58254}
58255
58256function opaqueSetBackground(gd, bgColor) {
58257 var blend = Color.combine(bgColor, 'white');
58258 setBackground(gd, blend);
58259}
58260
58261function setPlotContext(gd, config) {
58262 if(!gd._context) {
58263 gd._context = Lib.extendDeep({}, dfltConfig);
58264
58265 // stash <base> href, used to make robust clipPath URLs
58266 var base = d3.select('base');
58267 gd._context._baseUrl = base.size() && base.attr('href') ?
58268 window.location.href.split('#')[0] :
58269 '';
58270 }
58271
58272 var context = gd._context;
58273
58274 var i, keys, key;
58275
58276 if(config) {
58277 keys = Object.keys(config);
58278 for(i = 0; i < keys.length; i++) {
58279 key = keys[i];
58280 if(key === 'editable' || key === 'edits') continue;
58281 if(key in context) {
58282 if(key === 'setBackground' && config[key] === 'opaque') {
58283 context[key] = opaqueSetBackground;
58284 } else {
58285 context[key] = config[key];
58286 }
58287 }
58288 }
58289
58290 // map plot3dPixelRatio to plotGlPixelRatio for backward compatibility
58291 if(config.plot3dPixelRatio && !context.plotGlPixelRatio) {
58292 context.plotGlPixelRatio = context.plot3dPixelRatio;
58293 }
58294
58295 // now deal with editable and edits - first editable overrides
58296 // everything, then edits refines
58297 var editable = config.editable;
58298 if(editable !== undefined) {
58299 // we're not going to *use* context.editable, we're only going to
58300 // use context.edits... but keep it for the record
58301 context.editable = editable;
58302
58303 keys = Object.keys(context.edits);
58304 for(i = 0; i < keys.length; i++) {
58305 context.edits[keys[i]] = editable;
58306 }
58307 }
58308 if(config.edits) {
58309 keys = Object.keys(config.edits);
58310 for(i = 0; i < keys.length; i++) {
58311 key = keys[i];
58312 if(key in context.edits) {
58313 context.edits[key] = config.edits[key];
58314 }
58315 }
58316 }
58317
58318 // not part of the user-facing config options
58319 context._exportedPlot = config._exportedPlot;
58320 }
58321
58322 // staticPlot forces a bunch of others:
58323 if(context.staticPlot) {
58324 context.editable = false;
58325 context.edits = {};
58326 context.autosizable = false;
58327 context.scrollZoom = false;
58328 context.doubleClick = false;
58329 context.showTips = false;
58330 context.showLink = false;
58331 context.displayModeBar = false;
58332 }
58333
58334 // make sure hover-only devices have mode bar visible
58335 if(context.displayModeBar === 'hover' && !hasHover) {
58336 context.displayModeBar = true;
58337 }
58338
58339 // default and fallback for setBackground
58340 if(context.setBackground === 'transparent' || typeof context.setBackground !== 'function') {
58341 context.setBackground = setBackground;
58342 }
58343
58344 // Check if gd has a specified widht/height to begin with
58345 context._hasZeroHeight = context._hasZeroHeight || gd.clientHeight === 0;
58346 context._hasZeroWidth = context._hasZeroWidth || gd.clientWidth === 0;
58347
58348 // fill context._scrollZoom helper to help manage scrollZoom flaglist
58349 var szIn = context.scrollZoom;
58350 var szOut = context._scrollZoom = {};
58351 if(szIn === true) {
58352 szOut.cartesian = 1;
58353 szOut.gl3d = 1;
58354 szOut.geo = 1;
58355 szOut.mapbox = 1;
58356 } else if(typeof szIn === 'string') {
58357 var parts = szIn.split('+');
58358 for(i = 0; i < parts.length; i++) {
58359 szOut[parts[i]] = 1;
58360 }
58361 } else if(szIn !== false) {
58362 szOut.gl3d = 1;
58363 szOut.geo = 1;
58364 szOut.mapbox = 1;
58365 }
58366}
58367
58368
58369// convenience function to force a full redraw, mostly for use by plotly.js
58370function redraw(gd) {
58371 gd = Lib.getGraphDiv(gd);
58372
58373 if(!Lib.isPlotDiv(gd)) {
58374 throw new Error('This element is not a Plotly plot: ' + gd);
58375 }
58376
58377 helpers.cleanData(gd.data);
58378 helpers.cleanLayout(gd.layout);
58379
58380 gd.calcdata = undefined;
58381 return exports._doPlot(gd).then(function() {
58382 gd.emit('plotly_redraw');
58383 return gd;
58384 });
58385}
58386
58387/**
58388 * Convenience function to make idempotent plot option obvious to users.
58389 *
58390 * @param gd
58391 * @param {Object[]} data
58392 * @param {Object} layout
58393 * @param {Object} config
58394 */
58395function newPlot(gd, data, layout, config) {
58396 gd = Lib.getGraphDiv(gd);
58397
58398 // remove gl contexts
58399 Plots.cleanPlot([], {}, gd._fullData || [], gd._fullLayout || {});
58400
58401 Plots.purge(gd);
58402 return exports._doPlot(gd, data, layout, config);
58403}
58404
58405/**
58406 * Wrap negative indicies to their positive counterparts.
58407 *
58408 * @param {Number[]} indices An array of indices
58409 * @param {Number} maxIndex The maximum index allowable (arr.length - 1)
58410 */
58411function positivifyIndices(indices, maxIndex) {
58412 var parentLength = maxIndex + 1;
58413 var positiveIndices = [];
58414 var i;
58415 var index;
58416
58417 for(i = 0; i < indices.length; i++) {
58418 index = indices[i];
58419 if(index < 0) {
58420 positiveIndices.push(parentLength + index);
58421 } else {
58422 positiveIndices.push(index);
58423 }
58424 }
58425 return positiveIndices;
58426}
58427
58428/**
58429 * Ensures that an index array for manipulating gd.data is valid.
58430 *
58431 * Intended for use with addTraces, deleteTraces, and moveTraces.
58432 *
58433 * @param gd
58434 * @param indices
58435 * @param arrayName
58436 */
58437function assertIndexArray(gd, indices, arrayName) {
58438 var i,
58439 index;
58440
58441 for(i = 0; i < indices.length; i++) {
58442 index = indices[i];
58443
58444 // validate that indices are indeed integers
58445 if(index !== parseInt(index, 10)) {
58446 throw new Error('all values in ' + arrayName + ' must be integers');
58447 }
58448
58449 // check that all indices are in bounds for given gd.data array length
58450 if(index >= gd.data.length || index < -gd.data.length) {
58451 throw new Error(arrayName + ' must be valid indices for gd.data.');
58452 }
58453
58454 // check that indices aren't repeated
58455 if(indices.indexOf(index, i + 1) > -1 ||
58456 index >= 0 && indices.indexOf(-gd.data.length + index) > -1 ||
58457 index < 0 && indices.indexOf(gd.data.length + index) > -1) {
58458 throw new Error('each index in ' + arrayName + ' must be unique.');
58459 }
58460 }
58461}
58462
58463/**
58464 * Private function used by Plotly.moveTraces to check input args
58465 *
58466 * @param gd
58467 * @param currentIndices
58468 * @param newIndices
58469 */
58470function checkMoveTracesArgs(gd, currentIndices, newIndices) {
58471 // check that gd has attribute 'data' and 'data' is array
58472 if(!Array.isArray(gd.data)) {
58473 throw new Error('gd.data must be an array.');
58474 }
58475
58476 // validate currentIndices array
58477 if(typeof currentIndices === 'undefined') {
58478 throw new Error('currentIndices is a required argument.');
58479 } else if(!Array.isArray(currentIndices)) {
58480 currentIndices = [currentIndices];
58481 }
58482 assertIndexArray(gd, currentIndices, 'currentIndices');
58483
58484 // validate newIndices array if it exists
58485 if(typeof newIndices !== 'undefined' && !Array.isArray(newIndices)) {
58486 newIndices = [newIndices];
58487 }
58488 if(typeof newIndices !== 'undefined') {
58489 assertIndexArray(gd, newIndices, 'newIndices');
58490 }
58491
58492 // check currentIndices and newIndices are the same length if newIdices exists
58493 if(typeof newIndices !== 'undefined' && currentIndices.length !== newIndices.length) {
58494 throw new Error('current and new indices must be of equal length.');
58495 }
58496}
58497/**
58498 * A private function to reduce the type checking clutter in addTraces.
58499 *
58500 * @param gd
58501 * @param traces
58502 * @param newIndices
58503 */
58504function checkAddTracesArgs(gd, traces, newIndices) {
58505 var i, value;
58506
58507 // check that gd has attribute 'data' and 'data' is array
58508 if(!Array.isArray(gd.data)) {
58509 throw new Error('gd.data must be an array.');
58510 }
58511
58512 // make sure traces exists
58513 if(typeof traces === 'undefined') {
58514 throw new Error('traces must be defined.');
58515 }
58516
58517 // make sure traces is an array
58518 if(!Array.isArray(traces)) {
58519 traces = [traces];
58520 }
58521
58522 // make sure each value in traces is an object
58523 for(i = 0; i < traces.length; i++) {
58524 value = traces[i];
58525 if(typeof value !== 'object' || (Array.isArray(value) || value === null)) {
58526 throw new Error('all values in traces array must be non-array objects');
58527 }
58528 }
58529
58530 // make sure we have an index for each trace
58531 if(typeof newIndices !== 'undefined' && !Array.isArray(newIndices)) {
58532 newIndices = [newIndices];
58533 }
58534 if(typeof newIndices !== 'undefined' && newIndices.length !== traces.length) {
58535 throw new Error(
58536 'if indices is specified, traces.length must equal indices.length'
58537 );
58538 }
58539}
58540
58541/**
58542 * A private function to reduce the type checking clutter in spliceTraces.
58543 * Get all update Properties from gd.data. Validate inputs and outputs.
58544 * Used by prependTrace and extendTraces
58545 *
58546 * @param gd
58547 * @param update
58548 * @param indices
58549 * @param maxPoints
58550 */
58551function assertExtendTracesArgs(gd, update, indices, maxPoints) {
58552 var maxPointsIsObject = Lib.isPlainObject(maxPoints);
58553
58554 if(!Array.isArray(gd.data)) {
58555 throw new Error('gd.data must be an array');
58556 }
58557 if(!Lib.isPlainObject(update)) {
58558 throw new Error('update must be a key:value object');
58559 }
58560
58561 if(typeof indices === 'undefined') {
58562 throw new Error('indices must be an integer or array of integers');
58563 }
58564
58565 assertIndexArray(gd, indices, 'indices');
58566
58567 for(var key in update) {
58568 /*
58569 * Verify that the attribute to be updated contains as many trace updates
58570 * as indices. Failure must result in throw and no-op
58571 */
58572 if(!Array.isArray(update[key]) || update[key].length !== indices.length) {
58573 throw new Error('attribute ' + key + ' must be an array of length equal to indices array length');
58574 }
58575
58576 /*
58577 * if maxPoints is an object it must match keys and array lengths of 'update' 1:1
58578 */
58579 if(maxPointsIsObject &&
58580 (!(key in maxPoints) || !Array.isArray(maxPoints[key]) ||
58581 maxPoints[key].length !== update[key].length)) {
58582 throw new Error('when maxPoints is set as a key:value object it must contain a 1:1 ' +
58583 'corrispondence with the keys and number of traces in the update object');
58584 }
58585 }
58586}
58587
58588/**
58589 * A private function to reduce the type checking clutter in spliceTraces.
58590 *
58591 * @param {Object|HTMLDivElement} gd
58592 * @param {Object} update
58593 * @param {Number[]} indices
58594 * @param {Number||Object} maxPoints
58595 * @return {Object[]}
58596 */
58597function getExtendProperties(gd, update, indices, maxPoints) {
58598 var maxPointsIsObject = Lib.isPlainObject(maxPoints);
58599 var updateProps = [];
58600 var trace, target, prop, insert, maxp;
58601
58602 // allow scalar index to represent a single trace position
58603 if(!Array.isArray(indices)) indices = [indices];
58604
58605 // negative indices are wrapped around to their positive value. Equivalent to python indexing.
58606 indices = positivifyIndices(indices, gd.data.length - 1);
58607
58608 // loop through all update keys and traces and harvest validated data.
58609 for(var key in update) {
58610 for(var j = 0; j < indices.length; j++) {
58611 /*
58612 * Choose the trace indexed by the indices map argument and get the prop setter-getter
58613 * instance that references the key and value for this particular trace.
58614 */
58615 trace = gd.data[indices[j]];
58616 prop = nestedProperty(trace, key);
58617
58618 /*
58619 * Target is the existing gd.data.trace.dataArray value like "x" or "marker.size"
58620 * Target must exist as an Array to allow the extend operation to be performed.
58621 */
58622 target = prop.get();
58623 insert = update[key][j];
58624
58625 if(!Lib.isArrayOrTypedArray(insert)) {
58626 throw new Error('attribute: ' + key + ' index: ' + j + ' must be an array');
58627 }
58628 if(!Lib.isArrayOrTypedArray(target)) {
58629 throw new Error('cannot extend missing or non-array attribute: ' + key);
58630 }
58631 if(target.constructor !== insert.constructor) {
58632 throw new Error('cannot extend array with an array of a different type: ' + key);
58633 }
58634
58635 /*
58636 * maxPoints may be an object map or a scalar. If object select the key:value, else
58637 * Use the scalar maxPoints for all key and trace combinations.
58638 */
58639 maxp = maxPointsIsObject ? maxPoints[key][j] : maxPoints;
58640
58641 // could have chosen null here, -1 just tells us to not take a window
58642 if(!isNumeric(maxp)) maxp = -1;
58643
58644 /*
58645 * Wrap the nestedProperty in an object containing required data
58646 * for lengthening and windowing this particular trace - key combination.
58647 * Flooring maxp mirrors the behaviour of floats in the Array.slice JSnative function.
58648 */
58649 updateProps.push({
58650 prop: prop,
58651 target: target,
58652 insert: insert,
58653 maxp: Math.floor(maxp)
58654 });
58655 }
58656 }
58657
58658 // all target and insertion data now validated
58659 return updateProps;
58660}
58661
58662/**
58663 * A private function to key Extend and Prepend traces DRY
58664 *
58665 * @param {Object|HTMLDivElement} gd
58666 * @param {Object} update
58667 * @param {Number[]} indices
58668 * @param {Number||Object} maxPoints
58669 * @param {Function} updateArray
58670 * @return {Object}
58671 */
58672function spliceTraces(gd, update, indices, maxPoints, updateArray) {
58673 assertExtendTracesArgs(gd, update, indices, maxPoints);
58674
58675 var updateProps = getExtendProperties(gd, update, indices, maxPoints);
58676 var undoUpdate = {};
58677 var undoPoints = {};
58678
58679 for(var i = 0; i < updateProps.length; i++) {
58680 var prop = updateProps[i].prop;
58681 var maxp = updateProps[i].maxp;
58682
58683 // return new array and remainder
58684 var out = updateArray(updateProps[i].target, updateProps[i].insert, maxp);
58685 prop.set(out[0]);
58686
58687 // build the inverse update object for the undo operation
58688 if(!Array.isArray(undoUpdate[prop.astr])) undoUpdate[prop.astr] = [];
58689 undoUpdate[prop.astr].push(out[1]);
58690
58691 // build the matching maxPoints undo object containing original trace lengths
58692 if(!Array.isArray(undoPoints[prop.astr])) undoPoints[prop.astr] = [];
58693 undoPoints[prop.astr].push(updateProps[i].target.length);
58694 }
58695
58696 return {update: undoUpdate, maxPoints: undoPoints};
58697}
58698
58699function concatTypedArray(arr0, arr1) {
58700 var arr2 = new arr0.constructor(arr0.length + arr1.length);
58701 arr2.set(arr0);
58702 arr2.set(arr1, arr0.length);
58703 return arr2;
58704}
58705
58706/**
58707 * extend && prepend traces at indices with update arrays, window trace lengths to maxPoints
58708 *
58709 * Extend and Prepend have identical APIs. Prepend inserts an array at the head while Extend
58710 * inserts an array off the tail. Prepend truncates the tail of the array - counting maxPoints
58711 * from the head, whereas Extend truncates the head of the array, counting backward maxPoints
58712 * from the tail.
58713 *
58714 * If maxPoints is undefined, nonNumeric, negative or greater than extended trace length no
58715 * truncation / windowing will be performed. If its zero, well the whole trace is truncated.
58716 *
58717 * @param {Object|HTMLDivElement} gd The graph div
58718 * @param {Object} update The key:array map of target attributes to extend
58719 * @param {Number|Number[]} indices The locations of traces to be extended
58720 * @param {Number|Object} [maxPoints] Number of points for trace window after lengthening.
58721 *
58722 */
58723function extendTraces(gd, update, indices, maxPoints) {
58724 gd = Lib.getGraphDiv(gd);
58725
58726 function updateArray(target, insert, maxp) {
58727 var newArray, remainder;
58728
58729 if(Lib.isTypedArray(target)) {
58730 if(maxp < 0) {
58731 var none = new target.constructor(0);
58732 var both = concatTypedArray(target, insert);
58733
58734 if(maxp < 0) {
58735 newArray = both;
58736 remainder = none;
58737 } else {
58738 newArray = none;
58739 remainder = both;
58740 }
58741 } else {
58742 newArray = new target.constructor(maxp);
58743 remainder = new target.constructor(target.length + insert.length - maxp);
58744
58745 if(maxp === insert.length) {
58746 newArray.set(insert);
58747 remainder.set(target);
58748 } else if(maxp < insert.length) {
58749 var numberOfItemsFromInsert = insert.length - maxp;
58750
58751 newArray.set(insert.subarray(numberOfItemsFromInsert));
58752 remainder.set(target);
58753 remainder.set(insert.subarray(0, numberOfItemsFromInsert), target.length);
58754 } else {
58755 var numberOfItemsFromTarget = maxp - insert.length;
58756 var targetBegin = target.length - numberOfItemsFromTarget;
58757
58758 newArray.set(target.subarray(targetBegin));
58759 newArray.set(insert, numberOfItemsFromTarget);
58760 remainder.set(target.subarray(0, targetBegin));
58761 }
58762 }
58763 } else {
58764 newArray = target.concat(insert);
58765 remainder = (maxp >= 0 && maxp < newArray.length) ?
58766 newArray.splice(0, newArray.length - maxp) :
58767 [];
58768 }
58769
58770 return [newArray, remainder];
58771 }
58772
58773 var undo = spliceTraces(gd, update, indices, maxPoints, updateArray);
58774 var promise = exports.redraw(gd);
58775 var undoArgs = [gd, undo.update, indices, undo.maxPoints];
58776 Queue.add(gd, exports.prependTraces, undoArgs, extendTraces, arguments);
58777
58778 return promise;
58779}
58780
58781function prependTraces(gd, update, indices, maxPoints) {
58782 gd = Lib.getGraphDiv(gd);
58783
58784 function updateArray(target, insert, maxp) {
58785 var newArray, remainder;
58786
58787 if(Lib.isTypedArray(target)) {
58788 if(maxp <= 0) {
58789 var none = new target.constructor(0);
58790 var both = concatTypedArray(insert, target);
58791
58792 if(maxp < 0) {
58793 newArray = both;
58794 remainder = none;
58795 } else {
58796 newArray = none;
58797 remainder = both;
58798 }
58799 } else {
58800 newArray = new target.constructor(maxp);
58801 remainder = new target.constructor(target.length + insert.length - maxp);
58802
58803 if(maxp === insert.length) {
58804 newArray.set(insert);
58805 remainder.set(target);
58806 } else if(maxp < insert.length) {
58807 var numberOfItemsFromInsert = insert.length - maxp;
58808
58809 newArray.set(insert.subarray(0, numberOfItemsFromInsert));
58810 remainder.set(insert.subarray(numberOfItemsFromInsert));
58811 remainder.set(target, numberOfItemsFromInsert);
58812 } else {
58813 var numberOfItemsFromTarget = maxp - insert.length;
58814
58815 newArray.set(insert);
58816 newArray.set(target.subarray(0, numberOfItemsFromTarget), insert.length);
58817 remainder.set(target.subarray(numberOfItemsFromTarget));
58818 }
58819 }
58820 } else {
58821 newArray = insert.concat(target);
58822 remainder = (maxp >= 0 && maxp < newArray.length) ?
58823 newArray.splice(maxp, newArray.length) :
58824 [];
58825 }
58826
58827 return [newArray, remainder];
58828 }
58829
58830 var undo = spliceTraces(gd, update, indices, maxPoints, updateArray);
58831 var promise = exports.redraw(gd);
58832 var undoArgs = [gd, undo.update, indices, undo.maxPoints];
58833 Queue.add(gd, exports.extendTraces, undoArgs, prependTraces, arguments);
58834
58835 return promise;
58836}
58837
58838/**
58839 * Add data traces to an existing graph div.
58840 *
58841 * @param {Object|HTMLDivElement} gd The graph div
58842 * @param {Object[]} gd.data The array of traces we're adding to
58843 * @param {Object[]|Object} traces The object or array of objects to add
58844 * @param {Number[]|Number} [newIndices=[gd.data.length]] Locations to add traces
58845 *
58846 */
58847function addTraces(gd, traces, newIndices) {
58848 gd = Lib.getGraphDiv(gd);
58849
58850 var currentIndices = [];
58851 var undoFunc = exports.deleteTraces;
58852 var redoFunc = addTraces;
58853 var undoArgs = [gd, currentIndices];
58854 var redoArgs = [gd, traces]; // no newIndices here
58855 var i;
58856 var promise;
58857
58858 // all validation is done elsewhere to remove clutter here
58859 checkAddTracesArgs(gd, traces, newIndices);
58860
58861 // make sure traces is an array
58862 if(!Array.isArray(traces)) {
58863 traces = [traces];
58864 }
58865
58866 // make sure traces do not repeat existing ones
58867 traces = traces.map(function(trace) {
58868 return Lib.extendFlat({}, trace);
58869 });
58870
58871 helpers.cleanData(traces);
58872
58873 // add the traces to gd.data (no redrawing yet!)
58874 for(i = 0; i < traces.length; i++) {
58875 gd.data.push(traces[i]);
58876 }
58877
58878 // to continue, we need to call moveTraces which requires currentIndices
58879 for(i = 0; i < traces.length; i++) {
58880 currentIndices.push(-traces.length + i);
58881 }
58882
58883 // if the user didn't define newIndices, they just want the traces appended
58884 // i.e., we can simply redraw and be done
58885 if(typeof newIndices === 'undefined') {
58886 promise = exports.redraw(gd);
58887 Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs);
58888 return promise;
58889 }
58890
58891 // make sure indices is property defined
58892 if(!Array.isArray(newIndices)) {
58893 newIndices = [newIndices];
58894 }
58895
58896 try {
58897 // this is redundant, but necessary to not catch later possible errors!
58898 checkMoveTracesArgs(gd, currentIndices, newIndices);
58899 } catch(error) {
58900 // something went wrong, reset gd to be safe and rethrow error
58901 gd.data.splice(gd.data.length - traces.length, traces.length);
58902 throw error;
58903 }
58904
58905 // if we're here, the user has defined specific places to place the new traces
58906 // this requires some extra work that moveTraces will do
58907 Queue.startSequence(gd);
58908 Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs);
58909 promise = exports.moveTraces(gd, currentIndices, newIndices);
58910 Queue.stopSequence(gd);
58911 return promise;
58912}
58913
58914/**
58915 * Delete traces at `indices` from gd.data array.
58916 *
58917 * @param {Object|HTMLDivElement} gd The graph div
58918 * @param {Object[]} gd.data The array of traces we're removing from
58919 * @param {Number|Number[]} indices The indices
58920 */
58921function deleteTraces(gd, indices) {
58922 gd = Lib.getGraphDiv(gd);
58923
58924 var traces = [];
58925 var undoFunc = exports.addTraces;
58926 var redoFunc = deleteTraces;
58927 var undoArgs = [gd, traces, indices];
58928 var redoArgs = [gd, indices];
58929 var i;
58930 var deletedTrace;
58931
58932 // make sure indices are defined
58933 if(typeof indices === 'undefined') {
58934 throw new Error('indices must be an integer or array of integers.');
58935 } else if(!Array.isArray(indices)) {
58936 indices = [indices];
58937 }
58938 assertIndexArray(gd, indices, 'indices');
58939
58940 // convert negative indices to positive indices
58941 indices = positivifyIndices(indices, gd.data.length - 1);
58942
58943 // we want descending here so that splicing later doesn't affect indexing
58944 indices.sort(Lib.sorterDes);
58945 for(i = 0; i < indices.length; i += 1) {
58946 deletedTrace = gd.data.splice(indices[i], 1)[0];
58947 traces.push(deletedTrace);
58948 }
58949
58950 var promise = exports.redraw(gd);
58951 Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs);
58952
58953 return promise;
58954}
58955
58956/**
58957 * Move traces at currentIndices array to locations in newIndices array.
58958 *
58959 * If newIndices is omitted, currentIndices will be moved to the end. E.g.,
58960 * these are equivalent:
58961 *
58962 * Plotly.moveTraces(gd, [1, 2, 3], [-3, -2, -1])
58963 * Plotly.moveTraces(gd, [1, 2, 3])
58964 *
58965 * @param {Object|HTMLDivElement} gd The graph div
58966 * @param {Object[]} gd.data The array of traces we're removing from
58967 * @param {Number|Number[]} currentIndices The locations of traces to be moved
58968 * @param {Number|Number[]} [newIndices] The locations to move traces to
58969 *
58970 * Example calls:
58971 *
58972 * // move trace i to location x
58973 * Plotly.moveTraces(gd, i, x)
58974 *
58975 * // move trace i to end of array
58976 * Plotly.moveTraces(gd, i)
58977 *
58978 * // move traces i, j, k to end of array (i != j != k)
58979 * Plotly.moveTraces(gd, [i, j, k])
58980 *
58981 * // move traces [i, j, k] to [x, y, z] (i != j != k) (x != y != z)
58982 * Plotly.moveTraces(gd, [i, j, k], [x, y, z])
58983 *
58984 * // reorder all traces (assume there are 5--a, b, c, d, e)
58985 * Plotly.moveTraces(gd, [b, d, e, a, c]) // same as 'move to end'
58986 */
58987function moveTraces(gd, currentIndices, newIndices) {
58988 gd = Lib.getGraphDiv(gd);
58989
58990 var newData = [];
58991 var movingTraceMap = [];
58992 var undoFunc = moveTraces;
58993 var redoFunc = moveTraces;
58994 var undoArgs = [gd, newIndices, currentIndices];
58995 var redoArgs = [gd, currentIndices, newIndices];
58996 var i;
58997
58998 // to reduce complexity here, check args elsewhere
58999 // this throws errors where appropriate
59000 checkMoveTracesArgs(gd, currentIndices, newIndices);
59001
59002 // make sure currentIndices is an array
59003 currentIndices = Array.isArray(currentIndices) ? currentIndices : [currentIndices];
59004
59005 // if undefined, define newIndices to point to the end of gd.data array
59006 if(typeof newIndices === 'undefined') {
59007 newIndices = [];
59008 for(i = 0; i < currentIndices.length; i++) {
59009 newIndices.push(-currentIndices.length + i);
59010 }
59011 }
59012
59013 // make sure newIndices is an array if it's user-defined
59014 newIndices = Array.isArray(newIndices) ? newIndices : [newIndices];
59015
59016 // convert negative indices to positive indices (they're the same length)
59017 currentIndices = positivifyIndices(currentIndices, gd.data.length - 1);
59018 newIndices = positivifyIndices(newIndices, gd.data.length - 1);
59019
59020 // at this point, we've coerced the index arrays into predictable forms
59021
59022 // get the traces that aren't being moved around
59023 for(i = 0; i < gd.data.length; i++) {
59024 // if index isn't in currentIndices, include it in ignored!
59025 if(currentIndices.indexOf(i) === -1) {
59026 newData.push(gd.data[i]);
59027 }
59028 }
59029
59030 // get a mapping of indices to moving traces
59031 for(i = 0; i < currentIndices.length; i++) {
59032 movingTraceMap.push({newIndex: newIndices[i], trace: gd.data[currentIndices[i]]});
59033 }
59034
59035 // reorder this mapping by newIndex, ascending
59036 movingTraceMap.sort(function(a, b) {
59037 return a.newIndex - b.newIndex;
59038 });
59039
59040 // now, add the moving traces back in, in order!
59041 for(i = 0; i < movingTraceMap.length; i += 1) {
59042 newData.splice(movingTraceMap[i].newIndex, 0, movingTraceMap[i].trace);
59043 }
59044
59045 gd.data = newData;
59046
59047 var promise = exports.redraw(gd);
59048 Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs);
59049
59050 return promise;
59051}
59052
59053/**
59054 * restyle: update trace attributes of an existing plot
59055 *
59056 * Can be called two ways.
59057 *
59058 * Signature 1:
59059 * @param {String | HTMLDivElement} gd
59060 * the id or DOM element of the graph container div
59061 * @param {String} astr
59062 * attribute string (like `'marker.symbol'`) to update
59063 * @param {*} val
59064 * value to give this attribute
59065 * @param {Number[] | Number} [traces]
59066 * integer or array of integers for the traces to alter (all if omitted)
59067 *
59068 * Signature 2:
59069 * @param {String | HTMLDivElement} gd
59070 * (as in signature 1)
59071 * @param {Object} aobj
59072 * attribute object `{astr1: val1, astr2: val2 ...}`
59073 * allows setting multiple attributes simultaneously
59074 * @param {Number[] | Number} [traces]
59075 * (as in signature 1)
59076 *
59077 * `val` (or `val1`, `val2` ... in the object form) can be an array,
59078 * to apply different values to each trace.
59079 *
59080 * If the array is too short, it will wrap around (useful for
59081 * style files that want to specify cyclical default values).
59082 */
59083function restyle(gd, astr, val, _traces) {
59084 gd = Lib.getGraphDiv(gd);
59085 helpers.clearPromiseQueue(gd);
59086
59087 var aobj = {};
59088 if(typeof astr === 'string') aobj[astr] = val;
59089 else if(Lib.isPlainObject(astr)) {
59090 // the 3-arg form
59091 aobj = Lib.extendFlat({}, astr);
59092 if(_traces === undefined) _traces = val;
59093 } else {
59094 Lib.warn('Restyle fail.', astr, val, _traces);
59095 return Promise.reject();
59096 }
59097
59098 if(Object.keys(aobj).length) gd.changed = true;
59099
59100 var traces = helpers.coerceTraceIndices(gd, _traces);
59101
59102 var specs = _restyle(gd, aobj, traces);
59103 var flags = specs.flags;
59104
59105 // clear calcdata and/or axis types if required so they get regenerated
59106 if(flags.calc) gd.calcdata = undefined;
59107 if(flags.clearAxisTypes) helpers.clearAxisTypes(gd, traces, {});
59108
59109 // fill in redraw sequence
59110 var seq = [];
59111
59112 if(flags.fullReplot) {
59113 seq.push(exports._doPlot);
59114 } else {
59115 seq.push(Plots.previousPromises);
59116
59117 // maybe only call Plots.supplyDataDefaults in the splom case,
59118 // to skip over long and slow axes defaults
59119 Plots.supplyDefaults(gd);
59120
59121 if(flags.markerSize) {
59122 Plots.doCalcdata(gd);
59123 addAxRangeSequence(seq);
59124
59125 // TODO
59126 // if all axes have autorange:false, then
59127 // proceed to subroutines.doTraceStyle(),
59128 // otherwise we must go through addAxRangeSequence,
59129 // which in general must redraws 'all' axes
59130 }
59131
59132 if(flags.style) seq.push(subroutines.doTraceStyle);
59133 if(flags.colorbars) seq.push(subroutines.doColorBars);
59134
59135 seq.push(emitAfterPlot);
59136 }
59137
59138 seq.push(Plots.rehover, Plots.redrag);
59139
59140 Queue.add(gd,
59141 restyle, [gd, specs.undoit, specs.traces],
59142 restyle, [gd, specs.redoit, specs.traces]
59143 );
59144
59145 var plotDone = Lib.syncOrAsync(seq, gd);
59146 if(!plotDone || !plotDone.then) plotDone = Promise.resolve();
59147
59148 return plotDone.then(function() {
59149 gd.emit('plotly_restyle', specs.eventData);
59150 return gd;
59151 });
59152}
59153
59154// for undo: undefined initial vals must be turned into nulls
59155// so that we unset rather than ignore them
59156function undefinedToNull(val) {
59157 if(val === undefined) return null;
59158 return val;
59159}
59160
59161/**
59162 * Factory function to wrap nestedProperty with GUI edits if necessary
59163 * with GUI edits we add an optional prefix to the nestedProperty constructor
59164 * to prepend to the attribute string in the preGUI store.
59165 */
59166function makeNP(preGUI, guiEditFlag) {
59167 if(!guiEditFlag) return nestedProperty;
59168
59169 return function(container, attr, prefix) {
59170 var np = nestedProperty(container, attr);
59171 var npSet = np.set;
59172 np.set = function(val) {
59173 var fullAttr = (prefix || '') + attr;
59174 storeCurrent(fullAttr, np.get(), val, preGUI);
59175 npSet(val);
59176 };
59177 return np;
59178 };
59179}
59180
59181function storeCurrent(attr, val, newVal, preGUI) {
59182 if(Array.isArray(val) || Array.isArray(newVal)) {
59183 var arrayVal = Array.isArray(val) ? val : [];
59184 var arrayNew = Array.isArray(newVal) ? newVal : [];
59185 var maxLen = Math.max(arrayVal.length, arrayNew.length);
59186 for(var i = 0; i < maxLen; i++) {
59187 storeCurrent(attr + '[' + i + ']', arrayVal[i], arrayNew[i], preGUI);
59188 }
59189 } else if(Lib.isPlainObject(val) || Lib.isPlainObject(newVal)) {
59190 var objVal = Lib.isPlainObject(val) ? val : {};
59191 var objNew = Lib.isPlainObject(newVal) ? newVal : {};
59192 var objBoth = Lib.extendFlat({}, objVal, objNew);
59193 for(var key in objBoth) {
59194 storeCurrent(attr + '.' + key, objVal[key], objNew[key], preGUI);
59195 }
59196 } else if(preGUI[attr] === undefined) {
59197 preGUI[attr] = undefinedToNull(val);
59198 }
59199}
59200
59201/**
59202 * storeDirectGUIEdit: for routines that skip restyle/relayout and mock it
59203 * by emitting a plotly_restyle or plotly_relayout event, this routine
59204 * keeps track of the initial state in _preGUI for use by uirevision
59205 * Does *not* apply these changes to data/layout - that's the responsibility
59206 * of the calling routine.
59207 *
59208 * @param {object} container: the input attributes container (eg `layout` or a `trace`)
59209 * @param {object} preGUI: where original values should be stored, either
59210 * `layout._preGUI` or `layout._tracePreGUI[uid]`
59211 * @param {object} edits: the {attr: val} object as normally passed to `relayout` etc
59212 */
59213function _storeDirectGUIEdit(container, preGUI, edits) {
59214 for(var attr in edits) {
59215 var np = nestedProperty(container, attr);
59216 storeCurrent(attr, np.get(), edits[attr], preGUI);
59217 }
59218}
59219
59220function _restyle(gd, aobj, traces) {
59221 var fullLayout = gd._fullLayout;
59222 var fullData = gd._fullData;
59223 var data = gd.data;
59224 var guiEditFlag = fullLayout._guiEditing;
59225 var layoutNP = makeNP(fullLayout._preGUI, guiEditFlag);
59226 var eventData = Lib.extendDeepAll({}, aobj);
59227 var i;
59228
59229 cleanDeprecatedAttributeKeys(aobj);
59230
59231 // initialize flags
59232 var flags = editTypes.traceFlags();
59233
59234 // copies of the change (and previous values of anything affected)
59235 // for the undo / redo queue
59236 var redoit = {};
59237 var undoit = {};
59238 var axlist;
59239
59240 // make a new empty vals array for undoit
59241 function a0() { return traces.map(function() { return undefined; }); }
59242
59243 // for autoranging multiple axes
59244 function addToAxlist(axid) {
59245 var axName = Axes.id2name(axid);
59246 if(axlist.indexOf(axName) === -1) axlist.push(axName);
59247 }
59248
59249 function autorangeAttr(axName) { return 'LAYOUT' + axName + '.autorange'; }
59250
59251 function rangeAttr(axName) { return 'LAYOUT' + axName + '.range'; }
59252
59253 function getFullTrace(traceIndex) {
59254 // usually fullData maps 1:1 onto data, but with groupby transforms
59255 // the fullData index can be greater. Take the *first* matching trace.
59256 for(var j = traceIndex; j < fullData.length; j++) {
59257 if(fullData[j]._input === data[traceIndex]) return fullData[j];
59258 }
59259 // should never get here - and if we *do* it should cause an error
59260 // later on undefined fullTrace is passed to nestedProperty.
59261 }
59262
59263 // for attrs that interact (like scales & autoscales), save the
59264 // old vals before making the change
59265 // val=undefined will not set a value, just record what the value was.
59266 // val=null will delete the attribute
59267 // attr can be an array to set several at once (all to the same val)
59268 function doextra(attr, val, i) {
59269 if(Array.isArray(attr)) {
59270 attr.forEach(function(a) { doextra(a, val, i); });
59271 return;
59272 }
59273 // quit if explicitly setting this elsewhere
59274 if(attr in aobj || helpers.hasParent(aobj, attr)) return;
59275
59276 var extraparam;
59277 if(attr.substr(0, 6) === 'LAYOUT') {
59278 extraparam = layoutNP(gd.layout, attr.replace('LAYOUT', ''));
59279 } else {
59280 var tracei = traces[i];
59281 var preGUI = fullLayout._tracePreGUI[getFullTrace(tracei)._fullInput.uid];
59282 extraparam = makeNP(preGUI, guiEditFlag)(data[tracei], attr);
59283 }
59284
59285 if(!(attr in undoit)) {
59286 undoit[attr] = a0();
59287 }
59288 if(undoit[attr][i] === undefined) {
59289 undoit[attr][i] = undefinedToNull(extraparam.get());
59290 }
59291 if(val !== undefined) {
59292 extraparam.set(val);
59293 }
59294 }
59295
59296 function allBins(binAttr) {
59297 return function(j) {
59298 return fullData[j][binAttr];
59299 };
59300 }
59301
59302 function arrayBins(binAttr) {
59303 return function(vij, j) {
59304 return vij === false ? fullData[traces[j]][binAttr] : null;
59305 };
59306 }
59307
59308 // now make the changes to gd.data (and occasionally gd.layout)
59309 // and figure out what kind of graphics update we need to do
59310 for(var ai in aobj) {
59311 if(helpers.hasParent(aobj, ai)) {
59312 throw new Error('cannot set ' + ai + ' and a parent attribute simultaneously');
59313 }
59314
59315 var vi = aobj[ai];
59316 var cont;
59317 var contFull;
59318 var param;
59319 var oldVal;
59320 var newVal;
59321 var valObject;
59322
59323 // Backward compatibility shim for turning histogram autobin on,
59324 // or freezing previous autobinned values.
59325 // Replace obsolete `autobin(x|y): true` with `(x|y)bins: null`
59326 // and `autobin(x|y): false` with the `(x|y)bins` in `fullData`
59327 if(ai === 'autobinx' || ai === 'autobiny') {
59328 ai = ai.charAt(ai.length - 1) + 'bins';
59329 if(Array.isArray(vi)) vi = vi.map(arrayBins(ai));
59330 else if(vi === false) vi = traces.map(allBins(ai));
59331 else vi = null;
59332 }
59333
59334 redoit[ai] = vi;
59335
59336 if(ai.substr(0, 6) === 'LAYOUT') {
59337 param = layoutNP(gd.layout, ai.replace('LAYOUT', ''));
59338 undoit[ai] = [undefinedToNull(param.get())];
59339 // since we're allowing val to be an array, allow it here too,
59340 // even though that's meaningless
59341 param.set(Array.isArray(vi) ? vi[0] : vi);
59342 // ironically, the layout attrs in restyle only require replot,
59343 // not relayout
59344 flags.calc = true;
59345 continue;
59346 }
59347
59348 // set attribute in gd.data
59349 undoit[ai] = a0();
59350 for(i = 0; i < traces.length; i++) {
59351 cont = data[traces[i]];
59352 contFull = getFullTrace(traces[i]);
59353 var preGUI = fullLayout._tracePreGUI[contFull._fullInput.uid];
59354 param = makeNP(preGUI, guiEditFlag)(cont, ai);
59355 oldVal = param.get();
59356 newVal = Array.isArray(vi) ? vi[i % vi.length] : vi;
59357
59358 if(newVal === undefined) continue;
59359
59360 var finalPart = param.parts[param.parts.length - 1];
59361 var prefix = ai.substr(0, ai.length - finalPart.length - 1);
59362 var prefixDot = prefix ? prefix + '.' : '';
59363 var innerContFull = prefix ?
59364 nestedProperty(contFull, prefix).get() : contFull;
59365
59366 valObject = PlotSchema.getTraceValObject(contFull, param.parts);
59367
59368 if(valObject && valObject.impliedEdits && newVal !== null) {
59369 for(var impliedKey in valObject.impliedEdits) {
59370 doextra(Lib.relativeAttr(ai, impliedKey), valObject.impliedEdits[impliedKey], i);
59371 }
59372 } else if((finalPart === 'thicknessmode' || finalPart === 'lenmode') &&
59373 oldVal !== newVal &&
59374 (newVal === 'fraction' || newVal === 'pixels') &&
59375 innerContFull
59376 ) {
59377 // changing colorbar size modes,
59378 // make the resulting size not change
59379 // note that colorbar fractional sizing is based on the
59380 // original plot size, before anything (like a colorbar)
59381 // increases the margins
59382
59383 var gs = fullLayout._size;
59384 var orient = innerContFull.orient;
59385 var topOrBottom = (orient === 'top') || (orient === 'bottom');
59386 if(finalPart === 'thicknessmode') {
59387 var thicknorm = topOrBottom ? gs.h : gs.w;
59388 doextra(prefixDot + 'thickness', innerContFull.thickness *
59389 (newVal === 'fraction' ? 1 / thicknorm : thicknorm), i);
59390 } else {
59391 var lennorm = topOrBottom ? gs.w : gs.h;
59392 doextra(prefixDot + 'len', innerContFull.len *
59393 (newVal === 'fraction' ? 1 / lennorm : lennorm), i);
59394 }
59395 } else if(ai === 'type' && (
59396 (newVal === 'pie') !== (oldVal === 'pie') ||
59397 (newVal === 'funnelarea') !== (oldVal === 'funnelarea')
59398 )) {
59399 var labelsTo = 'x';
59400 var valuesTo = 'y';
59401 if((newVal === 'bar' || oldVal === 'bar') && cont.orientation === 'h') {
59402 labelsTo = 'y';
59403 valuesTo = 'x';
59404 }
59405 Lib.swapAttrs(cont, ['?', '?src'], 'labels', labelsTo);
59406 Lib.swapAttrs(cont, ['d?', '?0'], 'label', labelsTo);
59407 Lib.swapAttrs(cont, ['?', '?src'], 'values', valuesTo);
59408
59409 if(oldVal === 'pie' || oldVal === 'funnelarea') {
59410 nestedProperty(cont, 'marker.color')
59411 .set(nestedProperty(cont, 'marker.colors').get());
59412
59413 // super kludgy - but if all pies are gone we won't remove them otherwise
59414 fullLayout._pielayer.selectAll('g.trace').remove();
59415 } else if(Registry.traceIs(cont, 'cartesian')) {
59416 nestedProperty(cont, 'marker.colors')
59417 .set(nestedProperty(cont, 'marker.color').get());
59418 }
59419 }
59420
59421 undoit[ai][i] = undefinedToNull(oldVal);
59422 // set the new value - if val is an array, it's one el per trace
59423 // first check for attributes that get more complex alterations
59424 var swapAttrs = [
59425 'swapxy', 'swapxyaxes', 'orientation', 'orientationaxes'
59426 ];
59427 if(swapAttrs.indexOf(ai) !== -1) {
59428 // setting an orientation: make sure it's changing
59429 // before we swap everything else
59430 if(ai === 'orientation') {
59431 param.set(newVal);
59432 // obnoxious that we need this level of coupling... but in order to
59433 // properly handle setting orientation to `null` we need to mimic
59434 // the logic inside Bars.supplyDefaults for default orientation
59435 var defaultOrientation = (cont.x && !cont.y) ? 'h' : 'v';
59436 if((param.get() || defaultOrientation) === contFull.orientation) {
59437 continue;
59438 }
59439 } else if(ai === 'orientationaxes') {
59440 // orientationaxes has no value,
59441 // it flips everything and the axes
59442
59443 cont.orientation =
59444 {v: 'h', h: 'v'}[contFull.orientation];
59445 }
59446 helpers.swapXYData(cont);
59447 flags.calc = flags.clearAxisTypes = true;
59448 } else if(Plots.dataArrayContainers.indexOf(param.parts[0]) !== -1) {
59449 // TODO: use manageArrays.applyContainerArrayChanges here too
59450 helpers.manageArrayContainers(param, newVal, undoit);
59451 flags.calc = true;
59452 } else {
59453 if(valObject) {
59454 // must redo calcdata when restyling array values of arrayOk attributes
59455 // ... but no need to this for regl-based traces
59456 if(valObject.arrayOk &&
59457 !Registry.traceIs(contFull, 'regl') &&
59458 (Lib.isArrayOrTypedArray(newVal) || Lib.isArrayOrTypedArray(oldVal))
59459 ) {
59460 flags.calc = true;
59461 } else editTypes.update(flags, valObject);
59462 } else {
59463 /*
59464 * if we couldn't find valObject, assume a full recalc.
59465 * This can happen if you're changing type and making
59466 * some other edits too, so the modules we're
59467 * looking at don't have these attributes in them.
59468 */
59469 flags.calc = true;
59470 }
59471
59472 // all the other ones, just modify that one attribute
59473 param.set(newVal);
59474 }
59475 }
59476
59477 // swap the data attributes of the relevant x and y axes?
59478 if(['swapxyaxes', 'orientationaxes'].indexOf(ai) !== -1) {
59479 Axes.swap(gd, traces);
59480 }
59481
59482 // swap hovermode if set to "compare x/y data"
59483 if(ai === 'orientationaxes') {
59484 var hovermode = nestedProperty(gd.layout, 'hovermode');
59485 var h = hovermode.get();
59486 if(h === 'x') {
59487 hovermode.set('y');
59488 } else if(h === 'y') {
59489 hovermode.set('x');
59490 } else if(h === 'x unified') {
59491 hovermode.set('y unified');
59492 } else if(h === 'y unified') {
59493 hovermode.set('x unified');
59494 }
59495 }
59496
59497 // Major enough changes deserve autoscale and
59498 // non-reversed axes so people don't get confused
59499 //
59500 // Note: autobin (or its new analog bin clearing) is not included here
59501 // since we're not pushing bins back to gd.data, so if we have bin
59502 // info it was explicitly provided by the user.
59503 if(['orientation', 'type'].indexOf(ai) !== -1) {
59504 axlist = [];
59505 for(i = 0; i < traces.length; i++) {
59506 var trace = data[traces[i]];
59507
59508 if(Registry.traceIs(trace, 'cartesian')) {
59509 addToAxlist(trace.xaxis || 'x');
59510 addToAxlist(trace.yaxis || 'y');
59511 }
59512 }
59513
59514 doextra(axlist.map(autorangeAttr), true, 0);
59515 doextra(axlist.map(rangeAttr), [0, 1], 0);
59516 }
59517 }
59518
59519 if(flags.calc || flags.plot) {
59520 flags.fullReplot = true;
59521 }
59522
59523 return {
59524 flags: flags,
59525 undoit: undoit,
59526 redoit: redoit,
59527 traces: traces,
59528 eventData: Lib.extendDeepNoArrays([], [eventData, traces])
59529 };
59530}
59531
59532/**
59533 * Converts deprecated attribute keys to
59534 * the current API to ensure backwards compatibility.
59535 *
59536 * This is needed for the update mechanism to determine which
59537 * subroutines to run based on the actual attribute
59538 * definitions (that don't include the deprecated ones).
59539 *
59540 * E.g. Maps {'xaxis.title': 'A chart'} to {'xaxis.title.text': 'A chart'}
59541 * and {titlefont: {...}} to {'title.font': {...}}.
59542 *
59543 * @param aobj
59544 */
59545function cleanDeprecatedAttributeKeys(aobj) {
59546 var oldAxisTitleRegex = Lib.counterRegex('axis', '\.title', false, false);
59547 var colorbarRegex = /colorbar\.title$/;
59548 var keys = Object.keys(aobj);
59549 var i, key, value;
59550
59551 for(i = 0; i < keys.length; i++) {
59552 key = keys[i];
59553 value = aobj[key];
59554
59555 if((key === 'title' || oldAxisTitleRegex.test(key) || colorbarRegex.test(key)) &&
59556 (typeof value === 'string' || typeof value === 'number')) {
59557 replace(key, key.replace('title', 'title.text'));
59558 } else if(key.indexOf('titlefont') > -1) {
59559 replace(key, key.replace('titlefont', 'title.font'));
59560 } else if(key.indexOf('titleposition') > -1) {
59561 replace(key, key.replace('titleposition', 'title.position'));
59562 } else if(key.indexOf('titleside') > -1) {
59563 replace(key, key.replace('titleside', 'title.side'));
59564 } else if(key.indexOf('titleoffset') > -1) {
59565 replace(key, key.replace('titleoffset', 'title.offset'));
59566 }
59567 }
59568
59569 function replace(oldAttrStr, newAttrStr) {
59570 aobj[newAttrStr] = aobj[oldAttrStr];
59571 delete aobj[oldAttrStr];
59572 }
59573}
59574
59575/**
59576 * relayout: update layout attributes of an existing plot
59577 *
59578 * Can be called two ways:
59579 *
59580 * Signature 1:
59581 * @param {String | HTMLDivElement} gd
59582 * the id or dom element of the graph container div
59583 * @param {String} astr
59584 * attribute string (like `'xaxis.range[0]'`) to update
59585 * @param {*} val
59586 * value to give this attribute
59587 *
59588 * Signature 2:
59589 * @param {String | HTMLDivElement} gd
59590 * (as in signature 1)
59591 * @param {Object} aobj
59592 * attribute object `{astr1: val1, astr2: val2 ...}`
59593 * allows setting multiple attributes simultaneously
59594 */
59595function relayout(gd, astr, val) {
59596 gd = Lib.getGraphDiv(gd);
59597 helpers.clearPromiseQueue(gd);
59598
59599 var aobj = {};
59600 if(typeof astr === 'string') {
59601 aobj[astr] = val;
59602 } else if(Lib.isPlainObject(astr)) {
59603 aobj = Lib.extendFlat({}, astr);
59604 } else {
59605 Lib.warn('Relayout fail.', astr, val);
59606 return Promise.reject();
59607 }
59608
59609 if(Object.keys(aobj).length) gd.changed = true;
59610
59611 var specs = _relayout(gd, aobj);
59612 var flags = specs.flags;
59613
59614 // clear calcdata if required
59615 if(flags.calc) gd.calcdata = undefined;
59616
59617 // fill in redraw sequence
59618
59619 // even if we don't have anything left in aobj,
59620 // something may have happened within relayout that we
59621 // need to wait for
59622 var seq = [Plots.previousPromises];
59623
59624 if(flags.layoutReplot) {
59625 seq.push(subroutines.layoutReplot);
59626 } else if(Object.keys(aobj).length) {
59627 axRangeSupplyDefaultsByPass(gd, flags, specs) || Plots.supplyDefaults(gd);
59628
59629 if(flags.legend) seq.push(subroutines.doLegend);
59630 if(flags.layoutstyle) seq.push(subroutines.layoutStyles);
59631 if(flags.axrange) addAxRangeSequence(seq, specs.rangesAltered);
59632 if(flags.ticks) seq.push(subroutines.doTicksRelayout);
59633 if(flags.modebar) seq.push(subroutines.doModeBar);
59634 if(flags.camera) seq.push(subroutines.doCamera);
59635 if(flags.colorbars) seq.push(subroutines.doColorBars);
59636
59637 seq.push(emitAfterPlot);
59638 }
59639
59640 seq.push(Plots.rehover, Plots.redrag);
59641
59642 Queue.add(gd,
59643 relayout, [gd, specs.undoit],
59644 relayout, [gd, specs.redoit]
59645 );
59646
59647 var plotDone = Lib.syncOrAsync(seq, gd);
59648 if(!plotDone || !plotDone.then) plotDone = Promise.resolve(gd);
59649
59650 return plotDone.then(function() {
59651 gd.emit('plotly_relayout', specs.eventData);
59652 return gd;
59653 });
59654}
59655
59656// Optimization mostly for large splom traces where
59657// Plots.supplyDefaults can take > 100ms
59658function axRangeSupplyDefaultsByPass(gd, flags, specs) {
59659 var fullLayout = gd._fullLayout;
59660
59661 if(!flags.axrange) return false;
59662
59663 for(var k in flags) {
59664 if(k !== 'axrange' && flags[k]) return false;
59665 }
59666
59667 for(var axId in specs.rangesAltered) {
59668 var axName = Axes.id2name(axId);
59669 var axIn = gd.layout[axName];
59670 var axOut = fullLayout[axName];
59671 axOut.autorange = axIn.autorange;
59672 if(axIn.range) {
59673 axOut.range = axIn.range.slice();
59674 }
59675 axOut.cleanRange();
59676
59677 if(axOut._matchGroup) {
59678 for(var axId2 in axOut._matchGroup) {
59679 if(axId2 !== axId) {
59680 var ax2 = fullLayout[Axes.id2name(axId2)];
59681 ax2.autorange = axOut.autorange;
59682 ax2.range = axOut.range.slice();
59683 ax2._input.range = axOut.range.slice();
59684 }
59685 }
59686 }
59687 }
59688
59689 return true;
59690}
59691
59692function addAxRangeSequence(seq, rangesAltered) {
59693 // N.B. leave as sequence of subroutines (for now) instead of
59694 // subroutine of its own so that finalDraw always gets
59695 // executed after drawData
59696 var drawAxes = rangesAltered ?
59697 function(gd) {
59698 var axIds = [];
59699 var skipTitle = true;
59700
59701 for(var id in rangesAltered) {
59702 var ax = Axes.getFromId(gd, id);
59703 axIds.push(id);
59704
59705 if((ax.ticklabelposition || '').indexOf('inside') !== -1) {
59706 if(ax._anchorAxis) {
59707 axIds.push(ax._anchorAxis._id);
59708 }
59709 }
59710
59711 if(ax._matchGroup) {
59712 for(var id2 in ax._matchGroup) {
59713 if(!rangesAltered[id2]) {
59714 axIds.push(id2);
59715 }
59716 }
59717 }
59718
59719 if(ax.automargin) skipTitle = false;
59720 }
59721
59722 return Axes.draw(gd, axIds, {skipTitle: skipTitle});
59723 } :
59724 function(gd) {
59725 return Axes.draw(gd, 'redraw');
59726 };
59727
59728 seq.push(
59729 clearSelect,
59730 subroutines.doAutoRangeAndConstraints,
59731 drawAxes,
59732 subroutines.drawData,
59733 subroutines.finalDraw
59734 );
59735}
59736
59737var AX_RANGE_RE = /^[xyz]axis[0-9]*\.range(\[[0|1]\])?$/;
59738var AX_AUTORANGE_RE = /^[xyz]axis[0-9]*\.autorange$/;
59739var AX_DOMAIN_RE = /^[xyz]axis[0-9]*\.domain(\[[0|1]\])?$/;
59740
59741function _relayout(gd, aobj) {
59742 var layout = gd.layout;
59743 var fullLayout = gd._fullLayout;
59744 var guiEditFlag = fullLayout._guiEditing;
59745 var layoutNP = makeNP(fullLayout._preGUI, guiEditFlag);
59746 var keys = Object.keys(aobj);
59747 var axes = Axes.list(gd);
59748 var eventData = Lib.extendDeepAll({}, aobj);
59749 var arrayEdits = {};
59750
59751 var arrayStr, i, j;
59752
59753 cleanDeprecatedAttributeKeys(aobj);
59754 keys = Object.keys(aobj);
59755
59756 // look for 'allaxes', split out into all axes
59757 // in case of 3D the axis are nested within a scene which is held in _id
59758 for(i = 0; i < keys.length; i++) {
59759 if(keys[i].indexOf('allaxes') === 0) {
59760 for(j = 0; j < axes.length; j++) {
59761 var scene = axes[j]._id.substr(1);
59762 var axisAttr = (scene.indexOf('scene') !== -1) ? (scene + '.') : '';
59763 var newkey = keys[i].replace('allaxes', axisAttr + axes[j]._name);
59764
59765 if(!aobj[newkey]) aobj[newkey] = aobj[keys[i]];
59766 }
59767
59768 delete aobj[keys[i]];
59769 }
59770 }
59771
59772 // initialize flags
59773 var flags = editTypes.layoutFlags();
59774
59775 // copies of the change (and previous values of anything affected)
59776 // for the undo / redo queue
59777 var redoit = {};
59778 var undoit = {};
59779
59780 // for attrs that interact (like scales & autoscales), save the
59781 // old vals before making the change
59782 // val=undefined will not set a value, just record what the value was.
59783 // attr can be an array to set several at once (all to the same val)
59784 function doextra(attr, val) {
59785 if(Array.isArray(attr)) {
59786 attr.forEach(function(a) { doextra(a, val); });
59787 return;
59788 }
59789
59790 // if we have another value for this attribute (explicitly or
59791 // via a parent) do not override with this auto-generated extra
59792 if(attr in aobj || helpers.hasParent(aobj, attr)) return;
59793
59794 var p = layoutNP(layout, attr);
59795 if(!(attr in undoit)) {
59796 undoit[attr] = undefinedToNull(p.get());
59797 }
59798 if(val !== undefined) p.set(val);
59799 }
59800
59801 // for constraint enforcement: keep track of all axes (as {id: name})
59802 // we're editing the (auto)range of, so we can tell the others constrained
59803 // to scale with them that it's OK for them to shrink
59804 var rangesAltered = {};
59805 var ax;
59806
59807 function recordAlteredAxis(pleafPlus) {
59808 var axId = Axes.name2id(pleafPlus.split('.')[0]);
59809 rangesAltered[axId] = 1;
59810 return axId;
59811 }
59812
59813 // alter gd.layout
59814 for(var ai in aobj) {
59815 if(helpers.hasParent(aobj, ai)) {
59816 throw new Error('cannot set ' + ai + ' and a parent attribute simultaneously');
59817 }
59818
59819 var p = layoutNP(layout, ai);
59820 var vi = aobj[ai];
59821 var plen = p.parts.length;
59822 // p.parts may end with an index integer if the property is an array
59823 var pend = plen - 1;
59824 while(pend > 0 && typeof p.parts[pend] !== 'string') pend--;
59825 // last property in chain (leaf node)
59826 var pleaf = p.parts[pend];
59827 // leaf plus immediate parent
59828 var pleafPlus = p.parts[pend - 1] + '.' + pleaf;
59829 // trunk nodes (everything except the leaf)
59830 var ptrunk = p.parts.slice(0, pend).join('.');
59831 var parentIn = nestedProperty(gd.layout, ptrunk).get();
59832 var parentFull = nestedProperty(fullLayout, ptrunk).get();
59833 var vOld = p.get();
59834
59835 if(vi === undefined) continue;
59836
59837 redoit[ai] = vi;
59838
59839 // axis reverse is special - it is its own inverse
59840 // op and has no flag.
59841 undoit[ai] = (pleaf === 'reverse') ? vi : undefinedToNull(vOld);
59842
59843 var valObject = PlotSchema.getLayoutValObject(fullLayout, p.parts);
59844
59845 if(valObject && valObject.impliedEdits && vi !== null) {
59846 for(var impliedKey in valObject.impliedEdits) {
59847 doextra(Lib.relativeAttr(ai, impliedKey), valObject.impliedEdits[impliedKey]);
59848 }
59849 }
59850
59851 // Setting width or height to null must reset the graph's width / height
59852 // back to its initial value as computed during the first pass in Plots.plotAutoSize.
59853 //
59854 // To do so, we must manually set them back here using the _initialAutoSize cache.
59855 // can't use impliedEdits for this because behavior depends on vi
59856 if(['width', 'height'].indexOf(ai) !== -1) {
59857 if(vi) {
59858 doextra('autosize', null);
59859 // currently we don't support autosize one dim only - so
59860 // explicitly set the other one. Note that doextra will
59861 // ignore this if the same relayout call also provides oppositeAttr
59862 var oppositeAttr = ai === 'height' ? 'width' : 'height';
59863 doextra(oppositeAttr, fullLayout[oppositeAttr]);
59864 } else {
59865 fullLayout[ai] = gd._initialAutoSize[ai];
59866 }
59867 } else if(ai === 'autosize') {
59868 // depends on vi here too, so again can't use impliedEdits
59869 doextra('width', vi ? null : fullLayout.width);
59870 doextra('height', vi ? null : fullLayout.height);
59871 } else if(pleafPlus.match(AX_RANGE_RE)) {
59872 // check autorange vs range
59873
59874 recordAlteredAxis(pleafPlus);
59875 nestedProperty(fullLayout, ptrunk + '._inputRange').set(null);
59876 } else if(pleafPlus.match(AX_AUTORANGE_RE)) {
59877 recordAlteredAxis(pleafPlus);
59878 nestedProperty(fullLayout, ptrunk + '._inputRange').set(null);
59879 var axFull = nestedProperty(fullLayout, ptrunk).get();
59880 if(axFull._inputDomain) {
59881 // if we're autoranging and this axis has a constrained domain,
59882 // reset it so we don't get locked into a shrunken size
59883 axFull._input.domain = axFull._inputDomain.slice();
59884 }
59885 } else if(pleafPlus.match(AX_DOMAIN_RE)) {
59886 nestedProperty(fullLayout, ptrunk + '._inputDomain').set(null);
59887 }
59888
59889 // toggling axis type between log and linear: we need to convert
59890 // positions for components that are still using linearized values,
59891 // not data values like newer components.
59892 // previously we did this for log <-> not-log, but now only do it
59893 // for log <-> linear
59894 if(pleaf === 'type') {
59895 ax = parentIn;
59896 var toLog = parentFull.type === 'linear' && vi === 'log';
59897 var fromLog = parentFull.type === 'log' && vi === 'linear';
59898
59899 if(toLog || fromLog) {
59900 if(!ax || !ax.range) {
59901 // 2D never gets here, but 3D does
59902 // I don't think this is needed, but left here in case there
59903 // are edge cases I'm not thinking of.
59904 doextra(ptrunk + '.autorange', true);
59905 } else if(!parentFull.autorange) {
59906 // toggling log without autorange: need to also recalculate ranges
59907 // because log axes use linearized values for range endpoints
59908 var r0 = ax.range[0];
59909 var r1 = ax.range[1];
59910 if(toLog) {
59911 // if both limits are negative, autorange
59912 if(r0 <= 0 && r1 <= 0) {
59913 doextra(ptrunk + '.autorange', true);
59914 }
59915 // if one is negative, set it 6 orders below the other.
59916 if(r0 <= 0) r0 = r1 / 1e6;
59917 else if(r1 <= 0) r1 = r0 / 1e6;
59918 // now set the range values as appropriate
59919 doextra(ptrunk + '.range[0]', Math.log(r0) / Math.LN10);
59920 doextra(ptrunk + '.range[1]', Math.log(r1) / Math.LN10);
59921 } else {
59922 doextra(ptrunk + '.range[0]', Math.pow(10, r0));
59923 doextra(ptrunk + '.range[1]', Math.pow(10, r1));
59924 }
59925 } else if(toLog) {
59926 // just make sure the range is positive and in the right
59927 // order, it'll get recalculated later
59928 ax.range = (ax.range[1] > ax.range[0]) ? [1, 2] : [2, 1];
59929 }
59930
59931 // clear polar view initial stash for radial range so that
59932 // value get recomputed in correct units
59933 if(Array.isArray(fullLayout._subplots.polar) &&
59934 fullLayout._subplots.polar.length &&
59935 fullLayout[p.parts[0]] &&
59936 p.parts[1] === 'radialaxis'
59937 ) {
59938 delete fullLayout[p.parts[0]]._subplot.viewInitial['radialaxis.range'];
59939 }
59940
59941 // Annotations and images also need to convert to/from linearized coords
59942 // Shapes do not need this :)
59943 Registry.getComponentMethod('annotations', 'convertCoords')(gd, parentFull, vi, doextra);
59944 Registry.getComponentMethod('images', 'convertCoords')(gd, parentFull, vi, doextra);
59945 } else {
59946 // any other type changes: the range from the previous type
59947 // will not make sense, so autorange it.
59948 doextra(ptrunk + '.autorange', true);
59949 doextra(ptrunk + '.range', null);
59950 }
59951 nestedProperty(fullLayout, ptrunk + '._inputRange').set(null);
59952 } else if(pleaf.match(AX_NAME_PATTERN)) {
59953 var fullProp = nestedProperty(fullLayout, ai).get();
59954 var newType = (vi || {}).type;
59955
59956 // This can potentially cause strange behavior if the autotype is not
59957 // numeric (linear, because we don't auto-log) but the previous type
59958 // was log. That's a very strange edge case though
59959 if(!newType || newType === '-') newType = 'linear';
59960 Registry.getComponentMethod('annotations', 'convertCoords')(gd, fullProp, newType, doextra);
59961 Registry.getComponentMethod('images', 'convertCoords')(gd, fullProp, newType, doextra);
59962 }
59963
59964 // alter gd.layout
59965
59966 // collect array component edits for execution all together
59967 // so we can ensure consistent behavior adding/removing items
59968 // and order-independence for add/remove/edit all together in
59969 // one relayout call
59970 var containerArrayMatch = manageArrays.containerArrayMatch(ai);
59971 if(containerArrayMatch) {
59972 arrayStr = containerArrayMatch.array;
59973 i = containerArrayMatch.index;
59974 var propStr = containerArrayMatch.property;
59975 var updateValObject = valObject || {editType: 'calc'};
59976
59977 if(i !== '' && propStr === '') {
59978 // special handling of undoit if we're adding or removing an element
59979 // ie 'annotations[2]' which can be {...} (add) or null,
59980 // does not work when replacing the entire array
59981 if(manageArrays.isAddVal(vi)) {
59982 undoit[ai] = null;
59983 } else if(manageArrays.isRemoveVal(vi)) {
59984 undoit[ai] = (nestedProperty(layout, arrayStr).get() || [])[i];
59985 } else {
59986 Lib.warn('unrecognized full object value', aobj);
59987 }
59988 }
59989 editTypes.update(flags, updateValObject);
59990
59991 // prepare the edits object we'll send to applyContainerArrayChanges
59992 if(!arrayEdits[arrayStr]) arrayEdits[arrayStr] = {};
59993 var objEdits = arrayEdits[arrayStr][i];
59994 if(!objEdits) objEdits = arrayEdits[arrayStr][i] = {};
59995 objEdits[propStr] = vi;
59996
59997 delete aobj[ai];
59998 } else if(pleaf === 'reverse') {
59999 // handle axis reversal explicitly, as there's no 'reverse' attribute
60000
60001 if(parentIn.range) parentIn.range.reverse();
60002 else {
60003 doextra(ptrunk + '.autorange', true);
60004 parentIn.range = [1, 0];
60005 }
60006
60007 if(parentFull.autorange) flags.calc = true;
60008 else flags.plot = true;
60009 } else {
60010 if((fullLayout._has('scatter-like') && fullLayout._has('regl')) &&
60011 (ai === 'dragmode' &&
60012 (vi === 'lasso' || vi === 'select') &&
60013 !(vOld === 'lasso' || vOld === 'select'))
60014 ) {
60015 flags.plot = true;
60016 } else if(fullLayout._has('gl2d')) {
60017 flags.plot = true;
60018 } else if(valObject) editTypes.update(flags, valObject);
60019 else flags.calc = true;
60020
60021 p.set(vi);
60022 }
60023 }
60024
60025 // now we've collected component edits - execute them all together
60026 for(arrayStr in arrayEdits) {
60027 var finished = manageArrays.applyContainerArrayChanges(gd,
60028 layoutNP(layout, arrayStr), arrayEdits[arrayStr], flags, layoutNP);
60029 if(!finished) flags.plot = true;
60030 }
60031
60032 // figure out if we need to recalculate axis constraints
60033 for(var axId in rangesAltered) {
60034 ax = Axes.getFromId(gd, axId);
60035 var group = ax && ax._constraintGroup;
60036 if(group) {
60037 // Always recalc if we're changing constrained ranges.
60038 // Otherwise it's possible to violate the constraints by
60039 // specifying arbitrary ranges for all axes in the group.
60040 // this way some ranges may expand beyond what's specified,
60041 // as they do at first draw, to satisfy the constraints.
60042 flags.calc = true;
60043 for(var groupAxId in group) {
60044 if(!rangesAltered[groupAxId]) {
60045 Axes.getFromId(gd, groupAxId)._constraintShrinkable = true;
60046 }
60047 }
60048 }
60049 }
60050
60051 // If the autosize changed or height or width was explicitly specified,
60052 // this triggers a redraw
60053 // TODO: do we really need special aobj.height/width handling here?
60054 // couldn't editType do this?
60055 if(updateAutosize(gd) || aobj.height || aobj.width) flags.plot = true;
60056
60057 if(flags.plot || flags.calc) {
60058 flags.layoutReplot = true;
60059 }
60060
60061 // now all attribute mods are done, as are
60062 // redo and undo so we can save them
60063
60064 return {
60065 flags: flags,
60066 rangesAltered: rangesAltered,
60067 undoit: undoit,
60068 redoit: redoit,
60069 eventData: eventData
60070 };
60071}
60072
60073/*
60074 * updateAutosize: we made a change, does it change the autosize result?
60075 * puts the new size into fullLayout
60076 * returns true if either height or width changed
60077 */
60078function updateAutosize(gd) {
60079 var fullLayout = gd._fullLayout;
60080 var oldWidth = fullLayout.width;
60081 var oldHeight = fullLayout.height;
60082
60083 // calculate autosizing
60084 if(gd.layout.autosize) Plots.plotAutoSize(gd, gd.layout, fullLayout);
60085
60086 return (fullLayout.width !== oldWidth) || (fullLayout.height !== oldHeight);
60087}
60088
60089/**
60090 * update: update trace and layout attributes of an existing plot
60091 *
60092 * @param {String | HTMLDivElement} gd
60093 * the id or DOM element of the graph container div
60094 * @param {Object} traceUpdate
60095 * attribute object `{astr1: val1, astr2: val2 ...}`
60096 * corresponding to updates in the plot's traces
60097 * @param {Object} layoutUpdate
60098 * attribute object `{astr1: val1, astr2: val2 ...}`
60099 * corresponding to updates in the plot's layout
60100 * @param {Number[] | Number} [traces]
60101 * integer or array of integers for the traces to alter (all if omitted)
60102 *
60103 */
60104function update(gd, traceUpdate, layoutUpdate, _traces) {
60105 gd = Lib.getGraphDiv(gd);
60106 helpers.clearPromiseQueue(gd);
60107
60108 if(!Lib.isPlainObject(traceUpdate)) traceUpdate = {};
60109 if(!Lib.isPlainObject(layoutUpdate)) layoutUpdate = {};
60110
60111 if(Object.keys(traceUpdate).length) gd.changed = true;
60112 if(Object.keys(layoutUpdate).length) gd.changed = true;
60113
60114 var traces = helpers.coerceTraceIndices(gd, _traces);
60115
60116 var restyleSpecs = _restyle(gd, Lib.extendFlat({}, traceUpdate), traces);
60117 var restyleFlags = restyleSpecs.flags;
60118
60119 var relayoutSpecs = _relayout(gd, Lib.extendFlat({}, layoutUpdate));
60120 var relayoutFlags = relayoutSpecs.flags;
60121
60122 // clear calcdata and/or axis types if required
60123 if(restyleFlags.calc || relayoutFlags.calc) gd.calcdata = undefined;
60124 if(restyleFlags.clearAxisTypes) helpers.clearAxisTypes(gd, traces, layoutUpdate);
60125
60126 // fill in redraw sequence
60127 var seq = [];
60128
60129 if(relayoutFlags.layoutReplot) {
60130 // N.B. works fine when both
60131 // relayoutFlags.layoutReplot and restyleFlags.fullReplot are true
60132 seq.push(subroutines.layoutReplot);
60133 } else if(restyleFlags.fullReplot) {
60134 seq.push(exports._doPlot);
60135 } else {
60136 seq.push(Plots.previousPromises);
60137 axRangeSupplyDefaultsByPass(gd, relayoutFlags, relayoutSpecs) || Plots.supplyDefaults(gd);
60138
60139 if(restyleFlags.style) seq.push(subroutines.doTraceStyle);
60140 if(restyleFlags.colorbars || relayoutFlags.colorbars) seq.push(subroutines.doColorBars);
60141 if(relayoutFlags.legend) seq.push(subroutines.doLegend);
60142 if(relayoutFlags.layoutstyle) seq.push(subroutines.layoutStyles);
60143 if(relayoutFlags.axrange) addAxRangeSequence(seq, relayoutSpecs.rangesAltered);
60144 if(relayoutFlags.ticks) seq.push(subroutines.doTicksRelayout);
60145 if(relayoutFlags.modebar) seq.push(subroutines.doModeBar);
60146 if(relayoutFlags.camera) seq.push(subroutines.doCamera);
60147
60148 seq.push(emitAfterPlot);
60149 }
60150
60151 seq.push(Plots.rehover, Plots.redrag);
60152
60153 Queue.add(gd,
60154 update, [gd, restyleSpecs.undoit, relayoutSpecs.undoit, restyleSpecs.traces],
60155 update, [gd, restyleSpecs.redoit, relayoutSpecs.redoit, restyleSpecs.traces]
60156 );
60157
60158 var plotDone = Lib.syncOrAsync(seq, gd);
60159 if(!plotDone || !plotDone.then) plotDone = Promise.resolve(gd);
60160
60161 return plotDone.then(function() {
60162 gd.emit('plotly_update', {
60163 data: restyleSpecs.eventData,
60164 layout: relayoutSpecs.eventData
60165 });
60166
60167 return gd;
60168 });
60169}
60170
60171/*
60172 * internal-use-only restyle/relayout/update variants that record the initial
60173 * values in (fullLayout|fullTrace)._preGUI so changes can be persisted across
60174 * Plotly.react data updates, dependent on uirevision attributes
60175 */
60176function guiEdit(func) {
60177 return function wrappedEdit(gd) {
60178 gd._fullLayout._guiEditing = true;
60179 var p = func.apply(null, arguments);
60180 gd._fullLayout._guiEditing = false;
60181 return p;
60182 };
60183}
60184
60185// For connecting edited layout attributes to uirevision attrs
60186// If no `attr` we use `match[1] + '.uirevision'`
60187// Ordered by most common edits first, to minimize our search time
60188var layoutUIControlPatterns = [
60189 {pattern: /^hiddenlabels/, attr: 'legend.uirevision'},
60190 {pattern: /^((x|y)axis\d*)\.((auto)?range|title\.text)/},
60191
60192 // showspikes and modes include those nested inside scenes
60193 {pattern: /axis\d*\.showspikes$/, attr: 'modebar.uirevision'},
60194 {pattern: /(hover|drag)mode$/, attr: 'modebar.uirevision'},
60195
60196 {pattern: /^(scene\d*)\.camera/},
60197 {pattern: /^(geo\d*)\.(projection|center|fitbounds)/},
60198 {pattern: /^(ternary\d*\.[abc]axis)\.(min|title\.text)$/},
60199 {pattern: /^(polar\d*\.radialaxis)\.((auto)?range|angle|title\.text)/},
60200 {pattern: /^(polar\d*\.angularaxis)\.rotation/},
60201 {pattern: /^(mapbox\d*)\.(center|zoom|bearing|pitch)/},
60202
60203 {pattern: /^legend\.(x|y)$/, attr: 'editrevision'},
60204 {pattern: /^(shapes|annotations)/, attr: 'editrevision'},
60205 {pattern: /^title\.text$/, attr: 'editrevision'}
60206];
60207
60208// same for trace attributes: if `attr` is given it's in layout,
60209// or with no `attr` we use `trace.uirevision`
60210var traceUIControlPatterns = [
60211 {pattern: /^selectedpoints$/, attr: 'selectionrevision'},
60212 // "visible" includes trace.transforms[i].styles[j].value.visible
60213 {pattern: /(^|value\.)visible$/, attr: 'legend.uirevision'},
60214 {pattern: /^dimensions\[\d+\]\.constraintrange/},
60215 {pattern: /^node\.(x|y|groups)/}, // for Sankey nodes
60216 {pattern: /^level$/}, // for Sunburst, Treemap and Icicle traces
60217
60218 // below this you must be in editable: true mode
60219 // TODO: I still put name and title with `trace.uirevision`
60220 // reasonable or should these be `editrevision`?
60221 // Also applies to axis titles up in the layout section
60222
60223 // "name" also includes transform.styles
60224 {pattern: /(^|value\.)name$/},
60225 // including nested colorbar attributes (ie marker.colorbar)
60226 {pattern: /colorbar\.title\.text$/},
60227 {pattern: /colorbar\.(x|y)$/, attr: 'editrevision'}
60228];
60229
60230function findUIPattern(key, patternSpecs) {
60231 for(var i = 0; i < patternSpecs.length; i++) {
60232 var spec = patternSpecs[i];
60233 var match = key.match(spec.pattern);
60234 if(match) {
60235 return {head: match[1], attr: spec.attr};
60236 }
60237 }
60238}
60239
60240// We're finding the new uirevision before supplyDefaults, so do the
60241// inheritance manually. Note that only `undefined` inherits - other
60242// falsy values are returned.
60243function getNewRev(revAttr, container) {
60244 var newRev = nestedProperty(container, revAttr).get();
60245 if(newRev !== undefined) return newRev;
60246
60247 var parts = revAttr.split('.');
60248 parts.pop();
60249 while(parts.length > 1) {
60250 parts.pop();
60251 newRev = nestedProperty(container, parts.join('.') + '.uirevision').get();
60252 if(newRev !== undefined) return newRev;
60253 }
60254
60255 return container.uirevision;
60256}
60257
60258function getFullTraceIndexFromUid(uid, fullData) {
60259 for(var i = 0; i < fullData.length; i++) {
60260 if(fullData[i]._fullInput.uid === uid) return i;
60261 }
60262 return -1;
60263}
60264
60265function getTraceIndexFromUid(uid, data, tracei) {
60266 for(var i = 0; i < data.length; i++) {
60267 if(data[i].uid === uid) return i;
60268 }
60269 // fall back on trace order, but only if user didn't provide a uid for that trace
60270 return (!data[tracei] || data[tracei].uid) ? -1 : tracei;
60271}
60272
60273function valsMatch(v1, v2) {
60274 var v1IsObj = Lib.isPlainObject(v1);
60275 var v1IsArray = Array.isArray(v1);
60276 if(v1IsObj || v1IsArray) {
60277 return (
60278 (v1IsObj && Lib.isPlainObject(v2)) ||
60279 (v1IsArray && Array.isArray(v2))
60280 ) && JSON.stringify(v1) === JSON.stringify(v2);
60281 }
60282 return v1 === v2;
60283}
60284
60285function applyUIRevisions(data, layout, oldFullData, oldFullLayout) {
60286 var layoutPreGUI = oldFullLayout._preGUI;
60287 var key, revAttr, oldRev, newRev, match, preGUIVal, newNP, newVal;
60288 var bothInheritAutorange = [];
60289 var newRangeAccepted = {};
60290 for(key in layoutPreGUI) {
60291 match = findUIPattern(key, layoutUIControlPatterns);
60292 if(match) {
60293 revAttr = match.attr || (match.head + '.uirevision');
60294 oldRev = nestedProperty(oldFullLayout, revAttr).get();
60295 newRev = oldRev && getNewRev(revAttr, layout);
60296 if(newRev && (newRev === oldRev)) {
60297 preGUIVal = layoutPreGUI[key];
60298 if(preGUIVal === null) preGUIVal = undefined;
60299 newNP = nestedProperty(layout, key);
60300 newVal = newNP.get();
60301 if(valsMatch(newVal, preGUIVal)) {
60302 if(newVal === undefined && key.substr(key.length - 9) === 'autorange') {
60303 bothInheritAutorange.push(key.substr(0, key.length - 10));
60304 }
60305 newNP.set(undefinedToNull(nestedProperty(oldFullLayout, key).get()));
60306 continue;
60307 }
60308 }
60309 } else {
60310 Lib.warn('unrecognized GUI edit: ' + key);
60311 }
60312 // if we got this far, the new value was accepted as the new starting
60313 // point (either because it changed or revision changed)
60314 // so remove it from _preGUI for next time.
60315 delete layoutPreGUI[key];
60316
60317 if(key.substr(key.length - 8, 6) === 'range[') {
60318 newRangeAccepted[key.substr(0, key.length - 9)] = 1;
60319 }
60320 }
60321
60322 // Special logic for `autorange`, since it interacts with `range`:
60323 // If the new figure's matching `range` was kept, and `autorange`
60324 // wasn't supplied explicitly in either the original or the new figure,
60325 // we shouldn't alter that - but we may just have done that, so fix it.
60326 for(var i = 0; i < bothInheritAutorange.length; i++) {
60327 var axAttr = bothInheritAutorange[i];
60328 if(newRangeAccepted[axAttr]) {
60329 var newAx = nestedProperty(layout, axAttr).get();
60330 if(newAx) delete newAx.autorange;
60331 }
60332 }
60333
60334 // Now traces - try to match them up by uid (in case we added/deleted in
60335 // the middle), then fall back on index.
60336 var allTracePreGUI = oldFullLayout._tracePreGUI;
60337 for(var uid in allTracePreGUI) {
60338 var tracePreGUI = allTracePreGUI[uid];
60339 var newTrace = null;
60340 var fullInput;
60341 for(key in tracePreGUI) {
60342 // wait until we know we have preGUI values to look for traces
60343 // but if we don't find both, stop looking at this uid
60344 if(!newTrace) {
60345 var fulli = getFullTraceIndexFromUid(uid, oldFullData);
60346 if(fulli < 0) {
60347 // Somehow we didn't even have this trace in oldFullData...
60348 // I guess this could happen with `deleteTraces` or something
60349 delete allTracePreGUI[uid];
60350 break;
60351 }
60352 var fullTrace = oldFullData[fulli];
60353 fullInput = fullTrace._fullInput;
60354
60355 var newTracei = getTraceIndexFromUid(uid, data, fullInput.index);
60356 if(newTracei < 0) {
60357 // No match in new data
60358 delete allTracePreGUI[uid];
60359 break;
60360 }
60361 newTrace = data[newTracei];
60362 }
60363
60364 match = findUIPattern(key, traceUIControlPatterns);
60365 if(match) {
60366 if(match.attr) {
60367 oldRev = nestedProperty(oldFullLayout, match.attr).get();
60368 newRev = oldRev && getNewRev(match.attr, layout);
60369 } else {
60370 oldRev = fullInput.uirevision;
60371 // inheritance for trace.uirevision is simple, just layout.uirevision
60372 newRev = newTrace.uirevision;
60373 if(newRev === undefined) newRev = layout.uirevision;
60374 }
60375
60376 if(newRev && newRev === oldRev) {
60377 preGUIVal = tracePreGUI[key];
60378 if(preGUIVal === null) preGUIVal = undefined;
60379 newNP = nestedProperty(newTrace, key);
60380 newVal = newNP.get();
60381 if(valsMatch(newVal, preGUIVal)) {
60382 newNP.set(undefinedToNull(nestedProperty(fullInput, key).get()));
60383 continue;
60384 }
60385 }
60386 } else {
60387 Lib.warn('unrecognized GUI edit: ' + key + ' in trace uid ' + uid);
60388 }
60389 delete tracePreGUI[key];
60390 }
60391 }
60392}
60393
60394/**
60395 * Plotly.react:
60396 * A plot/update method that takes the full plot state (same API as plot/newPlot)
60397 * and diffs to determine the minimal update pathway
60398 *
60399 * @param {string id or DOM element} gd
60400 * the id or DOM element of the graph container div
60401 * @param {array of objects} data
60402 * array of traces, containing the data and display information for each trace
60403 * @param {object} layout
60404 * object describing the overall display of the plot,
60405 * all the stuff that doesn't pertain to any individual trace
60406 * @param {object} config
60407 * configuration options (see ./plot_config.js for more info)
60408 *
60409 * OR
60410 *
60411 * @param {string id or DOM element} gd
60412 * the id or DOM element of the graph container div
60413 * @param {object} figure
60414 * object containing `data`, `layout`, `config`, and `frames` members
60415 *
60416 */
60417function react(gd, data, layout, config) {
60418 var frames, plotDone;
60419
60420 function addFrames() { return exports.addFrames(gd, frames); }
60421
60422 gd = Lib.getGraphDiv(gd);
60423 helpers.clearPromiseQueue(gd);
60424
60425 var oldFullData = gd._fullData;
60426 var oldFullLayout = gd._fullLayout;
60427
60428 // you can use this as the initial draw as well as to update
60429 if(!Lib.isPlotDiv(gd) || !oldFullData || !oldFullLayout) {
60430 plotDone = exports.newPlot(gd, data, layout, config);
60431 } else {
60432 if(Lib.isPlainObject(data)) {
60433 var obj = data;
60434 data = obj.data;
60435 layout = obj.layout;
60436 config = obj.config;
60437 frames = obj.frames;
60438 }
60439
60440 var configChanged = false;
60441 // assume that if there's a config at all, we're reacting to it too,
60442 // and completely replace the previous config
60443 if(config) {
60444 var oldConfig = Lib.extendDeep({}, gd._context);
60445 gd._context = undefined;
60446 setPlotContext(gd, config);
60447 configChanged = diffConfig(oldConfig, gd._context);
60448 }
60449
60450 gd.data = data || [];
60451 helpers.cleanData(gd.data);
60452 gd.layout = layout || {};
60453 helpers.cleanLayout(gd.layout);
60454
60455 applyUIRevisions(gd.data, gd.layout, oldFullData, oldFullLayout);
60456
60457 // "true" skips updating calcdata and remapping arrays from calcTransforms,
60458 // which supplyDefaults usually does at the end, but we may need to NOT do
60459 // if the diff (which we haven't determined yet) says we'll recalc
60460 Plots.supplyDefaults(gd, {skipUpdateCalc: true});
60461
60462 var newFullData = gd._fullData;
60463 var newFullLayout = gd._fullLayout;
60464 var immutable = newFullLayout.datarevision === undefined;
60465 var transition = newFullLayout.transition;
60466
60467 var relayoutFlags = diffLayout(gd, oldFullLayout, newFullLayout, immutable, transition);
60468 var newDataRevision = relayoutFlags.newDataRevision;
60469 var restyleFlags = diffData(gd, oldFullData, newFullData, immutable, transition, newDataRevision);
60470
60471 // TODO: how to translate this part of relayout to Plotly.react?
60472 // // Setting width or height to null must reset the graph's width / height
60473 // // back to its initial value as computed during the first pass in Plots.plotAutoSize.
60474 // //
60475 // // To do so, we must manually set them back here using the _initialAutoSize cache.
60476 // if(['width', 'height'].indexOf(ai) !== -1 && vi === null) {
60477 // fullLayout[ai] = gd._initialAutoSize[ai];
60478 // }
60479
60480 if(updateAutosize(gd)) relayoutFlags.layoutReplot = true;
60481
60482 // clear calcdata and empty categories if required
60483 if(restyleFlags.calc || relayoutFlags.calc) {
60484 gd.calcdata = undefined;
60485 var allNames = Object.getOwnPropertyNames(newFullLayout);
60486 for(var q = 0; q < allNames.length; q++) {
60487 var name = allNames[q];
60488 var start = name.substring(0, 5);
60489 if(start === 'xaxis' || start === 'yaxis') {
60490 var emptyCategories = newFullLayout[name]._emptyCategories;
60491 if(emptyCategories) emptyCategories();
60492 }
60493 }
60494 // otherwise do the calcdata updates and calcTransform array remaps that we skipped earlier
60495 } else {
60496 Plots.supplyDefaultsUpdateCalc(gd.calcdata, newFullData);
60497 }
60498
60499 // Note: what restyle/relayout use impliedEdits and clearAxisTypes for
60500 // must be handled by the user when using Plotly.react.
60501
60502 // fill in redraw sequence
60503 var seq = [];
60504
60505 if(frames) {
60506 gd._transitionData = {};
60507 Plots.createTransitionData(gd);
60508 seq.push(addFrames);
60509 }
60510
60511 // Transition pathway,
60512 // only used when 'transition' is set by user and
60513 // when at least one animatable attribute has changed,
60514 // N.B. config changed aren't animatable
60515 if(newFullLayout.transition && !configChanged && (restyleFlags.anim || relayoutFlags.anim)) {
60516 if(relayoutFlags.ticks) seq.push(subroutines.doTicksRelayout);
60517
60518 Plots.doCalcdata(gd);
60519 subroutines.doAutoRangeAndConstraints(gd);
60520
60521 seq.push(function() {
60522 return Plots.transitionFromReact(gd, restyleFlags, relayoutFlags, oldFullLayout);
60523 });
60524 } else if(restyleFlags.fullReplot || relayoutFlags.layoutReplot || configChanged) {
60525 gd._fullLayout._skipDefaults = true;
60526 seq.push(exports._doPlot);
60527 } else {
60528 for(var componentType in relayoutFlags.arrays) {
60529 var indices = relayoutFlags.arrays[componentType];
60530 if(indices.length) {
60531 var drawOne = Registry.getComponentMethod(componentType, 'drawOne');
60532 if(drawOne !== Lib.noop) {
60533 for(var i = 0; i < indices.length; i++) {
60534 drawOne(gd, indices[i]);
60535 }
60536 } else {
60537 var draw = Registry.getComponentMethod(componentType, 'draw');
60538 if(draw === Lib.noop) {
60539 throw new Error('cannot draw components: ' + componentType);
60540 }
60541 draw(gd);
60542 }
60543 }
60544 }
60545
60546 seq.push(Plots.previousPromises);
60547 if(restyleFlags.style) seq.push(subroutines.doTraceStyle);
60548 if(restyleFlags.colorbars || relayoutFlags.colorbars) seq.push(subroutines.doColorBars);
60549 if(relayoutFlags.legend) seq.push(subroutines.doLegend);
60550 if(relayoutFlags.layoutstyle) seq.push(subroutines.layoutStyles);
60551 if(relayoutFlags.axrange) addAxRangeSequence(seq);
60552 if(relayoutFlags.ticks) seq.push(subroutines.doTicksRelayout);
60553 if(relayoutFlags.modebar) seq.push(subroutines.doModeBar);
60554 if(relayoutFlags.camera) seq.push(subroutines.doCamera);
60555 seq.push(emitAfterPlot);
60556 }
60557
60558 seq.push(Plots.rehover, Plots.redrag);
60559
60560 plotDone = Lib.syncOrAsync(seq, gd);
60561 if(!plotDone || !plotDone.then) plotDone = Promise.resolve(gd);
60562 }
60563
60564 return plotDone.then(function() {
60565 gd.emit('plotly_react', {
60566 data: data,
60567 layout: layout
60568 });
60569
60570 return gd;
60571 });
60572}
60573
60574function diffData(gd, oldFullData, newFullData, immutable, transition, newDataRevision) {
60575 var sameTraceLength = oldFullData.length === newFullData.length;
60576
60577 if(!transition && !sameTraceLength) {
60578 return {
60579 fullReplot: true,
60580 calc: true
60581 };
60582 }
60583
60584 var flags = editTypes.traceFlags();
60585 flags.arrays = {};
60586 flags.nChanges = 0;
60587 flags.nChangesAnim = 0;
60588
60589 var i, trace;
60590
60591 function getTraceValObject(parts) {
60592 var out = PlotSchema.getTraceValObject(trace, parts);
60593 if(!trace._module.animatable && out.anim) {
60594 out.anim = false;
60595 }
60596 return out;
60597 }
60598
60599 var diffOpts = {
60600 getValObject: getTraceValObject,
60601 flags: flags,
60602 immutable: immutable,
60603 transition: transition,
60604 newDataRevision: newDataRevision,
60605 gd: gd
60606 };
60607
60608 var seenUIDs = {};
60609
60610 for(i = 0; i < oldFullData.length; i++) {
60611 if(newFullData[i]) {
60612 trace = newFullData[i]._fullInput;
60613 if(Plots.hasMakesDataTransform(trace)) trace = newFullData[i];
60614 if(seenUIDs[trace.uid]) continue;
60615 seenUIDs[trace.uid] = 1;
60616
60617 getDiffFlags(oldFullData[i]._fullInput, trace, [], diffOpts);
60618 }
60619 }
60620
60621 if(flags.calc || flags.plot) {
60622 flags.fullReplot = true;
60623 }
60624
60625 if(transition && flags.nChanges && flags.nChangesAnim) {
60626 flags.anim = (flags.nChanges === flags.nChangesAnim) && sameTraceLength ? 'all' : 'some';
60627 }
60628
60629 return flags;
60630}
60631
60632function diffLayout(gd, oldFullLayout, newFullLayout, immutable, transition) {
60633 var flags = editTypes.layoutFlags();
60634 flags.arrays = {};
60635 flags.rangesAltered = {};
60636 flags.nChanges = 0;
60637 flags.nChangesAnim = 0;
60638
60639 function getLayoutValObject(parts) {
60640 return PlotSchema.getLayoutValObject(newFullLayout, parts);
60641 }
60642
60643 var diffOpts = {
60644 getValObject: getLayoutValObject,
60645 flags: flags,
60646 immutable: immutable,
60647 transition: transition,
60648 gd: gd
60649 };
60650
60651 getDiffFlags(oldFullLayout, newFullLayout, [], diffOpts);
60652
60653 if(flags.plot || flags.calc) {
60654 flags.layoutReplot = true;
60655 }
60656
60657 if(transition && flags.nChanges && flags.nChangesAnim) {
60658 flags.anim = flags.nChanges === flags.nChangesAnim ? 'all' : 'some';
60659 }
60660
60661 return flags;
60662}
60663
60664function getDiffFlags(oldContainer, newContainer, outerparts, opts) {
60665 var valObject, key, astr;
60666
60667 var getValObject = opts.getValObject;
60668 var flags = opts.flags;
60669 var immutable = opts.immutable;
60670 var inArray = opts.inArray;
60671 var arrayIndex = opts.arrayIndex;
60672
60673 function changed() {
60674 var editType = valObject.editType;
60675 if(inArray && editType.indexOf('arraydraw') !== -1) {
60676 Lib.pushUnique(flags.arrays[inArray], arrayIndex);
60677 return;
60678 }
60679 editTypes.update(flags, valObject);
60680
60681 if(editType !== 'none') {
60682 flags.nChanges++;
60683 }
60684
60685 // track animatable changes
60686 if(opts.transition && valObject.anim) {
60687 flags.nChangesAnim++;
60688 }
60689
60690 // track cartesian axes with altered ranges
60691 if(AX_RANGE_RE.test(astr) || AX_AUTORANGE_RE.test(astr)) {
60692 flags.rangesAltered[outerparts[0]] = 1;
60693 }
60694
60695 // clear _inputDomain on cartesian axes with altered domains
60696 if(AX_DOMAIN_RE.test(astr)) {
60697 nestedProperty(newContainer, '_inputDomain').set(null);
60698 }
60699
60700 // track datarevision changes
60701 if(key === 'datarevision') {
60702 flags.newDataRevision = 1;
60703 }
60704 }
60705
60706 function valObjectCanBeDataArray(valObject) {
60707 return valObject.valType === 'data_array' || valObject.arrayOk;
60708 }
60709
60710 for(key in oldContainer) {
60711 // short-circuit based on previous calls or previous keys that already maximized the pathway
60712 if(flags.calc && !opts.transition) return;
60713
60714 var oldVal = oldContainer[key];
60715 var newVal = newContainer[key];
60716 var parts = outerparts.concat(key);
60717 astr = parts.join('.');
60718
60719 if(key.charAt(0) === '_' || typeof oldVal === 'function' || oldVal === newVal) continue;
60720
60721 // FIXME: ax.tick0 and dtick get filled in during plotting (except for geo subplots),
60722 // and unlike other auto values they don't make it back into the input,
60723 // so newContainer won't have them.
60724 if((key === 'tick0' || key === 'dtick') && outerparts[0] !== 'geo') {
60725 var tickMode = newContainer.tickmode;
60726 if(tickMode === 'auto' || tickMode === 'array' || !tickMode) continue;
60727 }
60728 // FIXME: Similarly for axis ranges for 3D
60729 // contourcarpet doesn't HAVE zmin/zmax, they're just auto-added. It needs them.
60730 if(key === 'range' && newContainer.autorange) continue;
60731 if((key === 'zmin' || key === 'zmax') && newContainer.type === 'contourcarpet') continue;
60732
60733 valObject = getValObject(parts);
60734
60735 // in case type changed, we may not even *have* a valObject.
60736 if(!valObject) continue;
60737
60738 if(valObject._compareAsJSON && JSON.stringify(oldVal) === JSON.stringify(newVal)) continue;
60739
60740 var valType = valObject.valType;
60741 var i;
60742
60743 var canBeDataArray = valObjectCanBeDataArray(valObject);
60744 var wasArray = Array.isArray(oldVal);
60745 var nowArray = Array.isArray(newVal);
60746
60747 // hack for traces that modify the data in supplyDefaults, like
60748 // converting 1D to 2D arrays, which will always create new objects
60749 if(wasArray && nowArray) {
60750 var inputKey = '_input_' + key;
60751 var oldValIn = oldContainer[inputKey];
60752 var newValIn = newContainer[inputKey];
60753 if(Array.isArray(oldValIn) && oldValIn === newValIn) continue;
60754 }
60755
60756 if(newVal === undefined) {
60757 if(canBeDataArray && wasArray) flags.calc = true;
60758 else changed();
60759 } else if(valObject._isLinkedToArray) {
60760 var arrayEditIndices = [];
60761 var extraIndices = false;
60762 if(!inArray) flags.arrays[key] = arrayEditIndices;
60763
60764 var minLen = Math.min(oldVal.length, newVal.length);
60765 var maxLen = Math.max(oldVal.length, newVal.length);
60766 if(minLen !== maxLen) {
60767 if(valObject.editType === 'arraydraw') {
60768 extraIndices = true;
60769 } else {
60770 changed();
60771 continue;
60772 }
60773 }
60774
60775 for(i = 0; i < minLen; i++) {
60776 getDiffFlags(oldVal[i], newVal[i], parts.concat(i),
60777 // add array indices, but not if we're already in an array
60778 Lib.extendFlat({inArray: key, arrayIndex: i}, opts));
60779 }
60780
60781 // put this at the end so that we know our collected array indices are sorted
60782 // but the check for length changes happens up front so we can short-circuit
60783 // diffing if appropriate
60784 if(extraIndices) {
60785 for(i = minLen; i < maxLen; i++) {
60786 arrayEditIndices.push(i);
60787 }
60788 }
60789 } else if(!valType && Lib.isPlainObject(oldVal)) {
60790 getDiffFlags(oldVal, newVal, parts, opts);
60791 } else if(canBeDataArray) {
60792 if(wasArray && nowArray) {
60793 // don't try to diff two data arrays. If immutable we know the data changed,
60794 // if not, assume it didn't and let `layout.datarevision` tell us if it did
60795 if(immutable) {
60796 flags.calc = true;
60797 }
60798
60799 // look for animatable attributes when the data changed
60800 if(immutable || opts.newDataRevision) {
60801 changed();
60802 }
60803 } else if(wasArray !== nowArray) {
60804 flags.calc = true;
60805 } else changed();
60806 } else if(wasArray && nowArray) {
60807 // info array, colorscale, 'any' - these are short, just stringify.
60808 // I don't *think* that covers up any real differences post-validation, does it?
60809 // otherwise we need to dive in 1 (info_array) or 2 (colorscale) levels and compare
60810 // all elements.
60811 if(oldVal.length !== newVal.length || String(oldVal) !== String(newVal)) {
60812 changed();
60813 }
60814 } else {
60815 changed();
60816 }
60817 }
60818
60819 for(key in newContainer) {
60820 if(!(key in oldContainer || key.charAt(0) === '_' || typeof newContainer[key] === 'function')) {
60821 valObject = getValObject(outerparts.concat(key));
60822
60823 if(valObjectCanBeDataArray(valObject) && Array.isArray(newContainer[key])) {
60824 flags.calc = true;
60825 return;
60826 } else changed();
60827 }
60828 }
60829}
60830
60831/*
60832 * simple diff for config - for now, just treat all changes as equivalent
60833 */
60834function diffConfig(oldConfig, newConfig) {
60835 var key;
60836
60837 for(key in oldConfig) {
60838 if(key.charAt(0) === '_') continue;
60839 var oldVal = oldConfig[key];
60840 var newVal = newConfig[key];
60841 if(oldVal !== newVal) {
60842 if(Lib.isPlainObject(oldVal) && Lib.isPlainObject(newVal)) {
60843 if(diffConfig(oldVal, newVal)) {
60844 return true;
60845 }
60846 } else if(Array.isArray(oldVal) && Array.isArray(newVal)) {
60847 if(oldVal.length !== newVal.length) {
60848 return true;
60849 }
60850 for(var i = 0; i < oldVal.length; i++) {
60851 if(oldVal[i] !== newVal[i]) {
60852 if(Lib.isPlainObject(oldVal[i]) && Lib.isPlainObject(newVal[i])) {
60853 if(diffConfig(oldVal[i], newVal[i])) {
60854 return true;
60855 }
60856 } else {
60857 return true;
60858 }
60859 }
60860 }
60861 } else {
60862 return true;
60863 }
60864 }
60865 }
60866}
60867
60868/**
60869 * Animate to a frame, sequence of frame, frame group, or frame definition
60870 *
60871 * @param {string id or DOM element} gd
60872 * the id or DOM element of the graph container div
60873 *
60874 * @param {string or object or array of strings or array of objects} frameOrGroupNameOrFrameList
60875 * a single frame, array of frames, or group to which to animate. The intent is
60876 * inferred by the type of the input. Valid inputs are:
60877 *
60878 * - string, e.g. 'groupname': animate all frames of a given `group` in the order
60879 * in which they are defined via `Plotly.addFrames`.
60880 *
60881 * - array of strings, e.g. ['frame1', frame2']: a list of frames by name to which
60882 * to animate in sequence
60883 *
60884 * - object: {data: ...}: a frame definition to which to animate. The frame is not
60885 * and does not need to be added via `Plotly.addFrames`. It may contain any of
60886 * the properties of a frame, including `data`, `layout`, and `traces`. The
60887 * frame is used as provided and does not use the `baseframe` property.
60888 *
60889 * - array of objects, e.g. [{data: ...}, {data: ...}]: a list of frame objects,
60890 * each following the same rules as a single `object`.
60891 *
60892 * @param {object} animationOpts
60893 * configuration for the animation
60894 */
60895function animate(gd, frameOrGroupNameOrFrameList, animationOpts) {
60896 gd = Lib.getGraphDiv(gd);
60897
60898 if(!Lib.isPlotDiv(gd)) {
60899 throw new Error(
60900 'This element is not a Plotly plot: ' + gd + '. It\'s likely that you\'ve failed ' +
60901 'to create a plot before animating it. For more details, see ' +
60902 'https://plotly.com/javascript/animations/'
60903 );
60904 }
60905
60906 var trans = gd._transitionData;
60907
60908 // This is the queue of frames that will be animated as soon as possible. They
60909 // are popped immediately upon the *start* of a transition:
60910 if(!trans._frameQueue) {
60911 trans._frameQueue = [];
60912 }
60913
60914 animationOpts = Plots.supplyAnimationDefaults(animationOpts);
60915 var transitionOpts = animationOpts.transition;
60916 var frameOpts = animationOpts.frame;
60917
60918 // Since frames are popped immediately, an empty queue only means all frames have
60919 // *started* to transition, not that the animation is complete. To solve that,
60920 // track a separate counter that increments at the same time as frames are added
60921 // to the queue, but decrements only when the transition is complete.
60922 if(trans._frameWaitingCnt === undefined) {
60923 trans._frameWaitingCnt = 0;
60924 }
60925
60926 function getTransitionOpts(i) {
60927 if(Array.isArray(transitionOpts)) {
60928 if(i >= transitionOpts.length) {
60929 return transitionOpts[0];
60930 } else {
60931 return transitionOpts[i];
60932 }
60933 } else {
60934 return transitionOpts;
60935 }
60936 }
60937
60938 function getFrameOpts(i) {
60939 if(Array.isArray(frameOpts)) {
60940 if(i >= frameOpts.length) {
60941 return frameOpts[0];
60942 } else {
60943 return frameOpts[i];
60944 }
60945 } else {
60946 return frameOpts;
60947 }
60948 }
60949
60950 // Execute a callback after the wrapper function has been called n times.
60951 // This is used to defer the resolution until a transition has resolved *and*
60952 // the frame has completed. If it's not done this way, then we get a race
60953 // condition in which the animation might resolve before a transition is complete
60954 // or vice versa.
60955 function callbackOnNthTime(cb, n) {
60956 var cnt = 0;
60957 return function() {
60958 if(cb && ++cnt === n) {
60959 return cb();
60960 }
60961 };
60962 }
60963
60964 return new Promise(function(resolve, reject) {
60965 function discardExistingFrames() {
60966 if(trans._frameQueue.length === 0) {
60967 return;
60968 }
60969
60970 while(trans._frameQueue.length) {
60971 var next = trans._frameQueue.pop();
60972 if(next.onInterrupt) {
60973 next.onInterrupt();
60974 }
60975 }
60976
60977 gd.emit('plotly_animationinterrupted', []);
60978 }
60979
60980 function queueFrames(frameList) {
60981 if(frameList.length === 0) return;
60982
60983 for(var i = 0; i < frameList.length; i++) {
60984 var computedFrame;
60985
60986 if(frameList[i].type === 'byname') {
60987 // If it's a named frame, compute it:
60988 computedFrame = Plots.computeFrame(gd, frameList[i].name);
60989 } else {
60990 // Otherwise we must have been given a simple object, so treat
60991 // the input itself as the computed frame.
60992 computedFrame = frameList[i].data;
60993 }
60994
60995 var frameOpts = getFrameOpts(i);
60996 var transitionOpts = getTransitionOpts(i);
60997
60998 // It doesn't make much sense for the transition duration to be greater than
60999 // the frame duration, so limit it:
61000 transitionOpts.duration = Math.min(transitionOpts.duration, frameOpts.duration);
61001
61002 var nextFrame = {
61003 frame: computedFrame,
61004 name: frameList[i].name,
61005 frameOpts: frameOpts,
61006 transitionOpts: transitionOpts,
61007 };
61008 if(i === frameList.length - 1) {
61009 // The last frame in this .animate call stores the promise resolve
61010 // and reject callbacks. This is how we ensure that the animation
61011 // loop (which may exist as a result of a *different* .animate call)
61012 // still resolves or rejecdts this .animate call's promise. once it's
61013 // complete.
61014 nextFrame.onComplete = callbackOnNthTime(resolve, 2);
61015 nextFrame.onInterrupt = reject;
61016 }
61017
61018 trans._frameQueue.push(nextFrame);
61019 }
61020
61021 // Set it as never having transitioned to a frame. This will cause the animation
61022 // loop to immediately transition to the next frame (which, for immediate mode,
61023 // is the first frame in the list since all others would have been discarded
61024 // below)
61025 if(animationOpts.mode === 'immediate') {
61026 trans._lastFrameAt = -Infinity;
61027 }
61028
61029 // Only it's not already running, start a RAF loop. This could be avoided in the
61030 // case that there's only one frame, but it significantly complicated the logic
61031 // and only sped things up by about 5% or so for a lorenz attractor simulation.
61032 // It would be a fine thing to implement, but the benefit of that optimization
61033 // doesn't seem worth the extra complexity.
61034 if(!trans._animationRaf) {
61035 beginAnimationLoop();
61036 }
61037 }
61038
61039 function stopAnimationLoop() {
61040 gd.emit('plotly_animated');
61041
61042 // Be sure to unset also since it's how we know whether a loop is already running:
61043 window.cancelAnimationFrame(trans._animationRaf);
61044 trans._animationRaf = null;
61045 }
61046
61047 function nextFrame() {
61048 if(trans._currentFrame && trans._currentFrame.onComplete) {
61049 // Execute the callback and unset it to ensure it doesn't
61050 // accidentally get called twice
61051 trans._currentFrame.onComplete();
61052 }
61053
61054 var newFrame = trans._currentFrame = trans._frameQueue.shift();
61055
61056 if(newFrame) {
61057 // Since it's sometimes necessary to do deep digging into frame data,
61058 // we'll consider it not 100% impossible for nulls or numbers to sneak through,
61059 // so check when casting the name, just to be absolutely certain:
61060 var stringName = newFrame.name ? newFrame.name.toString() : null;
61061 gd._fullLayout._currentFrame = stringName;
61062
61063 trans._lastFrameAt = Date.now();
61064 trans._timeToNext = newFrame.frameOpts.duration;
61065
61066 // This is simply called and it's left to .transition to decide how to manage
61067 // interrupting current transitions. That means we don't need to worry about
61068 // how it resolves or what happens after this:
61069 Plots.transition(gd,
61070 newFrame.frame.data,
61071 newFrame.frame.layout,
61072 helpers.coerceTraceIndices(gd, newFrame.frame.traces),
61073 newFrame.frameOpts,
61074 newFrame.transitionOpts
61075 ).then(function() {
61076 if(newFrame.onComplete) {
61077 newFrame.onComplete();
61078 }
61079 });
61080
61081 gd.emit('plotly_animatingframe', {
61082 name: stringName,
61083 frame: newFrame.frame,
61084 animation: {
61085 frame: newFrame.frameOpts,
61086 transition: newFrame.transitionOpts,
61087 }
61088 });
61089 } else {
61090 // If there are no more frames, then stop the RAF loop:
61091 stopAnimationLoop();
61092 }
61093 }
61094
61095 function beginAnimationLoop() {
61096 gd.emit('plotly_animating');
61097
61098 // If no timer is running, then set last frame = long ago so that the next
61099 // frame is immediately transitioned:
61100 trans._lastFrameAt = -Infinity;
61101 trans._timeToNext = 0;
61102 trans._runningTransitions = 0;
61103 trans._currentFrame = null;
61104
61105 var doFrame = function() {
61106 // This *must* be requested before nextFrame since nextFrame may decide
61107 // to cancel it if there's nothing more to animated:
61108 trans._animationRaf = window.requestAnimationFrame(doFrame);
61109
61110 // Check if we're ready for a new frame:
61111 if(Date.now() - trans._lastFrameAt > trans._timeToNext) {
61112 nextFrame();
61113 }
61114 };
61115
61116 doFrame();
61117 }
61118
61119 // This is an animate-local counter that helps match up option input list
61120 // items with the particular frame.
61121 var configCounter = 0;
61122 function setTransitionConfig(frame) {
61123 if(Array.isArray(transitionOpts)) {
61124 if(configCounter >= transitionOpts.length) {
61125 frame.transitionOpts = transitionOpts[configCounter];
61126 } else {
61127 frame.transitionOpts = transitionOpts[0];
61128 }
61129 } else {
61130 frame.transitionOpts = transitionOpts;
61131 }
61132 configCounter++;
61133 return frame;
61134 }
61135
61136 // Disambiguate what's sort of frames have been received
61137 var i, frame;
61138 var frameList = [];
61139 var allFrames = frameOrGroupNameOrFrameList === undefined || frameOrGroupNameOrFrameList === null;
61140 var isFrameArray = Array.isArray(frameOrGroupNameOrFrameList);
61141 var isSingleFrame = !allFrames && !isFrameArray && Lib.isPlainObject(frameOrGroupNameOrFrameList);
61142
61143 if(isSingleFrame) {
61144 // In this case, a simple object has been passed to animate.
61145 frameList.push({
61146 type: 'object',
61147 data: setTransitionConfig(Lib.extendFlat({}, frameOrGroupNameOrFrameList))
61148 });
61149 } else if(allFrames || ['string', 'number'].indexOf(typeof frameOrGroupNameOrFrameList) !== -1) {
61150 // In this case, null or undefined has been passed so that we want to
61151 // animate *all* currently defined frames
61152 for(i = 0; i < trans._frames.length; i++) {
61153 frame = trans._frames[i];
61154
61155 if(!frame) continue;
61156
61157 if(allFrames || String(frame.group) === String(frameOrGroupNameOrFrameList)) {
61158 frameList.push({
61159 type: 'byname',
61160 name: String(frame.name),
61161 data: setTransitionConfig({name: frame.name})
61162 });
61163 }
61164 }
61165 } else if(isFrameArray) {
61166 for(i = 0; i < frameOrGroupNameOrFrameList.length; i++) {
61167 var frameOrName = frameOrGroupNameOrFrameList[i];
61168 if(['number', 'string'].indexOf(typeof frameOrName) !== -1) {
61169 frameOrName = String(frameOrName);
61170 // In this case, there's an array and this frame is a string name:
61171 frameList.push({
61172 type: 'byname',
61173 name: frameOrName,
61174 data: setTransitionConfig({name: frameOrName})
61175 });
61176 } else if(Lib.isPlainObject(frameOrName)) {
61177 frameList.push({
61178 type: 'object',
61179 data: setTransitionConfig(Lib.extendFlat({}, frameOrName))
61180 });
61181 }
61182 }
61183 }
61184
61185 // Verify that all of these frames actually exist; return and reject if not:
61186 for(i = 0; i < frameList.length; i++) {
61187 frame = frameList[i];
61188 if(frame.type === 'byname' && !trans._frameHash[frame.data.name]) {
61189 Lib.warn('animate failure: frame not found: "' + frame.data.name + '"');
61190 reject();
61191 return;
61192 }
61193 }
61194
61195 // If the mode is either next or immediate, then all currently queued frames must
61196 // be dumped and the corresponding .animate promises rejected.
61197 if(['next', 'immediate'].indexOf(animationOpts.mode) !== -1) {
61198 discardExistingFrames();
61199 }
61200
61201 if(animationOpts.direction === 'reverse') {
61202 frameList.reverse();
61203 }
61204
61205 var currentFrame = gd._fullLayout._currentFrame;
61206 if(currentFrame && animationOpts.fromcurrent) {
61207 var idx = -1;
61208 for(i = 0; i < frameList.length; i++) {
61209 frame = frameList[i];
61210 if(frame.type === 'byname' && frame.name === currentFrame) {
61211 idx = i;
61212 break;
61213 }
61214 }
61215
61216 if(idx > 0 && idx < frameList.length - 1) {
61217 var filteredFrameList = [];
61218 for(i = 0; i < frameList.length; i++) {
61219 frame = frameList[i];
61220 if(frameList[i].type !== 'byname' || i > idx) {
61221 filteredFrameList.push(frame);
61222 }
61223 }
61224 frameList = filteredFrameList;
61225 }
61226 }
61227
61228 if(frameList.length > 0) {
61229 queueFrames(frameList);
61230 } else {
61231 // This is the case where there were simply no frames. It's a little strange
61232 // since there's not much to do:
61233 gd.emit('plotly_animated');
61234 resolve();
61235 }
61236 });
61237}
61238
61239/**
61240 * Register new frames
61241 *
61242 * @param {string id or DOM element} gd
61243 * the id or DOM element of the graph container div
61244 *
61245 * @param {array of objects} frameList
61246 * list of frame definitions, in which each object includes any of:
61247 * - name: {string} name of frame to add
61248 * - data: {array of objects} trace data
61249 * - layout {object} layout definition
61250 * - traces {array} trace indices
61251 * - baseframe {string} name of frame from which this frame gets defaults
61252 *
61253 * @param {array of integers} indices
61254 * an array of integer indices matching the respective frames in `frameList`. If not
61255 * provided, an index will be provided in serial order. If already used, the frame
61256 * will be overwritten.
61257 */
61258function addFrames(gd, frameList, indices) {
61259 gd = Lib.getGraphDiv(gd);
61260
61261 if(frameList === null || frameList === undefined) {
61262 return Promise.resolve();
61263 }
61264
61265 if(!Lib.isPlotDiv(gd)) {
61266 throw new Error(
61267 'This element is not a Plotly plot: ' + gd + '. It\'s likely that you\'ve failed ' +
61268 'to create a plot before adding frames. For more details, see ' +
61269 'https://plotly.com/javascript/animations/'
61270 );
61271 }
61272
61273 var i, frame, j, idx;
61274 var _frames = gd._transitionData._frames;
61275 var _frameHash = gd._transitionData._frameHash;
61276
61277
61278 if(!Array.isArray(frameList)) {
61279 throw new Error('addFrames failure: frameList must be an Array of frame definitions' + frameList);
61280 }
61281
61282 // Create a sorted list of insertions since we run into lots of problems if these
61283 // aren't in ascending order of index:
61284 //
61285 // Strictly for sorting. Make sure this is guaranteed to never collide with any
61286 // already-exisisting indices:
61287 var bigIndex = _frames.length + frameList.length * 2;
61288
61289 var insertions = [];
61290 var _frameHashLocal = {};
61291 for(i = frameList.length - 1; i >= 0; i--) {
61292 if(!Lib.isPlainObject(frameList[i])) continue;
61293
61294 // The entire logic for checking for this type of name collision can be removed once we migrate to ES6 and
61295 // use a Map instead of an Object instance, as Map keys aren't converted to strings.
61296 var lookupName = frameList[i].name;
61297 var name = (_frameHash[lookupName] || _frameHashLocal[lookupName] || {}).name;
61298 var newName = frameList[i].name;
61299 var collisionPresent = _frameHash[name] || _frameHashLocal[name];
61300
61301 if(name && newName && typeof newName === 'number' && collisionPresent && numericNameWarningCount < numericNameWarningCountLimit) {
61302 numericNameWarningCount++;
61303
61304 Lib.warn('addFrames: overwriting frame "' + (_frameHash[name] || _frameHashLocal[name]).name +
61305 '" with a frame whose name of type "number" also equates to "' +
61306 name + '". This is valid but may potentially lead to unexpected ' +
61307 'behavior since all plotly.js frame names are stored internally ' +
61308 'as strings.');
61309
61310 if(numericNameWarningCount === numericNameWarningCountLimit) {
61311 Lib.warn('addFrames: This API call has yielded too many of these warnings. ' +
61312 'For the rest of this call, further warnings about numeric frame ' +
61313 'names will be suppressed.');
61314 }
61315 }
61316
61317 _frameHashLocal[lookupName] = {name: lookupName};
61318
61319 insertions.push({
61320 frame: Plots.supplyFrameDefaults(frameList[i]),
61321 index: (indices && indices[i] !== undefined && indices[i] !== null) ? indices[i] : bigIndex + i
61322 });
61323 }
61324
61325 // Sort this, taking note that undefined insertions end up at the end:
61326 insertions.sort(function(a, b) {
61327 if(a.index > b.index) return -1;
61328 if(a.index < b.index) return 1;
61329 return 0;
61330 });
61331
61332 var ops = [];
61333 var revops = [];
61334 var frameCount = _frames.length;
61335
61336 for(i = insertions.length - 1; i >= 0; i--) {
61337 frame = insertions[i].frame;
61338
61339 if(typeof frame.name === 'number') {
61340 Lib.warn('Warning: addFrames accepts frames with numeric names, but the numbers are' +
61341 'implicitly cast to strings');
61342 }
61343
61344 if(!frame.name) {
61345 // Repeatedly assign a default name, incrementing the counter each time until
61346 // we get a name that's not in the hashed lookup table:
61347 while(_frameHash[(frame.name = 'frame ' + gd._transitionData._counter++)]);
61348 }
61349
61350 if(_frameHash[frame.name]) {
61351 // If frame is present, overwrite its definition:
61352 for(j = 0; j < _frames.length; j++) {
61353 if((_frames[j] || {}).name === frame.name) break;
61354 }
61355 ops.push({type: 'replace', index: j, value: frame});
61356 revops.unshift({type: 'replace', index: j, value: _frames[j]});
61357 } else {
61358 // Otherwise insert it at the end of the list:
61359 idx = Math.max(0, Math.min(insertions[i].index, frameCount));
61360
61361 ops.push({type: 'insert', index: idx, value: frame});
61362 revops.unshift({type: 'delete', index: idx});
61363 frameCount++;
61364 }
61365 }
61366
61367 var undoFunc = Plots.modifyFrames;
61368 var redoFunc = Plots.modifyFrames;
61369 var undoArgs = [gd, revops];
61370 var redoArgs = [gd, ops];
61371
61372 if(Queue) Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs);
61373
61374 return Plots.modifyFrames(gd, ops);
61375}
61376
61377/**
61378 * Delete frame
61379 *
61380 * @param {string id or DOM element} gd
61381 * the id or DOM element of the graph container div
61382 *
61383 * @param {array of integers} frameList
61384 * list of integer indices of frames to be deleted
61385 */
61386function deleteFrames(gd, frameList) {
61387 gd = Lib.getGraphDiv(gd);
61388
61389 if(!Lib.isPlotDiv(gd)) {
61390 throw new Error('This element is not a Plotly plot: ' + gd);
61391 }
61392
61393 var i, idx;
61394 var _frames = gd._transitionData._frames;
61395 var ops = [];
61396 var revops = [];
61397
61398 if(!frameList) {
61399 frameList = [];
61400 for(i = 0; i < _frames.length; i++) {
61401 frameList.push(i);
61402 }
61403 }
61404
61405 frameList = frameList.slice();
61406 frameList.sort();
61407
61408 for(i = frameList.length - 1; i >= 0; i--) {
61409 idx = frameList[i];
61410 ops.push({type: 'delete', index: idx});
61411 revops.unshift({type: 'insert', index: idx, value: _frames[idx]});
61412 }
61413
61414 var undoFunc = Plots.modifyFrames;
61415 var redoFunc = Plots.modifyFrames;
61416 var undoArgs = [gd, revops];
61417 var redoArgs = [gd, ops];
61418
61419 if(Queue) Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs);
61420
61421 return Plots.modifyFrames(gd, ops);
61422}
61423
61424/**
61425 * Purge a graph container div back to its initial pre-_doPlot state
61426 *
61427 * @param {string id or DOM element} gd
61428 * the id or DOM element of the graph container div
61429 */
61430function purge(gd) {
61431 gd = Lib.getGraphDiv(gd);
61432
61433 var fullLayout = gd._fullLayout || {};
61434 var fullData = gd._fullData || [];
61435
61436 // remove gl contexts
61437 Plots.cleanPlot([], {}, fullData, fullLayout);
61438
61439 // purge properties
61440 Plots.purge(gd);
61441
61442 // purge event emitter methods
61443 Events.purge(gd);
61444
61445 // remove plot container
61446 if(fullLayout._container) fullLayout._container.remove();
61447
61448 // in contrast to _doPlots.purge which does NOT clear _context!
61449 delete gd._context;
61450
61451 return gd;
61452}
61453
61454// determines if the graph div requires a recalculation of its inverse matrix transforms by comparing old + new bounding boxes.
61455function calcInverseTransform(gd) {
61456 var fullLayout = gd._fullLayout;
61457
61458 var newBBox = gd.getBoundingClientRect();
61459 if(Lib.equalDomRects(newBBox, fullLayout._lastBBox)) return;
61460
61461 var m = fullLayout._invTransform = Lib.inverseTransformMatrix(Lib.getFullTransformMatrix(gd));
61462 fullLayout._invScaleX = Math.sqrt(m[0][0] * m[0][0] + m[0][1] * m[0][1] + m[0][2] * m[0][2]);
61463 fullLayout._invScaleY = Math.sqrt(m[1][0] * m[1][0] + m[1][1] * m[1][1] + m[1][2] * m[1][2]);
61464 fullLayout._lastBBox = newBBox;
61465}
61466
61467// -------------------------------------------------------
61468// makePlotFramework: Create the plot container and axes
61469// -------------------------------------------------------
61470function makePlotFramework(gd) {
61471 var gd3 = d3.select(gd);
61472 var fullLayout = gd._fullLayout;
61473
61474 fullLayout._calcInverseTransform = calcInverseTransform;
61475 fullLayout._calcInverseTransform(gd);
61476
61477 // Plot container
61478 fullLayout._container = gd3.selectAll('.plot-container').data([0]);
61479 fullLayout._container.enter()
61480 .insert('div', ':first-child')
61481 .classed('plot-container', true)
61482 .classed('plotly', true);
61483
61484 // Make the svg container
61485 fullLayout._paperdiv = fullLayout._container.selectAll('.svg-container').data([0]);
61486 fullLayout._paperdiv.enter().append('div')
61487 .classed('user-select-none', true)
61488 .classed('svg-container', true)
61489 .style('position', 'relative');
61490
61491 // Make the graph containers
61492 // start fresh each time we get here, so we know the order comes out
61493 // right, rather than enter/exit which can muck up the order
61494 // TODO: sort out all the ordering so we don't have to
61495 // explicitly delete anything
61496 // FIXME: parcoords reuses this object, not the best pattern
61497 fullLayout._glcontainer = fullLayout._paperdiv.selectAll('.gl-container')
61498 .data([{}]);
61499
61500 fullLayout._glcontainer.enter().append('div')
61501 .classed('gl-container', true);
61502
61503 fullLayout._paperdiv.selectAll('.main-svg').remove();
61504 fullLayout._paperdiv.select('.modebar-container').remove();
61505
61506 fullLayout._paper = fullLayout._paperdiv.insert('svg', ':first-child')
61507 .classed('main-svg', true);
61508
61509 fullLayout._toppaper = fullLayout._paperdiv.append('svg')
61510 .classed('main-svg', true);
61511
61512 fullLayout._modebardiv = fullLayout._paperdiv.append('div');
61513 delete fullLayout._modeBar;
61514
61515 fullLayout._hoverpaper = fullLayout._paperdiv.append('svg')
61516 .classed('main-svg', true);
61517
61518 if(!fullLayout._uid) {
61519 var otherUids = {};
61520 d3.selectAll('defs').each(function() {
61521 if(this.id) otherUids[this.id.split('-')[1]] = 1;
61522 });
61523 fullLayout._uid = Lib.randstr(otherUids);
61524 }
61525
61526 fullLayout._paperdiv.selectAll('.main-svg')
61527 .attr(xmlnsNamespaces.svgAttrs);
61528
61529 fullLayout._defs = fullLayout._paper.append('defs')
61530 .attr('id', 'defs-' + fullLayout._uid);
61531
61532 fullLayout._clips = fullLayout._defs.append('g')
61533 .classed('clips', true);
61534
61535 fullLayout._topdefs = fullLayout._toppaper.append('defs')
61536 .attr('id', 'topdefs-' + fullLayout._uid);
61537
61538 fullLayout._topclips = fullLayout._topdefs.append('g')
61539 .classed('clips', true);
61540
61541 fullLayout._bgLayer = fullLayout._paper.append('g')
61542 .classed('bglayer', true);
61543
61544 fullLayout._draggers = fullLayout._paper.append('g')
61545 .classed('draglayer', true);
61546
61547 // lower shape/image layer - note that this is behind
61548 // all subplots data/grids but above the backgrounds
61549 // except inset subplots, whose backgrounds are drawn
61550 // inside their own group so that they appear above
61551 // the data for the main subplot
61552 // lower shapes and images which are fully referenced to
61553 // a subplot still get drawn within the subplot's group
61554 // so they will work correctly on insets
61555 var layerBelow = fullLayout._paper.append('g')
61556 .classed('layer-below', true);
61557 fullLayout._imageLowerLayer = layerBelow.append('g')
61558 .classed('imagelayer', true);
61559 fullLayout._shapeLowerLayer = layerBelow.append('g')
61560 .classed('shapelayer', true);
61561
61562 // single cartesian layer for the whole plot
61563 fullLayout._cartesianlayer = fullLayout._paper.append('g').classed('cartesianlayer', true);
61564
61565 // single polar layer for the whole plot
61566 fullLayout._polarlayer = fullLayout._paper.append('g').classed('polarlayer', true);
61567
61568 // single ternary layer for the whole plot
61569 fullLayout._ternarylayer = fullLayout._paper.append('g').classed('ternarylayer', true);
61570
61571 // single geo layer for the whole plot
61572 fullLayout._geolayer = fullLayout._paper.append('g').classed('geolayer', true);
61573
61574 // single funnelarea layer for the whole plot
61575 fullLayout._funnelarealayer = fullLayout._paper.append('g').classed('funnelarealayer', true);
61576
61577 // single pie layer for the whole plot
61578 fullLayout._pielayer = fullLayout._paper.append('g').classed('pielayer', true);
61579
61580 // single treemap layer for the whole plot
61581 fullLayout._iciclelayer = fullLayout._paper.append('g').classed('iciclelayer', true);
61582
61583 // single treemap layer for the whole plot
61584 fullLayout._treemaplayer = fullLayout._paper.append('g').classed('treemaplayer', true);
61585
61586 // single sunburst layer for the whole plot
61587 fullLayout._sunburstlayer = fullLayout._paper.append('g').classed('sunburstlayer', true);
61588
61589 // single indicator layer for the whole plot
61590 fullLayout._indicatorlayer = fullLayout._toppaper.append('g').classed('indicatorlayer', true);
61591
61592 // fill in image server scrape-svg
61593 fullLayout._glimages = fullLayout._paper.append('g').classed('glimages', true);
61594
61595 // lastly upper shapes, info (legend, annotations) and hover layers go on top
61596 // these are in a different svg element normally, but get collapsed into a single
61597 // svg when exporting (after inserting 3D)
61598 // upper shapes/images are only those drawn above the whole plot, including subplots
61599 var layerAbove = fullLayout._toppaper.append('g')
61600 .classed('layer-above', true);
61601 fullLayout._imageUpperLayer = layerAbove.append('g')
61602 .classed('imagelayer', true);
61603 fullLayout._shapeUpperLayer = layerAbove.append('g')
61604 .classed('shapelayer', true);
61605
61606 fullLayout._infolayer = fullLayout._toppaper.append('g').classed('infolayer', true);
61607 fullLayout._menulayer = fullLayout._toppaper.append('g').classed('menulayer', true);
61608 fullLayout._zoomlayer = fullLayout._toppaper.append('g').classed('zoomlayer', true);
61609 fullLayout._hoverlayer = fullLayout._hoverpaper.append('g').classed('hoverlayer', true);
61610
61611 // Make the modebar container
61612 fullLayout._modebardiv
61613 .classed('modebar-container', true)
61614 .style('position', 'absolute')
61615 .style('top', '0px')
61616 .style('right', '0px');
61617
61618 gd.emit('plotly_framework');
61619}
61620
61621exports.animate = animate;
61622exports.addFrames = addFrames;
61623exports.deleteFrames = deleteFrames;
61624
61625exports.addTraces = addTraces;
61626exports.deleteTraces = deleteTraces;
61627exports.extendTraces = extendTraces;
61628exports.moveTraces = moveTraces;
61629exports.prependTraces = prependTraces;
61630
61631exports.newPlot = newPlot;
61632exports._doPlot = _doPlot;
61633exports.purge = purge;
61634
61635exports.react = react;
61636exports.redraw = redraw;
61637exports.relayout = relayout;
61638exports.restyle = restyle;
61639
61640exports.setPlotConfig = setPlotConfig;
61641
61642exports.update = update;
61643
61644exports._guiRelayout = guiEdit(relayout);
61645exports._guiRestyle = guiEdit(restyle);
61646exports._guiUpdate = guiEdit(update);
61647
61648exports._storeDirectGUIEdit = _storeDirectGUIEdit;
61649
61650},{"../components/color":157,"../components/drawing":179,"../constants/xmlns_namespaces":268,"../lib":287,"../lib/events":280,"../lib/queue":302,"../plots/cartesian/axes":334,"../plots/cartesian/constants":341,"../plots/cartesian/graph_interact":344,"../plots/cartesian/select":354,"../plots/plots":369,"../registry":376,"./edit_types":316,"./helpers":317,"./manage_arrays":319,"./plot_config":321,"./plot_schema":322,"./subroutines":324,"@plotly/d3":20,"fast-isnumeric":33,"has-hover":64}],321:[function(_dereq_,module,exports){
61651'use strict';
61652
61653/**
61654 * This will be transferred over to gd and overridden by
61655 * config args to Plotly.newPlot.
61656 *
61657 * The defaults are the appropriate settings for plotly.js,
61658 * so we get the right experience without any config argument.
61659 *
61660 * N.B. the config options are not coerced using Lib.coerce so keys
61661 * like `valType` and `values` are only set for documentation purposes
61662 * at the moment.
61663 */
61664
61665var configAttributes = {
61666 staticPlot: {
61667 valType: 'boolean',
61668 dflt: false,
61669 },
61670
61671 plotlyServerURL: {
61672 valType: 'string',
61673 dflt: '',
61674 },
61675
61676 editable: {
61677 valType: 'boolean',
61678 dflt: false,
61679 },
61680 edits: {
61681 annotationPosition: {
61682 valType: 'boolean',
61683 dflt: false,
61684 },
61685 annotationTail: {
61686 valType: 'boolean',
61687 dflt: false,
61688 },
61689 annotationText: {
61690 valType: 'boolean',
61691 dflt: false,
61692 },
61693 axisTitleText: {
61694 valType: 'boolean',
61695 dflt: false,
61696 },
61697 colorbarPosition: {
61698 valType: 'boolean',
61699 dflt: false,
61700 },
61701 colorbarTitleText: {
61702 valType: 'boolean',
61703 dflt: false,
61704 },
61705 legendPosition: {
61706 valType: 'boolean',
61707 dflt: false,
61708 },
61709 legendText: {
61710 valType: 'boolean',
61711 dflt: false,
61712 },
61713 shapePosition: {
61714 valType: 'boolean',
61715 dflt: false,
61716 },
61717 titleText: {
61718 valType: 'boolean',
61719 dflt: false,
61720 }
61721 },
61722
61723 autosizable: {
61724 valType: 'boolean',
61725 dflt: false,
61726 },
61727 responsive: {
61728 valType: 'boolean',
61729 dflt: false,
61730 },
61731 fillFrame: {
61732 valType: 'boolean',
61733 dflt: false,
61734 },
61735 frameMargins: {
61736 valType: 'number',
61737 dflt: 0,
61738 min: 0,
61739 max: 0.5,
61740 },
61741
61742 scrollZoom: {
61743 valType: 'flaglist',
61744 flags: ['cartesian', 'gl3d', 'geo', 'mapbox'],
61745 extras: [true, false],
61746 dflt: 'gl3d+geo+mapbox',
61747 },
61748 doubleClick: {
61749 valType: 'enumerated',
61750 values: [false, 'reset', 'autosize', 'reset+autosize'],
61751 dflt: 'reset+autosize',
61752 },
61753 doubleClickDelay: {
61754 valType: 'number',
61755 dflt: 300,
61756 min: 0,
61757 },
61758
61759 showAxisDragHandles: {
61760 valType: 'boolean',
61761 dflt: true,
61762 },
61763 showAxisRangeEntryBoxes: {
61764 valType: 'boolean',
61765 dflt: true,
61766 },
61767
61768 showTips: {
61769 valType: 'boolean',
61770 dflt: true,
61771 },
61772
61773 showLink: {
61774 valType: 'boolean',
61775 dflt: false,
61776 },
61777 linkText: {
61778 valType: 'string',
61779 dflt: 'Edit chart',
61780 noBlank: true,
61781 },
61782 sendData: {
61783 valType: 'boolean',
61784 dflt: true,
61785 },
61786 showSources: {
61787 valType: 'any',
61788 dflt: false,
61789 },
61790
61791 displayModeBar: {
61792 valType: 'enumerated',
61793 values: ['hover', true, false],
61794 dflt: 'hover',
61795 },
61796 showSendToCloud: {
61797 valType: 'boolean',
61798 dflt: false,
61799 },
61800 showEditInChartStudio: {
61801 valType: 'boolean',
61802 dflt: false,
61803 },
61804 modeBarButtonsToRemove: {
61805 valType: 'any',
61806 dflt: [],
61807 },
61808 modeBarButtonsToAdd: {
61809 valType: 'any',
61810 dflt: [],
61811 },
61812 modeBarButtons: {
61813 valType: 'any',
61814 dflt: false,
61815 },
61816 toImageButtonOptions: {
61817 valType: 'any',
61818 dflt: {},
61819 },
61820 displaylogo: {
61821 valType: 'boolean',
61822 dflt: true,
61823 },
61824 watermark: {
61825 valType: 'boolean',
61826 dflt: false,
61827 },
61828
61829 plotGlPixelRatio: {
61830 valType: 'number',
61831 dflt: 2,
61832 min: 1,
61833 max: 4,
61834 },
61835
61836 setBackground: {
61837 valType: 'any',
61838 dflt: 'transparent',
61839 },
61840
61841 topojsonURL: {
61842 valType: 'string',
61843 noBlank: true,
61844 dflt: 'https://cdn.plot.ly/',
61845 },
61846
61847 mapboxAccessToken: {
61848 valType: 'string',
61849 dflt: null,
61850 },
61851
61852 logging: {
61853 valType: 'integer',
61854 min: 0,
61855 max: 2,
61856 dflt: 1,
61857 },
61858
61859 notifyOnLogging: {
61860 valType: 'integer',
61861 min: 0,
61862 max: 2,
61863 dflt: 0,
61864 },
61865
61866 queueLength: {
61867 valType: 'integer',
61868 min: 0,
61869 dflt: 0,
61870 },
61871
61872 globalTransforms: {
61873 valType: 'any',
61874 dflt: [],
61875 },
61876
61877 locale: {
61878 valType: 'string',
61879 dflt: 'en-US',
61880 },
61881
61882 locales: {
61883 valType: 'any',
61884 dflt: {},
61885 }
61886};
61887
61888var dfltConfig = {};
61889
61890function crawl(src, target) {
61891 for(var k in src) {
61892 var obj = src[k];
61893 if(obj.valType) {
61894 target[k] = obj.dflt;
61895 } else {
61896 if(!target[k]) {
61897 target[k] = {};
61898 }
61899 crawl(obj, target[k]);
61900 }
61901 }
61902}
61903
61904crawl(configAttributes, dfltConfig);
61905
61906module.exports = {
61907 configAttributes: configAttributes,
61908 dfltConfig: dfltConfig
61909};
61910
61911},{}],322:[function(_dereq_,module,exports){
61912'use strict';
61913
61914var Registry = _dereq_('../registry');
61915var Lib = _dereq_('../lib');
61916
61917var baseAttributes = _dereq_('../plots/attributes');
61918var baseLayoutAttributes = _dereq_('../plots/layout_attributes');
61919var frameAttributes = _dereq_('../plots/frame_attributes');
61920var animationAttributes = _dereq_('../plots/animation_attributes');
61921var configAttributes = _dereq_('./plot_config').configAttributes;
61922
61923var editTypes = _dereq_('./edit_types');
61924
61925var extendDeepAll = Lib.extendDeepAll;
61926var isPlainObject = Lib.isPlainObject;
61927var isArrayOrTypedArray = Lib.isArrayOrTypedArray;
61928var nestedProperty = Lib.nestedProperty;
61929var valObjectMeta = Lib.valObjectMeta;
61930
61931var IS_SUBPLOT_OBJ = '_isSubplotObj';
61932var IS_LINKED_TO_ARRAY = '_isLinkedToArray';
61933var ARRAY_ATTR_REGEXPS = '_arrayAttrRegexps';
61934var DEPRECATED = '_deprecated';
61935var UNDERSCORE_ATTRS = [IS_SUBPLOT_OBJ, IS_LINKED_TO_ARRAY, ARRAY_ATTR_REGEXPS, DEPRECATED];
61936
61937exports.IS_SUBPLOT_OBJ = IS_SUBPLOT_OBJ;
61938exports.IS_LINKED_TO_ARRAY = IS_LINKED_TO_ARRAY;
61939exports.DEPRECATED = DEPRECATED;
61940exports.UNDERSCORE_ATTRS = UNDERSCORE_ATTRS;
61941
61942/** Outputs the full plotly.js plot schema
61943 *
61944 * @return {object}
61945 * - defs
61946 * - traces
61947 * - layout
61948 * - transforms
61949 * - frames
61950 * - animations
61951 * - config
61952 */
61953exports.get = function() {
61954 var traces = {};
61955
61956 Registry.allTypes.forEach(function(type) {
61957 traces[type] = getTraceAttributes(type);
61958 });
61959
61960 var transforms = {};
61961
61962 Object.keys(Registry.transformsRegistry).forEach(function(type) {
61963 transforms[type] = getTransformAttributes(type);
61964 });
61965
61966 return {
61967 defs: {
61968 valObjects: valObjectMeta,
61969 metaKeys: UNDERSCORE_ATTRS.concat(['description', 'role', 'editType', 'impliedEdits']),
61970 editType: {
61971 traces: editTypes.traces,
61972 layout: editTypes.layout
61973 },
61974 impliedEdits: {
61975 }
61976 },
61977
61978 traces: traces,
61979 layout: getLayoutAttributes(),
61980
61981 transforms: transforms,
61982
61983 frames: getFramesAttributes(),
61984 animation: formatAttributes(animationAttributes),
61985
61986 config: formatAttributes(configAttributes)
61987 };
61988};
61989
61990/**
61991 * Crawl the attribute tree, recursively calling a callback function
61992 *
61993 * @param {object} attrs
61994 * The node of the attribute tree (e.g. the root) from which recursion originates
61995 * @param {Function} callback
61996 * A callback function with the signature:
61997 * @callback callback
61998 * @param {object} attr an attribute
61999 * @param {String} attrName name string
62000 * @param {object[]} attrs all the attributes
62001 * @param {Number} level the recursion level, 0 at the root
62002 * @param {String} fullAttrString full attribute name (ie 'marker.line')
62003 * @param {Number} [specifiedLevel]
62004 * The level in the tree, in order to let the callback function detect descend or backtrack,
62005 * typically unsupplied (implied 0), just used by the self-recursive call.
62006 * The necessity arises because the tree traversal is not controlled by callback return values.
62007 * The decision to not use callback return values for controlling tree pruning arose from
62008 * the goal of keeping the crawler backwards compatible. Observe that one of the pruning conditions
62009 * precedes the callback call.
62010 * @param {string} [attrString]
62011 * the path to the current attribute, as an attribute string (ie 'marker.line')
62012 * typically unsupplied, but you may supply it if you want to disambiguate which attrs tree you
62013 * are starting from
62014 *
62015 * @return {object} transformOut
62016 * copy of transformIn that contains attribute defaults
62017 */
62018exports.crawl = function(attrs, callback, specifiedLevel, attrString) {
62019 var level = specifiedLevel || 0;
62020 attrString = attrString || '';
62021
62022 Object.keys(attrs).forEach(function(attrName) {
62023 var attr = attrs[attrName];
62024
62025 if(UNDERSCORE_ATTRS.indexOf(attrName) !== -1) return;
62026
62027 var fullAttrString = (attrString ? attrString + '.' : '') + attrName;
62028 callback(attr, attrName, attrs, level, fullAttrString);
62029
62030 if(exports.isValObject(attr)) return;
62031
62032 if(isPlainObject(attr) && attrName !== 'impliedEdits') {
62033 exports.crawl(attr, callback, level + 1, fullAttrString);
62034 }
62035 });
62036};
62037
62038/** Is object a value object (or a container object)?
62039 *
62040 * @param {object} obj
62041 * @return {boolean}
62042 * returns true for a valid value object and
62043 * false for tree nodes in the attribute hierarchy
62044 */
62045exports.isValObject = function(obj) {
62046 return obj && obj.valType !== undefined;
62047};
62048
62049/**
62050 * Find all data array attributes in a given trace object - including
62051 * `arrayOk` attributes.
62052 *
62053 * @param {object} trace
62054 * full trace object that contains a reference to `_module.attributes`
62055 *
62056 * @return {array} arrayAttributes
62057 * list of array attributes for the given trace
62058 */
62059exports.findArrayAttributes = function(trace) {
62060 var arrayAttributes = [];
62061 var stack = [];
62062 var isArrayStack = [];
62063 var baseContainer, baseAttrName;
62064
62065 function callback(attr, attrName, attrs, level) {
62066 stack = stack.slice(0, level).concat([attrName]);
62067 isArrayStack = isArrayStack.slice(0, level).concat([attr && attr._isLinkedToArray]);
62068
62069 var splittableAttr = (
62070 attr &&
62071 (attr.valType === 'data_array' || attr.arrayOk === true) &&
62072 !(stack[level - 1] === 'colorbar' && (attrName === 'ticktext' || attrName === 'tickvals'))
62073 );
62074
62075 // Manually exclude 'colorbar.tickvals' and 'colorbar.ticktext' for now
62076 // which are declared as `valType: 'data_array'` but scale independently of
62077 // the coordinate arrays.
62078 //
62079 // Down the road, we might want to add a schema field (e.g `uncorrelatedArray: true`)
62080 // to distinguish attributes of the likes.
62081
62082 if(!splittableAttr) return;
62083
62084 crawlIntoTrace(baseContainer, 0, '');
62085 }
62086
62087 function crawlIntoTrace(container, i, astrPartial) {
62088 var item = container[stack[i]];
62089 var newAstrPartial = astrPartial + stack[i];
62090 if(i === stack.length - 1) {
62091 if(isArrayOrTypedArray(item)) {
62092 arrayAttributes.push(baseAttrName + newAstrPartial);
62093 }
62094 } else {
62095 if(isArrayStack[i]) {
62096 if(Array.isArray(item)) {
62097 for(var j = 0; j < item.length; j++) {
62098 if(isPlainObject(item[j])) {
62099 crawlIntoTrace(item[j], i + 1, newAstrPartial + '[' + j + '].');
62100 }
62101 }
62102 }
62103 } else if(isPlainObject(item)) {
62104 crawlIntoTrace(item, i + 1, newAstrPartial + '.');
62105 }
62106 }
62107 }
62108
62109 baseContainer = trace;
62110 baseAttrName = '';
62111 exports.crawl(baseAttributes, callback);
62112 if(trace._module && trace._module.attributes) {
62113 exports.crawl(trace._module.attributes, callback);
62114 }
62115
62116 var transforms = trace.transforms;
62117 if(transforms) {
62118 for(var i = 0; i < transforms.length; i++) {
62119 var transform = transforms[i];
62120 var module = transform._module;
62121
62122 if(module) {
62123 baseAttrName = 'transforms[' + i + '].';
62124 baseContainer = transform;
62125
62126 exports.crawl(module.attributes, callback);
62127 }
62128 }
62129 }
62130
62131 return arrayAttributes;
62132};
62133
62134/*
62135 * Find the valObject for one attribute in an existing trace
62136 *
62137 * @param {object} trace
62138 * full trace object that contains a reference to `_module.attributes`
62139 * @param {object} parts
62140 * an array of parts, like ['transforms', 1, 'value']
62141 * typically from nestedProperty(...).parts
62142 *
62143 * @return {object|false}
62144 * the valObject for this attribute, or the last found parent
62145 * in some cases the innermost valObject will not exist, for example
62146 * `valType: 'any'` attributes where we might set a part of the attribute.
62147 * In that case, stop at the deepest valObject we *do* find.
62148 */
62149exports.getTraceValObject = function(trace, parts) {
62150 var head = parts[0];
62151 var i = 1; // index to start recursing from
62152 var moduleAttrs, valObject;
62153
62154 if(head === 'transforms') {
62155 if(parts.length === 1) {
62156 return baseAttributes.transforms;
62157 }
62158 var transforms = trace.transforms;
62159 if(!Array.isArray(transforms) || !transforms.length) return false;
62160 var tNum = parts[1];
62161 if(!isIndex(tNum) || tNum >= transforms.length) {
62162 return false;
62163 }
62164 moduleAttrs = (Registry.transformsRegistry[transforms[tNum].type] || {}).attributes;
62165 valObject = moduleAttrs && moduleAttrs[parts[2]];
62166 i = 3; // start recursing only inside the transform
62167 } else {
62168 // first look in the module for this trace
62169 // components have already merged their trace attributes in here
62170 var _module = trace._module;
62171 if(!_module) _module = (Registry.modules[trace.type || baseAttributes.type.dflt] || {})._module;
62172 if(!_module) return false;
62173
62174 moduleAttrs = _module.attributes;
62175 valObject = moduleAttrs && moduleAttrs[head];
62176
62177 // then look in the subplot attributes
62178 if(!valObject) {
62179 var subplotModule = _module.basePlotModule;
62180 if(subplotModule && subplotModule.attributes) {
62181 valObject = subplotModule.attributes[head];
62182 }
62183 }
62184
62185 // finally look in the global attributes
62186 if(!valObject) valObject = baseAttributes[head];
62187 }
62188
62189 return recurseIntoValObject(valObject, parts, i);
62190};
62191
62192/*
62193 * Find the valObject for one layout attribute
62194 *
62195 * @param {array} parts
62196 * an array of parts, like ['annotations', 1, 'x']
62197 * typically from nestedProperty(...).parts
62198 *
62199 * @return {object|false}
62200 * the valObject for this attribute, or the last found parent
62201 * in some cases the innermost valObject will not exist, for example
62202 * `valType: 'any'` attributes where we might set a part of the attribute.
62203 * In that case, stop at the deepest valObject we *do* find.
62204 */
62205exports.getLayoutValObject = function(fullLayout, parts) {
62206 var valObject = layoutHeadAttr(fullLayout, parts[0]);
62207
62208 return recurseIntoValObject(valObject, parts, 1);
62209};
62210
62211function layoutHeadAttr(fullLayout, head) {
62212 var i, key, _module, attributes;
62213
62214 // look for attributes of the subplot types used on the plot
62215 var basePlotModules = fullLayout._basePlotModules;
62216 if(basePlotModules) {
62217 var out;
62218 for(i = 0; i < basePlotModules.length; i++) {
62219 _module = basePlotModules[i];
62220 if(_module.attrRegex && _module.attrRegex.test(head)) {
62221 // if a module defines overrides, these take precedence
62222 // initially this is to allow gl2d different editTypes from svg cartesian
62223 if(_module.layoutAttrOverrides) return _module.layoutAttrOverrides;
62224
62225 // otherwise take the first attributes we find
62226 if(!out && _module.layoutAttributes) out = _module.layoutAttributes;
62227 }
62228
62229 // a module can also override the behavior of base (and component) module layout attrs
62230 // again see gl2d for initial use case
62231 var baseOverrides = _module.baseLayoutAttrOverrides;
62232 if(baseOverrides && head in baseOverrides) return baseOverrides[head];
62233 }
62234 if(out) return out;
62235 }
62236
62237 // look for layout attributes contributed by traces on the plot
62238 var modules = fullLayout._modules;
62239 if(modules) {
62240 for(i = 0; i < modules.length; i++) {
62241 attributes = modules[i].layoutAttributes;
62242 if(attributes && head in attributes) {
62243 return attributes[head];
62244 }
62245 }
62246 }
62247
62248 /*
62249 * Next look in components.
62250 * Components that define a schema have already merged this into
62251 * base and subplot attribute defs, so ignore these.
62252 * Others (older style) all put all their attributes
62253 * inside a container matching the module `name`
62254 * eg `attributes` (array) or `legend` (object)
62255 */
62256 for(key in Registry.componentsRegistry) {
62257 _module = Registry.componentsRegistry[key];
62258 if(_module.name === 'colorscale' && head.indexOf('coloraxis') === 0) {
62259 return _module.layoutAttributes[head];
62260 } else if(!_module.schema && (head === _module.name)) {
62261 return _module.layoutAttributes;
62262 }
62263 }
62264
62265 if(head in baseLayoutAttributes) return baseLayoutAttributes[head];
62266
62267 return false;
62268}
62269
62270function recurseIntoValObject(valObject, parts, i) {
62271 if(!valObject) return false;
62272
62273 if(valObject._isLinkedToArray) {
62274 // skip array index, abort if we try to dive into an array without an index
62275 if(isIndex(parts[i])) i++;
62276 else if(i < parts.length) return false;
62277 }
62278
62279 // now recurse as far as we can. Occasionally we have an attribute
62280 // setting an internal part below what's in the schema; just return
62281 // the innermost schema item we find.
62282 for(; i < parts.length; i++) {
62283 var newValObject = valObject[parts[i]];
62284 if(isPlainObject(newValObject)) valObject = newValObject;
62285 else break;
62286
62287 if(i === parts.length - 1) break;
62288
62289 if(valObject._isLinkedToArray) {
62290 i++;
62291 if(!isIndex(parts[i])) return false;
62292 } else if(valObject.valType === 'info_array') {
62293 i++;
62294 var index = parts[i];
62295 if(!isIndex(index)) return false;
62296
62297 var items = valObject.items;
62298 if(Array.isArray(items)) {
62299 if(index >= items.length) return false;
62300 if(valObject.dimensions === 2) {
62301 i++;
62302 if(parts.length === i) return valObject;
62303 var index2 = parts[i];
62304 if(!isIndex(index2)) return false;
62305 valObject = items[index][index2];
62306 } else valObject = items[index];
62307 } else {
62308 valObject = items;
62309 }
62310 }
62311 }
62312
62313 return valObject;
62314}
62315
62316// note: this is different from Lib.isIndex, this one doesn't accept numeric
62317// strings, only actual numbers.
62318function isIndex(val) {
62319 return val === Math.round(val) && val >= 0;
62320}
62321
62322function getTraceAttributes(type) {
62323 var _module, basePlotModule;
62324
62325 _module = Registry.modules[type]._module,
62326 basePlotModule = _module.basePlotModule;
62327
62328 var attributes = {};
62329
62330 // make 'type' the first attribute in the object
62331 attributes.type = null;
62332
62333 var copyBaseAttributes = extendDeepAll({}, baseAttributes);
62334 var copyModuleAttributes = extendDeepAll({}, _module.attributes);
62335
62336 // prune global-level trace attributes that are already defined in a trace
62337 exports.crawl(copyModuleAttributes, function(attr, attrName, attrs, level, fullAttrString) {
62338 nestedProperty(copyBaseAttributes, fullAttrString).set(undefined);
62339 // Prune undefined attributes
62340 if(attr === undefined) nestedProperty(copyModuleAttributes, fullAttrString).set(undefined);
62341 });
62342
62343 // base attributes (same for all trace types)
62344 extendDeepAll(attributes, copyBaseAttributes);
62345
62346 // prune-out base attributes based on trace module categories
62347 if(Registry.traceIs(type, 'noOpacity')) {
62348 delete attributes.opacity;
62349 }
62350 if(!Registry.traceIs(type, 'showLegend')) {
62351 delete attributes.showlegend;
62352 delete attributes.legendgroup;
62353 }
62354 if(Registry.traceIs(type, 'noHover')) {
62355 delete attributes.hoverinfo;
62356 delete attributes.hoverlabel;
62357 }
62358 if(!_module.selectPoints) {
62359 delete attributes.selectedpoints;
62360 }
62361
62362 // module attributes
62363 extendDeepAll(attributes, copyModuleAttributes);
62364
62365 // subplot attributes
62366 if(basePlotModule.attributes) {
62367 extendDeepAll(attributes, basePlotModule.attributes);
62368 }
62369
62370 // 'type' gets overwritten by baseAttributes; reset it here
62371 attributes.type = type;
62372
62373 var out = {
62374 meta: _module.meta || {},
62375 categories: _module.categories || {},
62376 animatable: Boolean(_module.animatable),
62377 type: type,
62378 attributes: formatAttributes(attributes),
62379 };
62380
62381 // trace-specific layout attributes
62382 if(_module.layoutAttributes) {
62383 var layoutAttributes = {};
62384
62385 extendDeepAll(layoutAttributes, _module.layoutAttributes);
62386 out.layoutAttributes = formatAttributes(layoutAttributes);
62387 }
62388
62389 // drop anim:true in non-animatable modules
62390 if(!_module.animatable) {
62391 exports.crawl(out, function(attr) {
62392 if(exports.isValObject(attr) && 'anim' in attr) {
62393 delete attr.anim;
62394 }
62395 });
62396 }
62397
62398 return out;
62399}
62400
62401function getLayoutAttributes() {
62402 var layoutAttributes = {};
62403 var key, _module;
62404
62405 // global layout attributes
62406 extendDeepAll(layoutAttributes, baseLayoutAttributes);
62407
62408 // add base plot module layout attributes
62409 for(key in Registry.subplotsRegistry) {
62410 _module = Registry.subplotsRegistry[key];
62411
62412 if(!_module.layoutAttributes) continue;
62413
62414 if(Array.isArray(_module.attr)) {
62415 for(var i = 0; i < _module.attr.length; i++) {
62416 handleBasePlotModule(layoutAttributes, _module, _module.attr[i]);
62417 }
62418 } else {
62419 var astr = _module.attr === 'subplot' ? _module.name : _module.attr;
62420 handleBasePlotModule(layoutAttributes, _module, astr);
62421 }
62422 }
62423
62424 // add registered components layout attributes
62425 for(key in Registry.componentsRegistry) {
62426 _module = Registry.componentsRegistry[key];
62427 var schema = _module.schema;
62428
62429 if(schema && (schema.subplots || schema.layout)) {
62430 /*
62431 * Components with defined schema have already been merged in at register time
62432 * but a few components define attributes that apply only to xaxis
62433 * not yaxis (rangeselector, rangeslider) - delete from y schema.
62434 * Note that the input attributes for xaxis/yaxis are the same object
62435 * so it's not possible to only add them to xaxis from the start.
62436 * If we ever have such asymmetry the other way, or anywhere else,
62437 * we will need to extend both this code and mergeComponentAttrsToSubplot
62438 * (which will not find yaxis only for example)
62439 */
62440 var subplots = schema.subplots;
62441 if(subplots && subplots.xaxis && !subplots.yaxis) {
62442 for(var xkey in subplots.xaxis) {
62443 delete layoutAttributes.yaxis[xkey];
62444 }
62445 }
62446 } else if(_module.name === 'colorscale') {
62447 extendDeepAll(layoutAttributes, _module.layoutAttributes);
62448 } else if(_module.layoutAttributes) {
62449 // older style without schema need to be explicitly merged in now
62450 insertAttrs(layoutAttributes, _module.layoutAttributes, _module.name);
62451 }
62452 }
62453
62454 return {
62455 layoutAttributes: formatAttributes(layoutAttributes)
62456 };
62457}
62458
62459function getTransformAttributes(type) {
62460 var _module = Registry.transformsRegistry[type];
62461 var attributes = extendDeepAll({}, _module.attributes);
62462
62463 // add registered components transform attributes
62464 Object.keys(Registry.componentsRegistry).forEach(function(k) {
62465 var _module = Registry.componentsRegistry[k];
62466
62467 if(_module.schema && _module.schema.transforms && _module.schema.transforms[type]) {
62468 Object.keys(_module.schema.transforms[type]).forEach(function(v) {
62469 insertAttrs(attributes, _module.schema.transforms[type][v], v);
62470 });
62471 }
62472 });
62473
62474 return {
62475 attributes: formatAttributes(attributes)
62476 };
62477}
62478
62479function getFramesAttributes() {
62480 var attrs = {
62481 frames: extendDeepAll({}, frameAttributes)
62482 };
62483
62484 formatAttributes(attrs);
62485
62486 return attrs.frames;
62487}
62488
62489function formatAttributes(attrs) {
62490 mergeValTypeAndRole(attrs);
62491 formatArrayContainers(attrs);
62492 stringify(attrs);
62493
62494 return attrs;
62495}
62496
62497function mergeValTypeAndRole(attrs) {
62498 function makeSrcAttr(attrName) {
62499 return {
62500 valType: 'string',
62501 editType: 'none'
62502 };
62503 }
62504
62505 function callback(attr, attrName, attrs) {
62506 if(exports.isValObject(attr)) {
62507 if(attr.arrayOk === true || attr.valType === 'data_array') {
62508 // all 'arrayOk' and 'data_array' attrs have a corresponding 'src' attr
62509 attrs[attrName + 'src'] = makeSrcAttr(attrName);
62510 }
62511 } else if(isPlainObject(attr)) {
62512 // all attrs container objects get role 'object'
62513 attr.role = 'object';
62514 }
62515 }
62516
62517 exports.crawl(attrs, callback);
62518}
62519
62520function formatArrayContainers(attrs) {
62521 function callback(attr, attrName, attrs) {
62522 if(!attr) return;
62523
62524 var itemName = attr[IS_LINKED_TO_ARRAY];
62525
62526 if(!itemName) return;
62527
62528 delete attr[IS_LINKED_TO_ARRAY];
62529
62530 attrs[attrName] = { items: {} };
62531 attrs[attrName].items[itemName] = attr;
62532 attrs[attrName].role = 'object';
62533 }
62534
62535 exports.crawl(attrs, callback);
62536}
62537
62538// this can take around 10ms and should only be run from PlotSchema.get(),
62539// to ensure JSON.stringify(PlotSchema.get()) gives the intended result.
62540function stringify(attrs) {
62541 function walk(attr) {
62542 for(var k in attr) {
62543 if(isPlainObject(attr[k])) {
62544 walk(attr[k]);
62545 } else if(Array.isArray(attr[k])) {
62546 for(var i = 0; i < attr[k].length; i++) {
62547 walk(attr[k][i]);
62548 }
62549 } else {
62550 // as JSON.stringify(/test/) // => {}
62551 if(attr[k] instanceof RegExp) {
62552 attr[k] = attr[k].toString();
62553 }
62554 }
62555 }
62556 }
62557
62558 walk(attrs);
62559}
62560
62561
62562function handleBasePlotModule(layoutAttributes, _module, astr) {
62563 var np = nestedProperty(layoutAttributes, astr);
62564 var attrs = extendDeepAll({}, _module.layoutAttributes);
62565
62566 attrs[IS_SUBPLOT_OBJ] = true;
62567 np.set(attrs);
62568}
62569
62570function insertAttrs(baseAttrs, newAttrs, astr) {
62571 var np = nestedProperty(baseAttrs, astr);
62572
62573 np.set(extendDeepAll(np.get() || {}, newAttrs));
62574}
62575
62576},{"../lib":287,"../plots/animation_attributes":328,"../plots/attributes":330,"../plots/frame_attributes":364,"../plots/layout_attributes":367,"../registry":376,"./edit_types":316,"./plot_config":321}],323:[function(_dereq_,module,exports){
62577'use strict';
62578
62579var Lib = _dereq_('../lib');
62580var plotAttributes = _dereq_('../plots/attributes');
62581
62582var TEMPLATEITEMNAME = 'templateitemname';
62583
62584var templateAttrs = {
62585 name: {
62586 valType: 'string',
62587 editType: 'none',
62588 }
62589};
62590templateAttrs[TEMPLATEITEMNAME] = {
62591 valType: 'string',
62592 editType: 'calc',
62593};
62594
62595/**
62596 * templatedArray: decorate an attributes object with templating (and array)
62597 * properties.
62598 *
62599 * @param {string} name: the singular form of the array name. Sets
62600 * `_isLinkedToArray` to this, so the schema knows to treat this as an array.
62601 * @param {object} attrs: the item attributes. Since all callers are expected
62602 * to be constructing this object on the spot, we mutate it here for
62603 * performance, rather than extending a new object with it.
62604 *
62605 * @returns {object}: the decorated `attrs` object
62606 */
62607exports.templatedArray = function(name, attrs) {
62608 attrs._isLinkedToArray = name;
62609 attrs.name = templateAttrs.name;
62610 attrs[TEMPLATEITEMNAME] = templateAttrs[TEMPLATEITEMNAME];
62611 return attrs;
62612};
62613
62614/**
62615 * traceTemplater: logic for matching traces to trace templates
62616 *
62617 * @param {object} dataTemplate: collection of {traceType: [{template}, ...]}
62618 * ie each type the template applies to contains a list of template objects,
62619 * to be provided cyclically to data traces of that type.
62620 *
62621 * @returns {object}: {newTrace}, a function:
62622 * newTrace(traceIn): that takes the input traceIn, coerces its type, then
62623 * uses that type to find the next template to apply. returns the output
62624 * traceOut with template attached, ready to continue supplyDefaults.
62625 */
62626exports.traceTemplater = function(dataTemplate) {
62627 var traceCounts = {};
62628 var traceType, typeTemplates;
62629
62630 for(traceType in dataTemplate) {
62631 typeTemplates = dataTemplate[traceType];
62632 if(Array.isArray(typeTemplates) && typeTemplates.length) {
62633 traceCounts[traceType] = 0;
62634 }
62635 }
62636
62637 function newTrace(traceIn) {
62638 traceType = Lib.coerce(traceIn, {}, plotAttributes, 'type');
62639 var traceOut = {type: traceType, _template: null};
62640 if(traceType in traceCounts) {
62641 typeTemplates = dataTemplate[traceType];
62642 // cycle through traces in the template set for this type
62643 var typei = traceCounts[traceType] % typeTemplates.length;
62644 traceCounts[traceType]++;
62645 traceOut._template = typeTemplates[typei];
62646 } else {
62647 // TODO: anything we should do for types missing from the template?
62648 // try to apply some other type? Or just bail as we do here?
62649 // Actually I think yes, we should apply other types; would be nice
62650 // if all scatter* could inherit from each other, and if histogram
62651 // could inherit from bar, etc... but how to specify this? And do we
62652 // compose them, or if a type is present require it to be complete?
62653 // Actually this could apply to layout too - 3D annotations
62654 // inheriting from 2D, axes of different types inheriting from each
62655 // other...
62656 }
62657 return traceOut;
62658 }
62659
62660 return {
62661 newTrace: newTrace
62662 // TODO: function to figure out what's left & what didn't work
62663 };
62664};
62665
62666/**
62667 * newContainer: Create a new sub-container inside `container` and propagate any
62668 * applicable template to it. If there's no template, still propagates
62669 * `undefined` so relinkPrivate will not retain an old template!
62670 *
62671 * @param {object} container: the outer container, should already have _template
62672 * if there *is* a template for this plot
62673 * @param {string} name: the key of the new container to make
62674 * @param {string} baseName: if applicable, a base attribute to take the
62675 * template from, ie for xaxis3 the base would be xaxis
62676 *
62677 * @returns {object}: an object for inclusion _full*, empty except for the
62678 * appropriate template piece
62679 */
62680exports.newContainer = function(container, name, baseName) {
62681 var template = container._template;
62682 var part = template && (template[name] || (baseName && template[baseName]));
62683 if(!Lib.isPlainObject(part)) part = null;
62684
62685 var out = container[name] = {_template: part};
62686 return out;
62687};
62688
62689/**
62690 * arrayTemplater: special logic for templating both defaults and specific items
62691 * in a container array (annotations etc)
62692 *
62693 * @param {object} container: the outer container, should already have _template
62694 * if there *is* a template for this plot
62695 * @param {string} name: the name of the array to template (ie 'annotations')
62696 * will be used to find default ('annotationdefaults' object) and specific
62697 * ('annotations' array) template specs.
62698 * @param {string} inclusionAttr: the attribute determining this item's
62699 * inclusion in the output, usually 'visible' or 'enabled'
62700 *
62701 * @returns {object}: {newItem, defaultItems}, both functions:
62702 * newItem(itemIn): create an output item, bare except for the correct
62703 * template and name(s), as the base for supplyDefaults
62704 * defaultItems(): to be called after all newItem calls, return any
62705 * specific template items that have not already beeen included,
62706 * also as bare output items ready for supplyDefaults.
62707 */
62708exports.arrayTemplater = function(container, name, inclusionAttr) {
62709 var template = container._template;
62710 var defaultsTemplate = template && template[arrayDefaultKey(name)];
62711 var templateItems = template && template[name];
62712 if(!Array.isArray(templateItems) || !templateItems.length) {
62713 templateItems = [];
62714 }
62715
62716 var usedNames = {};
62717
62718 function newItem(itemIn) {
62719 // include name and templateitemname in the output object for ALL
62720 // container array items. Note: you could potentially use different
62721 // name and templateitemname, if you're using one template to make
62722 // another template. templateitemname would be the name in the original
62723 // template, and name is the new "subclassed" item name.
62724 var out = {name: itemIn.name, _input: itemIn};
62725 var templateItemName = out[TEMPLATEITEMNAME] = itemIn[TEMPLATEITEMNAME];
62726
62727 // no itemname: use the default template
62728 if(!validItemName(templateItemName)) {
62729 out._template = defaultsTemplate;
62730 return out;
62731 }
62732
62733 // look for an item matching this itemname
62734 // note these do not inherit from the default template, only the item.
62735 for(var i = 0; i < templateItems.length; i++) {
62736 var templateItem = templateItems[i];
62737 if(templateItem.name === templateItemName) {
62738 // Note: it's OK to use a template item more than once
62739 // but using it at least once will stop it from generating
62740 // a default item at the end.
62741 usedNames[templateItemName] = 1;
62742 out._template = templateItem;
62743 return out;
62744 }
62745 }
62746
62747 // Didn't find a matching template item, so since this item is intended
62748 // to only be modifications it's most likely broken. Hide it unless
62749 // it's explicitly marked visible - in which case it gets NO template,
62750 // not even the default.
62751 out[inclusionAttr] = itemIn[inclusionAttr] || false;
62752 // special falsy value we can look for in validateTemplate
62753 out._template = false;
62754 return out;
62755 }
62756
62757 function defaultItems() {
62758 var out = [];
62759 for(var i = 0; i < templateItems.length; i++) {
62760 var templateItem = templateItems[i];
62761 var name = templateItem.name;
62762 // only allow named items to be added as defaults,
62763 // and only allow each name once
62764 if(validItemName(name) && !usedNames[name]) {
62765 var outi = {
62766 _template: templateItem,
62767 name: name,
62768 _input: {_templateitemname: name}
62769 };
62770 outi[TEMPLATEITEMNAME] = templateItem[TEMPLATEITEMNAME];
62771 out.push(outi);
62772 usedNames[name] = 1;
62773 }
62774 }
62775 return out;
62776 }
62777
62778 return {
62779 newItem: newItem,
62780 defaultItems: defaultItems
62781 };
62782};
62783
62784function validItemName(name) {
62785 return name && typeof name === 'string';
62786}
62787
62788function arrayDefaultKey(name) {
62789 var lastChar = name.length - 1;
62790 if(name.charAt(lastChar) !== 's') {
62791 Lib.warn('bad argument to arrayDefaultKey: ' + name);
62792 }
62793 return name.substr(0, name.length - 1) + 'defaults';
62794}
62795exports.arrayDefaultKey = arrayDefaultKey;
62796
62797/**
62798 * arrayEditor: helper for editing array items that may have come from
62799 * template defaults (in which case they will not exist in the input yet)
62800 *
62801 * @param {object} parentIn: the input container (eg gd.layout)
62802 * @param {string} containerStr: the attribute string for the container inside
62803 * `parentIn`.
62804 * @param {object} itemOut: the _full* item (eg gd._fullLayout.annotations[0])
62805 * that we'll be editing. Assumed to have been created by `arrayTemplater`.
62806 *
62807 * @returns {object}: {modifyBase, modifyItem, getUpdateObj, applyUpdate}, all functions:
62808 * modifyBase(attr, value): Add an update that's *not* related to the item.
62809 * `attr` is the full attribute string.
62810 * modifyItem(attr, value): Add an update to the item. `attr` is just the
62811 * portion of the attribute string inside the item.
62812 * getUpdateObj(): Get the final constructed update object, to use in
62813 * `restyle` or `relayout`. Also resets the update object in case this
62814 * update was canceled.
62815 * applyUpdate(attr, value): optionally add an update `attr: value`,
62816 * then apply it to `parent` which should be the parent of `containerIn`,
62817 * ie the object to which `containerStr` is the attribute string.
62818 */
62819exports.arrayEditor = function(parentIn, containerStr, itemOut) {
62820 var lengthIn = (Lib.nestedProperty(parentIn, containerStr).get() || []).length;
62821 var index = itemOut._index;
62822 // Check that we are indeed off the end of this container.
62823 // Otherwise a devious user could put a key `_templateitemname` in their
62824 // own input and break lots of things.
62825 var templateItemName = (index >= lengthIn) && (itemOut._input || {})._templateitemname;
62826 if(templateItemName) index = lengthIn;
62827 var itemStr = containerStr + '[' + index + ']';
62828
62829 var update;
62830 function resetUpdate() {
62831 update = {};
62832 if(templateItemName) {
62833 update[itemStr] = {};
62834 update[itemStr][TEMPLATEITEMNAME] = templateItemName;
62835 }
62836 }
62837 resetUpdate();
62838
62839 function modifyBase(attr, value) {
62840 update[attr] = value;
62841 }
62842
62843 function modifyItem(attr, value) {
62844 if(templateItemName) {
62845 // we're making a new object: edit that object
62846 Lib.nestedProperty(update[itemStr], attr).set(value);
62847 } else {
62848 // we're editing an existing object: include *just* the edit
62849 update[itemStr + '.' + attr] = value;
62850 }
62851 }
62852
62853 function getUpdateObj() {
62854 var updateOut = update;
62855 resetUpdate();
62856 return updateOut;
62857 }
62858
62859 function applyUpdate(attr, value) {
62860 if(attr) modifyItem(attr, value);
62861 var updateToApply = getUpdateObj();
62862 for(var key in updateToApply) {
62863 Lib.nestedProperty(parentIn, key).set(updateToApply[key]);
62864 }
62865 }
62866
62867 return {
62868 modifyBase: modifyBase,
62869 modifyItem: modifyItem,
62870 getUpdateObj: getUpdateObj,
62871 applyUpdate: applyUpdate
62872 };
62873};
62874
62875},{"../lib":287,"../plots/attributes":330}],324:[function(_dereq_,module,exports){
62876'use strict';
62877
62878var d3 = _dereq_('@plotly/d3');
62879var Registry = _dereq_('../registry');
62880var Plots = _dereq_('../plots/plots');
62881
62882var Lib = _dereq_('../lib');
62883var clearGlCanvases = _dereq_('../lib/clear_gl_canvases');
62884
62885var Color = _dereq_('../components/color');
62886var Drawing = _dereq_('../components/drawing');
62887var Titles = _dereq_('../components/titles');
62888var ModeBar = _dereq_('../components/modebar');
62889
62890var Axes = _dereq_('../plots/cartesian/axes');
62891var alignmentConstants = _dereq_('../constants/alignment');
62892var axisConstraints = _dereq_('../plots/cartesian/constraints');
62893var enforceAxisConstraints = axisConstraints.enforce;
62894var cleanAxisConstraints = axisConstraints.clean;
62895var doAutoRange = _dereq_('../plots/cartesian/autorange').doAutoRange;
62896
62897var SVG_TEXT_ANCHOR_START = 'start';
62898var SVG_TEXT_ANCHOR_MIDDLE = 'middle';
62899var SVG_TEXT_ANCHOR_END = 'end';
62900
62901exports.layoutStyles = function(gd) {
62902 return Lib.syncOrAsync([Plots.doAutoMargin, lsInner], gd);
62903};
62904
62905function overlappingDomain(xDomain, yDomain, domains) {
62906 for(var i = 0; i < domains.length; i++) {
62907 var existingX = domains[i][0];
62908 var existingY = domains[i][1];
62909
62910 if(existingX[0] >= xDomain[1] || existingX[1] <= xDomain[0]) {
62911 continue;
62912 }
62913 if(existingY[0] < yDomain[1] && existingY[1] > yDomain[0]) {
62914 return true;
62915 }
62916 }
62917 return false;
62918}
62919
62920function lsInner(gd) {
62921 var fullLayout = gd._fullLayout;
62922 var gs = fullLayout._size;
62923 var pad = gs.p;
62924 var axList = Axes.list(gd, '', true);
62925 var i, subplot, plotinfo, ax, xa, ya;
62926
62927 fullLayout._paperdiv.style({
62928 width: (gd._context.responsive && fullLayout.autosize && !gd._context._hasZeroWidth && !gd.layout.width) ? '100%' : fullLayout.width + 'px',
62929 height: (gd._context.responsive && fullLayout.autosize && !gd._context._hasZeroHeight && !gd.layout.height) ? '100%' : fullLayout.height + 'px'
62930 })
62931 .selectAll('.main-svg')
62932 .call(Drawing.setSize, fullLayout.width, fullLayout.height);
62933 gd._context.setBackground(gd, fullLayout.paper_bgcolor);
62934
62935 exports.drawMainTitle(gd);
62936 ModeBar.manage(gd);
62937
62938 // _has('cartesian') means SVG specifically, not GL2D - but GL2D
62939 // can still get here because it makes some of the SVG structure
62940 // for shared features like selections.
62941 if(!fullLayout._has('cartesian')) {
62942 return Plots.previousPromises(gd);
62943 }
62944
62945 function getLinePosition(ax, counterAx, side) {
62946 var lwHalf = ax._lw / 2;
62947
62948 if(ax._id.charAt(0) === 'x') {
62949 if(!counterAx) return gs.t + gs.h * (1 - (ax.position || 0)) + (lwHalf % 1);
62950 else if(side === 'top') return counterAx._offset - pad - lwHalf;
62951 return counterAx._offset + counterAx._length + pad + lwHalf;
62952 }
62953
62954 if(!counterAx) return gs.l + gs.w * (ax.position || 0) + (lwHalf % 1);
62955 else if(side === 'right') return counterAx._offset + counterAx._length + pad + lwHalf;
62956 return counterAx._offset - pad - lwHalf;
62957 }
62958
62959 // some preparation of axis position info
62960 for(i = 0; i < axList.length; i++) {
62961 ax = axList[i];
62962
62963 var counterAx = ax._anchorAxis;
62964
62965 // clear axis line positions, to be set in the subplot loop below
62966 ax._linepositions = {};
62967
62968 // stash crispRounded linewidth so we don't need to pass gd all over the place
62969 ax._lw = Drawing.crispRound(gd, ax.linewidth, 1);
62970
62971 // figure out the main axis line and main mirror line position.
62972 // it's easier to follow the logic if we handle these separately from
62973 // ax._linepositions, which are only used by mirror=allticks
62974 // for non-main-subplot ticks, and mirror=all(ticks)? for zero line
62975 // hiding logic
62976 ax._mainLinePosition = getLinePosition(ax, counterAx, ax.side);
62977 ax._mainMirrorPosition = (ax.mirror && counterAx) ?
62978 getLinePosition(ax, counterAx,
62979 alignmentConstants.OPPOSITE_SIDE[ax.side]) : null;
62980 }
62981
62982 // figure out which backgrounds we need to draw,
62983 // and in which layers to put them
62984 var lowerBackgroundIDs = [];
62985 var backgroundIds = [];
62986 var lowerDomains = [];
62987 // no need to draw background when paper and plot color are the same color,
62988 // activate mode just for large splom (which benefit the most from this
62989 // optimization), but this could apply to all cartesian subplots.
62990 var noNeedForBg = (
62991 Color.opacity(fullLayout.paper_bgcolor) === 1 &&
62992 Color.opacity(fullLayout.plot_bgcolor) === 1 &&
62993 fullLayout.paper_bgcolor === fullLayout.plot_bgcolor
62994 );
62995
62996 for(subplot in fullLayout._plots) {
62997 plotinfo = fullLayout._plots[subplot];
62998
62999 if(plotinfo.mainplot) {
63000 // mainplot is a reference to the main plot this one is overlaid on
63001 // so if it exists, this is an overlaid plot and we don't need to
63002 // give it its own background
63003 if(plotinfo.bg) {
63004 plotinfo.bg.remove();
63005 }
63006 plotinfo.bg = undefined;
63007 } else {
63008 var xDomain = plotinfo.xaxis.domain;
63009 var yDomain = plotinfo.yaxis.domain;
63010 var plotgroup = plotinfo.plotgroup;
63011
63012 if(overlappingDomain(xDomain, yDomain, lowerDomains)) {
63013 var pgNode = plotgroup.node();
63014 var plotgroupBg = plotinfo.bg = Lib.ensureSingle(plotgroup, 'rect', 'bg');
63015 pgNode.insertBefore(plotgroupBg.node(), pgNode.childNodes[0]);
63016 backgroundIds.push(subplot);
63017 } else {
63018 plotgroup.select('rect.bg').remove();
63019 lowerDomains.push([xDomain, yDomain]);
63020 if(!noNeedForBg) {
63021 lowerBackgroundIDs.push(subplot);
63022 backgroundIds.push(subplot);
63023 }
63024 }
63025 }
63026 }
63027
63028 // now create all the lower-layer backgrounds at once now that
63029 // we have the list of subplots that need them
63030 var lowerBackgrounds = fullLayout._bgLayer.selectAll('.bg')
63031 .data(lowerBackgroundIDs);
63032
63033 lowerBackgrounds.enter().append('rect')
63034 .classed('bg', true);
63035
63036 lowerBackgrounds.exit().remove();
63037
63038 lowerBackgrounds.each(function(subplot) {
63039 fullLayout._plots[subplot].bg = d3.select(this);
63040 });
63041
63042 // style all backgrounds
63043 for(i = 0; i < backgroundIds.length; i++) {
63044 plotinfo = fullLayout._plots[backgroundIds[i]];
63045 xa = plotinfo.xaxis;
63046 ya = plotinfo.yaxis;
63047
63048 if(plotinfo.bg && xa._offset !== undefined && ya._offset !== undefined) {
63049 plotinfo.bg
63050 .call(Drawing.setRect,
63051 xa._offset - pad, ya._offset - pad,
63052 xa._length + 2 * pad, ya._length + 2 * pad)
63053 .call(Color.fill, fullLayout.plot_bgcolor)
63054 .style('stroke-width', 0);
63055 }
63056 }
63057
63058 if(!fullLayout._hasOnlyLargeSploms) {
63059 for(subplot in fullLayout._plots) {
63060 plotinfo = fullLayout._plots[subplot];
63061 xa = plotinfo.xaxis;
63062 ya = plotinfo.yaxis;
63063
63064 // Clip so that data only shows up on the plot area.
63065 var clipId = plotinfo.clipId = 'clip' + fullLayout._uid + subplot + 'plot';
63066
63067 var plotClip = Lib.ensureSingleById(fullLayout._clips, 'clipPath', clipId, function(s) {
63068 s.classed('plotclip', true)
63069 .append('rect');
63070 });
63071
63072 plotinfo.clipRect = plotClip.select('rect').attr({
63073 width: xa._length,
63074 height: ya._length
63075 });
63076
63077 Drawing.setTranslate(plotinfo.plot, xa._offset, ya._offset);
63078
63079 var plotClipId;
63080 var layerClipId;
63081
63082 if(plotinfo._hasClipOnAxisFalse) {
63083 plotClipId = null;
63084 layerClipId = clipId;
63085 } else {
63086 plotClipId = clipId;
63087 layerClipId = null;
63088 }
63089
63090 Drawing.setClipUrl(plotinfo.plot, plotClipId, gd);
63091
63092 // stash layer clipId value (null or same as clipId)
63093 // to DRY up Drawing.setClipUrl calls on trace-module and trace layers
63094 // downstream
63095 plotinfo.layerClipId = layerClipId;
63096 }
63097 }
63098
63099 var xLinesXLeft, xLinesXRight, xLinesYBottom, xLinesYTop,
63100 leftYLineWidth, rightYLineWidth;
63101 var yLinesYBottom, yLinesYTop, yLinesXLeft, yLinesXRight,
63102 connectYBottom, connectYTop;
63103 var extraSubplot;
63104
63105 function xLinePath(y) {
63106 return 'M' + xLinesXLeft + ',' + y + 'H' + xLinesXRight;
63107 }
63108
63109 function xLinePathFree(y) {
63110 return 'M' + xa._offset + ',' + y + 'h' + xa._length;
63111 }
63112
63113 function yLinePath(x) {
63114 return 'M' + x + ',' + yLinesYTop + 'V' + yLinesYBottom;
63115 }
63116
63117 function yLinePathFree(x) {
63118 return 'M' + x + ',' + ya._offset + 'v' + ya._length;
63119 }
63120
63121 function mainPath(ax, pathFn, pathFnFree) {
63122 if(!ax.showline || subplot !== ax._mainSubplot) return '';
63123 if(!ax._anchorAxis) return pathFnFree(ax._mainLinePosition);
63124 var out = pathFn(ax._mainLinePosition);
63125 if(ax.mirror) out += pathFn(ax._mainMirrorPosition);
63126 return out;
63127 }
63128
63129 for(subplot in fullLayout._plots) {
63130 plotinfo = fullLayout._plots[subplot];
63131 xa = plotinfo.xaxis;
63132 ya = plotinfo.yaxis;
63133
63134 /*
63135 * x lines get longer where they meet y lines, to make a crisp corner.
63136 * The x lines get the padding (margin.pad) plus the y line width to
63137 * fill up the corner nicely. Free x lines are excluded - they always
63138 * span exactly the data area of the plot
63139 *
63140 * | XXXXX
63141 * | XXXXX
63142 * |
63143 * +------
63144 * x1
63145 * -----
63146 * x2
63147 */
63148 var xPath = 'M0,0';
63149 if(shouldShowLinesOrTicks(xa, subplot)) {
63150 leftYLineWidth = findCounterAxisLineWidth(xa, 'left', ya, axList);
63151 xLinesXLeft = xa._offset - (leftYLineWidth ? (pad + leftYLineWidth) : 0);
63152 rightYLineWidth = findCounterAxisLineWidth(xa, 'right', ya, axList);
63153 xLinesXRight = xa._offset + xa._length + (rightYLineWidth ? (pad + rightYLineWidth) : 0);
63154 xLinesYBottom = getLinePosition(xa, ya, 'bottom');
63155 xLinesYTop = getLinePosition(xa, ya, 'top');
63156
63157 // save axis line positions for extra ticks to reference
63158 // each subplot that gets ticks from "allticks" gets an entry:
63159 // [left or bottom, right or top]
63160 extraSubplot = (!xa._anchorAxis || subplot !== xa._mainSubplot);
63161 if(extraSubplot && (xa.mirror === 'allticks' || xa.mirror === 'all')) {
63162 xa._linepositions[subplot] = [xLinesYBottom, xLinesYTop];
63163 }
63164
63165 xPath = mainPath(xa, xLinePath, xLinePathFree);
63166 if(extraSubplot && xa.showline && (xa.mirror === 'all' || xa.mirror === 'allticks')) {
63167 xPath += xLinePath(xLinesYBottom) + xLinePath(xLinesYTop);
63168 }
63169
63170 plotinfo.xlines
63171 .style('stroke-width', xa._lw + 'px')
63172 .call(Color.stroke, xa.showline ?
63173 xa.linecolor : 'rgba(0,0,0,0)');
63174 }
63175 plotinfo.xlines.attr('d', xPath);
63176
63177 /*
63178 * y lines that meet x axes get longer only by margin.pad, because
63179 * the x axes fill in the corner space. Free y axes, like free x axes,
63180 * always span exactly the data area of the plot
63181 *
63182 * | | XXXX
63183 * y2| y1| XXXX
63184 * | | XXXX
63185 * |
63186 * +-----
63187 */
63188 var yPath = 'M0,0';
63189 if(shouldShowLinesOrTicks(ya, subplot)) {
63190 connectYBottom = findCounterAxisLineWidth(ya, 'bottom', xa, axList);
63191 yLinesYBottom = ya._offset + ya._length + (connectYBottom ? pad : 0);
63192 connectYTop = findCounterAxisLineWidth(ya, 'top', xa, axList);
63193 yLinesYTop = ya._offset - (connectYTop ? pad : 0);
63194 yLinesXLeft = getLinePosition(ya, xa, 'left');
63195 yLinesXRight = getLinePosition(ya, xa, 'right');
63196
63197 extraSubplot = (!ya._anchorAxis || subplot !== ya._mainSubplot);
63198 if(extraSubplot && (ya.mirror === 'allticks' || ya.mirror === 'all')) {
63199 ya._linepositions[subplot] = [yLinesXLeft, yLinesXRight];
63200 }
63201
63202 yPath = mainPath(ya, yLinePath, yLinePathFree);
63203 if(extraSubplot && ya.showline && (ya.mirror === 'all' || ya.mirror === 'allticks')) {
63204 yPath += yLinePath(yLinesXLeft) + yLinePath(yLinesXRight);
63205 }
63206
63207 plotinfo.ylines
63208 .style('stroke-width', ya._lw + 'px')
63209 .call(Color.stroke, ya.showline ?
63210 ya.linecolor : 'rgba(0,0,0,0)');
63211 }
63212 plotinfo.ylines.attr('d', yPath);
63213 }
63214
63215 Axes.makeClipPaths(gd);
63216
63217 return Plots.previousPromises(gd);
63218}
63219
63220function shouldShowLinesOrTicks(ax, subplot) {
63221 return (ax.ticks || ax.showline) &&
63222 (subplot === ax._mainSubplot || ax.mirror === 'all' || ax.mirror === 'allticks');
63223}
63224
63225/*
63226 * should we draw a line on counterAx at this side of ax?
63227 * It's assumed that counterAx is known to overlay the subplot we're working on
63228 * but it may not be its main axis.
63229 */
63230function shouldShowLineThisSide(ax, side, counterAx) {
63231 // does counterAx get a line at all?
63232 if(!counterAx.showline || !counterAx._lw) return false;
63233
63234 // are we drawing *all* lines for counterAx?
63235 if(counterAx.mirror === 'all' || counterAx.mirror === 'allticks') return true;
63236
63237 var anchorAx = counterAx._anchorAxis;
63238
63239 // is this a free axis? free axes can only have a subplot side-line with all(ticks)? mirroring
63240 if(!anchorAx) return false;
63241
63242 // in order to handle cases where the user forgot to anchor this axis correctly
63243 // (because its default anchor has the same domain on the relevant end)
63244 // check whether the relevant position is the same.
63245 var sideIndex = alignmentConstants.FROM_BL[side];
63246 if(counterAx.side === side) {
63247 return anchorAx.domain[sideIndex] === ax.domain[sideIndex];
63248 }
63249 return counterAx.mirror && anchorAx.domain[1 - sideIndex] === ax.domain[1 - sideIndex];
63250}
63251
63252/*
63253 * Is there another axis intersecting `side` end of `ax`?
63254 * First look at `counterAx` (the axis for this subplot),
63255 * then at all other potential counteraxes on or overlaying this subplot.
63256 * Take the line width from the first one that has a line.
63257 */
63258function findCounterAxisLineWidth(ax, side, counterAx, axList) {
63259 if(shouldShowLineThisSide(ax, side, counterAx)) {
63260 return counterAx._lw;
63261 }
63262 for(var i = 0; i < axList.length; i++) {
63263 var axi = axList[i];
63264 if(axi._mainAxis === counterAx._mainAxis && shouldShowLineThisSide(ax, side, axi)) {
63265 return axi._lw;
63266 }
63267 }
63268 return 0;
63269}
63270
63271exports.drawMainTitle = function(gd) {
63272 var fullLayout = gd._fullLayout;
63273
63274 var textAnchor = getMainTitleTextAnchor(fullLayout);
63275 var dy = getMainTitleDy(fullLayout);
63276
63277 Titles.draw(gd, 'gtitle', {
63278 propContainer: fullLayout,
63279 propName: 'title.text',
63280 placeholder: fullLayout._dfltTitle.plot,
63281 attributes: {
63282 x: getMainTitleX(fullLayout, textAnchor),
63283 y: getMainTitleY(fullLayout, dy),
63284 'text-anchor': textAnchor,
63285 dy: dy
63286 }
63287 });
63288};
63289
63290function getMainTitleX(fullLayout, textAnchor) {
63291 var title = fullLayout.title;
63292 var gs = fullLayout._size;
63293 var hPadShift = 0;
63294
63295 if(textAnchor === SVG_TEXT_ANCHOR_START) {
63296 hPadShift = title.pad.l;
63297 } else if(textAnchor === SVG_TEXT_ANCHOR_END) {
63298 hPadShift = -title.pad.r;
63299 }
63300
63301 switch(title.xref) {
63302 case 'paper':
63303 return gs.l + gs.w * title.x + hPadShift;
63304 case 'container':
63305 default:
63306 return fullLayout.width * title.x + hPadShift;
63307 }
63308}
63309
63310function getMainTitleY(fullLayout, dy) {
63311 var title = fullLayout.title;
63312 var gs = fullLayout._size;
63313 var vPadShift = 0;
63314
63315 if(dy === '0em' || !dy) {
63316 vPadShift = -title.pad.b;
63317 } else if(dy === alignmentConstants.CAP_SHIFT + 'em') {
63318 vPadShift = title.pad.t;
63319 }
63320
63321 if(title.y === 'auto') {
63322 return gs.t / 2;
63323 } else {
63324 switch(title.yref) {
63325 case 'paper':
63326 return gs.t + gs.h - gs.h * title.y + vPadShift;
63327 case 'container':
63328 default:
63329 return fullLayout.height - fullLayout.height * title.y + vPadShift;
63330 }
63331 }
63332}
63333
63334function getMainTitleTextAnchor(fullLayout) {
63335 var title = fullLayout.title;
63336
63337 var textAnchor = SVG_TEXT_ANCHOR_MIDDLE;
63338 if(Lib.isRightAnchor(title)) {
63339 textAnchor = SVG_TEXT_ANCHOR_END;
63340 } else if(Lib.isLeftAnchor(title)) {
63341 textAnchor = SVG_TEXT_ANCHOR_START;
63342 }
63343
63344 return textAnchor;
63345}
63346
63347function getMainTitleDy(fullLayout) {
63348 var title = fullLayout.title;
63349
63350 var dy = '0em';
63351 if(Lib.isTopAnchor(title)) {
63352 dy = alignmentConstants.CAP_SHIFT + 'em';
63353 } else if(Lib.isMiddleAnchor(title)) {
63354 dy = alignmentConstants.MID_SHIFT + 'em';
63355 }
63356
63357 return dy;
63358}
63359
63360exports.doTraceStyle = function(gd) {
63361 var calcdata = gd.calcdata;
63362 var editStyleCalls = [];
63363 var i;
63364
63365 for(i = 0; i < calcdata.length; i++) {
63366 var cd = calcdata[i];
63367 var cd0 = cd[0] || {};
63368 var trace = cd0.trace || {};
63369 var _module = trace._module || {};
63370
63371 // See if we need to do arraysToCalcdata
63372 // call it regardless of what change we made, in case
63373 // supplyDefaults brought in an array that was already
63374 // in gd.data but not in gd._fullData previously
63375 var arraysToCalcdata = _module.arraysToCalcdata;
63376 if(arraysToCalcdata) arraysToCalcdata(cd, trace);
63377
63378 var editStyle = _module.editStyle;
63379 if(editStyle) editStyleCalls.push({fn: editStyle, cd0: cd0});
63380 }
63381
63382 if(editStyleCalls.length) {
63383 for(i = 0; i < editStyleCalls.length; i++) {
63384 var edit = editStyleCalls[i];
63385 edit.fn(gd, edit.cd0);
63386 }
63387 clearGlCanvases(gd);
63388 exports.redrawReglTraces(gd);
63389 }
63390
63391 Plots.style(gd);
63392 Registry.getComponentMethod('legend', 'draw')(gd);
63393
63394 return Plots.previousPromises(gd);
63395};
63396
63397exports.doColorBars = function(gd) {
63398 Registry.getComponentMethod('colorbar', 'draw')(gd);
63399 return Plots.previousPromises(gd);
63400};
63401
63402// force plot() to redo the layout and replot with the modified layout
63403exports.layoutReplot = function(gd) {
63404 var layout = gd.layout;
63405 gd.layout = undefined;
63406 return Registry.call('_doPlot', gd, '', layout);
63407};
63408
63409exports.doLegend = function(gd) {
63410 Registry.getComponentMethod('legend', 'draw')(gd);
63411 return Plots.previousPromises(gd);
63412};
63413
63414exports.doTicksRelayout = function(gd) {
63415 Axes.draw(gd, 'redraw');
63416
63417 if(gd._fullLayout._hasOnlyLargeSploms) {
63418 Registry.subplotsRegistry.splom.updateGrid(gd);
63419 clearGlCanvases(gd);
63420 exports.redrawReglTraces(gd);
63421 }
63422
63423 exports.drawMainTitle(gd);
63424 return Plots.previousPromises(gd);
63425};
63426
63427exports.doModeBar = function(gd) {
63428 var fullLayout = gd._fullLayout;
63429
63430 ModeBar.manage(gd);
63431
63432 for(var i = 0; i < fullLayout._basePlotModules.length; i++) {
63433 var updateFx = fullLayout._basePlotModules[i].updateFx;
63434 if(updateFx) updateFx(gd);
63435 }
63436
63437 return Plots.previousPromises(gd);
63438};
63439
63440exports.doCamera = function(gd) {
63441 var fullLayout = gd._fullLayout;
63442 var sceneIds = fullLayout._subplots.gl3d;
63443
63444 for(var i = 0; i < sceneIds.length; i++) {
63445 var sceneLayout = fullLayout[sceneIds[i]];
63446 var scene = sceneLayout._scene;
63447
63448 scene.setViewport(sceneLayout);
63449 }
63450};
63451
63452exports.drawData = function(gd) {
63453 var fullLayout = gd._fullLayout;
63454
63455 clearGlCanvases(gd);
63456
63457 // loop over the base plot modules present on graph
63458 var basePlotModules = fullLayout._basePlotModules;
63459 for(var i = 0; i < basePlotModules.length; i++) {
63460 basePlotModules[i].plot(gd);
63461 }
63462
63463 exports.redrawReglTraces(gd);
63464
63465 // styling separate from drawing
63466 Plots.style(gd);
63467
63468 // draw components that can be drawn on axes,
63469 // and that do not push the margins
63470 Registry.getComponentMethod('shapes', 'draw')(gd);
63471 Registry.getComponentMethod('annotations', 'draw')(gd);
63472 Registry.getComponentMethod('images', 'draw')(gd);
63473
63474 // Mark the first render as complete
63475 fullLayout._replotting = false;
63476
63477 return Plots.previousPromises(gd);
63478};
63479
63480// Draw (or redraw) all regl-based traces in one go,
63481// useful during drag and selection where buffers of targeted traces are updated,
63482// but all traces need to be redrawn following clearGlCanvases.
63483//
63484// Note that _module.plot for regl trace does NOT draw things
63485// on the canvas, they only update the buffers.
63486// Drawing is perform here.
63487//
63488// TODO try adding per-subplot option using gl.SCISSOR_TEST for
63489// non-overlaying, disjoint subplots.
63490//
63491// TODO try to include parcoords in here.
63492// https://github.com/plotly/plotly.js/issues/3069
63493exports.redrawReglTraces = function(gd) {
63494 var fullLayout = gd._fullLayout;
63495
63496 if(fullLayout._has('regl')) {
63497 var fullData = gd._fullData;
63498 var cartesianIds = [];
63499 var polarIds = [];
63500 var i, sp;
63501
63502 if(fullLayout._hasOnlyLargeSploms) {
63503 fullLayout._splomGrid.draw();
63504 }
63505
63506 // N.B.
63507 // - Loop over fullData (not _splomScenes) to preserve splom trace-to-trace ordering
63508 // - Fill list if subplot ids (instead of fullLayout._subplots) to handle cases where all traces
63509 // of a given module are `visible !== true`
63510 for(i = 0; i < fullData.length; i++) {
63511 var trace = fullData[i];
63512
63513 if(trace.visible === true && trace._length !== 0) {
63514 if(trace.type === 'splom') {
63515 fullLayout._splomScenes[trace.uid].draw();
63516 } else if(trace.type === 'scattergl') {
63517 Lib.pushUnique(cartesianIds, trace.xaxis + trace.yaxis);
63518 } else if(trace.type === 'scatterpolargl') {
63519 Lib.pushUnique(polarIds, trace.subplot);
63520 }
63521 }
63522 }
63523
63524 for(i = 0; i < cartesianIds.length; i++) {
63525 sp = fullLayout._plots[cartesianIds[i]];
63526 if(sp._scene) sp._scene.draw();
63527 }
63528
63529 for(i = 0; i < polarIds.length; i++) {
63530 sp = fullLayout[polarIds[i]]._subplot;
63531 if(sp._scene) sp._scene.draw();
63532 }
63533 }
63534};
63535
63536exports.doAutoRangeAndConstraints = function(gd) {
63537 var axList = Axes.list(gd, '', true);
63538 var ax;
63539
63540 var autoRangeDone = {};
63541
63542 for(var i = 0; i < axList.length; i++) {
63543 ax = axList[i];
63544
63545 if(!autoRangeDone[ax._id]) {
63546 autoRangeDone[ax._id] = 1;
63547 cleanAxisConstraints(gd, ax);
63548 doAutoRange(gd, ax);
63549
63550 // For matching axes, just propagate this autorange to the group.
63551 // The extra arg to doAutoRange avoids recalculating the range,
63552 // since doAutoRange by itself accounts for all matching axes. but
63553 // there are other side-effects of doAutoRange that we still want.
63554 var matchGroup = ax._matchGroup;
63555 if(matchGroup) {
63556 for(var id2 in matchGroup) {
63557 var ax2 = Axes.getFromId(gd, id2);
63558 doAutoRange(gd, ax2, ax.range);
63559 autoRangeDone[id2] = 1;
63560 }
63561 }
63562 }
63563 }
63564
63565 enforceAxisConstraints(gd);
63566};
63567
63568// An initial paint must be completed before these components can be
63569// correctly sized and the whole plot re-margined. fullLayout._replotting must
63570// be set to false before these will work properly.
63571exports.finalDraw = function(gd) {
63572 // TODO: rangesliders really belong in marginPushers but they need to be
63573 // drawn after data - can we at least get the margin pushing part separated
63574 // out and done earlier?
63575 Registry.getComponentMethod('rangeslider', 'draw')(gd);
63576 // TODO: rangeselector only needs to be here (in addition to drawMarginPushers)
63577 // because the margins need to be fully determined before we can call
63578 // autorange and update axis ranges (which rangeselector needs to know which
63579 // button is active). Can we break out its automargin step from its draw step?
63580 Registry.getComponentMethod('rangeselector', 'draw')(gd);
63581};
63582
63583exports.drawMarginPushers = function(gd) {
63584 Registry.getComponentMethod('legend', 'draw')(gd);
63585 Registry.getComponentMethod('rangeselector', 'draw')(gd);
63586 Registry.getComponentMethod('sliders', 'draw')(gd);
63587 Registry.getComponentMethod('updatemenus', 'draw')(gd);
63588 Registry.getComponentMethod('colorbar', 'draw')(gd);
63589};
63590
63591},{"../components/color":157,"../components/drawing":179,"../components/modebar":220,"../components/titles":255,"../constants/alignment":262,"../lib":287,"../lib/clear_gl_canvases":275,"../plots/cartesian/autorange":333,"../plots/cartesian/axes":334,"../plots/cartesian/constraints":342,"../plots/plots":369,"../registry":376,"@plotly/d3":20}],325:[function(_dereq_,module,exports){
63592'use strict';
63593
63594var Lib = _dereq_('../lib');
63595var isPlainObject = Lib.isPlainObject;
63596var PlotSchema = _dereq_('./plot_schema');
63597var Plots = _dereq_('../plots/plots');
63598var plotAttributes = _dereq_('../plots/attributes');
63599var Template = _dereq_('./plot_template');
63600var dfltConfig = _dereq_('./plot_config').dfltConfig;
63601
63602/**
63603 * Plotly.makeTemplate: create a template off an existing figure to reuse
63604 * style attributes on other figures.
63605 *
63606 * Note: separated from the rest of templates because otherwise we get circular
63607 * references due to PlotSchema.
63608 *
63609 * @param {object|DOM element|string} figure: The figure to base the template on
63610 * should contain a trace array `figure.data`
63611 * and a layout object `figure.layout`
63612 * @returns {object} template: the extracted template - can then be used as
63613 * `layout.template` in another figure.
63614 */
63615exports.makeTemplate = function(figure) {
63616 figure = Lib.isPlainObject(figure) ? figure : Lib.getGraphDiv(figure);
63617 figure = Lib.extendDeep({_context: dfltConfig}, {data: figure.data, layout: figure.layout});
63618 Plots.supplyDefaults(figure);
63619 var data = figure.data || [];
63620 var layout = figure.layout || {};
63621 // copy over a few items to help follow the schema
63622 layout._basePlotModules = figure._fullLayout._basePlotModules;
63623 layout._modules = figure._fullLayout._modules;
63624
63625 var template = {
63626 data: {},
63627 layout: {}
63628 };
63629
63630 /*
63631 * Note: we do NOT validate template values, we just take what's in the
63632 * user inputs data and layout, not the validated values in fullData and
63633 * fullLayout. Even if we were to validate here, there's no guarantee that
63634 * these values would still be valid when applied to a new figure, which
63635 * may contain different trace modes, different axes, etc. So it's
63636 * important that when applying a template we still validate the template
63637 * values, rather than just using them as defaults.
63638 */
63639
63640 data.forEach(function(trace) {
63641 // TODO: What if no style info is extracted for this trace. We may
63642 // not want an empty object as the null value.
63643 // TODO: allow transforms to contribute to templates?
63644 // as it stands they are ignored, which may be for the best...
63645
63646 var traceTemplate = {};
63647 walkStyleKeys(trace, traceTemplate, getTraceInfo.bind(null, trace));
63648
63649 var traceType = Lib.coerce(trace, {}, plotAttributes, 'type');
63650 var typeTemplates = template.data[traceType];
63651 if(!typeTemplates) typeTemplates = template.data[traceType] = [];
63652 typeTemplates.push(traceTemplate);
63653 });
63654
63655 walkStyleKeys(layout, template.layout, getLayoutInfo.bind(null, layout));
63656
63657 /*
63658 * Compose the new template with an existing one to the same effect
63659 *
63660 * NOTE: there's a possibility of slightly different behavior: if the plot
63661 * has an invalid value and the old template has a valid value for the same
63662 * attribute, the plot will use the old template value but this routine
63663 * will pull the invalid value (resulting in the original default).
63664 * In the general case it's not possible to solve this with a single value,
63665 * since valid options can be context-dependent. It could be solved with
63666 * a *list* of values, but that would be huge complexity for little gain.
63667 */
63668 delete template.layout.template;
63669 var oldTemplate = layout.template;
63670 if(isPlainObject(oldTemplate)) {
63671 var oldLayoutTemplate = oldTemplate.layout;
63672
63673 var i, traceType, oldTypeTemplates, oldTypeLen, typeTemplates, typeLen;
63674
63675 if(isPlainObject(oldLayoutTemplate)) {
63676 mergeTemplates(oldLayoutTemplate, template.layout);
63677 }
63678 var oldDataTemplate = oldTemplate.data;
63679 if(isPlainObject(oldDataTemplate)) {
63680 for(traceType in template.data) {
63681 oldTypeTemplates = oldDataTemplate[traceType];
63682 if(Array.isArray(oldTypeTemplates)) {
63683 typeTemplates = template.data[traceType];
63684 typeLen = typeTemplates.length;
63685 oldTypeLen = oldTypeTemplates.length;
63686 for(i = 0; i < typeLen; i++) {
63687 mergeTemplates(oldTypeTemplates[i % oldTypeLen], typeTemplates[i]);
63688 }
63689 for(i = typeLen; i < oldTypeLen; i++) {
63690 typeTemplates.push(Lib.extendDeep({}, oldTypeTemplates[i]));
63691 }
63692 }
63693 }
63694 for(traceType in oldDataTemplate) {
63695 if(!(traceType in template.data)) {
63696 template.data[traceType] = Lib.extendDeep([], oldDataTemplate[traceType]);
63697 }
63698 }
63699 }
63700 }
63701
63702 return template;
63703};
63704
63705function mergeTemplates(oldTemplate, newTemplate) {
63706 // we don't care about speed here, just make sure we have a totally
63707 // distinct object from the previous template
63708 oldTemplate = Lib.extendDeep({}, oldTemplate);
63709
63710 // sort keys so we always get annotationdefaults before annotations etc
63711 // so arrayTemplater will work right
63712 var oldKeys = Object.keys(oldTemplate).sort();
63713 var i, j;
63714
63715 function mergeOne(oldVal, newVal, key) {
63716 if(isPlainObject(newVal) && isPlainObject(oldVal)) {
63717 mergeTemplates(oldVal, newVal);
63718 } else if(Array.isArray(newVal) && Array.isArray(oldVal)) {
63719 // Note: omitted `inclusionAttr` from arrayTemplater here,
63720 // it's irrelevant as we only want the resulting `_template`.
63721 var templater = Template.arrayTemplater({_template: oldTemplate}, key);
63722 for(j = 0; j < newVal.length; j++) {
63723 var item = newVal[j];
63724 var oldItem = templater.newItem(item)._template;
63725 if(oldItem) mergeTemplates(oldItem, item);
63726 }
63727 var defaultItems = templater.defaultItems();
63728 for(j = 0; j < defaultItems.length; j++) newVal.push(defaultItems[j]._template);
63729
63730 // templateitemname only applies to receiving plots
63731 for(j = 0; j < newVal.length; j++) delete newVal[j].templateitemname;
63732 }
63733 }
63734
63735 for(i = 0; i < oldKeys.length; i++) {
63736 var key = oldKeys[i];
63737 var oldVal = oldTemplate[key];
63738 if(key in newTemplate) {
63739 mergeOne(oldVal, newTemplate[key], key);
63740 } else newTemplate[key] = oldVal;
63741
63742 // if this is a base key from the old template (eg xaxis), look for
63743 // extended keys (eg xaxis2) in the new template to merge into
63744 if(getBaseKey(key) === key) {
63745 for(var key2 in newTemplate) {
63746 var baseKey2 = getBaseKey(key2);
63747 if(key2 !== baseKey2 && baseKey2 === key && !(key2 in oldTemplate)) {
63748 mergeOne(oldVal, newTemplate[key2], key);
63749 }
63750 }
63751 }
63752 }
63753}
63754
63755function getBaseKey(key) {
63756 return key.replace(/[0-9]+$/, '');
63757}
63758
63759function walkStyleKeys(parent, templateOut, getAttributeInfo, path, basePath) {
63760 var pathAttr = basePath && getAttributeInfo(basePath);
63761 for(var key in parent) {
63762 var child = parent[key];
63763 var nextPath = getNextPath(parent, key, path);
63764 var nextBasePath = getNextPath(parent, key, basePath);
63765 var attr = getAttributeInfo(nextBasePath);
63766 if(!attr) {
63767 var baseKey = getBaseKey(key);
63768 if(baseKey !== key) {
63769 nextBasePath = getNextPath(parent, baseKey, basePath);
63770 attr = getAttributeInfo(nextBasePath);
63771 }
63772 }
63773
63774 // we'll get an attr if path starts with a valid part, then has an
63775 // invalid ending. Make sure we got all the way to the end.
63776 if(pathAttr && (pathAttr === attr)) continue;
63777
63778 if(!attr || attr._noTemplating ||
63779 attr.valType === 'data_array' ||
63780 (attr.arrayOk && Array.isArray(child))
63781 ) {
63782 continue;
63783 }
63784
63785 if(!attr.valType && isPlainObject(child)) {
63786 walkStyleKeys(child, templateOut, getAttributeInfo, nextPath, nextBasePath);
63787 } else if(attr._isLinkedToArray && Array.isArray(child)) {
63788 var dfltDone = false;
63789 var namedIndex = 0;
63790 var usedNames = {};
63791 for(var i = 0; i < child.length; i++) {
63792 var item = child[i];
63793 if(isPlainObject(item)) {
63794 var name = item.name;
63795 if(name) {
63796 if(!usedNames[name]) {
63797 // named array items: allow all attributes except data arrays
63798 walkStyleKeys(item, templateOut, getAttributeInfo,
63799 getNextPath(child, namedIndex, nextPath),
63800 getNextPath(child, namedIndex, nextBasePath));
63801 namedIndex++;
63802 usedNames[name] = 1;
63803 }
63804 } else if(!dfltDone) {
63805 var dfltKey = Template.arrayDefaultKey(key);
63806 var dfltPath = getNextPath(parent, dfltKey, path);
63807
63808 // getAttributeInfo will fail if we try to use dfltKey directly.
63809 // Instead put this item into the next array element, then
63810 // pull it out and move it to dfltKey.
63811 var pathInArray = getNextPath(child, namedIndex, nextPath);
63812 walkStyleKeys(item, templateOut, getAttributeInfo, pathInArray,
63813 getNextPath(child, namedIndex, nextBasePath));
63814 var itemPropInArray = Lib.nestedProperty(templateOut, pathInArray);
63815 var dfltProp = Lib.nestedProperty(templateOut, dfltPath);
63816 dfltProp.set(itemPropInArray.get());
63817 itemPropInArray.set(null);
63818
63819 dfltDone = true;
63820 }
63821 }
63822 }
63823 } else {
63824 var templateProp = Lib.nestedProperty(templateOut, nextPath);
63825 templateProp.set(child);
63826 }
63827 }
63828}
63829
63830function getLayoutInfo(layout, path) {
63831 return PlotSchema.getLayoutValObject(
63832 layout, Lib.nestedProperty({}, path).parts
63833 );
63834}
63835
63836function getTraceInfo(trace, path) {
63837 return PlotSchema.getTraceValObject(
63838 trace, Lib.nestedProperty({}, path).parts
63839 );
63840}
63841
63842function getNextPath(parent, key, path) {
63843 var nextPath;
63844 if(!path) nextPath = key;
63845 else if(Array.isArray(parent)) nextPath = path + '[' + key + ']';
63846 else nextPath = path + '.' + key;
63847
63848 return nextPath;
63849}
63850
63851/**
63852 * validateTemplate: Test for consistency between the given figure and
63853 * a template, either already included in the figure or given separately.
63854 * Note that not every issue we identify here is necessarily a problem,
63855 * it depends on what you're using the template for.
63856 *
63857 * @param {object|DOM element} figure: the plot, with {data, layout} members,
63858 * to test the template against
63859 * @param {Optional(object)} template: the template, with its own {data, layout},
63860 * to test. If omitted, we will look for a template already attached as the
63861 * plot's `layout.template` attribute.
63862 *
63863 * @returns {array} array of error objects each containing:
63864 * - {string} code
63865 * error code ('missing', 'unused', 'reused', 'noLayout', 'noData')
63866 * - {string} msg
63867 * a full readable description of the issue.
63868 */
63869exports.validateTemplate = function(figureIn, template) {
63870 var figure = Lib.extendDeep({}, {
63871 _context: dfltConfig,
63872 data: figureIn.data,
63873 layout: figureIn.layout
63874 });
63875 var layout = figure.layout || {};
63876 if(!isPlainObject(template)) template = layout.template || {};
63877 var layoutTemplate = template.layout;
63878 var dataTemplate = template.data;
63879 var errorList = [];
63880
63881 figure.layout = layout;
63882 figure.layout.template = template;
63883 Plots.supplyDefaults(figure);
63884
63885 var fullLayout = figure._fullLayout;
63886 var fullData = figure._fullData;
63887
63888 var layoutPaths = {};
63889 function crawlLayoutForContainers(obj, paths) {
63890 for(var key in obj) {
63891 if(key.charAt(0) !== '_' && isPlainObject(obj[key])) {
63892 var baseKey = getBaseKey(key);
63893 var nextPaths = [];
63894 var i;
63895 for(i = 0; i < paths.length; i++) {
63896 nextPaths.push(getNextPath(obj, key, paths[i]));
63897 if(baseKey !== key) nextPaths.push(getNextPath(obj, baseKey, paths[i]));
63898 }
63899 for(i = 0; i < nextPaths.length; i++) {
63900 layoutPaths[nextPaths[i]] = 1;
63901 }
63902 crawlLayoutForContainers(obj[key], nextPaths);
63903 }
63904 }
63905 }
63906
63907 function crawlLayoutTemplateForContainers(obj, path) {
63908 for(var key in obj) {
63909 if(key.indexOf('defaults') === -1 && isPlainObject(obj[key])) {
63910 var nextPath = getNextPath(obj, key, path);
63911 if(layoutPaths[nextPath]) {
63912 crawlLayoutTemplateForContainers(obj[key], nextPath);
63913 } else {
63914 errorList.push({code: 'unused', path: nextPath});
63915 }
63916 }
63917 }
63918 }
63919
63920 if(!isPlainObject(layoutTemplate)) {
63921 errorList.push({code: 'layout'});
63922 } else {
63923 crawlLayoutForContainers(fullLayout, ['layout']);
63924 crawlLayoutTemplateForContainers(layoutTemplate, 'layout');
63925 }
63926
63927 if(!isPlainObject(dataTemplate)) {
63928 errorList.push({code: 'data'});
63929 } else {
63930 var typeCount = {};
63931 var traceType;
63932 for(var i = 0; i < fullData.length; i++) {
63933 var fullTrace = fullData[i];
63934 traceType = fullTrace.type;
63935 typeCount[traceType] = (typeCount[traceType] || 0) + 1;
63936 if(!fullTrace._fullInput._template) {
63937 // this takes care of the case of traceType in the data but not
63938 // the template
63939 errorList.push({
63940 code: 'missing',
63941 index: fullTrace._fullInput.index,
63942 traceType: traceType
63943 });
63944 }
63945 }
63946 for(traceType in dataTemplate) {
63947 var templateCount = dataTemplate[traceType].length;
63948 var dataCount = typeCount[traceType] || 0;
63949 if(templateCount > dataCount) {
63950 errorList.push({
63951 code: 'unused',
63952 traceType: traceType,
63953 templateCount: templateCount,
63954 dataCount: dataCount
63955 });
63956 } else if(dataCount > templateCount) {
63957 errorList.push({
63958 code: 'reused',
63959 traceType: traceType,
63960 templateCount: templateCount,
63961 dataCount: dataCount
63962 });
63963 }
63964 }
63965 }
63966
63967 // _template: false is when someone tried to modify an array item
63968 // but there was no template with matching name
63969 function crawlForMissingTemplates(obj, path) {
63970 for(var key in obj) {
63971 if(key.charAt(0) === '_') continue;
63972 var val = obj[key];
63973 var nextPath = getNextPath(obj, key, path);
63974 if(isPlainObject(val)) {
63975 if(Array.isArray(obj) && val._template === false && val.templateitemname) {
63976 errorList.push({
63977 code: 'missing',
63978 path: nextPath,
63979 templateitemname: val.templateitemname
63980 });
63981 }
63982 crawlForMissingTemplates(val, nextPath);
63983 } else if(Array.isArray(val) && hasPlainObject(val)) {
63984 crawlForMissingTemplates(val, nextPath);
63985 }
63986 }
63987 }
63988 crawlForMissingTemplates({data: fullData, layout: fullLayout}, '');
63989
63990 if(errorList.length) return errorList.map(format);
63991};
63992
63993function hasPlainObject(arr) {
63994 for(var i = 0; i < arr.length; i++) {
63995 if(isPlainObject(arr[i])) return true;
63996 }
63997}
63998
63999function format(opts) {
64000 var msg;
64001 switch(opts.code) {
64002 case 'data':
64003 msg = 'The template has no key data.';
64004 break;
64005 case 'layout':
64006 msg = 'The template has no key layout.';
64007 break;
64008 case 'missing':
64009 if(opts.path) {
64010 msg = 'There are no templates for item ' + opts.path +
64011 ' with name ' + opts.templateitemname;
64012 } else {
64013 msg = 'There are no templates for trace ' + opts.index +
64014 ', of type ' + opts.traceType + '.';
64015 }
64016 break;
64017 case 'unused':
64018 if(opts.path) {
64019 msg = 'The template item at ' + opts.path +
64020 ' was not used in constructing the plot.';
64021 } else if(opts.dataCount) {
64022 msg = 'Some of the templates of type ' + opts.traceType +
64023 ' were not used. The template has ' + opts.templateCount +
64024 ' traces, the data only has ' + opts.dataCount +
64025 ' of this type.';
64026 } else {
64027 msg = 'The template has ' + opts.templateCount +
64028 ' traces of type ' + opts.traceType +
64029 ' but there are none in the data.';
64030 }
64031 break;
64032 case 'reused':
64033 msg = 'Some of the templates of type ' + opts.traceType +
64034 ' were used more than once. The template has ' +
64035 opts.templateCount + ' traces, the data has ' +
64036 opts.dataCount + ' of this type.';
64037 break;
64038 }
64039 opts.msg = msg;
64040
64041 return opts;
64042}
64043
64044},{"../lib":287,"../plots/attributes":330,"../plots/plots":369,"./plot_config":321,"./plot_schema":322,"./plot_template":323}],326:[function(_dereq_,module,exports){
64045'use strict';
64046
64047var isNumeric = _dereq_('fast-isnumeric');
64048
64049var plotApi = _dereq_('./plot_api');
64050var plots = _dereq_('../plots/plots');
64051var Lib = _dereq_('../lib');
64052
64053var helpers = _dereq_('../snapshot/helpers');
64054var toSVG = _dereq_('../snapshot/tosvg');
64055var svgToImg = _dereq_('../snapshot/svgtoimg');
64056var version = _dereq_('../version').version;
64057
64058var attrs = {
64059 format: {
64060 valType: 'enumerated',
64061 values: ['png', 'jpeg', 'webp', 'svg', 'full-json'],
64062 dflt: 'png',
64063 },
64064 width: {
64065 valType: 'number',
64066 min: 1,
64067 },
64068 height: {
64069 valType: 'number',
64070 min: 1,
64071 },
64072 scale: {
64073 valType: 'number',
64074 min: 0,
64075 dflt: 1,
64076 },
64077 setBackground: {
64078 valType: 'any',
64079 dflt: false,
64080 },
64081 imageDataOnly: {
64082 valType: 'boolean',
64083 dflt: false,
64084 }
64085};
64086
64087/** Plotly.toImage
64088 *
64089 * @param {object | string | HTML div} gd
64090 * can either be a data/layout/config object
64091 * or an existing graph <div>
64092 * or an id to an existing graph <div>
64093 * @param {object} opts (see above)
64094 * @return {promise}
64095 */
64096function toImage(gd, opts) {
64097 opts = opts || {};
64098
64099 var data;
64100 var layout;
64101 var config;
64102 var fullLayout;
64103
64104 if(Lib.isPlainObject(gd)) {
64105 data = gd.data || [];
64106 layout = gd.layout || {};
64107 config = gd.config || {};
64108 fullLayout = {};
64109 } else {
64110 gd = Lib.getGraphDiv(gd);
64111 data = Lib.extendDeep([], gd.data);
64112 layout = Lib.extendDeep({}, gd.layout);
64113 config = gd._context;
64114 fullLayout = gd._fullLayout || {};
64115 }
64116
64117 function isImpliedOrValid(attr) {
64118 return !(attr in opts) || Lib.validate(opts[attr], attrs[attr]);
64119 }
64120
64121 if((!isImpliedOrValid('width') && opts.width !== null) ||
64122 (!isImpliedOrValid('height') && opts.height !== null)) {
64123 throw new Error('Height and width should be pixel values.');
64124 }
64125
64126 if(!isImpliedOrValid('format')) {
64127 throw new Error('Export format is not ' + Lib.join2(attrs.format.values, ', ', ' or ') + '.');
64128 }
64129
64130 var fullOpts = {};
64131
64132 function coerce(attr, dflt) {
64133 return Lib.coerce(opts, fullOpts, attrs, attr, dflt);
64134 }
64135
64136 var format = coerce('format');
64137 var width = coerce('width');
64138 var height = coerce('height');
64139 var scale = coerce('scale');
64140 var setBackground = coerce('setBackground');
64141 var imageDataOnly = coerce('imageDataOnly');
64142
64143 // put the cloned div somewhere off screen before attaching to DOM
64144 var clonedGd = document.createElement('div');
64145 clonedGd.style.position = 'absolute';
64146 clonedGd.style.left = '-5000px';
64147 document.body.appendChild(clonedGd);
64148
64149 // extend layout with image options
64150 var layoutImage = Lib.extendFlat({}, layout);
64151 if(width) {
64152 layoutImage.width = width;
64153 } else if(opts.width === null && isNumeric(fullLayout.width)) {
64154 layoutImage.width = fullLayout.width;
64155 }
64156 if(height) {
64157 layoutImage.height = height;
64158 } else if(opts.height === null && isNumeric(fullLayout.height)) {
64159 layoutImage.height = fullLayout.height;
64160 }
64161
64162 // extend config for static plot
64163 var configImage = Lib.extendFlat({}, config, {
64164 _exportedPlot: true,
64165 staticPlot: true,
64166 setBackground: setBackground
64167 });
64168
64169 var redrawFunc = helpers.getRedrawFunc(clonedGd);
64170
64171 function wait() {
64172 return new Promise(function(resolve) {
64173 setTimeout(resolve, helpers.getDelay(clonedGd._fullLayout));
64174 });
64175 }
64176
64177 function convert() {
64178 return new Promise(function(resolve, reject) {
64179 var svg = toSVG(clonedGd, format, scale);
64180 var width = clonedGd._fullLayout.width;
64181 var height = clonedGd._fullLayout.height;
64182
64183 function cleanup() {
64184 plotApi.purge(clonedGd);
64185 document.body.removeChild(clonedGd);
64186 }
64187
64188 if(format === 'full-json') {
64189 var json = plots.graphJson(clonedGd, false, 'keepdata', 'object', true, true);
64190 json.version = version;
64191 json = JSON.stringify(json);
64192 cleanup();
64193 if(imageDataOnly) {
64194 return resolve(json);
64195 } else {
64196 return resolve(helpers.encodeJSON(json));
64197 }
64198 }
64199
64200 cleanup();
64201
64202 if(format === 'svg') {
64203 if(imageDataOnly) {
64204 return resolve(svg);
64205 } else {
64206 return resolve(helpers.encodeSVG(svg));
64207 }
64208 }
64209
64210 var canvas = document.createElement('canvas');
64211 canvas.id = Lib.randstr();
64212
64213 svgToImg({
64214 format: format,
64215 width: width,
64216 height: height,
64217 scale: scale,
64218 canvas: canvas,
64219 svg: svg,
64220 // ask svgToImg to return a Promise
64221 // rather than EventEmitter
64222 // leave EventEmitter for backward
64223 // compatibility
64224 promise: true
64225 })
64226 .then(resolve)
64227 .catch(reject);
64228 });
64229 }
64230
64231 function urlToImageData(url) {
64232 if(imageDataOnly) {
64233 return url.replace(helpers.IMAGE_URL_PREFIX, '');
64234 } else {
64235 return url;
64236 }
64237 }
64238
64239 return new Promise(function(resolve, reject) {
64240 plotApi.newPlot(clonedGd, data, layoutImage, configImage)
64241 .then(redrawFunc)
64242 .then(wait)
64243 .then(convert)
64244 .then(function(url) { resolve(urlToImageData(url)); })
64245 .catch(function(err) { reject(err); });
64246 });
64247}
64248
64249module.exports = toImage;
64250
64251},{"../lib":287,"../plots/plots":369,"../snapshot/helpers":380,"../snapshot/svgtoimg":382,"../snapshot/tosvg":384,"../version":549,"./plot_api":320,"fast-isnumeric":33}],327:[function(_dereq_,module,exports){
64252'use strict';
64253
64254var Lib = _dereq_('../lib');
64255var Plots = _dereq_('../plots/plots');
64256var PlotSchema = _dereq_('./plot_schema');
64257var dfltConfig = _dereq_('./plot_config').dfltConfig;
64258
64259var isPlainObject = Lib.isPlainObject;
64260var isArray = Array.isArray;
64261var isArrayOrTypedArray = Lib.isArrayOrTypedArray;
64262
64263/**
64264 * Validate a data array and layout object.
64265 *
64266 * @param {array} data
64267 * @param {object} layout
64268 *
64269 * @return {array} array of error objects each containing:
64270 * - {string} code
64271 * error code ('object', 'array', 'schema', 'unused', 'invisible' or 'value')
64272 * - {string} container
64273 * container where the error occurs ('data' or 'layout')
64274 * - {number} trace
64275 * trace index of the 'data' container where the error occurs
64276 * - {array} path
64277 * nested path to the key that causes the error
64278 * - {string} astr
64279 * attribute string variant of 'path' compatible with Plotly.restyle and
64280 * Plotly.relayout.
64281 * - {string} msg
64282 * error message (shown in console in logger config argument is enable)
64283 */
64284module.exports = function validate(data, layout) {
64285 if(data === undefined) data = [];
64286 if(layout === undefined) layout = {};
64287
64288 var schema = PlotSchema.get();
64289 var errorList = [];
64290 var gd = {_context: Lib.extendFlat({}, dfltConfig)};
64291
64292 var dataIn, layoutIn;
64293
64294 if(isArray(data)) {
64295 gd.data = Lib.extendDeep([], data);
64296 dataIn = data;
64297 } else {
64298 gd.data = [];
64299 dataIn = [];
64300 errorList.push(format('array', 'data'));
64301 }
64302
64303 if(isPlainObject(layout)) {
64304 gd.layout = Lib.extendDeep({}, layout);
64305 layoutIn = layout;
64306 } else {
64307 gd.layout = {};
64308 layoutIn = {};
64309 if(arguments.length > 1) {
64310 errorList.push(format('object', 'layout'));
64311 }
64312 }
64313
64314 // N.B. dataIn and layoutIn are in general not the same as
64315 // gd.data and gd.layout after supplyDefaults as some attributes
64316 // in gd.data and gd.layout (still) get mutated during this step.
64317
64318 Plots.supplyDefaults(gd);
64319
64320 var dataOut = gd._fullData;
64321 var len = dataIn.length;
64322
64323 for(var i = 0; i < len; i++) {
64324 var traceIn = dataIn[i];
64325 var base = ['data', i];
64326
64327 if(!isPlainObject(traceIn)) {
64328 errorList.push(format('object', base));
64329 continue;
64330 }
64331
64332 var traceOut = dataOut[i];
64333 var traceType = traceOut.type;
64334 var traceSchema = schema.traces[traceType].attributes;
64335
64336 // PlotSchema does something fancy with trace 'type', reset it here
64337 // to make the trace schema compatible with Lib.validate.
64338 traceSchema.type = {
64339 valType: 'enumerated',
64340 values: [traceType]
64341 };
64342
64343 if(traceOut.visible === false && traceIn.visible !== false) {
64344 errorList.push(format('invisible', base));
64345 }
64346
64347 crawl(traceIn, traceOut, traceSchema, errorList, base);
64348
64349 var transformsIn = traceIn.transforms;
64350 var transformsOut = traceOut.transforms;
64351
64352 if(transformsIn) {
64353 if(!isArray(transformsIn)) {
64354 errorList.push(format('array', base, ['transforms']));
64355 }
64356
64357 base.push('transforms');
64358
64359 for(var j = 0; j < transformsIn.length; j++) {
64360 var path = ['transforms', j];
64361 var transformType = transformsIn[j].type;
64362
64363 if(!isPlainObject(transformsIn[j])) {
64364 errorList.push(format('object', base, path));
64365 continue;
64366 }
64367
64368 var transformSchema = schema.transforms[transformType] ?
64369 schema.transforms[transformType].attributes :
64370 {};
64371
64372 // add 'type' to transform schema to validate the transform type
64373 transformSchema.type = {
64374 valType: 'enumerated',
64375 values: Object.keys(schema.transforms)
64376 };
64377
64378 crawl(transformsIn[j], transformsOut[j], transformSchema, errorList, base, path);
64379 }
64380 }
64381 }
64382
64383 var layoutOut = gd._fullLayout;
64384 var layoutSchema = fillLayoutSchema(schema, dataOut);
64385
64386 crawl(layoutIn, layoutOut, layoutSchema, errorList, 'layout');
64387
64388 // return undefined if no validation errors were found
64389 return (errorList.length === 0) ? void(0) : errorList;
64390};
64391
64392function crawl(objIn, objOut, schema, list, base, path) {
64393 path = path || [];
64394
64395 var keys = Object.keys(objIn);
64396
64397 for(var i = 0; i < keys.length; i++) {
64398 var k = keys[i];
64399
64400 // transforms are handled separately
64401 if(k === 'transforms') continue;
64402
64403 var p = path.slice();
64404 p.push(k);
64405
64406 var valIn = objIn[k];
64407 var valOut = objOut[k];
64408
64409 var nestedSchema = getNestedSchema(schema, k);
64410 var nestedValType = (nestedSchema || {}).valType;
64411 var isInfoArray = nestedValType === 'info_array';
64412 var isColorscale = nestedValType === 'colorscale';
64413 var items = (nestedSchema || {}).items;
64414
64415 if(!isInSchema(schema, k)) {
64416 list.push(format('schema', base, p));
64417 } else if(isPlainObject(valIn) && isPlainObject(valOut) && nestedValType !== 'any') {
64418 crawl(valIn, valOut, nestedSchema, list, base, p);
64419 } else if(isInfoArray && isArray(valIn)) {
64420 if(valIn.length > valOut.length) {
64421 list.push(format('unused', base, p.concat(valOut.length)));
64422 }
64423 var len = valOut.length;
64424 var arrayItems = Array.isArray(items);
64425 if(arrayItems) len = Math.min(len, items.length);
64426 var m, n, item, valInPart, valOutPart;
64427 if(nestedSchema.dimensions === 2) {
64428 for(n = 0; n < len; n++) {
64429 if(isArray(valIn[n])) {
64430 if(valIn[n].length > valOut[n].length) {
64431 list.push(format('unused', base, p.concat(n, valOut[n].length)));
64432 }
64433 var len2 = valOut[n].length;
64434 for(m = 0; m < (arrayItems ? Math.min(len2, items[n].length) : len2); m++) {
64435 item = arrayItems ? items[n][m] : items;
64436 valInPart = valIn[n][m];
64437 valOutPart = valOut[n][m];
64438 if(!Lib.validate(valInPart, item)) {
64439 list.push(format('value', base, p.concat(n, m), valInPart));
64440 } else if(valOutPart !== valInPart && valOutPart !== +valInPart) {
64441 list.push(format('dynamic', base, p.concat(n, m), valInPart, valOutPart));
64442 }
64443 }
64444 } else {
64445 list.push(format('array', base, p.concat(n), valIn[n]));
64446 }
64447 }
64448 } else {
64449 for(n = 0; n < len; n++) {
64450 item = arrayItems ? items[n] : items;
64451 valInPart = valIn[n];
64452 valOutPart = valOut[n];
64453 if(!Lib.validate(valInPart, item)) {
64454 list.push(format('value', base, p.concat(n), valInPart));
64455 } else if(valOutPart !== valInPart && valOutPart !== +valInPart) {
64456 list.push(format('dynamic', base, p.concat(n), valInPart, valOutPart));
64457 }
64458 }
64459 }
64460 } else if(nestedSchema.items && !isInfoArray && isArray(valIn)) {
64461 var _nestedSchema = items[Object.keys(items)[0]];
64462 var indexList = [];
64463
64464 var j, _p;
64465
64466 // loop over valOut items while keeping track of their
64467 // corresponding input container index (given by _index)
64468 for(j = 0; j < valOut.length; j++) {
64469 var _index = valOut[j]._index || j;
64470
64471 _p = p.slice();
64472 _p.push(_index);
64473
64474 if(isPlainObject(valIn[_index]) && isPlainObject(valOut[j])) {
64475 indexList.push(_index);
64476 var valInj = valIn[_index];
64477 var valOutj = valOut[j];
64478 if(isPlainObject(valInj) && valInj.visible !== false && valOutj.visible === false) {
64479 list.push(format('invisible', base, _p));
64480 } else crawl(valInj, valOutj, _nestedSchema, list, base, _p);
64481 }
64482 }
64483
64484 // loop over valIn to determine where it went wrong for some items
64485 for(j = 0; j < valIn.length; j++) {
64486 _p = p.slice();
64487 _p.push(j);
64488
64489 if(!isPlainObject(valIn[j])) {
64490 list.push(format('object', base, _p, valIn[j]));
64491 } else if(indexList.indexOf(j) === -1) {
64492 list.push(format('unused', base, _p));
64493 }
64494 }
64495 } else if(!isPlainObject(valIn) && isPlainObject(valOut)) {
64496 list.push(format('object', base, p, valIn));
64497 } else if(!isArrayOrTypedArray(valIn) && isArrayOrTypedArray(valOut) && !isInfoArray && !isColorscale) {
64498 list.push(format('array', base, p, valIn));
64499 } else if(!(k in objOut)) {
64500 list.push(format('unused', base, p, valIn));
64501 } else if(!Lib.validate(valIn, nestedSchema)) {
64502 list.push(format('value', base, p, valIn));
64503 } else if(nestedSchema.valType === 'enumerated' &&
64504 ((nestedSchema.coerceNumber && valIn !== +valOut) || valIn !== valOut)
64505 ) {
64506 list.push(format('dynamic', base, p, valIn, valOut));
64507 }
64508 }
64509
64510 return list;
64511}
64512
64513// the 'full' layout schema depends on the traces types presents
64514function fillLayoutSchema(schema, dataOut) {
64515 var layoutSchema = schema.layout.layoutAttributes;
64516
64517 for(var i = 0; i < dataOut.length; i++) {
64518 var traceOut = dataOut[i];
64519 var traceSchema = schema.traces[traceOut.type];
64520 var traceLayoutAttr = traceSchema.layoutAttributes;
64521
64522 if(traceLayoutAttr) {
64523 if(traceOut.subplot) {
64524 Lib.extendFlat(layoutSchema[traceSchema.attributes.subplot.dflt], traceLayoutAttr);
64525 } else {
64526 Lib.extendFlat(layoutSchema, traceLayoutAttr);
64527 }
64528 }
64529 }
64530
64531 return layoutSchema;
64532}
64533
64534// validation error codes
64535var code2msgFunc = {
64536 object: function(base, astr) {
64537 var prefix;
64538
64539 if(base === 'layout' && astr === '') prefix = 'The layout argument';
64540 else if(base[0] === 'data' && astr === '') {
64541 prefix = 'Trace ' + base[1] + ' in the data argument';
64542 } else prefix = inBase(base) + 'key ' + astr;
64543
64544 return prefix + ' must be linked to an object container';
64545 },
64546 array: function(base, astr) {
64547 var prefix;
64548
64549 if(base === 'data') prefix = 'The data argument';
64550 else prefix = inBase(base) + 'key ' + astr;
64551
64552 return prefix + ' must be linked to an array container';
64553 },
64554 schema: function(base, astr) {
64555 return inBase(base) + 'key ' + astr + ' is not part of the schema';
64556 },
64557 unused: function(base, astr, valIn) {
64558 var target = isPlainObject(valIn) ? 'container' : 'key';
64559
64560 return inBase(base) + target + ' ' + astr + ' did not get coerced';
64561 },
64562 dynamic: function(base, astr, valIn, valOut) {
64563 return [
64564 inBase(base) + 'key',
64565 astr,
64566 '(set to \'' + valIn + '\')',
64567 'got reset to',
64568 '\'' + valOut + '\'',
64569 'during defaults.'
64570 ].join(' ');
64571 },
64572 invisible: function(base, astr) {
64573 return (
64574 astr ? (inBase(base) + 'item ' + astr) : ('Trace ' + base[1])
64575 ) + ' got defaulted to be not visible';
64576 },
64577 value: function(base, astr, valIn) {
64578 return [
64579 inBase(base) + 'key ' + astr,
64580 'is set to an invalid value (' + valIn + ')'
64581 ].join(' ');
64582 }
64583};
64584
64585function inBase(base) {
64586 if(isArray(base)) return 'In data trace ' + base[1] + ', ';
64587
64588 return 'In ' + base + ', ';
64589}
64590
64591function format(code, base, path, valIn, valOut) {
64592 path = path || '';
64593
64594 var container, trace;
64595
64596 // container is either 'data' or 'layout
64597 // trace is the trace index if 'data', null otherwise
64598
64599 if(isArray(base)) {
64600 container = base[0];
64601 trace = base[1];
64602 } else {
64603 container = base;
64604 trace = null;
64605 }
64606
64607 var astr = convertPathToAttributeString(path);
64608 var msg = code2msgFunc[code](base, astr, valIn, valOut);
64609
64610 // log to console if logger config option is enabled
64611 Lib.log(msg);
64612
64613 return {
64614 code: code,
64615 container: container,
64616 trace: trace,
64617 path: path,
64618 astr: astr,
64619 msg: msg
64620 };
64621}
64622
64623function isInSchema(schema, key) {
64624 var parts = splitKey(key);
64625 var keyMinusId = parts.keyMinusId;
64626 var id = parts.id;
64627
64628 if((keyMinusId in schema) && schema[keyMinusId]._isSubplotObj && id) {
64629 return true;
64630 }
64631
64632 return (key in schema);
64633}
64634
64635function getNestedSchema(schema, key) {
64636 if(key in schema) return schema[key];
64637
64638 var parts = splitKey(key);
64639
64640 return schema[parts.keyMinusId];
64641}
64642
64643var idRegex = Lib.counterRegex('([a-z]+)');
64644
64645function splitKey(key) {
64646 var idMatch = key.match(idRegex);
64647
64648 return {
64649 keyMinusId: idMatch && idMatch[1],
64650 id: idMatch && idMatch[2]
64651 };
64652}
64653
64654function convertPathToAttributeString(path) {
64655 if(!isArray(path)) return String(path);
64656
64657 var astr = '';
64658
64659 for(var i = 0; i < path.length; i++) {
64660 var p = path[i];
64661
64662 if(typeof p === 'number') {
64663 astr = astr.substr(0, astr.length - 1) + '[' + p + ']';
64664 } else {
64665 astr += p;
64666 }
64667
64668 if(i < path.length - 1) astr += '.';
64669 }
64670
64671 return astr;
64672}
64673
64674},{"../lib":287,"../plots/plots":369,"./plot_config":321,"./plot_schema":322}],328:[function(_dereq_,module,exports){
64675'use strict';
64676
64677module.exports = {
64678 mode: {
64679 valType: 'enumerated',
64680 dflt: 'afterall',
64681 values: ['immediate', 'next', 'afterall'],
64682 },
64683 direction: {
64684 valType: 'enumerated',
64685 values: ['forward', 'reverse'],
64686 dflt: 'forward',
64687 },
64688 fromcurrent: {
64689 valType: 'boolean',
64690 dflt: false,
64691 },
64692 frame: {
64693 duration: {
64694 valType: 'number',
64695 min: 0,
64696 dflt: 500,
64697 },
64698 redraw: {
64699 valType: 'boolean',
64700 dflt: true,
64701 },
64702 },
64703 transition: {
64704 duration: {
64705 valType: 'number',
64706 min: 0,
64707 dflt: 500,
64708 editType: 'none',
64709 },
64710 easing: {
64711 valType: 'enumerated',
64712 dflt: 'cubic-in-out',
64713 values: [
64714 'linear',
64715 'quad',
64716 'cubic',
64717 'sin',
64718 'exp',
64719 'circle',
64720 'elastic',
64721 'back',
64722 'bounce',
64723 'linear-in',
64724 'quad-in',
64725 'cubic-in',
64726 'sin-in',
64727 'exp-in',
64728 'circle-in',
64729 'elastic-in',
64730 'back-in',
64731 'bounce-in',
64732 'linear-out',
64733 'quad-out',
64734 'cubic-out',
64735 'sin-out',
64736 'exp-out',
64737 'circle-out',
64738 'elastic-out',
64739 'back-out',
64740 'bounce-out',
64741 'linear-in-out',
64742 'quad-in-out',
64743 'cubic-in-out',
64744 'sin-in-out',
64745 'exp-in-out',
64746 'circle-in-out',
64747 'elastic-in-out',
64748 'back-in-out',
64749 'bounce-in-out'
64750 ],
64751 editType: 'none',
64752 },
64753 ordering: {
64754 valType: 'enumerated',
64755 values: ['layout first', 'traces first'],
64756 dflt: 'layout first',
64757 editType: 'none',
64758 }
64759 }
64760};
64761
64762},{}],329:[function(_dereq_,module,exports){
64763'use strict';
64764
64765var Lib = _dereq_('../lib');
64766var Template = _dereq_('../plot_api/plot_template');
64767
64768/** Convenience wrapper for making array container logic DRY and consistent
64769 *
64770 * @param {object} parentObjIn
64771 * user input object where the container in question is linked
64772 * (i.e. either a user trace object or the user layout object)
64773 *
64774 * @param {object} parentObjOut
64775 * full object where the coerced container will be linked
64776 * (i.e. either a full trace object or the full layout object)
64777 *
64778 * @param {object} opts
64779 * options object:
64780 * - name {string}
64781 * name of the key linking the container in question
64782 * - inclusionAttr {string}
64783 * name of the item attribute for inclusion/exclusion. Default is 'visible'.
64784 * Since inclusion is true, use eg 'enabled' instead of 'disabled'.
64785 * - handleItemDefaults {function}
64786 * defaults method to be called on each item in the array container in question
64787 *
64788 * Its arguments are:
64789 * - itemIn {object} item in user layout
64790 * - itemOut {object} item in full layout
64791 * - parentObj {object} (as in closure)
64792 * - opts {object} (as in closure)
64793 * N.B.
64794 *
64795 * - opts is passed to handleItemDefaults so it can also store
64796 * links to supplementary data (e.g. fullData for layout components)
64797 *
64798 */
64799module.exports = function handleArrayContainerDefaults(parentObjIn, parentObjOut, opts) {
64800 var name = opts.name;
64801 var inclusionAttr = opts.inclusionAttr || 'visible';
64802
64803 var previousContOut = parentObjOut[name];
64804
64805 var contIn = Lib.isArrayOrTypedArray(parentObjIn[name]) ? parentObjIn[name] : [];
64806 var contOut = parentObjOut[name] = [];
64807 var templater = Template.arrayTemplater(parentObjOut, name, inclusionAttr);
64808 var i, itemOut;
64809
64810 for(i = 0; i < contIn.length; i++) {
64811 var itemIn = contIn[i];
64812
64813 if(!Lib.isPlainObject(itemIn)) {
64814 itemOut = templater.newItem({});
64815 itemOut[inclusionAttr] = false;
64816 } else {
64817 itemOut = templater.newItem(itemIn);
64818 }
64819
64820 itemOut._index = i;
64821
64822 if(itemOut[inclusionAttr] !== false) {
64823 opts.handleItemDefaults(itemIn, itemOut, parentObjOut, opts);
64824 }
64825
64826 contOut.push(itemOut);
64827 }
64828
64829 var defaultItems = templater.defaultItems();
64830 for(i = 0; i < defaultItems.length; i++) {
64831 itemOut = defaultItems[i];
64832 itemOut._index = contOut.length;
64833 opts.handleItemDefaults({}, itemOut, parentObjOut, opts, {});
64834 contOut.push(itemOut);
64835 }
64836
64837 // in case this array gets its defaults rebuilt independent of the whole layout,
64838 // relink the private keys just for this array.
64839 if(Lib.isArrayOrTypedArray(previousContOut)) {
64840 var len = Math.min(previousContOut.length, contOut.length);
64841 for(i = 0; i < len; i++) {
64842 Lib.relinkPrivateKeys(contOut[i], previousContOut[i]);
64843 }
64844 }
64845
64846 return contOut;
64847};
64848
64849},{"../lib":287,"../plot_api/plot_template":323}],330:[function(_dereq_,module,exports){
64850'use strict';
64851
64852var fontAttrs = _dereq_('./font_attributes');
64853var fxAttrs = _dereq_('../components/fx/attributes');
64854
64855module.exports = {
64856 type: {
64857 valType: 'enumerated',
64858 values: [], // listed dynamically
64859 dflt: 'scatter',
64860 editType: 'calc+clearAxisTypes',
64861 _noTemplating: true // we handle this at a higher level
64862 },
64863 visible: {
64864 valType: 'enumerated',
64865 values: [true, false, 'legendonly'],
64866 dflt: true,
64867 editType: 'calc',
64868 },
64869 showlegend: {
64870 valType: 'boolean',
64871 dflt: true,
64872 editType: 'style',
64873 },
64874 legendgroup: {
64875 valType: 'string',
64876 dflt: '',
64877 editType: 'style',
64878 },
64879 legendgrouptitle: {
64880 text: {
64881 valType: 'string',
64882 dflt: '',
64883 editType: 'style',
64884 },
64885 font: fontAttrs({
64886 editType: 'style',
64887 }),
64888 editType: 'style',
64889 },
64890 legendrank: {
64891 valType: 'number',
64892 dflt: 1000,
64893 editType: 'style',
64894 },
64895 opacity: {
64896 valType: 'number',
64897 min: 0,
64898 max: 1,
64899 dflt: 1,
64900 editType: 'style',
64901 },
64902 name: {
64903 valType: 'string',
64904 editType: 'style',
64905 },
64906 uid: {
64907 valType: 'string',
64908 editType: 'plot',
64909 anim: true,
64910 },
64911 ids: {
64912 valType: 'data_array',
64913 editType: 'calc',
64914 anim: true,
64915 },
64916 customdata: {
64917 valType: 'data_array',
64918 editType: 'calc',
64919 },
64920 meta: {
64921 valType: 'any',
64922 arrayOk: true,
64923 editType: 'plot',
64924 },
64925
64926 // N.B. these cannot be 'data_array' as they do not have the same length as
64927 // other data arrays and arrayOk attributes in general
64928 //
64929 // Maybe add another valType:
64930 // https://github.com/plotly/plotly.js/issues/1894
64931 selectedpoints: {
64932 valType: 'any',
64933 editType: 'calc',
64934 },
64935
64936 hoverinfo: {
64937 valType: 'flaglist',
64938 flags: ['x', 'y', 'z', 'text', 'name'],
64939 extras: ['all', 'none', 'skip'],
64940 arrayOk: true,
64941 dflt: 'all',
64942 editType: 'none',
64943 },
64944 hoverlabel: fxAttrs.hoverlabel,
64945 stream: {
64946 token: {
64947 valType: 'string',
64948 noBlank: true,
64949 strict: true,
64950 editType: 'calc',
64951 },
64952 maxpoints: {
64953 valType: 'number',
64954 min: 0,
64955 max: 10000,
64956 dflt: 500,
64957 editType: 'calc',
64958 },
64959 editType: 'calc'
64960 },
64961 transforms: {
64962 _isLinkedToArray: 'transform',
64963 editType: 'calc',
64964 },
64965 uirevision: {
64966 valType: 'any',
64967 editType: 'none',
64968 }
64969};
64970
64971},{"../components/fx/attributes":188,"./font_attributes":363}],331:[function(_dereq_,module,exports){
64972'use strict';
64973
64974var isNumeric = _dereq_('fast-isnumeric');
64975var Lib = _dereq_('../../lib');
64976var dateTime2ms = Lib.dateTime2ms;
64977var incrementMonth = Lib.incrementMonth;
64978var constants = _dereq_('../../constants/numerical');
64979var ONEAVGMONTH = constants.ONEAVGMONTH;
64980
64981module.exports = function alignPeriod(trace, ax, axLetter, vals) {
64982 if(ax.type !== 'date') return {vals: vals};
64983
64984 var alignment = trace[axLetter + 'periodalignment'];
64985 if(!alignment) return {vals: vals};
64986
64987 var period = trace[axLetter + 'period'];
64988 var mPeriod;
64989 if(isNumeric(period)) {
64990 period = +period;
64991 if(period <= 0) return {vals: vals};
64992 } else if(typeof period === 'string' && period.charAt(0) === 'M') {
64993 var n = +(period.substring(1));
64994 if(n > 0 && Math.round(n) === n) {
64995 mPeriod = n;
64996 } else return {vals: vals};
64997 }
64998
64999 var calendar = ax.calendar;
65000
65001 var isStart = 'start' === alignment;
65002 // var isMiddle = 'middle' === alignment;
65003 var isEnd = 'end' === alignment;
65004
65005 var period0 = trace[axLetter + 'period0'];
65006 var base = dateTime2ms(period0, calendar) || 0;
65007
65008 var newVals = [];
65009 var starts = [];
65010 var ends = [];
65011
65012 var len = vals.length;
65013 for(var i = 0; i < len; i++) {
65014 var v = vals[i];
65015
65016 var nEstimated, startTime, endTime;
65017 if(mPeriod) {
65018 // guess at how many periods away from base we are
65019 nEstimated = Math.round((v - base) / (mPeriod * ONEAVGMONTH));
65020 endTime = incrementMonth(base, mPeriod * nEstimated, calendar);
65021
65022 // iterate to get the exact bounds before and after v
65023 // there may be ways to make this faster, but most of the time
65024 // we'll only execute each loop zero or one time.
65025 while(endTime > v) {
65026 endTime = incrementMonth(endTime, -mPeriod, calendar);
65027 }
65028 while(endTime <= v) {
65029 endTime = incrementMonth(endTime, mPeriod, calendar);
65030 }
65031
65032 // now we know endTime is the boundary immediately after v
65033 // so startTime is obtained by incrementing backward one period.
65034 startTime = incrementMonth(endTime, -mPeriod, calendar);
65035 } else { // case of ms
65036 nEstimated = Math.round((v - base) / period);
65037 endTime = base + nEstimated * period;
65038
65039 while(endTime > v) {
65040 endTime -= period;
65041 }
65042 while(endTime <= v) {
65043 endTime += period;
65044 }
65045
65046 startTime = endTime - period;
65047 }
65048
65049 newVals[i] = (
65050 isStart ? startTime :
65051 isEnd ? endTime :
65052 (startTime + endTime) / 2
65053 );
65054
65055 starts[i] = startTime;
65056 ends[i] = endTime;
65057 }
65058
65059 return {
65060 vals: newVals,
65061 starts: starts,
65062 ends: ends
65063 };
65064};
65065
65066},{"../../constants/numerical":267,"../../lib":287,"fast-isnumeric":33}],332:[function(_dereq_,module,exports){
65067'use strict';
65068
65069
65070module.exports = {
65071 xaxis: {
65072 valType: 'subplotid',
65073 dflt: 'x',
65074 editType: 'calc+clearAxisTypes',
65075 },
65076 yaxis: {
65077 valType: 'subplotid',
65078 dflt: 'y',
65079 editType: 'calc+clearAxisTypes',
65080 }
65081};
65082
65083},{}],333:[function(_dereq_,module,exports){
65084'use strict';
65085
65086var d3 = _dereq_('@plotly/d3');
65087var isNumeric = _dereq_('fast-isnumeric');
65088
65089var Lib = _dereq_('../../lib');
65090var FP_SAFE = _dereq_('../../constants/numerical').FP_SAFE;
65091var Registry = _dereq_('../../registry');
65092var Drawing = _dereq_('../../components/drawing');
65093
65094var axIds = _dereq_('./axis_ids');
65095var getFromId = axIds.getFromId;
65096var isLinked = axIds.isLinked;
65097
65098module.exports = {
65099 getAutoRange: getAutoRange,
65100 makePadFn: makePadFn,
65101 doAutoRange: doAutoRange,
65102 findExtremes: findExtremes,
65103 concatExtremes: concatExtremes
65104};
65105
65106/**
65107 * getAutoRange
65108 *
65109 * Collects all _extremes values corresponding to a given axis
65110 * and computes its auto range.
65111 *
65112 * Note that getAutoRange uses return values from findExtremes.
65113 *
65114 * @param {object} gd:
65115 * graph div object with filled-in fullData and fullLayout, in particular
65116 * with filled-in '_extremes' containers:
65117 * {
65118 * val: calcdata value,
65119 * pad: extra pixels beyond this value,
65120 * extrapad: bool, does this point want 5% extra padding
65121 * }
65122 * @param {object} ax:
65123 * full axis object, in particular with filled-in '_traceIndices'
65124 * and '_annIndices' / '_shapeIndices' if applicable
65125 * @return {array}
65126 * an array of [min, max]. These are calcdata for log and category axes
65127 * and data for linear and date axes.
65128 *
65129 * TODO: we want to change log to data as well, but it's hard to do this
65130 * maintaining backward compatibility. category will always have to use calcdata
65131 * though, because otherwise values between categories (or outside all categories)
65132 * would be impossible.
65133 */
65134function getAutoRange(gd, ax) {
65135 var i, j;
65136 var newRange = [];
65137
65138 var fullLayout = gd._fullLayout;
65139 var getPadMin = makePadFn(fullLayout, ax, 0);
65140 var getPadMax = makePadFn(fullLayout, ax, 1);
65141 var extremes = concatExtremes(gd, ax);
65142 var minArray = extremes.min;
65143 var maxArray = extremes.max;
65144
65145 if(minArray.length === 0 || maxArray.length === 0) {
65146 return Lib.simpleMap(ax.range, ax.r2l);
65147 }
65148
65149 var minmin = minArray[0].val;
65150 var maxmax = maxArray[0].val;
65151
65152 for(i = 1; i < minArray.length; i++) {
65153 if(minmin !== maxmax) break;
65154 minmin = Math.min(minmin, minArray[i].val);
65155 }
65156 for(i = 1; i < maxArray.length; i++) {
65157 if(minmin !== maxmax) break;
65158 maxmax = Math.max(maxmax, maxArray[i].val);
65159 }
65160
65161 var axReverse = false;
65162
65163 if(ax.range) {
65164 var rng = Lib.simpleMap(ax.range, ax.r2l);
65165 axReverse = rng[1] < rng[0];
65166 }
65167 // one-time setting to easily reverse the axis
65168 // when plotting from code
65169 if(ax.autorange === 'reversed') {
65170 axReverse = true;
65171 ax.autorange = true;
65172 }
65173
65174 var rangeMode = ax.rangemode;
65175 var toZero = rangeMode === 'tozero';
65176 var nonNegative = rangeMode === 'nonnegative';
65177 var axLen = ax._length;
65178 // don't allow padding to reduce the data to < 10% of the length
65179 var minSpan = axLen / 10;
65180
65181 var mbest = 0;
65182 var minpt, maxpt, minbest, maxbest, dp, dv;
65183
65184 for(i = 0; i < minArray.length; i++) {
65185 minpt = minArray[i];
65186 for(j = 0; j < maxArray.length; j++) {
65187 maxpt = maxArray[j];
65188 dv = maxpt.val - minpt.val - calcBreaksLength(ax, minpt.val, maxpt.val);
65189 if(dv > 0) {
65190 dp = axLen - getPadMin(minpt) - getPadMax(maxpt);
65191 if(dp > minSpan) {
65192 if(dv / dp > mbest) {
65193 minbest = minpt;
65194 maxbest = maxpt;
65195 mbest = dv / dp;
65196 }
65197 } else if(dv / axLen > mbest) {
65198 // in case of padding longer than the axis
65199 // at least include the unpadded data values.
65200 minbest = {val: minpt.val, nopad: 1};
65201 maxbest = {val: maxpt.val, nopad: 1};
65202 mbest = dv / axLen;
65203 }
65204 }
65205 }
65206 }
65207
65208 function maximumPad(prev, pt) {
65209 return Math.max(prev, getPadMax(pt));
65210 }
65211
65212 if(minmin === maxmax) {
65213 var lower = minmin - 1;
65214 var upper = minmin + 1;
65215 if(toZero) {
65216 if(minmin === 0) {
65217 // The only value we have on this axis is 0, and we want to
65218 // autorange so zero is one end.
65219 // In principle this could be [0, 1] or [-1, 0] but usually
65220 // 'tozero' pins 0 to the low end, so follow that.
65221 newRange = [0, 1];
65222 } else {
65223 var maxPad = (minmin > 0 ? maxArray : minArray).reduce(maximumPad, 0);
65224 // we're pushing a single value away from the edge due to its
65225 // padding, with the other end clamped at zero
65226 // 0.5 means don't push it farther than the center.
65227 var rangeEnd = minmin / (1 - Math.min(0.5, maxPad / axLen));
65228 newRange = minmin > 0 ? [0, rangeEnd] : [rangeEnd, 0];
65229 }
65230 } else if(nonNegative) {
65231 newRange = [Math.max(0, lower), Math.max(1, upper)];
65232 } else {
65233 newRange = [lower, upper];
65234 }
65235 } else {
65236 if(toZero) {
65237 if(minbest.val >= 0) {
65238 minbest = {val: 0, nopad: 1};
65239 }
65240 if(maxbest.val <= 0) {
65241 maxbest = {val: 0, nopad: 1};
65242 }
65243 } else if(nonNegative) {
65244 if(minbest.val - mbest * getPadMin(minbest) < 0) {
65245 minbest = {val: 0, nopad: 1};
65246 }
65247 if(maxbest.val <= 0) {
65248 maxbest = {val: 1, nopad: 1};
65249 }
65250 }
65251
65252 // in case it changed again...
65253 mbest = (maxbest.val - minbest.val - calcBreaksLength(ax, minpt.val, maxpt.val)) /
65254 (axLen - getPadMin(minbest) - getPadMax(maxbest));
65255
65256 newRange = [
65257 minbest.val - mbest * getPadMin(minbest),
65258 maxbest.val + mbest * getPadMax(maxbest)
65259 ];
65260 }
65261
65262 // maintain reversal
65263 if(axReverse) newRange.reverse();
65264
65265 return Lib.simpleMap(newRange, ax.l2r || Number);
65266}
65267
65268// find axis rangebreaks in [v0,v1] and compute its length in value space
65269function calcBreaksLength(ax, v0, v1) {
65270 var lBreaks = 0;
65271 if(ax.rangebreaks) {
65272 var rangebreaksOut = ax.locateBreaks(v0, v1);
65273 for(var i = 0; i < rangebreaksOut.length; i++) {
65274 var brk = rangebreaksOut[i];
65275 lBreaks += brk.max - brk.min;
65276 }
65277 }
65278 return lBreaks;
65279}
65280
65281/*
65282 * calculate the pixel padding for ax._min and ax._max entries with
65283 * optional extrapad as 5% of the total axis length
65284 */
65285function makePadFn(fullLayout, ax, max) {
65286 // 5% padding for points that specify extrapad: true
65287 var extrappad = 0.05 * ax._length;
65288
65289 var anchorAxis = ax._anchorAxis || {};
65290
65291 if(
65292 (ax.ticklabelposition || '').indexOf('inside') !== -1 ||
65293 (anchorAxis.ticklabelposition || '').indexOf('inside') !== -1
65294 ) {
65295 var axReverse = ax.autorange === 'reversed';
65296 if(!axReverse) {
65297 var rng = Lib.simpleMap(ax.range, ax.r2l);
65298 axReverse = rng[1] < rng[0];
65299 }
65300 if(axReverse) max = !max;
65301 }
65302
65303 var zero = 0;
65304 if(!isLinked(fullLayout, ax._id)) {
65305 zero = padInsideLabelsOnAnchorAxis(fullLayout, ax, max);
65306 }
65307 extrappad = Math.max(zero, extrappad);
65308
65309 // domain-constrained axes: base extrappad on the unconstrained
65310 // domain so it's consistent as the domain changes
65311 if((ax.constrain === 'domain') && ax._inputDomain) {
65312 extrappad *= (ax._inputDomain[1] - ax._inputDomain[0]) /
65313 (ax.domain[1] - ax.domain[0]);
65314 }
65315
65316 return function getPad(pt) {
65317 if(pt.nopad) return 0;
65318 return pt.pad + (pt.extrapad ? extrappad : zero);
65319 };
65320}
65321
65322var TEXTPAD = 3;
65323
65324function padInsideLabelsOnAnchorAxis(fullLayout, ax, max) {
65325 var pad = 0;
65326
65327 var isX = ax._id.charAt(0) === 'x';
65328
65329 for(var subplot in fullLayout._plots) {
65330 var plotinfo = fullLayout._plots[subplot];
65331
65332 if(ax._id !== plotinfo.xaxis._id && ax._id !== plotinfo.yaxis._id) continue;
65333
65334 var anchorAxis = (isX ? plotinfo.yaxis : plotinfo.xaxis) || {};
65335
65336 if((anchorAxis.ticklabelposition || '').indexOf('inside') !== -1) {
65337 // increase padding to make more room for inside tick labels of the counter axis
65338 if((
65339 !max && (
65340 anchorAxis.side === 'left' ||
65341 anchorAxis.side === 'bottom'
65342 )
65343 ) || (
65344 max && (
65345 anchorAxis.side === 'top' ||
65346 anchorAxis.side === 'right'
65347 )
65348 )) {
65349 if(anchorAxis._vals) {
65350 var rad = Lib.deg2rad(anchorAxis._tickAngles[anchorAxis._id + 'tick'] || 0);
65351 var cosA = Math.abs(Math.cos(rad));
65352 var sinA = Math.abs(Math.sin(rad));
65353
65354 // no stashed bounding boxes - stash bounding boxes
65355 if(!anchorAxis._vals[0].bb) {
65356 var cls = anchorAxis._id + 'tick';
65357 var tickLabels = anchorAxis._selections[cls];
65358 tickLabels.each(function(d) {
65359 var thisLabel = d3.select(this);
65360 var mathjaxGroup = thisLabel.select('.text-math-group');
65361 if(mathjaxGroup.empty()) {
65362 d.bb = Drawing.bBox(thisLabel.node());
65363 }
65364 });
65365 }
65366
65367 // use bounding boxes
65368 for(var i = 0; i < anchorAxis._vals.length; i++) {
65369 var t = anchorAxis._vals[i];
65370 var bb = t.bb;
65371
65372 if(bb) {
65373 var w = 2 * TEXTPAD + bb.width;
65374 var h = 2 * TEXTPAD + bb.height;
65375
65376 pad = Math.max(pad, isX ?
65377 Math.max(w * cosA, h * sinA) :
65378 Math.max(h * cosA, w * sinA)
65379 );
65380 }
65381 }
65382 }
65383
65384 if(anchorAxis.ticks === 'inside' && anchorAxis.ticklabelposition === 'inside') {
65385 pad += anchorAxis.ticklen || 0;
65386 }
65387 }
65388 }
65389 }
65390
65391 return pad;
65392}
65393
65394function concatExtremes(gd, ax, noMatch) {
65395 var axId = ax._id;
65396 var fullData = gd._fullData;
65397 var fullLayout = gd._fullLayout;
65398 var minArray = [];
65399 var maxArray = [];
65400 var i, j, d;
65401
65402 function _concat(cont, indices) {
65403 for(i = 0; i < indices.length; i++) {
65404 var item = cont[indices[i]];
65405 var extremes = (item._extremes || {})[axId];
65406 if(item.visible === true && extremes) {
65407 for(j = 0; j < extremes.min.length; j++) {
65408 d = extremes.min[j];
65409 collapseMinArray(minArray, d.val, d.pad, {extrapad: d.extrapad});
65410 }
65411 for(j = 0; j < extremes.max.length; j++) {
65412 d = extremes.max[j];
65413 collapseMaxArray(maxArray, d.val, d.pad, {extrapad: d.extrapad});
65414 }
65415 }
65416 }
65417 }
65418
65419 _concat(fullData, ax._traceIndices);
65420 _concat(fullLayout.annotations || [], ax._annIndices || []);
65421 _concat(fullLayout.shapes || [], ax._shapeIndices || []);
65422
65423 // Include the extremes from other matched axes with this one
65424 if(ax._matchGroup && !noMatch) {
65425 for(var axId2 in ax._matchGroup) {
65426 if(axId2 !== ax._id) {
65427 var ax2 = getFromId(gd, axId2);
65428 var extremes2 = concatExtremes(gd, ax2, true);
65429 // convert padding on the second axis to the first with lenRatio
65430 var lenRatio = ax._length / ax2._length;
65431 for(j = 0; j < extremes2.min.length; j++) {
65432 d = extremes2.min[j];
65433 collapseMinArray(minArray, d.val, d.pad * lenRatio, {extrapad: d.extrapad});
65434 }
65435 for(j = 0; j < extremes2.max.length; j++) {
65436 d = extremes2.max[j];
65437 collapseMaxArray(maxArray, d.val, d.pad * lenRatio, {extrapad: d.extrapad});
65438 }
65439 }
65440 }
65441 }
65442
65443 return {min: minArray, max: maxArray};
65444}
65445
65446function doAutoRange(gd, ax, presetRange) {
65447 ax.setScale();
65448
65449 if(ax.autorange) {
65450 ax.range = presetRange ? presetRange.slice() : getAutoRange(gd, ax);
65451
65452 ax._r = ax.range.slice();
65453 ax._rl = Lib.simpleMap(ax._r, ax.r2l);
65454
65455 // doAutoRange will get called on fullLayout,
65456 // but we want to report its results back to layout
65457
65458 var axIn = ax._input;
65459
65460 // before we edit _input, store preGUI values
65461 var edits = {};
65462 edits[ax._attr + '.range'] = ax.range;
65463 edits[ax._attr + '.autorange'] = ax.autorange;
65464 Registry.call('_storeDirectGUIEdit', gd.layout, gd._fullLayout._preGUI, edits);
65465
65466 axIn.range = ax.range.slice();
65467 axIn.autorange = ax.autorange;
65468 }
65469
65470 var anchorAx = ax._anchorAxis;
65471
65472 if(anchorAx && anchorAx.rangeslider) {
65473 var axeRangeOpts = anchorAx.rangeslider[ax._name];
65474 if(axeRangeOpts) {
65475 if(axeRangeOpts.rangemode === 'auto') {
65476 axeRangeOpts.range = getAutoRange(gd, ax);
65477 }
65478 }
65479 anchorAx._input.rangeslider[ax._name] = Lib.extendFlat({}, axeRangeOpts);
65480 }
65481}
65482
65483/**
65484 * findExtremes
65485 *
65486 * Find min/max extremes of an array of coordinates on a given axis.
65487 *
65488 * Note that findExtremes is called during `calc`, when we don't yet know the axis
65489 * length; all the inputs should be based solely on the trace data, nothing
65490 * about the axis layout.
65491 *
65492 * Note that `ppad` and `vpad` as well as their asymmetric variants refer to
65493 * the before and after padding of the passed `data` array, not to the whole axis.
65494 *
65495 * @param {object} ax: full axis object
65496 * relies on
65497 * - ax.type
65498 * - ax._m (just its sign)
65499 * - ax.d2l
65500 * @param {array} data:
65501 * array of numbers (i.e. already run though ax.d2c)
65502 * @param {object} opts:
65503 * available keys are:
65504 * vpad: (number or number array) pad values (data value +-vpad)
65505 * ppad: (number or number array) pad pixels (pixel location +-ppad)
65506 * ppadplus, ppadminus, vpadplus, vpadminus:
65507 * separate padding for each side, overrides symmetric
65508 * padded: (boolean) add 5% padding to both ends
65509 * (unless one end is overridden by tozero)
65510 * tozero: (boolean) make sure to include zero if axis is linear,
65511 * and make it a tight bound if possible
65512 * vpadLinearized: (boolean) whether or not vpad (or vpadplus/vpadminus)
65513 * is linearized (for log scale axes)
65514 *
65515 * @return {object}
65516 * - min {array of objects}
65517 * - max {array of objects}
65518 * each object item has fields:
65519 * - val {number}
65520 * - pad {number}
65521 * - extrappad {number}
65522 * - opts {object}: a ref to the passed "options" object
65523 */
65524function findExtremes(ax, data, opts) {
65525 if(!opts) opts = {};
65526 if(!ax._m) ax.setScale();
65527
65528 var minArray = [];
65529 var maxArray = [];
65530
65531 var len = data.length;
65532 var extrapad = opts.padded || false;
65533 var tozero = opts.tozero && (ax.type === 'linear' || ax.type === '-');
65534 var isLog = ax.type === 'log';
65535 var hasArrayOption = false;
65536 var vpadLinearized = opts.vpadLinearized || false;
65537 var i, v, di, dmin, dmax, ppadiplus, ppadiminus, vmin, vmax;
65538
65539 function makePadAccessor(item) {
65540 if(Array.isArray(item)) {
65541 hasArrayOption = true;
65542 return function(i) { return Math.max(Number(item[i]||0), 0); };
65543 } else {
65544 var v = Math.max(Number(item||0), 0);
65545 return function() { return v; };
65546 }
65547 }
65548
65549 var ppadplus = makePadAccessor((ax._m > 0 ?
65550 opts.ppadplus : opts.ppadminus) || opts.ppad || 0);
65551 var ppadminus = makePadAccessor((ax._m > 0 ?
65552 opts.ppadminus : opts.ppadplus) || opts.ppad || 0);
65553 var vpadplus = makePadAccessor(opts.vpadplus || opts.vpad);
65554 var vpadminus = makePadAccessor(opts.vpadminus || opts.vpad);
65555
65556 if(!hasArrayOption) {
65557 // with no arrays other than `data` we don't need to consider
65558 // every point, only the extreme data points
65559 vmin = Infinity;
65560 vmax = -Infinity;
65561
65562 if(isLog) {
65563 for(i = 0; i < len; i++) {
65564 v = data[i];
65565 // data is not linearized yet so we still have to filter out negative logs
65566 if(v < vmin && v > 0) vmin = v;
65567 if(v > vmax && v < FP_SAFE) vmax = v;
65568 }
65569 } else {
65570 for(i = 0; i < len; i++) {
65571 v = data[i];
65572 if(v < vmin && v > -FP_SAFE) vmin = v;
65573 if(v > vmax && v < FP_SAFE) vmax = v;
65574 }
65575 }
65576
65577 data = [vmin, vmax];
65578 len = 2;
65579 }
65580
65581 var collapseOpts = {tozero: tozero, extrapad: extrapad};
65582
65583 function addItem(i) {
65584 di = data[i];
65585 if(!isNumeric(di)) return;
65586 ppadiplus = ppadplus(i);
65587 ppadiminus = ppadminus(i);
65588
65589 if(vpadLinearized) {
65590 dmin = ax.c2l(di) - vpadminus(i);
65591 dmax = ax.c2l(di) + vpadplus(i);
65592 } else {
65593 vmin = di - vpadminus(i);
65594 vmax = di + vpadplus(i);
65595 // special case for log axes: if vpad makes this object span
65596 // more than an order of mag, clip it to one order. This is so
65597 // we don't have non-positive errors or absurdly large lower
65598 // range due to rounding errors
65599 if(isLog && vmin < vmax / 10) vmin = vmax / 10;
65600
65601 dmin = ax.c2l(vmin);
65602 dmax = ax.c2l(vmax);
65603 }
65604
65605 if(tozero) {
65606 dmin = Math.min(0, dmin);
65607 dmax = Math.max(0, dmax);
65608 }
65609 if(goodNumber(dmin)) {
65610 collapseMinArray(minArray, dmin, ppadiminus, collapseOpts);
65611 }
65612 if(goodNumber(dmax)) {
65613 collapseMaxArray(maxArray, dmax, ppadiplus, collapseOpts);
65614 }
65615 }
65616
65617 // For efficiency covering monotonic or near-monotonic data,
65618 // check a few points at both ends first and then sweep
65619 // through the middle
65620 var iMax = Math.min(6, len);
65621 for(i = 0; i < iMax; i++) addItem(i);
65622 for(i = len - 1; i >= iMax; i--) addItem(i);
65623
65624 return {
65625 min: minArray,
65626 max: maxArray,
65627 opts: opts
65628 };
65629}
65630
65631function collapseMinArray(array, newVal, newPad, opts) {
65632 collapseArray(array, newVal, newPad, opts, lessOrEqual);
65633}
65634
65635function collapseMaxArray(array, newVal, newPad, opts) {
65636 collapseArray(array, newVal, newPad, opts, greaterOrEqual);
65637}
65638
65639/**
65640 * collapseArray
65641 *
65642 * Takes items from 'array' and compares them to 'newVal', 'newPad'.
65643 *
65644 * @param {array} array:
65645 * current set of min or max extremes
65646 * @param {number} newVal:
65647 * new value to compare against
65648 * @param {number} newPad:
65649 * pad value associated with 'newVal'
65650 * @param {object} opts:
65651 * - tozero {boolean}
65652 * - extrapad {number}
65653 * @param {function} atLeastAsExtreme:
65654 * comparison function, use
65655 * - lessOrEqual for min 'array' and
65656 * - greaterOrEqual for max 'array'
65657 *
65658 * In practice, 'array' is either
65659 * - 'extremes[ax._id].min' or
65660 * - 'extremes[ax._id].max
65661 * found in traces and layout items that affect autorange.
65662 *
65663 * Since we don't yet know the relationship between pixels and values
65664 * (that's what we're trying to figure out!) AND we don't yet know how
65665 * many pixels `extrapad` represents (it's going to be 5% of the length,
65666 * but we don't want to have to redo calc just because length changed)
65667 * two point must satisfy three criteria simultaneously for one to supersede the other:
65668 * - at least as extreme a `val`
65669 * - at least as big a `pad`
65670 * - an unpadded point cannot supersede a padded point, but any other combination can
65671 *
65672 * Then:
65673 * - If the item supersedes the new point, set includeThis false
65674 * - If the new pt supersedes the item, delete it from 'array'
65675 */
65676function collapseArray(array, newVal, newPad, opts, atLeastAsExtreme) {
65677 var tozero = opts.tozero;
65678 var extrapad = opts.extrapad;
65679 var includeThis = true;
65680
65681 for(var j = 0; j < array.length && includeThis; j++) {
65682 var v = array[j];
65683 if(atLeastAsExtreme(v.val, newVal) && v.pad >= newPad && (v.extrapad || !extrapad)) {
65684 includeThis = false;
65685 break;
65686 } else if(atLeastAsExtreme(newVal, v.val) && v.pad <= newPad && (extrapad || !v.extrapad)) {
65687 array.splice(j, 1);
65688 j--;
65689 }
65690 }
65691 if(includeThis) {
65692 var clipAtZero = (tozero && newVal === 0);
65693 array.push({
65694 val: newVal,
65695 pad: clipAtZero ? 0 : newPad,
65696 extrapad: clipAtZero ? false : extrapad
65697 });
65698 }
65699}
65700
65701// In order to stop overflow errors, don't consider points
65702// too close to the limits of js floating point
65703function goodNumber(v) {
65704 return isNumeric(v) && Math.abs(v) < FP_SAFE;
65705}
65706
65707function lessOrEqual(v0, v1) { return v0 <= v1; }
65708function greaterOrEqual(v0, v1) { return v0 >= v1; }
65709
65710},{"../../components/drawing":179,"../../constants/numerical":267,"../../lib":287,"../../registry":376,"./axis_ids":338,"@plotly/d3":20,"fast-isnumeric":33}],334:[function(_dereq_,module,exports){
65711'use strict';
65712
65713var d3 = _dereq_('@plotly/d3');
65714var isNumeric = _dereq_('fast-isnumeric');
65715var Plots = _dereq_('../../plots/plots');
65716
65717var Registry = _dereq_('../../registry');
65718var Lib = _dereq_('../../lib');
65719var strTranslate = Lib.strTranslate;
65720var svgTextUtils = _dereq_('../../lib/svg_text_utils');
65721var Titles = _dereq_('../../components/titles');
65722var Color = _dereq_('../../components/color');
65723var Drawing = _dereq_('../../components/drawing');
65724
65725var axAttrs = _dereq_('./layout_attributes');
65726var cleanTicks = _dereq_('./clean_ticks');
65727
65728var constants = _dereq_('../../constants/numerical');
65729var ONEMAXYEAR = constants.ONEMAXYEAR;
65730var ONEAVGYEAR = constants.ONEAVGYEAR;
65731var ONEMINYEAR = constants.ONEMINYEAR;
65732var ONEMAXQUARTER = constants.ONEMAXQUARTER;
65733var ONEAVGQUARTER = constants.ONEAVGQUARTER;
65734var ONEMINQUARTER = constants.ONEMINQUARTER;
65735var ONEMAXMONTH = constants.ONEMAXMONTH;
65736var ONEAVGMONTH = constants.ONEAVGMONTH;
65737var ONEMINMONTH = constants.ONEMINMONTH;
65738var ONEWEEK = constants.ONEWEEK;
65739var ONEDAY = constants.ONEDAY;
65740var HALFDAY = ONEDAY / 2;
65741var ONEHOUR = constants.ONEHOUR;
65742var ONEMIN = constants.ONEMIN;
65743var ONESEC = constants.ONESEC;
65744var MINUS_SIGN = constants.MINUS_SIGN;
65745var BADNUM = constants.BADNUM;
65746
65747var ZERO_PATH = { K: 'zeroline' };
65748var GRID_PATH = { K: 'gridline', L: 'path' };
65749var TICK_PATH = { K: 'tick', L: 'path' };
65750var TICK_TEXT = { K: 'tick', L: 'text' };
65751
65752var alignmentConstants = _dereq_('../../constants/alignment');
65753var MID_SHIFT = alignmentConstants.MID_SHIFT;
65754var CAP_SHIFT = alignmentConstants.CAP_SHIFT;
65755var LINE_SPACING = alignmentConstants.LINE_SPACING;
65756var OPPOSITE_SIDE = alignmentConstants.OPPOSITE_SIDE;
65757
65758var TEXTPAD = 3;
65759
65760var axes = module.exports = {};
65761
65762axes.setConvert = _dereq_('./set_convert');
65763var autoType = _dereq_('./axis_autotype');
65764
65765var axisIds = _dereq_('./axis_ids');
65766var idSort = axisIds.idSort;
65767var isLinked = axisIds.isLinked;
65768
65769// tight coupling to chart studio
65770axes.id2name = axisIds.id2name;
65771axes.name2id = axisIds.name2id;
65772axes.cleanId = axisIds.cleanId;
65773axes.list = axisIds.list;
65774axes.listIds = axisIds.listIds;
65775axes.getFromId = axisIds.getFromId;
65776axes.getFromTrace = axisIds.getFromTrace;
65777
65778var autorange = _dereq_('./autorange');
65779axes.getAutoRange = autorange.getAutoRange;
65780axes.findExtremes = autorange.findExtremes;
65781
65782var epsilon = 0.0001;
65783function expandRange(range) {
65784 var delta = (range[1] - range[0]) * epsilon;
65785 return [
65786 range[0] - delta,
65787 range[1] + delta
65788 ];
65789}
65790
65791/*
65792 * find the list of possible axes to reference with an xref or yref attribute
65793 * and coerce it to that list
65794 *
65795 * attr: the attribute we're generating a reference for. Should end in 'x' or 'y'
65796 * but can be prefixed, like 'ax' for annotation's arrow x
65797 * dflt: the default to coerce to, or blank to use the first axis (falling back on
65798 * extraOption if there is no axis)
65799 * extraOption: aside from existing axes with this letter, what non-axis value is allowed?
65800 * Only required if it's different from `dflt`
65801 */
65802axes.coerceRef = function(containerIn, containerOut, gd, attr, dflt, extraOption) {
65803 var axLetter = attr.charAt(attr.length - 1);
65804 var axlist = gd._fullLayout._subplots[axLetter + 'axis'];
65805 var refAttr = attr + 'ref';
65806 var attrDef = {};
65807
65808 if(!dflt) dflt = axlist[0] || (typeof extraOption === 'string' ? extraOption : extraOption[0]);
65809 if(!extraOption) extraOption = dflt;
65810 axlist = axlist.concat(axlist.map(function(x) { return x + ' domain'; }));
65811
65812 // data-ref annotations are not supported in gl2d yet
65813
65814 attrDef[refAttr] = {
65815 valType: 'enumerated',
65816 values: axlist.concat(extraOption ?
65817 (typeof extraOption === 'string' ? [extraOption] : extraOption) :
65818 []),
65819 dflt: dflt
65820 };
65821
65822 // xref, yref
65823 return Lib.coerce(containerIn, containerOut, attrDef, refAttr);
65824};
65825
65826/*
65827 * Get the type of an axis reference. This can be 'range', 'domain', or 'paper'.
65828 * This assumes ar is a valid axis reference and returns 'range' if it doesn't
65829 * match the patterns for 'paper' or 'domain'.
65830 *
65831 * ar: the axis reference string
65832 *
65833 */
65834axes.getRefType = function(ar) {
65835 if(ar === undefined) { return ar; }
65836 if(ar === 'paper') { return 'paper'; }
65837 if(ar === 'pixel') { return 'pixel'; }
65838 if(/( domain)$/.test(ar)) { return 'domain'; } else { return 'range'; }
65839};
65840
65841/*
65842 * coerce position attributes (range-type) that can be either on axes or absolute
65843 * (paper or pixel) referenced. The biggest complication here is that we don't know
65844 * before looking at the axis whether the value must be a number or not (it may be
65845 * a date string), so we can't use the regular valType='number' machinery
65846 *
65847 * axRef (string): the axis this position is referenced to, or:
65848 * paper: fraction of the plot area
65849 * pixel: pixels relative to some starting position
65850 * attr (string): the attribute in containerOut we are coercing
65851 * dflt (number): the default position, as a fraction or pixels. If the attribute
65852 * is to be axis-referenced, this will be converted to an axis data value
65853 *
65854 * Also cleans the values, since the attribute definition itself has to say
65855 * valType: 'any' to handle date axes. This allows us to accept:
65856 * - for category axes: category names, and convert them here into serial numbers.
65857 * Note that this will NOT work for axis range endpoints, because we don't know
65858 * the category list yet (it's set by ax.makeCalcdata during calc)
65859 * but it works for component (note, shape, images) positions.
65860 * - for date axes: JS Dates or milliseconds, and convert to date strings
65861 * - for other types: coerce them to numbers
65862 */
65863axes.coercePosition = function(containerOut, gd, coerce, axRef, attr, dflt) {
65864 var cleanPos, pos;
65865 var axRefType = axes.getRefType(axRef);
65866 if(axRefType !== 'range') {
65867 cleanPos = Lib.ensureNumber;
65868 pos = coerce(attr, dflt);
65869 } else {
65870 var ax = axes.getFromId(gd, axRef);
65871 dflt = ax.fraction2r(dflt);
65872 pos = coerce(attr, dflt);
65873 cleanPos = ax.cleanPos;
65874 }
65875 containerOut[attr] = cleanPos(pos);
65876};
65877
65878axes.cleanPosition = function(pos, gd, axRef) {
65879 var cleanPos = (axRef === 'paper' || axRef === 'pixel') ?
65880 Lib.ensureNumber :
65881 axes.getFromId(gd, axRef).cleanPos;
65882
65883 return cleanPos(pos);
65884};
65885
65886axes.redrawComponents = function(gd, axIds) {
65887 axIds = axIds ? axIds : axes.listIds(gd);
65888
65889 var fullLayout = gd._fullLayout;
65890
65891 function _redrawOneComp(moduleName, methodName, stashName, shortCircuit) {
65892 var method = Registry.getComponentMethod(moduleName, methodName);
65893 var stash = {};
65894
65895 for(var i = 0; i < axIds.length; i++) {
65896 var ax = fullLayout[axes.id2name(axIds[i])];
65897 var indices = ax[stashName];
65898
65899 for(var j = 0; j < indices.length; j++) {
65900 var ind = indices[j];
65901
65902 if(!stash[ind]) {
65903 method(gd, ind);
65904 stash[ind] = 1;
65905 // once is enough for images (which doesn't use the `i` arg anyway)
65906 if(shortCircuit) return;
65907 }
65908 }
65909 }
65910 }
65911
65912 // annotations and shapes 'draw' method is slow,
65913 // use the finer-grained 'drawOne' method instead
65914 _redrawOneComp('annotations', 'drawOne', '_annIndices');
65915 _redrawOneComp('shapes', 'drawOne', '_shapeIndices');
65916 _redrawOneComp('images', 'draw', '_imgIndices', true);
65917};
65918
65919var getDataConversions = axes.getDataConversions = function(gd, trace, target, targetArray) {
65920 var ax;
65921
65922 // If target points to an axis, use the type we already have for that
65923 // axis to find the data type. Otherwise use the values to autotype.
65924 var d2cTarget = (target === 'x' || target === 'y' || target === 'z') ?
65925 target :
65926 targetArray;
65927
65928 // In the case of an array target, make a mock data array
65929 // and call supplyDefaults to the data type and
65930 // setup the data-to-calc method.
65931 if(Array.isArray(d2cTarget)) {
65932 ax = {
65933 type: autoType(targetArray, undefined, {
65934 autotypenumbers: gd._fullLayout.autotypenumbers
65935 }),
65936 _categories: []
65937 };
65938 axes.setConvert(ax);
65939
65940 // build up ax._categories (usually done during ax.makeCalcdata()
65941 if(ax.type === 'category') {
65942 for(var i = 0; i < targetArray.length; i++) {
65943 ax.d2c(targetArray[i]);
65944 }
65945 }
65946 // TODO what to do for transforms?
65947 } else {
65948 ax = axes.getFromTrace(gd, trace, d2cTarget);
65949 }
65950
65951 // if 'target' has corresponding axis
65952 // -> use setConvert method
65953 if(ax) return {d2c: ax.d2c, c2d: ax.c2d};
65954
65955 // special case for 'ids'
65956 // -> cast to String
65957 if(d2cTarget === 'ids') return {d2c: toString, c2d: toString};
65958
65959 // otherwise (e.g. numeric-array of 'marker.color' or 'marker.size')
65960 // -> cast to Number
65961
65962 return {d2c: toNum, c2d: toNum};
65963};
65964
65965function toNum(v) { return +v; }
65966function toString(v) { return String(v); }
65967
65968axes.getDataToCoordFunc = function(gd, trace, target, targetArray) {
65969 return getDataConversions(gd, trace, target, targetArray).d2c;
65970};
65971
65972// get counteraxis letter for this axis (name or id)
65973// this can also be used as the id for default counter axis
65974axes.counterLetter = function(id) {
65975 var axLetter = id.charAt(0);
65976 if(axLetter === 'x') return 'y';
65977 if(axLetter === 'y') return 'x';
65978};
65979
65980// incorporate a new minimum difference and first tick into
65981// forced
65982// note that _forceTick0 is linearized, so needs to be turned into
65983// a range value for setting tick0
65984axes.minDtick = function(ax, newDiff, newFirst, allow) {
65985 // doesn't make sense to do forced min dTick on log or category axes,
65986 // and the plot itself may decide to cancel (ie non-grouped bars)
65987 if(['log', 'category', 'multicategory'].indexOf(ax.type) !== -1 || !allow) {
65988 ax._minDtick = 0;
65989 } else if(ax._minDtick === undefined) {
65990 // undefined means there's nothing there yet
65991
65992 ax._minDtick = newDiff;
65993 ax._forceTick0 = newFirst;
65994 } else if(ax._minDtick) {
65995 if((ax._minDtick / newDiff + 1e-6) % 1 < 2e-6 &&
65996 // existing minDtick is an integer multiple of newDiff
65997 // (within rounding err)
65998 // and forceTick0 can be shifted to newFirst
65999
66000 (((newFirst - ax._forceTick0) / newDiff % 1) +
66001 1.000001) % 1 < 2e-6) {
66002 ax._minDtick = newDiff;
66003 ax._forceTick0 = newFirst;
66004 } else if((newDiff / ax._minDtick + 1e-6) % 1 > 2e-6 ||
66005 // if the converse is true (newDiff is a multiple of minDtick and
66006 // newFirst can be shifted to forceTick0) then do nothing - same
66007 // forcing stands. Otherwise, cancel forced minimum
66008
66009 (((newFirst - ax._forceTick0) / ax._minDtick % 1) +
66010 1.000001) % 1 > 2e-6) {
66011 ax._minDtick = 0;
66012 }
66013 }
66014};
66015
66016// save a copy of the initial axis ranges in fullLayout
66017// use them in mode bar and dblclick events
66018axes.saveRangeInitial = function(gd, overwrite) {
66019 var axList = axes.list(gd, '', true);
66020 var hasOneAxisChanged = false;
66021
66022 for(var i = 0; i < axList.length; i++) {
66023 var ax = axList[i];
66024 var isNew = (ax._rangeInitial === undefined);
66025 var hasChanged = isNew || !(
66026 ax.range[0] === ax._rangeInitial[0] &&
66027 ax.range[1] === ax._rangeInitial[1]
66028 );
66029
66030 if((isNew && ax.autorange === false) || (overwrite && hasChanged)) {
66031 ax._rangeInitial = ax.range.slice();
66032 hasOneAxisChanged = true;
66033 }
66034 }
66035
66036 return hasOneAxisChanged;
66037};
66038
66039// save a copy of the initial spike visibility
66040axes.saveShowSpikeInitial = function(gd, overwrite) {
66041 var axList = axes.list(gd, '', true);
66042 var hasOneAxisChanged = false;
66043 var allSpikesEnabled = 'on';
66044
66045 for(var i = 0; i < axList.length; i++) {
66046 var ax = axList[i];
66047 var isNew = (ax._showSpikeInitial === undefined);
66048 var hasChanged = isNew || !(ax.showspikes === ax._showspikes);
66049
66050 if(isNew || (overwrite && hasChanged)) {
66051 ax._showSpikeInitial = ax.showspikes;
66052 hasOneAxisChanged = true;
66053 }
66054
66055 if(allSpikesEnabled === 'on' && !ax.showspikes) {
66056 allSpikesEnabled = 'off';
66057 }
66058 }
66059 gd._fullLayout._cartesianSpikesEnabled = allSpikesEnabled;
66060 return hasOneAxisChanged;
66061};
66062
66063axes.autoBin = function(data, ax, nbins, is2d, calendar, size) {
66064 var dataMin = Lib.aggNums(Math.min, null, data);
66065 var dataMax = Lib.aggNums(Math.max, null, data);
66066
66067 if(ax.type === 'category' || ax.type === 'multicategory') {
66068 return {
66069 start: dataMin - 0.5,
66070 end: dataMax + 0.5,
66071 size: Math.max(1, Math.round(size) || 1),
66072 _dataSpan: dataMax - dataMin,
66073 };
66074 }
66075
66076 if(!calendar) calendar = ax.calendar;
66077
66078 // piggyback off tick code to make "nice" bin sizes and edges
66079 var dummyAx;
66080 if(ax.type === 'log') {
66081 dummyAx = {
66082 type: 'linear',
66083 range: [dataMin, dataMax]
66084 };
66085 } else {
66086 dummyAx = {
66087 type: ax.type,
66088 range: Lib.simpleMap([dataMin, dataMax], ax.c2r, 0, calendar),
66089 calendar: calendar
66090 };
66091 }
66092 axes.setConvert(dummyAx);
66093
66094 size = size && cleanTicks.dtick(size, dummyAx.type);
66095
66096 if(size) {
66097 dummyAx.dtick = size;
66098 dummyAx.tick0 = cleanTicks.tick0(undefined, dummyAx.type, calendar);
66099 } else {
66100 var size0;
66101 if(nbins) size0 = ((dataMax - dataMin) / nbins);
66102 else {
66103 // totally auto: scale off std deviation so the highest bin is
66104 // somewhat taller than the total number of bins, but don't let
66105 // the size get smaller than the 'nice' rounded down minimum
66106 // difference between values
66107 var distinctData = Lib.distinctVals(data);
66108 var msexp = Math.pow(10, Math.floor(
66109 Math.log(distinctData.minDiff) / Math.LN10));
66110 var minSize = msexp * Lib.roundUp(
66111 distinctData.minDiff / msexp, [0.9, 1.9, 4.9, 9.9], true);
66112 size0 = Math.max(minSize, 2 * Lib.stdev(data) /
66113 Math.pow(data.length, is2d ? 0.25 : 0.4));
66114
66115 // fallback if ax.d2c output BADNUMs
66116 // e.g. when user try to plot categorical bins
66117 // on a layout.xaxis.type: 'linear'
66118 if(!isNumeric(size0)) size0 = 1;
66119 }
66120
66121 axes.autoTicks(dummyAx, size0);
66122 }
66123
66124 var finalSize = dummyAx.dtick;
66125 var binStart = axes.tickIncrement(
66126 axes.tickFirst(dummyAx), finalSize, 'reverse', calendar);
66127 var binEnd, bincount;
66128
66129 // check for too many data points right at the edges of bins
66130 // (>50% within 1% of bin edges) or all data points integral
66131 // and offset the bins accordingly
66132 if(typeof finalSize === 'number') {
66133 binStart = autoShiftNumericBins(binStart, data, dummyAx, dataMin, dataMax);
66134
66135 bincount = 1 + Math.floor((dataMax - binStart) / finalSize);
66136 binEnd = binStart + bincount * finalSize;
66137 } else {
66138 // month ticks - should be the only nonlinear kind we have at this point.
66139 // dtick (as supplied by axes.autoTick) only has nonlinear values on
66140 // date and log axes, but even if you display a histogram on a log axis
66141 // we bin it on a linear axis (which one could argue against, but that's
66142 // a separate issue)
66143 if(dummyAx.dtick.charAt(0) === 'M') {
66144 binStart = autoShiftMonthBins(binStart, data, finalSize, dataMin, calendar);
66145 }
66146
66147 // calculate the endpoint for nonlinear ticks - you have to
66148 // just increment until you're done
66149 binEnd = binStart;
66150 bincount = 0;
66151 while(binEnd <= dataMax) {
66152 binEnd = axes.tickIncrement(binEnd, finalSize, false, calendar);
66153 bincount++;
66154 }
66155 }
66156
66157 return {
66158 start: ax.c2r(binStart, 0, calendar),
66159 end: ax.c2r(binEnd, 0, calendar),
66160 size: finalSize,
66161 _dataSpan: dataMax - dataMin
66162 };
66163};
66164
66165
66166function autoShiftNumericBins(binStart, data, ax, dataMin, dataMax) {
66167 var edgecount = 0;
66168 var midcount = 0;
66169 var intcount = 0;
66170 var blankCount = 0;
66171
66172 function nearEdge(v) {
66173 // is a value within 1% of a bin edge?
66174 return (1 + (v - binStart) * 100 / ax.dtick) % 100 < 2;
66175 }
66176
66177 for(var i = 0; i < data.length; i++) {
66178 if(data[i] % 1 === 0) intcount++;
66179 else if(!isNumeric(data[i])) blankCount++;
66180
66181 if(nearEdge(data[i])) edgecount++;
66182 if(nearEdge(data[i] + ax.dtick / 2)) midcount++;
66183 }
66184 var dataCount = data.length - blankCount;
66185
66186 if(intcount === dataCount && ax.type !== 'date') {
66187 if(ax.dtick < 1) {
66188 // all integers: if bin size is <1, it's because
66189 // that was specifically requested (large nbins)
66190 // so respect that... but center the bins containing
66191 // integers on those integers
66192
66193 binStart = dataMin - 0.5 * ax.dtick;
66194 } else {
66195 // otherwise start half an integer down regardless of
66196 // the bin size, just enough to clear up endpoint
66197 // ambiguity about which integers are in which bins.
66198
66199 binStart -= 0.5;
66200 if(binStart + ax.dtick < dataMin) binStart += ax.dtick;
66201 }
66202 } else if(midcount < dataCount * 0.1) {
66203 if(edgecount > dataCount * 0.3 ||
66204 nearEdge(dataMin) || nearEdge(dataMax)) {
66205 // lots of points at the edge, not many in the middle
66206 // shift half a bin
66207 var binshift = ax.dtick / 2;
66208 binStart += (binStart + binshift < dataMin) ? binshift : -binshift;
66209 }
66210 }
66211 return binStart;
66212}
66213
66214
66215function autoShiftMonthBins(binStart, data, dtick, dataMin, calendar) {
66216 var stats = Lib.findExactDates(data, calendar);
66217 // number of data points that needs to be an exact value
66218 // to shift that increment to (near) the bin center
66219 var threshold = 0.8;
66220
66221 if(stats.exactDays > threshold) {
66222 var numMonths = Number(dtick.substr(1));
66223
66224 if((stats.exactYears > threshold) && (numMonths % 12 === 0)) {
66225 // The exact middle of a non-leap-year is 1.5 days into July
66226 // so if we start the bins here, all but leap years will
66227 // get hover-labeled as exact years.
66228 binStart = axes.tickIncrement(binStart, 'M6', 'reverse') + ONEDAY * 1.5;
66229 } else if(stats.exactMonths > threshold) {
66230 // Months are not as clean, but if we shift half the *longest*
66231 // month (31/2 days) then 31-day months will get labeled exactly
66232 // and shorter months will get labeled with the correct month
66233 // but shifted 12-36 hours into it.
66234 binStart = axes.tickIncrement(binStart, 'M1', 'reverse') + ONEDAY * 15.5;
66235 } else {
66236 // Shifting half a day is exact, but since these are month bins it
66237 // will always give a somewhat odd-looking label, until we do something
66238 // smarter like showing the bin boundaries (or the bounds of the actual
66239 // data in each bin)
66240 binStart -= HALFDAY;
66241 }
66242 var nextBinStart = axes.tickIncrement(binStart, dtick);
66243
66244 if(nextBinStart <= dataMin) return nextBinStart;
66245 }
66246 return binStart;
66247}
66248
66249// ----------------------------------------------------
66250// Ticks and grids
66251// ----------------------------------------------------
66252
66253// ensure we have tick0, dtick, and tick rounding calculated
66254axes.prepTicks = function(ax, opts) {
66255 var rng = Lib.simpleMap(ax.range, ax.r2l, undefined, undefined, opts);
66256
66257 ax._dtickInit = ax.dtick;
66258 ax._tick0Init = ax.tick0;
66259
66260 // calculate max number of (auto) ticks to display based on plot size
66261 if(ax.tickmode === 'auto' || !ax.dtick) {
66262 var nt = ax.nticks;
66263 var minPx;
66264
66265 if(!nt) {
66266 if(ax.type === 'category' || ax.type === 'multicategory') {
66267 minPx = ax.tickfont ? Lib.bigFont(ax.tickfont.size || 12) : 15;
66268 nt = ax._length / minPx;
66269 } else {
66270 minPx = ax._id.charAt(0) === 'y' ? 40 : 80;
66271 nt = Lib.constrain(ax._length / minPx, 4, 9) + 1;
66272 }
66273
66274 // radial axes span half their domain,
66275 // multiply nticks value by two to get correct number of auto ticks.
66276 if(ax._name === 'radialaxis') nt *= 2;
66277 }
66278
66279 // add a couple of extra digits for filling in ticks when we
66280 // have explicit tickvals without tick text
66281 if(ax.tickmode === 'array') nt *= 100;
66282
66283
66284 ax._roughDTick = Math.abs(rng[1] - rng[0]) / nt;
66285 axes.autoTicks(ax, ax._roughDTick);
66286
66287 // check for a forced minimum dtick
66288 if(ax._minDtick > 0 && ax.dtick < ax._minDtick * 2) {
66289 ax.dtick = ax._minDtick;
66290 ax.tick0 = ax.l2r(ax._forceTick0);
66291 }
66292 }
66293
66294 if(ax.ticklabelmode === 'period') {
66295 adjustPeriodDelta(ax);
66296 }
66297
66298 // check for missing tick0
66299 if(!ax.tick0) {
66300 ax.tick0 = (ax.type === 'date') ? '2000-01-01' : 0;
66301 }
66302
66303 // ensure we don't try to make ticks below our minimum precision
66304 // see https://github.com/plotly/plotly.js/issues/2892
66305 if(ax.type === 'date' && ax.dtick < 0.1) ax.dtick = 0.1;
66306
66307 // now figure out rounding of tick values
66308 autoTickRound(ax);
66309};
66310
66311function nMonths(dtick) {
66312 return +(dtick.substring(1));
66313}
66314
66315function adjustPeriodDelta(ax) { // adjusts ax.dtick and sets ax._definedDelta
66316 var definedDelta;
66317
66318 function mDate() {
66319 return !(
66320 isNumeric(ax.dtick) ||
66321 ax.dtick.charAt(0) !== 'M'
66322 );
66323 }
66324 var isMDate = mDate();
66325 var tickformat = axes.getTickFormat(ax);
66326 if(tickformat) {
66327 var noDtick = ax._dtickInit !== ax.dtick;
66328 if(
66329 !(/%[fLQsSMX]/.test(tickformat))
66330 // %f: microseconds as a decimal number [000000, 999999]
66331 // %L: milliseconds as a decimal number [000, 999]
66332 // %Q: milliseconds since UNIX epoch
66333 // %s: seconds since UNIX epoch
66334 // %S: second as a decimal number [00,61]
66335 // %M: minute as a decimal number [00,59]
66336 // %X: the locale’s time, such as %-I:%M:%S %p
66337 ) {
66338 if(
66339 /%[HI]/.test(tickformat)
66340 // %H: hour (24-hour clock) as a decimal number [00,23]
66341 // %I: hour (12-hour clock) as a decimal number [01,12]
66342 ) {
66343 definedDelta = ONEHOUR;
66344 if(noDtick && !isMDate && ax.dtick < ONEHOUR) ax.dtick = ONEHOUR;
66345 } else if(
66346 /%p/.test(tickformat) // %p: either AM or PM
66347 ) {
66348 definedDelta = HALFDAY;
66349 if(noDtick && !isMDate && ax.dtick < HALFDAY) ax.dtick = HALFDAY;
66350 } else if(
66351 /%[Aadejuwx]/.test(tickformat)
66352 // %A: full weekday name
66353 // %a: abbreviated weekday name
66354 // %d: zero-padded day of the month as a decimal number [01,31]
66355 // %e: space-padded day of the month as a decimal number [ 1,31]
66356 // %j: day of the year as a decimal number [001,366]
66357 // %u: Monday-based (ISO 8601) weekday as a decimal number [1,7]
66358 // %w: Sunday-based weekday as a decimal number [0,6]
66359 // %x: the locale’s date, such as %-m/%-d/%Y
66360 ) {
66361 definedDelta = ONEDAY;
66362 if(noDtick && !isMDate && ax.dtick < ONEDAY) ax.dtick = ONEDAY;
66363 } else if(
66364 /%[UVW]/.test(tickformat)
66365 // %U: Sunday-based week of the year as a decimal number [00,53]
66366 // %V: ISO 8601 week of the year as a decimal number [01, 53]
66367 // %W: Monday-based week of the year as a decimal number [00,53]
66368 ) {
66369 definedDelta = ONEWEEK;
66370 if(noDtick && !isMDate && ax.dtick < ONEWEEK) ax.dtick = ONEWEEK;
66371 } else if(
66372 /%[Bbm]/.test(tickformat)
66373 // %B: full month name
66374 // %b: abbreviated month name
66375 // %m: month as a decimal number [01,12]
66376 ) {
66377 definedDelta = ONEAVGMONTH;
66378 if(noDtick && (
66379 isMDate ? nMonths(ax.dtick) < 1 : ax.dtick < ONEMINMONTH)
66380 ) ax.dtick = 'M1';
66381 } else if(
66382 /%[q]/.test(tickformat)
66383 // %q: quarter of the year as a decimal number [1,4]
66384 ) {
66385 definedDelta = ONEAVGQUARTER;
66386 if(noDtick && (
66387 isMDate ? nMonths(ax.dtick) < 3 : ax.dtick < ONEMINQUARTER)
66388 ) ax.dtick = 'M3';
66389 } else if(
66390 /%[Yy]/.test(tickformat)
66391 // %Y: year with century as a decimal number, such as 1999
66392 // %y: year without century as a decimal number [00,99]
66393 ) {
66394 definedDelta = ONEAVGYEAR;
66395 if(noDtick && (
66396 isMDate ? nMonths(ax.dtick) < 12 : ax.dtick < ONEMINYEAR)
66397 ) ax.dtick = 'M12';
66398 }
66399 }
66400 }
66401
66402 isMDate = mDate();
66403 if(isMDate && ax.tick0 === ax._dowTick0) {
66404 // discard Sunday/Monday tweaks
66405 ax.tick0 = ax._rawTick0;
66406 }
66407
66408 ax._definedDelta = definedDelta;
66409}
66410
66411function positionPeriodTicks(tickVals, ax, definedDelta) {
66412 for(var i = 0; i < tickVals.length; i++) {
66413 var v = tickVals[i].value;
66414
66415 var a = i;
66416 var b = i + 1;
66417 if(i < tickVals.length - 1) {
66418 a = i;
66419 b = i + 1;
66420 } else if(i > 0) {
66421 a = i - 1;
66422 b = i;
66423 } else {
66424 a = i;
66425 b = i;
66426 }
66427
66428 var A = tickVals[a].value;
66429 var B = tickVals[b].value;
66430 var actualDelta = Math.abs(B - A);
66431 var delta = definedDelta || actualDelta;
66432 var periodLength = 0;
66433
66434 if(delta >= ONEMINYEAR) {
66435 if(actualDelta >= ONEMINYEAR && actualDelta <= ONEMAXYEAR) {
66436 periodLength = actualDelta;
66437 } else {
66438 periodLength = ONEAVGYEAR;
66439 }
66440 } else if(definedDelta === ONEAVGQUARTER && delta >= ONEMINQUARTER) {
66441 if(actualDelta >= ONEMINQUARTER && actualDelta <= ONEMAXQUARTER) {
66442 periodLength = actualDelta;
66443 } else {
66444 periodLength = ONEAVGQUARTER;
66445 }
66446 } else if(delta >= ONEMINMONTH) {
66447 if(actualDelta >= ONEMINMONTH && actualDelta <= ONEMAXMONTH) {
66448 periodLength = actualDelta;
66449 } else {
66450 periodLength = ONEAVGMONTH;
66451 }
66452 } else if(definedDelta === ONEWEEK && delta >= ONEWEEK) {
66453 periodLength = ONEWEEK;
66454 } else if(delta >= ONEDAY) {
66455 periodLength = ONEDAY;
66456 } else if(definedDelta === HALFDAY && delta >= HALFDAY) {
66457 periodLength = HALFDAY;
66458 } else if(definedDelta === ONEHOUR && delta >= ONEHOUR) {
66459 periodLength = ONEHOUR;
66460 }
66461
66462 var inBetween;
66463 if(periodLength >= actualDelta) {
66464 // ensure new label positions remain between ticks
66465 periodLength = actualDelta;
66466 inBetween = true;
66467 }
66468
66469 var endPeriod = v + periodLength;
66470 if(ax.rangebreaks && periodLength > 0) {
66471 var nAll = 84; // highly divisible 7 * 12
66472 var n = 0;
66473 for(var c = 0; c < nAll; c++) {
66474 var r = (c + 0.5) / nAll;
66475 if(ax.maskBreaks(v * (1 - r) + r * endPeriod) !== BADNUM) n++;
66476 }
66477 periodLength *= n / nAll;
66478
66479 if(!periodLength) {
66480 tickVals[i].drop = true;
66481 }
66482
66483 if(inBetween && actualDelta > ONEWEEK) periodLength = actualDelta; // center monthly & longer periods
66484 }
66485
66486 if(
66487 periodLength > 0 || // not instant
66488 i === 0 // taking care first tick added
66489 ) {
66490 tickVals[i].periodX = v + periodLength / 2;
66491 }
66492 }
66493}
66494
66495// calculate the ticks: text, values, positioning
66496// if ticks are set to automatic, determine the right values (tick0,dtick)
66497// in any case, set tickround to # of digits to round tick labels to,
66498// or codes to this effect for log and date scales
66499axes.calcTicks = function calcTicks(ax, opts) {
66500 axes.prepTicks(ax, opts);
66501 var rng = Lib.simpleMap(ax.range, ax.r2l, undefined, undefined, opts);
66502
66503 // now that we've figured out the auto values for formatting
66504 // in case we're missing some ticktext, we can break out for array ticks
66505 if(ax.tickmode === 'array') return arrayTicks(ax);
66506
66507 // add a tiny bit so we get ticks which may have rounded out
66508 var exRng = expandRange(rng);
66509 var startTick = exRng[0];
66510 var endTick = exRng[1];
66511 // check for reversed axis
66512 var axrev = (rng[1] < rng[0]);
66513 var minRange = Math.min(rng[0], rng[1]);
66514 var maxRange = Math.max(rng[0], rng[1]);
66515
66516 var isDLog = (ax.type === 'log') && !(isNumeric(ax.dtick) || ax.dtick.charAt(0) === 'L');
66517 var isPeriod = ax.ticklabelmode === 'period';
66518
66519 // find the first tick
66520 ax._tmin = axes.tickFirst(ax, opts);
66521
66522 // No visible ticks? Quit.
66523 // I've only seen this on category axes with all categories off the edge.
66524 if((ax._tmin < startTick) !== axrev) return [];
66525
66526 // return the full set of tick vals
66527 if(ax.type === 'category' || ax.type === 'multicategory') {
66528 endTick = (axrev) ? Math.max(-0.5, endTick) :
66529 Math.min(ax._categories.length - 0.5, endTick);
66530 }
66531
66532 var x = ax._tmin;
66533
66534 if(ax.rangebreaks && ax._tick0Init !== ax.tick0) {
66535 // adjust tick0
66536 x = moveOutsideBreak(x, ax);
66537 if(!axrev) {
66538 x = axes.tickIncrement(x, ax.dtick, !axrev, ax.calendar);
66539 }
66540 }
66541
66542 if(isPeriod) {
66543 // add one item to label period before tick0
66544 x = axes.tickIncrement(x, ax.dtick, !axrev, ax.calendar);
66545 }
66546
66547 var maxTicks = Math.max(1000, ax._length || 0);
66548 var tickVals = [];
66549 var xPrevious = null;
66550 for(;
66551 (axrev) ? (x >= endTick) : (x <= endTick);
66552 x = axes.tickIncrement(x, ax.dtick, axrev, ax.calendar)
66553 ) {
66554 if(ax.rangebreaks) {
66555 if(!axrev) {
66556 if(x < startTick) continue;
66557 if(ax.maskBreaks(x) === BADNUM && moveOutsideBreak(x, ax) >= maxRange) break;
66558 }
66559 }
66560
66561 // prevent infinite loops - no more than one tick per pixel,
66562 // and make sure each value is different from the previous
66563 if(tickVals.length > maxTicks || x === xPrevious) break;
66564 xPrevious = x;
66565
66566 var minor = false;
66567 if(isDLog && (x !== (x | 0))) {
66568 minor = true;
66569 }
66570
66571 tickVals.push({
66572 minor: minor,
66573 value: x
66574 });
66575 }
66576
66577 if(isPeriod) positionPeriodTicks(tickVals, ax, ax._definedDelta);
66578
66579 var i;
66580 if(ax.rangebreaks) {
66581 var flip = ax._id.charAt(0) === 'y';
66582
66583 var fontSize = 1; // one pixel minimum
66584 if(ax.tickmode === 'auto') {
66585 fontSize = ax.tickfont ? ax.tickfont.size : 12;
66586 }
66587
66588 var prevL = NaN;
66589 for(i = tickVals.length - 1; i > -1; i--) {
66590 if(tickVals[i].drop) {
66591 tickVals.splice(i, 1);
66592 continue;
66593 }
66594
66595 tickVals[i].value = moveOutsideBreak(tickVals[i].value, ax);
66596
66597 // avoid overlaps
66598 var l = ax.c2p(tickVals[i].value);
66599 if(flip ?
66600 (prevL > l - fontSize) :
66601 (prevL < l + fontSize)
66602 ) { // ensure one pixel minimum
66603 tickVals.splice(axrev ? i + 1 : i, 1);
66604 } else {
66605 prevL = l;
66606 }
66607 }
66608 }
66609
66610 // If same angle over a full circle, the last tick vals is a duplicate.
66611 // TODO must do something similar for angular date axes.
66612 if(isAngular(ax) && Math.abs(rng[1] - rng[0]) === 360) {
66613 tickVals.pop();
66614 }
66615
66616 // save the last tick as well as first, so we can
66617 // show the exponent only on the last one
66618 ax._tmax = (tickVals[tickVals.length - 1] || {}).value;
66619
66620 // for showing the rest of a date when the main tick label is only the
66621 // latter part: ax._prevDateHead holds what we showed most recently.
66622 // Start with it cleared and mark that we're in calcTicks (ie calculating a
66623 // whole string of these so we should care what the previous date head was!)
66624 ax._prevDateHead = '';
66625 ax._inCalcTicks = true;
66626
66627 var ticksOut = [];
66628 var t, p;
66629 for(i = 0; i < tickVals.length; i++) {
66630 var _minor = tickVals[i].minor;
66631 var _value = tickVals[i].value;
66632
66633 t = axes.tickText(
66634 ax,
66635 _value,
66636 false, // hover
66637 _minor // noSuffixPrefix
66638 );
66639
66640 p = tickVals[i].periodX;
66641 if(p !== undefined) {
66642 t.periodX = p;
66643 if(p > maxRange || p < minRange) { // hide label if outside the range
66644 if(p > maxRange) t.periodX = maxRange;
66645 if(p < minRange) t.periodX = minRange;
66646
66647 t.text = ' '; // don't use an empty string here which can confuse automargin (issue 5132)
66648 ax._prevDateHead = '';
66649 }
66650 }
66651
66652 ticksOut.push(t);
66653 }
66654
66655 ax._inCalcTicks = false;
66656
66657 return ticksOut;
66658};
66659
66660function arrayTicks(ax) {
66661 var vals = ax.tickvals;
66662 var text = ax.ticktext;
66663 var ticksOut = new Array(vals.length);
66664 var rng = Lib.simpleMap(ax.range, ax.r2l);
66665 var exRng = expandRange(rng);
66666 var tickMin = Math.min(exRng[0], exRng[1]);
66667 var tickMax = Math.max(exRng[0], exRng[1]);
66668 var j = 0;
66669
66670 // without a text array, just format the given values as any other ticks
66671 // except with more precision to the numbers
66672 if(!Array.isArray(text)) text = [];
66673
66674 // make sure showing ticks doesn't accidentally add new categories
66675 // TODO multicategory, if we allow ticktext / tickvals
66676 var tickVal2l = ax.type === 'category' ? ax.d2l_noadd : ax.d2l;
66677
66678 // array ticks on log axes always show the full number
66679 // (if no explicit ticktext overrides it)
66680 if(ax.type === 'log' && String(ax.dtick).charAt(0) !== 'L') {
66681 ax.dtick = 'L' + Math.pow(10, Math.floor(Math.min(ax.range[0], ax.range[1])) - 1);
66682 }
66683
66684 for(var i = 0; i < vals.length; i++) {
66685 var vali = tickVal2l(vals[i]);
66686 if(vali > tickMin && vali < tickMax) {
66687 if(text[i] === undefined) ticksOut[j] = axes.tickText(ax, vali);
66688 else ticksOut[j] = tickTextObj(ax, vali, String(text[i]));
66689 j++;
66690 }
66691 }
66692
66693 if(j < vals.length) ticksOut.splice(j, vals.length - j);
66694
66695 if(ax.rangebreaks) {
66696 // remove ticks falling inside rangebreaks
66697 ticksOut = ticksOut.filter(function(d) {
66698 return ax.maskBreaks(d.x) !== BADNUM;
66699 });
66700 }
66701
66702 return ticksOut;
66703}
66704
66705var roundBase10 = [2, 5, 10];
66706var roundBase24 = [1, 2, 3, 6, 12];
66707var roundBase60 = [1, 2, 5, 10, 15, 30];
66708// 2&3 day ticks are weird, but need something btwn 1&7
66709var roundDays = [1, 2, 3, 7, 14];
66710// approx. tick positions for log axes, showing all (1) and just 1, 2, 5 (2)
66711// these don't have to be exact, just close enough to round to the right value
66712var roundLog1 = [-0.046, 0, 0.301, 0.477, 0.602, 0.699, 0.778, 0.845, 0.903, 0.954, 1];
66713var roundLog2 = [-0.301, 0, 0.301, 0.699, 1];
66714// N.B. `thetaunit; 'radians' angular axes must be converted to degrees
66715var roundAngles = [15, 30, 45, 90, 180];
66716
66717function roundDTick(roughDTick, base, roundingSet) {
66718 return base * Lib.roundUp(roughDTick / base, roundingSet);
66719}
66720
66721// autoTicks: calculate best guess at pleasant ticks for this axis
66722// inputs:
66723// ax - an axis object
66724// roughDTick - rough tick spacing (to be turned into a nice round number)
66725// outputs (into ax):
66726// tick0: starting point for ticks (not necessarily on the graph)
66727// usually 0 for numeric (=10^0=1 for log) or jan 1, 2000 for dates
66728// dtick: the actual, nice round tick spacing, usually a little larger than roughDTick
66729// if the ticks are spaced linearly (linear scale, categories,
66730// log with only full powers, date ticks < month),
66731// this will just be a number
66732// months: M#
66733// years: M# where # is 12*number of years
66734// log with linear ticks: L# where # is the linear tick spacing
66735// log showing powers plus some intermediates:
66736// D1 shows all digits, D2 shows 2 and 5
66737axes.autoTicks = function(ax, roughDTick) {
66738 var base;
66739
66740 function getBase(v) {
66741 return Math.pow(v, Math.floor(Math.log(roughDTick) / Math.LN10));
66742 }
66743
66744 if(ax.type === 'date') {
66745 ax.tick0 = Lib.dateTick0(ax.calendar, 0);
66746
66747 // the criteria below are all based on the rough spacing we calculate
66748 // being > half of the final unit - so precalculate twice the rough val
66749 var roughX2 = 2 * roughDTick;
66750
66751 if(roughX2 > ONEAVGYEAR) {
66752 roughDTick /= ONEAVGYEAR;
66753 base = getBase(10);
66754 ax.dtick = 'M' + (12 * roundDTick(roughDTick, base, roundBase10));
66755 } else if(roughX2 > ONEAVGMONTH) {
66756 roughDTick /= ONEAVGMONTH;
66757 ax.dtick = 'M' + roundDTick(roughDTick, 1, roundBase24);
66758 } else if(roughX2 > ONEDAY) {
66759 ax.dtick = roundDTick(roughDTick, ONEDAY, ax._hasDayOfWeekBreaks ? [1, 2, 7, 14] : roundDays);
66760 // get week ticks on sunday
66761 // this will also move the base tick off 2000-01-01 if dtick is
66762 // 2 or 3 days... but that's a weird enough case that we'll ignore it.
66763 var tickformat = axes.getTickFormat(ax);
66764 var isPeriod = ax.ticklabelmode === 'period';
66765 if(isPeriod) ax._rawTick0 = ax.tick0;
66766
66767 if(/%[uVW]/.test(tickformat)) {
66768 ax.tick0 = Lib.dateTick0(ax.calendar, 2); // Monday
66769 } else {
66770 ax.tick0 = Lib.dateTick0(ax.calendar, 1); // Sunday
66771 }
66772
66773 if(isPeriod) ax._dowTick0 = ax.tick0;
66774 } else if(roughX2 > ONEHOUR) {
66775 ax.dtick = roundDTick(roughDTick, ONEHOUR, roundBase24);
66776 } else if(roughX2 > ONEMIN) {
66777 ax.dtick = roundDTick(roughDTick, ONEMIN, roundBase60);
66778 } else if(roughX2 > ONESEC) {
66779 ax.dtick = roundDTick(roughDTick, ONESEC, roundBase60);
66780 } else {
66781 // milliseconds
66782 base = getBase(10);
66783 ax.dtick = roundDTick(roughDTick, base, roundBase10);
66784 }
66785 } else if(ax.type === 'log') {
66786 ax.tick0 = 0;
66787 var rng = Lib.simpleMap(ax.range, ax.r2l);
66788
66789 if(roughDTick > 0.7) {
66790 // only show powers of 10
66791 ax.dtick = Math.ceil(roughDTick);
66792 } else if(Math.abs(rng[1] - rng[0]) < 1) {
66793 // span is less than one power of 10
66794 var nt = 1.5 * Math.abs((rng[1] - rng[0]) / roughDTick);
66795
66796 // ticks on a linear scale, labeled fully
66797 roughDTick = Math.abs(Math.pow(10, rng[1]) -
66798 Math.pow(10, rng[0])) / nt;
66799 base = getBase(10);
66800 ax.dtick = 'L' + roundDTick(roughDTick, base, roundBase10);
66801 } else {
66802 // include intermediates between powers of 10,
66803 // labeled with small digits
66804 // ax.dtick = "D2" (show 2 and 5) or "D1" (show all digits)
66805 ax.dtick = (roughDTick > 0.3) ? 'D2' : 'D1';
66806 }
66807 } else if(ax.type === 'category' || ax.type === 'multicategory') {
66808 ax.tick0 = 0;
66809 ax.dtick = Math.ceil(Math.max(roughDTick, 1));
66810 } else if(isAngular(ax)) {
66811 ax.tick0 = 0;
66812 base = 1;
66813 ax.dtick = roundDTick(roughDTick, base, roundAngles);
66814 } else {
66815 // auto ticks always start at 0
66816 ax.tick0 = 0;
66817 base = getBase(10);
66818 ax.dtick = roundDTick(roughDTick, base, roundBase10);
66819 }
66820
66821 // prevent infinite loops
66822 if(ax.dtick === 0) ax.dtick = 1;
66823
66824 // TODO: this is from log axis histograms with autorange off
66825 if(!isNumeric(ax.dtick) && typeof ax.dtick !== 'string') {
66826 var olddtick = ax.dtick;
66827 ax.dtick = 1;
66828 throw 'ax.dtick error: ' + String(olddtick);
66829 }
66830};
66831
66832// after dtick is already known, find tickround = precision
66833// to display in tick labels
66834// for numeric ticks, integer # digits after . to round to
66835// for date ticks, the last date part to show (y,m,d,H,M,S)
66836// or an integer # digits past seconds
66837function autoTickRound(ax) {
66838 var dtick = ax.dtick;
66839
66840 ax._tickexponent = 0;
66841 if(!isNumeric(dtick) && typeof dtick !== 'string') {
66842 dtick = 1;
66843 }
66844
66845 if(ax.type === 'category' || ax.type === 'multicategory') {
66846 ax._tickround = null;
66847 }
66848 if(ax.type === 'date') {
66849 // If tick0 is unusual, give tickround a bit more information
66850 // not necessarily *all* the information in tick0 though, if it's really odd
66851 // minimal string length for tick0: 'd' is 10, 'M' is 16, 'S' is 19
66852 // take off a leading minus (year < 0) and i (intercalary month) so length is consistent
66853 var tick0ms = ax.r2l(ax.tick0);
66854 var tick0str = ax.l2r(tick0ms).replace(/(^-|i)/g, '');
66855 var tick0len = tick0str.length;
66856
66857 if(String(dtick).charAt(0) === 'M') {
66858 // any tick0 more specific than a year: alway show the full date
66859 if(tick0len > 10 || tick0str.substr(5) !== '01-01') ax._tickround = 'd';
66860 // show the month unless ticks are full multiples of a year
66861 else ax._tickround = (+(dtick.substr(1)) % 12 === 0) ? 'y' : 'm';
66862 } else if((dtick >= ONEDAY && tick0len <= 10) || (dtick >= ONEDAY * 15)) ax._tickround = 'd';
66863 else if((dtick >= ONEMIN && tick0len <= 16) || (dtick >= ONEHOUR)) ax._tickround = 'M';
66864 else if((dtick >= ONESEC && tick0len <= 19) || (dtick >= ONEMIN)) ax._tickround = 'S';
66865 else {
66866 // tickround is a number of digits of fractional seconds
66867 // of any two adjacent ticks, at least one will have the maximum fractional digits
66868 // of all possible ticks - so take the max. length of tick0 and the next one
66869 var tick1len = ax.l2r(tick0ms + dtick).replace(/^-/, '').length;
66870 ax._tickround = Math.max(tick0len, tick1len) - 20;
66871
66872 // We shouldn't get here... but in case there's a situation I'm
66873 // not thinking of where tick0str and tick1str are identical or
66874 // something, fall back on maximum precision
66875 if(ax._tickround < 0) ax._tickround = 4;
66876 }
66877 } else if(isNumeric(dtick) || dtick.charAt(0) === 'L') {
66878 // linear or log (except D1, D2)
66879 var rng = ax.range.map(ax.r2d || Number);
66880 if(!isNumeric(dtick)) dtick = Number(dtick.substr(1));
66881 // 2 digits past largest digit of dtick
66882 ax._tickround = 2 - Math.floor(Math.log(dtick) / Math.LN10 + 0.01);
66883
66884 var maxend = Math.max(Math.abs(rng[0]), Math.abs(rng[1]));
66885 var rangeexp = Math.floor(Math.log(maxend) / Math.LN10 + 0.01);
66886 var minexponent = ax.minexponent === undefined ? 3 : ax.minexponent;
66887 if(Math.abs(rangeexp) > minexponent) {
66888 if(isSIFormat(ax.exponentformat) && !beyondSI(rangeexp)) {
66889 ax._tickexponent = 3 * Math.round((rangeexp - 1) / 3);
66890 } else ax._tickexponent = rangeexp;
66891 }
66892 } else {
66893 // D1 or D2 (log)
66894 ax._tickround = null;
66895 }
66896}
66897
66898// months and years don't have constant millisecond values
66899// (but a year is always 12 months so we only need months)
66900// log-scale ticks are also not consistently spaced, except
66901// for pure powers of 10
66902// numeric ticks always have constant differences, other datetime ticks
66903// can all be calculated as constant number of milliseconds
66904axes.tickIncrement = function(x, dtick, axrev, calendar) {
66905 var axSign = axrev ? -1 : 1;
66906
66907 // includes linear, all dates smaller than month, and pure 10^n in log
66908 if(isNumeric(dtick)) return Lib.increment(x, axSign * dtick);
66909
66910 // everything else is a string, one character plus a number
66911 var tType = dtick.charAt(0);
66912 var dtSigned = axSign * Number(dtick.substr(1));
66913
66914 // Dates: months (or years - see Lib.incrementMonth)
66915 if(tType === 'M') return Lib.incrementMonth(x, dtSigned, calendar);
66916
66917 // Log scales: Linear, Digits
66918 if(tType === 'L') return Math.log(Math.pow(10, x) + dtSigned) / Math.LN10;
66919
66920 // log10 of 2,5,10, or all digits (logs just have to be
66921 // close enough to round)
66922 if(tType === 'D') {
66923 var tickset = (dtick === 'D2') ? roundLog2 : roundLog1;
66924 var x2 = x + axSign * 0.01;
66925 var frac = Lib.roundUp(Lib.mod(x2, 1), tickset, axrev);
66926
66927 return Math.floor(x2) +
66928 Math.log(d3.round(Math.pow(10, frac), 1)) / Math.LN10;
66929 }
66930
66931 throw 'unrecognized dtick ' + String(dtick);
66932};
66933
66934// calculate the first tick on an axis
66935axes.tickFirst = function(ax, opts) {
66936 var r2l = ax.r2l || Number;
66937 var rng = Lib.simpleMap(ax.range, r2l, undefined, undefined, opts);
66938 var axrev = rng[1] < rng[0];
66939 var sRound = axrev ? Math.floor : Math.ceil;
66940 // add a tiny extra bit to make sure we get ticks
66941 // that may have been rounded out
66942 var r0 = expandRange(rng)[0];
66943 var dtick = ax.dtick;
66944 var tick0 = r2l(ax.tick0);
66945
66946 if(isNumeric(dtick)) {
66947 var tmin = sRound((r0 - tick0) / dtick) * dtick + tick0;
66948
66949 // make sure no ticks outside the category list
66950 if(ax.type === 'category' || ax.type === 'multicategory') {
66951 tmin = Lib.constrain(tmin, 0, ax._categories.length - 1);
66952 }
66953 return tmin;
66954 }
66955
66956 var tType = dtick.charAt(0);
66957 var dtNum = Number(dtick.substr(1));
66958
66959 // Dates: months (or years)
66960 if(tType === 'M') {
66961 var cnt = 0;
66962 var t0 = tick0;
66963 var t1, mult, newDTick;
66964
66965 // This algorithm should work for *any* nonlinear (but close to linear!)
66966 // tick spacing. Limit to 10 iterations, for gregorian months it's normally <=3.
66967 while(cnt < 10) {
66968 t1 = axes.tickIncrement(t0, dtick, axrev, ax.calendar);
66969 if((t1 - r0) * (t0 - r0) <= 0) {
66970 // t1 and t0 are on opposite sides of r0! we've succeeded!
66971 if(axrev) return Math.min(t0, t1);
66972 return Math.max(t0, t1);
66973 }
66974 mult = (r0 - ((t0 + t1) / 2)) / (t1 - t0);
66975 newDTick = tType + ((Math.abs(Math.round(mult)) || 1) * dtNum);
66976 t0 = axes.tickIncrement(t0, newDTick, mult < 0 ? !axrev : axrev, ax.calendar);
66977 cnt++;
66978 }
66979 Lib.error('tickFirst did not converge', ax);
66980 return t0;
66981 } else if(tType === 'L') {
66982 // Log scales: Linear, Digits
66983
66984 return Math.log(sRound(
66985 (Math.pow(10, r0) - tick0) / dtNum) * dtNum + tick0) / Math.LN10;
66986 } else if(tType === 'D') {
66987 var tickset = (dtick === 'D2') ? roundLog2 : roundLog1;
66988 var frac = Lib.roundUp(Lib.mod(r0, 1), tickset, axrev);
66989
66990 return Math.floor(r0) +
66991 Math.log(d3.round(Math.pow(10, frac), 1)) / Math.LN10;
66992 } else throw 'unrecognized dtick ' + String(dtick);
66993};
66994
66995// draw the text for one tick.
66996// px,py are the location on gd.paper
66997// prefix is there so the x axis ticks can be dropped a line
66998// ax is the axis layout, x is the tick value
66999// hover is a (truthy) flag for whether to show numbers with a bit
67000// more precision for hovertext
67001axes.tickText = function(ax, x, hover, noSuffixPrefix) {
67002 var out = tickTextObj(ax, x);
67003 var arrayMode = ax.tickmode === 'array';
67004 var extraPrecision = hover || arrayMode;
67005 var axType = ax.type;
67006 // TODO multicategory, if we allow ticktext / tickvals
67007 var tickVal2l = axType === 'category' ? ax.d2l_noadd : ax.d2l;
67008 var i;
67009
67010 if(arrayMode && Array.isArray(ax.ticktext)) {
67011 var rng = Lib.simpleMap(ax.range, ax.r2l);
67012 var minDiff = (Math.abs(rng[1] - rng[0]) - (ax._lBreaks || 0)) / 10000;
67013
67014 for(i = 0; i < ax.ticktext.length; i++) {
67015 if(Math.abs(x - tickVal2l(ax.tickvals[i])) < minDiff) break;
67016 }
67017 if(i < ax.ticktext.length) {
67018 out.text = String(ax.ticktext[i]);
67019 return out;
67020 }
67021 }
67022
67023 function isHidden(showAttr) {
67024 if(showAttr === undefined) return true;
67025 if(hover) return showAttr === 'none';
67026
67027 var firstOrLast = {
67028 first: ax._tmin,
67029 last: ax._tmax
67030 }[showAttr];
67031
67032 return showAttr !== 'all' && x !== firstOrLast;
67033 }
67034
67035 var hideexp = hover ?
67036 'never' :
67037 ax.exponentformat !== 'none' && isHidden(ax.showexponent) ? 'hide' : '';
67038
67039 if(axType === 'date') formatDate(ax, out, hover, extraPrecision);
67040 else if(axType === 'log') formatLog(ax, out, hover, extraPrecision, hideexp);
67041 else if(axType === 'category') formatCategory(ax, out);
67042 else if(axType === 'multicategory') formatMultiCategory(ax, out, hover);
67043 else if(isAngular(ax)) formatAngle(ax, out, hover, extraPrecision, hideexp);
67044 else formatLinear(ax, out, hover, extraPrecision, hideexp);
67045
67046 // add prefix and suffix
67047 if(!noSuffixPrefix) {
67048 if(ax.tickprefix && !isHidden(ax.showtickprefix)) out.text = ax.tickprefix + out.text;
67049 if(ax.ticksuffix && !isHidden(ax.showticksuffix)) out.text += ax.ticksuffix;
67050 }
67051
67052 // Setup ticks and grid lines boundaries
67053 // at 1/2 a 'category' to the left/bottom
67054 if(ax.tickson === 'boundaries' || ax.showdividers) {
67055 var inbounds = function(v) {
67056 var p = ax.l2p(v);
67057 return p >= 0 && p <= ax._length ? v : null;
67058 };
67059
67060 out.xbnd = [
67061 inbounds(out.x - 0.5),
67062 inbounds(out.x + ax.dtick - 0.5)
67063 ];
67064 }
67065
67066 return out;
67067};
67068
67069/**
67070 * create text for a hover label on this axis, with special handling of
67071 * log axes (where negative values can't be displayed but can appear in hover text)
67072 *
67073 * @param {object} ax: the axis to format text for
67074 * @param {number or array of numbers} values: calcdata value(s) to format
67075 * @param {Optional(string)} hoverformat: trace (x|y)hoverformat to override axis.hoverformat
67076 *
67077 * @returns {string} `val` formatted as a string appropriate to this axis, or
67078 * first value and second value as a range (ie '<val1> - <val2>') if the second value is provided and
67079 * it's different from the first value.
67080 */
67081axes.hoverLabelText = function(ax, values, hoverformat) {
67082 if(hoverformat) ax = Lib.extendFlat({}, ax, {hoverformat: hoverformat});
67083
67084 var val = Array.isArray(values) ? values[0] : values;
67085 var val2 = Array.isArray(values) ? values[1] : undefined;
67086 if(val2 !== undefined && val2 !== val) {
67087 return (
67088 axes.hoverLabelText(ax, val, hoverformat) + ' - ' +
67089 axes.hoverLabelText(ax, val2, hoverformat)
67090 );
67091 }
67092
67093 var logOffScale = (ax.type === 'log' && val <= 0);
67094 var tx = axes.tickText(ax, ax.c2l(logOffScale ? -val : val), 'hover').text;
67095
67096 if(logOffScale) {
67097 return val === 0 ? '0' : MINUS_SIGN + tx;
67098 }
67099
67100 // TODO: should we do something special if the axis calendar and
67101 // the data calendar are different? Somehow display both dates with
67102 // their system names? Right now it will just display in the axis calendar
67103 // but users could add the other one as text.
67104 return tx;
67105};
67106
67107function tickTextObj(ax, x, text) {
67108 var tf = ax.tickfont || {};
67109
67110 return {
67111 x: x,
67112 dx: 0,
67113 dy: 0,
67114 text: text || '',
67115 fontSize: tf.size,
67116 font: tf.family,
67117 fontColor: tf.color
67118 };
67119}
67120
67121function formatDate(ax, out, hover, extraPrecision) {
67122 var tr = ax._tickround;
67123 var fmt = (hover && ax.hoverformat) || axes.getTickFormat(ax);
67124
67125 if(extraPrecision) {
67126 // second or sub-second precision: extra always shows max digits.
67127 // for other fields, extra precision just adds one field.
67128 if(isNumeric(tr)) tr = 4;
67129 else tr = {y: 'm', m: 'd', d: 'M', M: 'S', S: 4}[tr];
67130 }
67131
67132 var dateStr = Lib.formatDate(out.x, fmt, tr, ax._dateFormat, ax.calendar, ax._extraFormat);
67133 var headStr;
67134
67135 var splitIndex = dateStr.indexOf('\n');
67136 if(splitIndex !== -1) {
67137 headStr = dateStr.substr(splitIndex + 1);
67138 dateStr = dateStr.substr(0, splitIndex);
67139 }
67140
67141 if(extraPrecision) {
67142 // if extraPrecision led to trailing zeros, strip them off
67143 // actually, this can lead to removing even more zeros than
67144 // in the original rounding, but that's fine because in these
67145 // contexts uniformity is not so important (if there's even
67146 // anything to be uniform with!)
67147
67148 // can we remove the whole time part?
67149 if(dateStr === '00:00:00' || dateStr === '00:00') {
67150 dateStr = headStr;
67151 headStr = '';
67152 } else if(dateStr.length === 8) {
67153 // strip off seconds if they're zero (zero fractional seconds
67154 // are already omitted)
67155 // but we never remove minutes and leave just hours
67156 dateStr = dateStr.replace(/:00$/, '');
67157 }
67158 }
67159
67160 if(headStr) {
67161 if(hover) {
67162 // hover puts it all on one line, so headPart works best up front
67163 // except for year headPart: turn this into "Jan 1, 2000" etc.
67164 if(tr === 'd') dateStr += ', ' + headStr;
67165 else dateStr = headStr + (dateStr ? ', ' + dateStr : '');
67166 } else {
67167 if(
67168 !ax._inCalcTicks ||
67169 ax._prevDateHead !== headStr
67170 ) {
67171 ax._prevDateHead = headStr;
67172 dateStr += '<br>' + headStr;
67173 } else {
67174 var isInside = insideTicklabelposition(ax);
67175 var side = ax._realSide || ax.side; // polar mocks the side of the radial axis
67176 if(
67177 (!isInside && side === 'top') ||
67178 (isInside && side === 'bottom')
67179 ) {
67180 dateStr += '<br> ';
67181 }
67182 }
67183 }
67184 }
67185
67186 out.text = dateStr;
67187}
67188
67189function formatLog(ax, out, hover, extraPrecision, hideexp) {
67190 var dtick = ax.dtick;
67191 var x = out.x;
67192 var tickformat = ax.tickformat;
67193 var dtChar0 = typeof dtick === 'string' && dtick.charAt(0);
67194
67195 if(hideexp === 'never') {
67196 // If this is a hover label, then we must *never* hide the exponent
67197 // for the sake of display, which could give the wrong value by
67198 // potentially many orders of magnitude. If hideexp was 'never', then
67199 // it's now succeeded by preventing the other condition from automating
67200 // this choice. Thus we can unset it so that the axis formatting takes
67201 // precedence.
67202 hideexp = '';
67203 }
67204
67205 if(extraPrecision && (dtChar0 !== 'L')) {
67206 dtick = 'L3';
67207 dtChar0 = 'L';
67208 }
67209
67210 if(tickformat || (dtChar0 === 'L')) {
67211 out.text = numFormat(Math.pow(10, x), ax, hideexp, extraPrecision);
67212 } else if(isNumeric(dtick) || ((dtChar0 === 'D') && (Lib.mod(x + 0.01, 1) < 0.1))) {
67213 var p = Math.round(x);
67214 var absP = Math.abs(p);
67215 var exponentFormat = ax.exponentformat;
67216 if(exponentFormat === 'power' || (isSIFormat(exponentFormat) && beyondSI(p))) {
67217 if(p === 0) out.text = 1;
67218 else if(p === 1) out.text = '10';
67219 else out.text = '10<sup>' + (p > 1 ? '' : MINUS_SIGN) + absP + '</sup>';
67220
67221 out.fontSize *= 1.25;
67222 } else if((exponentFormat === 'e' || exponentFormat === 'E') && absP > 2) {
67223 out.text = '1' + exponentFormat + (p > 0 ? '+' : MINUS_SIGN) + absP;
67224 } else {
67225 out.text = numFormat(Math.pow(10, x), ax, '', 'fakehover');
67226 if(dtick === 'D1' && ax._id.charAt(0) === 'y') {
67227 out.dy -= out.fontSize / 6;
67228 }
67229 }
67230 } else if(dtChar0 === 'D') {
67231 out.text = String(Math.round(Math.pow(10, Lib.mod(x, 1))));
67232 out.fontSize *= 0.75;
67233 } else throw 'unrecognized dtick ' + String(dtick);
67234
67235 // if 9's are printed on log scale, move the 10's away a bit
67236 if(ax.dtick === 'D1') {
67237 var firstChar = String(out.text).charAt(0);
67238 if(firstChar === '0' || firstChar === '1') {
67239 if(ax._id.charAt(0) === 'y') {
67240 out.dx -= out.fontSize / 4;
67241 } else {
67242 out.dy += out.fontSize / 2;
67243 out.dx += (ax.range[1] > ax.range[0] ? 1 : -1) *
67244 out.fontSize * (x < 0 ? 0.5 : 0.25);
67245 }
67246 }
67247 }
67248}
67249
67250function formatCategory(ax, out) {
67251 var tt = ax._categories[Math.round(out.x)];
67252 if(tt === undefined) tt = '';
67253 out.text = String(tt);
67254}
67255
67256function formatMultiCategory(ax, out, hover) {
67257 var v = Math.round(out.x);
67258 var cats = ax._categories[v] || [];
67259 var tt = cats[1] === undefined ? '' : String(cats[1]);
67260 var tt2 = cats[0] === undefined ? '' : String(cats[0]);
67261
67262 if(hover) {
67263 // TODO is this what we want?
67264 out.text = tt2 + ' - ' + tt;
67265 } else {
67266 // setup for secondary labels
67267 out.text = tt;
67268 out.text2 = tt2;
67269 }
67270}
67271
67272function formatLinear(ax, out, hover, extraPrecision, hideexp) {
67273 if(hideexp === 'never') {
67274 // If this is a hover label, then we must *never* hide the exponent
67275 // for the sake of display, which could give the wrong value by
67276 // potentially many orders of magnitude. If hideexp was 'never', then
67277 // it's now succeeded by preventing the other condition from automating
67278 // this choice. Thus we can unset it so that the axis formatting takes
67279 // precedence.
67280 hideexp = '';
67281 } else if(ax.showexponent === 'all' && Math.abs(out.x / ax.dtick) < 1e-6) {
67282 // don't add an exponent to zero if we're showing all exponents
67283 // so the only reason you'd show an exponent on zero is if it's the
67284 // ONLY tick to get an exponent (first or last)
67285 hideexp = 'hide';
67286 }
67287 out.text = numFormat(out.x, ax, hideexp, extraPrecision);
67288}
67289
67290function formatAngle(ax, out, hover, extraPrecision, hideexp) {
67291 if(ax.thetaunit === 'radians' && !hover) {
67292 var num = out.x / 180;
67293
67294 if(num === 0) {
67295 out.text = '0';
67296 } else {
67297 var frac = num2frac(num);
67298
67299 if(frac[1] >= 100) {
67300 out.text = numFormat(Lib.deg2rad(out.x), ax, hideexp, extraPrecision);
67301 } else {
67302 var isNeg = out.x < 0;
67303
67304 if(frac[1] === 1) {
67305 if(frac[0] === 1) out.text = 'π';
67306 else out.text = frac[0] + 'π';
67307 } else {
67308 out.text = [
67309 '<sup>', frac[0], '</sup>',
67310 '⁄',
67311 '<sub>', frac[1], '</sub>',
67312 'π'
67313 ].join('');
67314 }
67315
67316 if(isNeg) out.text = MINUS_SIGN + out.text;
67317 }
67318 }
67319 } else {
67320 out.text = numFormat(out.x, ax, hideexp, extraPrecision);
67321 }
67322}
67323
67324// inspired by
67325// https://github.com/yisibl/num2fraction/blob/master/index.js
67326function num2frac(num) {
67327 function almostEq(a, b) {
67328 return Math.abs(a - b) <= 1e-6;
67329 }
67330
67331 function findGCD(a, b) {
67332 return almostEq(b, 0) ? a : findGCD(b, a % b);
67333 }
67334
67335 function findPrecision(n) {
67336 var e = 1;
67337 while(!almostEq(Math.round(n * e) / e, n)) {
67338 e *= 10;
67339 }
67340 return e;
67341 }
67342
67343 var precision = findPrecision(num);
67344 var number = num * precision;
67345 var gcd = Math.abs(findGCD(number, precision));
67346
67347 return [
67348 // numerator
67349 Math.round(number / gcd),
67350 // denominator
67351 Math.round(precision / gcd)
67352 ];
67353}
67354
67355// format a number (tick value) according to the axis settings
67356// new, more reliable procedure than d3.round or similar:
67357// add half the rounding increment, then stringify and truncate
67358// also automatically switch to sci. notation
67359var SIPREFIXES = ['f', 'p', 'n', 'μ', 'm', '', 'k', 'M', 'G', 'T'];
67360
67361function isSIFormat(exponentFormat) {
67362 return exponentFormat === 'SI' || exponentFormat === 'B';
67363}
67364
67365// are we beyond the range of common SI prefixes?
67366// 10^-16 -> 1x10^-16
67367// 10^-15 -> 1f
67368// ...
67369// 10^14 -> 100T
67370// 10^15 -> 1x10^15
67371// 10^16 -> 1x10^16
67372function beyondSI(exponent) {
67373 return exponent > 14 || exponent < -15;
67374}
67375
67376function numFormat(v, ax, fmtoverride, hover) {
67377 var isNeg = v < 0;
67378 // max number of digits past decimal point to show
67379 var tickRound = ax._tickround;
67380 var exponentFormat = fmtoverride || ax.exponentformat || 'B';
67381 var exponent = ax._tickexponent;
67382 var tickformat = axes.getTickFormat(ax);
67383 var separatethousands = ax.separatethousands;
67384
67385 // special case for hover: set exponent just for this value, and
67386 // add a couple more digits of precision over tick labels
67387 if(hover) {
67388 // make a dummy axis obj to get the auto rounding and exponent
67389 var ah = {
67390 exponentformat: exponentFormat,
67391 minexponent: ax.minexponent,
67392 dtick: ax.showexponent === 'none' ? ax.dtick :
67393 (isNumeric(v) ? Math.abs(v) || 1 : 1),
67394 // if not showing any exponents, don't change the exponent
67395 // from what we calculate
67396 range: ax.showexponent === 'none' ? ax.range.map(ax.r2d) : [0, v || 1]
67397 };
67398 autoTickRound(ah);
67399 tickRound = (Number(ah._tickround) || 0) + 4;
67400 exponent = ah._tickexponent;
67401 if(ax.hoverformat) tickformat = ax.hoverformat;
67402 }
67403
67404 if(tickformat) return ax._numFormat(tickformat)(v).replace(/-/g, MINUS_SIGN);
67405
67406 // 'epsilon' - rounding increment
67407 var e = Math.pow(10, -tickRound) / 2;
67408
67409 // exponentFormat codes:
67410 // 'e' (1.2e+6, default)
67411 // 'E' (1.2E+6)
67412 // 'SI' (1.2M)
67413 // 'B' (same as SI except 10^9=B not G)
67414 // 'none' (1200000)
67415 // 'power' (1.2x10^6)
67416 // 'hide' (1.2, use 3rd argument=='hide' to eg
67417 // only show exponent on last tick)
67418 if(exponentFormat === 'none') exponent = 0;
67419
67420 // take the sign out, put it back manually at the end
67421 // - makes cases easier
67422 v = Math.abs(v);
67423 if(v < e) {
67424 // 0 is just 0, but may get exponent if it's the last tick
67425 v = '0';
67426 isNeg = false;
67427 } else {
67428 v += e;
67429 // take out a common exponent, if any
67430 if(exponent) {
67431 v *= Math.pow(10, -exponent);
67432 tickRound += exponent;
67433 }
67434 // round the mantissa
67435 if(tickRound === 0) v = String(Math.floor(v));
67436 else if(tickRound < 0) {
67437 v = String(Math.round(v));
67438 v = v.substr(0, v.length + tickRound);
67439 for(var i = tickRound; i < 0; i++) v += '0';
67440 } else {
67441 v = String(v);
67442 var dp = v.indexOf('.') + 1;
67443 if(dp) v = v.substr(0, dp + tickRound).replace(/\.?0+$/, '');
67444 }
67445 // insert appropriate decimal point and thousands separator
67446 v = Lib.numSeparate(v, ax._separators, separatethousands);
67447 }
67448
67449 // add exponent
67450 if(exponent && exponentFormat !== 'hide') {
67451 if(isSIFormat(exponentFormat) && beyondSI(exponent)) exponentFormat = 'power';
67452
67453 var signedExponent;
67454 if(exponent < 0) signedExponent = MINUS_SIGN + -exponent;
67455 else if(exponentFormat !== 'power') signedExponent = '+' + exponent;
67456 else signedExponent = String(exponent);
67457
67458 if(exponentFormat === 'e' || exponentFormat === 'E') {
67459 v += exponentFormat + signedExponent;
67460 } else if(exponentFormat === 'power') {
67461 v += '×10<sup>' + signedExponent + '</sup>';
67462 } else if(exponentFormat === 'B' && exponent === 9) {
67463 v += 'B';
67464 } else if(isSIFormat(exponentFormat)) {
67465 v += SIPREFIXES[exponent / 3 + 5];
67466 }
67467 }
67468
67469 // put sign back in and return
67470 // replace standard minus character (which is technically a hyphen)
67471 // with a true minus sign
67472 if(isNeg) return MINUS_SIGN + v;
67473 return v;
67474}
67475
67476axes.getTickFormat = function(ax) {
67477 var i;
67478
67479 function convertToMs(dtick) {
67480 return typeof dtick !== 'string' ? dtick : Number(dtick.replace('M', '')) * ONEAVGMONTH;
67481 }
67482
67483 function compareLogTicks(left, right) {
67484 var priority = ['L', 'D'];
67485 if(typeof left === typeof right) {
67486 if(typeof left === 'number') {
67487 return left - right;
67488 } else {
67489 var leftPriority = priority.indexOf(left.charAt(0));
67490 var rightPriority = priority.indexOf(right.charAt(0));
67491 if(leftPriority === rightPriority) {
67492 return Number(left.replace(/(L|D)/g, '')) - Number(right.replace(/(L|D)/g, ''));
67493 } else {
67494 return leftPriority - rightPriority;
67495 }
67496 }
67497 } else {
67498 return typeof left === 'number' ? 1 : -1;
67499 }
67500 }
67501
67502 function isProperStop(dtick, range, convert) {
67503 var convertFn = convert || function(x) { return x;};
67504 var leftDtick = range[0];
67505 var rightDtick = range[1];
67506 return ((!leftDtick && typeof leftDtick !== 'number') || convertFn(leftDtick) <= convertFn(dtick)) &&
67507 ((!rightDtick && typeof rightDtick !== 'number') || convertFn(rightDtick) >= convertFn(dtick));
67508 }
67509
67510 function isProperLogStop(dtick, range) {
67511 var isLeftDtickNull = range[0] === null;
67512 var isRightDtickNull = range[1] === null;
67513 var isDtickInRangeLeft = compareLogTicks(dtick, range[0]) >= 0;
67514 var isDtickInRangeRight = compareLogTicks(dtick, range[1]) <= 0;
67515 return (isLeftDtickNull || isDtickInRangeLeft) && (isRightDtickNull || isDtickInRangeRight);
67516 }
67517
67518 var tickstop, stopi;
67519 if(ax.tickformatstops && ax.tickformatstops.length > 0) {
67520 switch(ax.type) {
67521 case 'date':
67522 case 'linear': {
67523 for(i = 0; i < ax.tickformatstops.length; i++) {
67524 stopi = ax.tickformatstops[i];
67525 if(stopi.enabled && isProperStop(ax.dtick, stopi.dtickrange, convertToMs)) {
67526 tickstop = stopi;
67527 break;
67528 }
67529 }
67530 break;
67531 }
67532 case 'log': {
67533 for(i = 0; i < ax.tickformatstops.length; i++) {
67534 stopi = ax.tickformatstops[i];
67535 if(stopi.enabled && isProperLogStop(ax.dtick, stopi.dtickrange)) {
67536 tickstop = stopi;
67537 break;
67538 }
67539 }
67540 break;
67541 }
67542 default:
67543 }
67544 }
67545 return tickstop ? tickstop.value : ax.tickformat;
67546};
67547
67548// getSubplots - extract all subplot IDs we need
67549// as an array of items like 'xy', 'x2y', 'x2y2'...
67550// sorted by x (x,x2,x3...) then y
67551// optionally restrict to only subplots containing axis object ax
67552//
67553// NOTE: this is currently only used OUTSIDE plotly.js (toolpanel, webapp)
67554// ideally we get rid of it there (or just copy this there) and remove it here
67555axes.getSubplots = function(gd, ax) {
67556 var subplotObj = gd._fullLayout._subplots;
67557 var allSubplots = subplotObj.cartesian.concat(subplotObj.gl2d || []);
67558
67559 var out = ax ? axes.findSubplotsWithAxis(allSubplots, ax) : allSubplots;
67560
67561 out.sort(function(a, b) {
67562 var aParts = a.substr(1).split('y');
67563 var bParts = b.substr(1).split('y');
67564
67565 if(aParts[0] === bParts[0]) return +aParts[1] - +bParts[1];
67566 return +aParts[0] - +bParts[0];
67567 });
67568
67569 return out;
67570};
67571
67572// find all subplots with axis 'ax'
67573// NOTE: this is only used in axes.getSubplots (only used outside plotly.js) and
67574// gl2d/convert (where it restricts axis subplots to only those with gl2d)
67575axes.findSubplotsWithAxis = function(subplots, ax) {
67576 var axMatch = new RegExp(
67577 (ax._id.charAt(0) === 'x') ? ('^' + ax._id + 'y') : (ax._id + '$')
67578 );
67579 var subplotsWithAx = [];
67580
67581 for(var i = 0; i < subplots.length; i++) {
67582 var sp = subplots[i];
67583 if(axMatch.test(sp)) subplotsWithAx.push(sp);
67584 }
67585
67586 return subplotsWithAx;
67587};
67588
67589// makeClipPaths: prepare clipPaths for all single axes and all possible xy pairings
67590axes.makeClipPaths = function(gd) {
67591 var fullLayout = gd._fullLayout;
67592
67593 // for more info: https://github.com/plotly/plotly.js/issues/2595
67594 if(fullLayout._hasOnlyLargeSploms) return;
67595
67596 var fullWidth = {_offset: 0, _length: fullLayout.width, _id: ''};
67597 var fullHeight = {_offset: 0, _length: fullLayout.height, _id: ''};
67598 var xaList = axes.list(gd, 'x', true);
67599 var yaList = axes.list(gd, 'y', true);
67600 var clipList = [];
67601 var i, j;
67602
67603 for(i = 0; i < xaList.length; i++) {
67604 clipList.push({x: xaList[i], y: fullHeight});
67605 for(j = 0; j < yaList.length; j++) {
67606 if(i === 0) clipList.push({x: fullWidth, y: yaList[j]});
67607 clipList.push({x: xaList[i], y: yaList[j]});
67608 }
67609 }
67610
67611 // selectors don't work right with camelCase tags,
67612 // have to use class instead
67613 // https://groups.google.com/forum/#!topic/d3-js/6EpAzQ2gU9I
67614 var axClips = fullLayout._clips.selectAll('.axesclip')
67615 .data(clipList, function(d) { return d.x._id + d.y._id; });
67616
67617 axClips.enter().append('clipPath')
67618 .classed('axesclip', true)
67619 .attr('id', function(d) { return 'clip' + fullLayout._uid + d.x._id + d.y._id; })
67620 .append('rect');
67621
67622 axClips.exit().remove();
67623
67624 axClips.each(function(d) {
67625 d3.select(this).select('rect').attr({
67626 x: d.x._offset || 0,
67627 y: d.y._offset || 0,
67628 width: d.x._length || 1,
67629 height: d.y._length || 1
67630 });
67631 });
67632};
67633
67634/**
67635 * Main multi-axis drawing routine!
67636 *
67637 * @param {DOM element} gd : graph div
67638 * @param {string or array of strings} arg : polymorphic argument
67639 * @param {object} opts:
67640 * - @param {boolean} skipTitle : optional flag to skip axis title draw/update
67641 *
67642 * Signature 1: Axes.draw(gd, 'redraw')
67643 * use this to clear and redraw all axes on graph
67644 *
67645 * Signature 2: Axes.draw(gd, '')
67646 * use this to draw all axes on graph w/o the selectAll().remove()
67647 * of the 'redraw' signature
67648 *
67649 * Signature 3: Axes.draw(gd, [axId, axId2, ...])
67650 * where the items are axis id string,
67651 * use this to update multiple axes in one call
67652 *
67653 * N.B draw updates:
67654 * - ax._r (stored range for use by zoom/pan)
67655 * - ax._rl (stored linearized range for use by zoom/pan)
67656 */
67657axes.draw = function(gd, arg, opts) {
67658 var fullLayout = gd._fullLayout;
67659
67660 if(arg === 'redraw') {
67661 fullLayout._paper.selectAll('g.subplot').each(function(d) {
67662 var id = d[0];
67663 var plotinfo = fullLayout._plots[id];
67664 if(plotinfo) {
67665 var xa = plotinfo.xaxis;
67666 var ya = plotinfo.yaxis;
67667
67668 plotinfo.xaxislayer.selectAll('.' + xa._id + 'tick').remove();
67669 plotinfo.yaxislayer.selectAll('.' + ya._id + 'tick').remove();
67670 plotinfo.xaxislayer.selectAll('.' + xa._id + 'tick2').remove();
67671 plotinfo.yaxislayer.selectAll('.' + ya._id + 'tick2').remove();
67672 plotinfo.xaxislayer.selectAll('.' + xa._id + 'divider').remove();
67673 plotinfo.yaxislayer.selectAll('.' + ya._id + 'divider').remove();
67674
67675 if(plotinfo.gridlayer) plotinfo.gridlayer.selectAll('path').remove();
67676 if(plotinfo.zerolinelayer) plotinfo.zerolinelayer.selectAll('path').remove();
67677
67678 fullLayout._infolayer.select('.g-' + xa._id + 'title').remove();
67679 fullLayout._infolayer.select('.g-' + ya._id + 'title').remove();
67680 }
67681 });
67682 }
67683
67684 var axList = (!arg || arg === 'redraw') ? axes.listIds(gd) : arg;
67685
67686 return Lib.syncOrAsync(axList.map(function(axId) {
67687 return function() {
67688 if(!axId) return;
67689
67690 var ax = axes.getFromId(gd, axId);
67691 var axDone = axes.drawOne(gd, ax, opts);
67692
67693 ax._r = ax.range.slice();
67694 ax._rl = Lib.simpleMap(ax._r, ax.r2l);
67695
67696 return axDone;
67697 };
67698 }));
67699};
67700
67701/**
67702 * Draw one cartesian axis
67703 *
67704 * @param {DOM element} gd
67705 * @param {object} ax (full) axis object
67706 * @param {object} opts
67707 * - @param {boolean} skipTitle (set to true to skip axis title draw call)
67708 *
67709 * Depends on:
67710 * - ax._mainSubplot (from linkSubplots)
67711 * - ax._mainAxis
67712 * - ax._anchorAxis
67713 * - ax._subplotsWith
67714 * - ax._counterDomainMin, ax._counterDomainMax (optionally, from linkSubplots)
67715 * - ax._tickAngles (on redraw only, old value relinked during supplyDefaults)
67716 * - ax._mainLinePosition (from lsInner)
67717 * - ax._mainMirrorPosition
67718 * - ax._linepositions
67719 *
67720 * Fills in:
67721 * - ax._vals:
67722 * - ax._gridVals:
67723 * - ax._selections:
67724 * - ax._tickAngles:
67725 * - ax._depth (when required only):
67726 * - and calls ax.setScale
67727 */
67728axes.drawOne = function(gd, ax, opts) {
67729 opts = opts || {};
67730
67731 var i, sp, plotinfo;
67732
67733 ax.setScale();
67734
67735 var fullLayout = gd._fullLayout;
67736 var axId = ax._id;
67737 var axLetter = axId.charAt(0);
67738 var counterLetter = axes.counterLetter(axId);
67739 var mainPlotinfo = fullLayout._plots[ax._mainSubplot];
67740
67741 // this happens when updating matched group with 'missing' axes
67742 if(!mainPlotinfo) return;
67743
67744 var mainAxLayer = mainPlotinfo[axLetter + 'axislayer'];
67745 var mainLinePosition = ax._mainLinePosition;
67746 var mainMirrorPosition = ax._mainMirrorPosition;
67747
67748 var vals = ax._vals = axes.calcTicks(ax);
67749
67750 // Add a couple of axis properties that should cause us to recreate
67751 // elements. Used in d3 data function.
67752 var axInfo = [ax.mirror, mainLinePosition, mainMirrorPosition].join('_');
67753 for(i = 0; i < vals.length; i++) {
67754 vals[i].axInfo = axInfo;
67755 }
67756
67757 // stash selections to avoid DOM queries e.g.
67758 // - stash tickLabels selection, so that drawTitle can use it to scoot title
67759 ax._selections = {};
67760 // stash tick angle (including the computed 'auto' values) per tick-label class
67761 // linkup 'previous' tick angles on redraws
67762 if(ax._tickAngles) ax._prevTickAngles = ax._tickAngles;
67763 ax._tickAngles = {};
67764 // measure [in px] between axis position and outward-most part of bounding box
67765 // (touching either the tick label or ticks)
67766 // depth can be expansive to compute, so we only do so when required
67767 ax._depth = null;
67768
67769 // calcLabelLevelBbox can be expensive,
67770 // so make sure to not call it twice during the same Axes.drawOne call
67771 // by stashing label-level bounding boxes per tick-label class
67772 var llbboxes = {};
67773 function getLabelLevelBbox(suffix) {
67774 var cls = axId + (suffix || 'tick');
67775 if(!llbboxes[cls]) llbboxes[cls] = calcLabelLevelBbox(ax, cls);
67776 return llbboxes[cls];
67777 }
67778
67779 if(!ax.visible) return;
67780
67781 var transTickFn = axes.makeTransTickFn(ax);
67782 var transTickLabelFn = axes.makeTransTickLabelFn(ax);
67783
67784 var tickVals;
67785 // We remove zero lines, grid lines, and inside ticks if they're within 1px of the end
67786 // The key case here is removing zero lines when the axis bound is zero
67787 var valsClipped;
67788
67789 var insideTicks = ax.ticks === 'inside';
67790 var outsideTicks = ax.ticks === 'outside';
67791
67792 if(ax.tickson === 'boundaries') {
67793 var boundaryVals = getBoundaryVals(ax, vals);
67794 valsClipped = axes.clipEnds(ax, boundaryVals);
67795 tickVals = insideTicks ? valsClipped : boundaryVals;
67796 } else {
67797 valsClipped = axes.clipEnds(ax, vals);
67798 tickVals = (insideTicks && ax.ticklabelmode !== 'period') ? valsClipped : vals;
67799 }
67800
67801 var gridVals = ax._gridVals = valsClipped;
67802 var dividerVals = getDividerVals(ax, vals);
67803
67804 if(!fullLayout._hasOnlyLargeSploms) {
67805 var subplotsWithAx = ax._subplotsWith;
67806
67807 // keep track of which subplots (by main counter axis) we've already
67808 // drawn grids for, so we don't overdraw overlaying subplots
67809 var finishedGrids = {};
67810
67811 for(i = 0; i < subplotsWithAx.length; i++) {
67812 sp = subplotsWithAx[i];
67813 plotinfo = fullLayout._plots[sp];
67814
67815 var counterAxis = plotinfo[counterLetter + 'axis'];
67816 var mainCounterID = counterAxis._mainAxis._id;
67817 if(finishedGrids[mainCounterID]) continue;
67818 finishedGrids[mainCounterID] = 1;
67819
67820 var gridPath = axLetter === 'x' ?
67821 'M0,' + counterAxis._offset + 'v' + counterAxis._length :
67822 'M' + counterAxis._offset + ',0h' + counterAxis._length;
67823
67824 axes.drawGrid(gd, ax, {
67825 vals: gridVals,
67826 counterAxis: counterAxis,
67827 layer: plotinfo.gridlayer.select('.' + axId),
67828 path: gridPath,
67829 transFn: transTickFn
67830 });
67831 axes.drawZeroLine(gd, ax, {
67832 counterAxis: counterAxis,
67833 layer: plotinfo.zerolinelayer,
67834 path: gridPath,
67835 transFn: transTickFn
67836 });
67837 }
67838 }
67839
67840 var tickSigns = axes.getTickSigns(ax);
67841 var tickSubplots = [];
67842
67843 if(ax.ticks) {
67844 var mainTickPath = axes.makeTickPath(ax, mainLinePosition, tickSigns[2]);
67845 var mirrorTickPath;
67846 var fullTickPath;
67847 if(ax._anchorAxis && ax.mirror && ax.mirror !== true) {
67848 mirrorTickPath = axes.makeTickPath(ax, mainMirrorPosition, tickSigns[3]);
67849 fullTickPath = mainTickPath + mirrorTickPath;
67850 } else {
67851 mirrorTickPath = '';
67852 fullTickPath = mainTickPath;
67853 }
67854
67855 var tickPath;
67856 if(ax.showdividers && outsideTicks && ax.tickson === 'boundaries') {
67857 var dividerLookup = {};
67858 for(i = 0; i < dividerVals.length; i++) {
67859 dividerLookup[dividerVals[i].x] = 1;
67860 }
67861 tickPath = function(d) {
67862 return dividerLookup[d.x] ? mirrorTickPath : fullTickPath;
67863 };
67864 } else {
67865 tickPath = fullTickPath;
67866 }
67867
67868 axes.drawTicks(gd, ax, {
67869 vals: tickVals,
67870 layer: mainAxLayer,
67871 path: tickPath,
67872 transFn: transTickFn
67873 });
67874
67875 if(ax.mirror === 'allticks') {
67876 tickSubplots = Object.keys(ax._linepositions || {});
67877 }
67878 }
67879
67880 for(i = 0; i < tickSubplots.length; i++) {
67881 sp = tickSubplots[i];
67882 plotinfo = fullLayout._plots[sp];
67883 // [bottom or left, top or right], free and main are handled above
67884 var linepositions = ax._linepositions[sp] || [];
67885 var spTickPath = axes.makeTickPath(ax, linepositions[0], tickSigns[0]) +
67886 axes.makeTickPath(ax, linepositions[1], tickSigns[1]);
67887
67888 axes.drawTicks(gd, ax, {
67889 vals: tickVals,
67890 layer: plotinfo[axLetter + 'axislayer'],
67891 path: spTickPath,
67892 transFn: transTickFn
67893 });
67894 }
67895
67896 var seq = [];
67897
67898 // tick labels - for now just the main labels.
67899 // TODO: mirror labels, esp for subplots
67900
67901 seq.push(function() {
67902 return axes.drawLabels(gd, ax, {
67903 vals: vals,
67904 layer: mainAxLayer,
67905 plotinfo: plotinfo,
67906 transFn: transTickLabelFn,
67907 labelFns: axes.makeLabelFns(ax, mainLinePosition)
67908 });
67909 });
67910
67911 if(ax.type === 'multicategory') {
67912 var pad = {x: 2, y: 10}[axLetter];
67913
67914 seq.push(function() {
67915 var bboxKey = {x: 'height', y: 'width'}[axLetter];
67916 var standoff = getLabelLevelBbox()[bboxKey] + pad +
67917 (ax._tickAngles[axId + 'tick'] ? ax.tickfont.size * LINE_SPACING : 0);
67918
67919 return axes.drawLabels(gd, ax, {
67920 vals: getSecondaryLabelVals(ax, vals),
67921 layer: mainAxLayer,
67922 cls: axId + 'tick2',
67923 repositionOnUpdate: true,
67924 secondary: true,
67925 transFn: transTickFn,
67926 labelFns: axes.makeLabelFns(ax, mainLinePosition + standoff * tickSigns[4])
67927 });
67928 });
67929
67930 seq.push(function() {
67931 ax._depth = tickSigns[4] * (getLabelLevelBbox('tick2')[ax.side] - mainLinePosition);
67932
67933 return drawDividers(gd, ax, {
67934 vals: dividerVals,
67935 layer: mainAxLayer,
67936 path: axes.makeTickPath(ax, mainLinePosition, tickSigns[4], ax._depth),
67937 transFn: transTickFn
67938 });
67939 });
67940 } else if(ax.title.hasOwnProperty('standoff')) {
67941 seq.push(function() {
67942 ax._depth = tickSigns[4] * (getLabelLevelBbox()[ax.side] - mainLinePosition);
67943 });
67944 }
67945
67946 var hasRangeSlider = Registry.getComponentMethod('rangeslider', 'isVisible')(ax);
67947
67948 seq.push(function() {
67949 var s = ax.side.charAt(0);
67950 var sMirror = OPPOSITE_SIDE[ax.side].charAt(0);
67951 var pos = axes.getPxPosition(gd, ax);
67952 var outsideTickLen = outsideTicks ? ax.ticklen : 0;
67953 var llbbox;
67954
67955 var push;
67956 var mirrorPush;
67957 var rangeSliderPush;
67958
67959 if(ax.automargin || hasRangeSlider) {
67960 if(ax.type === 'multicategory') {
67961 llbbox = getLabelLevelBbox('tick2');
67962 } else {
67963 llbbox = getLabelLevelBbox();
67964 if(axLetter === 'x' && s === 'b') {
67965 ax._depth = Math.max(llbbox.width > 0 ? llbbox.bottom - pos : 0, outsideTickLen);
67966 }
67967 }
67968 }
67969
67970 if(ax.automargin) {
67971 push = {x: 0, y: 0, r: 0, l: 0, t: 0, b: 0};
67972 var domainIndices = [0, 1];
67973
67974 if(axLetter === 'x') {
67975 if(s === 'b') {
67976 push[s] = ax._depth;
67977 } else {
67978 push[s] = ax._depth = Math.max(llbbox.width > 0 ? pos - llbbox.top : 0, outsideTickLen);
67979 domainIndices.reverse();
67980 }
67981
67982 if(llbbox.width > 0) {
67983 var rExtra = llbbox.right - (ax._offset + ax._length);
67984 if(rExtra > 0) {
67985 push.xr = 1;
67986 push.r = rExtra;
67987 }
67988 var lExtra = ax._offset - llbbox.left;
67989 if(lExtra > 0) {
67990 push.xl = 0;
67991 push.l = lExtra;
67992 }
67993 }
67994 } else {
67995 if(s === 'l') {
67996 push[s] = ax._depth = Math.max(llbbox.height > 0 ? pos - llbbox.left : 0, outsideTickLen);
67997 } else {
67998 push[s] = ax._depth = Math.max(llbbox.height > 0 ? llbbox.right - pos : 0, outsideTickLen);
67999 domainIndices.reverse();
68000 }
68001
68002 if(llbbox.height > 0) {
68003 var bExtra = llbbox.bottom - (ax._offset + ax._length);
68004 if(bExtra > 0) {
68005 push.yb = 0;
68006 push.b = bExtra;
68007 }
68008 var tExtra = ax._offset - llbbox.top;
68009 if(tExtra > 0) {
68010 push.yt = 1;
68011 push.t = tExtra;
68012 }
68013 }
68014 }
68015
68016 push[counterLetter] = ax.anchor === 'free' ?
68017 ax.position :
68018 ax._anchorAxis.domain[domainIndices[0]];
68019
68020 if(ax.title.text !== fullLayout._dfltTitle[axLetter]) {
68021 push[s] += approxTitleDepth(ax) + (ax.title.standoff || 0);
68022 }
68023
68024 if(ax.mirror && ax.anchor !== 'free') {
68025 mirrorPush = {x: 0, y: 0, r: 0, l: 0, t: 0, b: 0};
68026
68027 mirrorPush[sMirror] = ax.linewidth;
68028 if(ax.mirror && ax.mirror !== true) mirrorPush[sMirror] += outsideTickLen;
68029
68030 if(ax.mirror === true || ax.mirror === 'ticks') {
68031 mirrorPush[counterLetter] = ax._anchorAxis.domain[domainIndices[1]];
68032 } else if(ax.mirror === 'all' || ax.mirror === 'allticks') {
68033 mirrorPush[counterLetter] = [ax._counterDomainMin, ax._counterDomainMax][domainIndices[1]];
68034 }
68035 }
68036 }
68037
68038 if(hasRangeSlider) {
68039 rangeSliderPush = Registry.getComponentMethod('rangeslider', 'autoMarginOpts')(gd, ax);
68040 }
68041
68042 Plots.autoMargin(gd, axAutoMarginID(ax), push);
68043 Plots.autoMargin(gd, axMirrorAutoMarginID(ax), mirrorPush);
68044 Plots.autoMargin(gd, rangeSliderAutoMarginID(ax), rangeSliderPush);
68045 });
68046
68047 if(!opts.skipTitle &&
68048 !(hasRangeSlider && ax.side === 'bottom')
68049 ) {
68050 seq.push(function() { return drawTitle(gd, ax); });
68051 }
68052
68053 return Lib.syncOrAsync(seq);
68054};
68055
68056function getBoundaryVals(ax, vals) {
68057 var out = [];
68058 var i;
68059
68060 // boundaryVals are never used for labels;
68061 // no need to worry about the other tickTextObj keys
68062 var _push = function(d, bndIndex) {
68063 var xb = d.xbnd[bndIndex];
68064 if(xb !== null) {
68065 out.push(Lib.extendFlat({}, d, {x: xb}));
68066 }
68067 };
68068
68069 if(vals.length) {
68070 for(i = 0; i < vals.length; i++) {
68071 _push(vals[i], 0);
68072 }
68073 _push(vals[i - 1], 1);
68074 }
68075
68076 return out;
68077}
68078
68079function getSecondaryLabelVals(ax, vals) {
68080 var out = [];
68081 var lookup = {};
68082
68083 for(var i = 0; i < vals.length; i++) {
68084 var d = vals[i];
68085 if(lookup[d.text2]) {
68086 lookup[d.text2].push(d.x);
68087 } else {
68088 lookup[d.text2] = [d.x];
68089 }
68090 }
68091
68092 for(var k in lookup) {
68093 out.push(tickTextObj(ax, Lib.interp(lookup[k], 0.5), k));
68094 }
68095
68096 return out;
68097}
68098
68099function getDividerVals(ax, vals) {
68100 var out = [];
68101 var i, current;
68102
68103 var reversed = (vals.length && vals[vals.length - 1].x < vals[0].x);
68104
68105 // never used for labels;
68106 // no need to worry about the other tickTextObj keys
68107 var _push = function(d, bndIndex) {
68108 var xb = d.xbnd[bndIndex];
68109 if(xb !== null) {
68110 out.push(Lib.extendFlat({}, d, {x: xb}));
68111 }
68112 };
68113
68114 if(ax.showdividers && vals.length) {
68115 for(i = 0; i < vals.length; i++) {
68116 var d = vals[i];
68117 if(d.text2 !== current) {
68118 _push(d, reversed ? 1 : 0);
68119 }
68120 current = d.text2;
68121 }
68122 _push(vals[i - 1], reversed ? 0 : 1);
68123 }
68124
68125 return out;
68126}
68127
68128function calcLabelLevelBbox(ax, cls) {
68129 var top, bottom;
68130 var left, right;
68131
68132 if(ax._selections[cls].size()) {
68133 top = Infinity;
68134 bottom = -Infinity;
68135 left = Infinity;
68136 right = -Infinity;
68137 ax._selections[cls].each(function() {
68138 var thisLabel = selectTickLabel(this);
68139 // Use parent node <g.(x|y)tick>, to make Drawing.bBox
68140 // retrieve a bbox computed with transform info
68141 //
68142 // To improve perf, it would be nice to use `thisLabel.node()`
68143 // (like in fixLabelOverlaps) instead and use Axes.getPxPosition
68144 // together with the makeLabelFns outputs and `tickangle`
68145 // to compute one bbox per (tick value x tick style)
68146 var bb = Drawing.bBox(thisLabel.node().parentNode);
68147 top = Math.min(top, bb.top);
68148 bottom = Math.max(bottom, bb.bottom);
68149 left = Math.min(left, bb.left);
68150 right = Math.max(right, bb.right);
68151 });
68152 } else {
68153 top = 0;
68154 bottom = 0;
68155 left = 0;
68156 right = 0;
68157 }
68158
68159 return {
68160 top: top,
68161 bottom: bottom,
68162 left: left,
68163 right: right,
68164 height: bottom - top,
68165 width: right - left
68166 };
68167}
68168
68169/**
68170 * Which direction do the 'ax.side' values, and free ticks go?
68171 *
68172 * @param {object} ax (full) axis object
68173 * - {string} _id (starting with 'x' or 'y')
68174 * - {string} side
68175 * - {string} ticks
68176 * @return {array} all entries are either -1 or 1
68177 * - [0]: sign for top/right ticks (i.e. negative SVG direction)
68178 * - [1]: sign for bottom/left ticks (i.e. positive SVG direction)
68179 * - [2]: sign for ticks corresponding to 'ax.side'
68180 * - [3]: sign for ticks mirroring 'ax.side'
68181 * - [4]: sign of arrow starting at axis pointing towards margin
68182 */
68183axes.getTickSigns = function(ax) {
68184 var axLetter = ax._id.charAt(0);
68185 var sideOpposite = {x: 'top', y: 'right'}[axLetter];
68186 var main = ax.side === sideOpposite ? 1 : -1;
68187 var out = [-1, 1, main, -main];
68188 // then we flip if outside XOR y axis
68189 if((ax.ticks !== 'inside') === (axLetter === 'x')) {
68190 out = out.map(function(v) { return -v; });
68191 }
68192 // independent of `ticks`; do not flip this one
68193 if(ax.side) {
68194 out.push({l: -1, t: -1, r: 1, b: 1}[ax.side.charAt(0)]);
68195 }
68196 return out;
68197};
68198
68199/**
68200 * Make axis translate transform function
68201 *
68202 * @param {object} ax (full) axis object
68203 * - {string} _id
68204 * - {number} _offset
68205 * - {fn} l2p
68206 * @return {fn} function of calcTicks items
68207 */
68208axes.makeTransTickFn = function(ax) {
68209 return ax._id.charAt(0) === 'x' ?
68210 function(d) { return strTranslate(ax._offset + ax.l2p(d.x), 0); } :
68211 function(d) { return strTranslate(0, ax._offset + ax.l2p(d.x)); };
68212};
68213
68214axes.makeTransTickLabelFn = function(ax) {
68215 var uv = getTickLabelUV(ax);
68216 var u = uv[0];
68217 var v = uv[1];
68218
68219 return ax._id.charAt(0) === 'x' ?
68220 function(d) {
68221 return strTranslate(
68222 u + ax._offset + ax.l2p(getPosX(d)),
68223 v
68224 );
68225 } :
68226 function(d) {
68227 return strTranslate(
68228 v,
68229 u + ax._offset + ax.l2p(getPosX(d))
68230 );
68231 };
68232};
68233
68234function getPosX(d) {
68235 return d.periodX !== undefined ? d.periodX : d.x;
68236}
68237
68238// u is a shift along the axis,
68239// v is a shift perpendicular to the axis
68240function getTickLabelUV(ax) {
68241 var ticklabelposition = ax.ticklabelposition || '';
68242 var has = function(str) {
68243 return ticklabelposition.indexOf(str) !== -1;
68244 };
68245
68246 var isTop = has('top');
68247 var isLeft = has('left');
68248 var isRight = has('right');
68249 var isBottom = has('bottom');
68250 var isInside = has('inside');
68251
68252 var isAligned = isBottom || isLeft || isTop || isRight;
68253
68254 // early return
68255 if(!isAligned && !isInside) return [0, 0];
68256
68257 var side = ax.side;
68258
68259 var u = isAligned ? (ax.tickwidth || 0) / 2 : 0;
68260 var v = TEXTPAD;
68261
68262 var fontSize = ax.tickfont ? ax.tickfont.size : 12;
68263 if(isBottom || isTop) {
68264 u += fontSize * CAP_SHIFT;
68265 v += (ax.linewidth || 0) / 2;
68266 }
68267 if(isLeft || isRight) {
68268 u += (ax.linewidth || 0) / 2;
68269 v += TEXTPAD;
68270 }
68271 if(isInside && side === 'top') {
68272 v -= fontSize * (1 - CAP_SHIFT);
68273 }
68274
68275 if(isLeft || isTop) u = -u;
68276 if(side === 'bottom' || side === 'right') v = -v;
68277
68278 return [
68279 isAligned ? u : 0,
68280 isInside ? v : 0
68281 ];
68282}
68283
68284/**
68285 * Make axis tick path string
68286 *
68287 * @param {object} ax (full) axis object
68288 * - {string} _id
68289 * - {number} ticklen
68290 * - {number} linewidth
68291 * @param {number} shift along direction of ticklen
68292 * @param {1 or -1} sgn tick sign
68293 * @param {number (optional)} len tick length
68294 * @return {string}
68295 */
68296axes.makeTickPath = function(ax, shift, sgn, len) {
68297 len = len !== undefined ? len : ax.ticklen;
68298
68299 var axLetter = ax._id.charAt(0);
68300 var pad = (ax.linewidth || 1) / 2;
68301
68302 return axLetter === 'x' ?
68303 'M0,' + (shift + pad * sgn) + 'v' + (len * sgn) :
68304 'M' + (shift + pad * sgn) + ',0h' + (len * sgn);
68305};
68306
68307/**
68308 * Make axis tick label x, y and anchor functions
68309 *
68310 * @param {object} ax (full) axis object
68311 * - {string} _id
68312 * - {string} ticks
68313 * - {number} ticklen
68314 * - {string} side
68315 * - {number} linewidth
68316 * - {number} tickfont.size
68317 * - {boolean} showline
68318 * @param {number} shift
68319 * @param {number} angle [in degrees] ...
68320 * @return {object}
68321 * - {fn} xFn
68322 * - {fn} yFn
68323 * - {fn} anchorFn
68324 * - {fn} heightFn
68325 * - {number} labelStandoff (gap parallel to ticks)
68326 * - {number} labelShift (gap perpendicular to ticks)
68327 */
68328axes.makeLabelFns = function(ax, shift, angle) {
68329 var ticklabelposition = ax.ticklabelposition || '';
68330 var has = function(str) {
68331 return ticklabelposition.indexOf(str) !== -1;
68332 };
68333
68334 var isTop = has('top');
68335 var isLeft = has('left');
68336 var isRight = has('right');
68337 var isBottom = has('bottom');
68338 var isAligned = isBottom || isLeft || isTop || isRight;
68339
68340 var insideTickLabels = has('inside');
68341 var labelsOverTicks =
68342 (ticklabelposition === 'inside' && ax.ticks === 'inside') ||
68343 (!insideTickLabels && ax.ticks === 'outside' && ax.tickson !== 'boundaries');
68344
68345 var labelStandoff = 0;
68346 var labelShift = 0;
68347
68348 var tickLen = labelsOverTicks ? ax.ticklen : 0;
68349 if(insideTickLabels) {
68350 tickLen *= -1;
68351 } else if(isAligned) {
68352 tickLen = 0;
68353 }
68354
68355 if(labelsOverTicks) {
68356 labelStandoff += tickLen;
68357 if(angle) {
68358 var rad = Lib.deg2rad(angle);
68359 labelStandoff = tickLen * Math.cos(rad) + 1;
68360 labelShift = tickLen * Math.sin(rad);
68361 }
68362 }
68363
68364 if(ax.showticklabels && (labelsOverTicks || ax.showline)) {
68365 labelStandoff += 0.2 * ax.tickfont.size;
68366 }
68367 labelStandoff += (ax.linewidth || 1) / 2 * (insideTickLabels ? -1 : 1);
68368
68369 var out = {
68370 labelStandoff: labelStandoff,
68371 labelShift: labelShift
68372 };
68373
68374 var x0, y0, ff, flipIt;
68375 var xQ = 0;
68376
68377 var side = ax.side;
68378 var axLetter = ax._id.charAt(0);
68379 var tickangle = ax.tickangle;
68380 var endSide;
68381 if(axLetter === 'x') {
68382 endSide =
68383 (!insideTickLabels && side === 'bottom') ||
68384 (insideTickLabels && side === 'top');
68385
68386 flipIt = endSide ? 1 : -1;
68387 if(insideTickLabels) flipIt *= -1;
68388
68389 x0 = labelShift * flipIt;
68390 y0 = shift + labelStandoff * flipIt;
68391 ff = endSide ? 1 : -0.2;
68392 if(Math.abs(tickangle) === 90) {
68393 if(insideTickLabels) {
68394 ff += MID_SHIFT;
68395 } else {
68396 if(tickangle === -90 && side === 'bottom') {
68397 ff = CAP_SHIFT;
68398 } else if(tickangle === 90 && side === 'top') {
68399 ff = MID_SHIFT;
68400 } else {
68401 ff = 0.5;
68402 }
68403 }
68404
68405 xQ = (MID_SHIFT / 2) * (tickangle / 90);
68406 }
68407
68408 out.xFn = function(d) { return d.dx + x0 + xQ * d.fontSize; };
68409 out.yFn = function(d) { return d.dy + y0 + d.fontSize * ff; };
68410 out.anchorFn = function(d, a) {
68411 if(isAligned) {
68412 if(isLeft) return 'end';
68413 if(isRight) return 'start';
68414 }
68415
68416 if(!isNumeric(a) || a === 0 || a === 180) {
68417 return 'middle';
68418 }
68419
68420 return ((a * flipIt < 0) !== insideTickLabels) ? 'end' : 'start';
68421 };
68422 out.heightFn = function(d, a, h) {
68423 return (a < -60 || a > 60) ? -0.5 * h :
68424 ((ax.side === 'top') !== insideTickLabels) ? -h :
68425 0;
68426 };
68427 } else if(axLetter === 'y') {
68428 endSide =
68429 (!insideTickLabels && side === 'left') ||
68430 (insideTickLabels && side === 'right');
68431
68432 flipIt = endSide ? 1 : -1;
68433 if(insideTickLabels) flipIt *= -1;
68434
68435 x0 = labelStandoff;
68436 y0 = labelShift * flipIt;
68437 ff = 0;
68438 if(!insideTickLabels && Math.abs(tickangle) === 90) {
68439 if(
68440 (tickangle === -90 && side === 'left') ||
68441 (tickangle === 90 && side === 'right')
68442 ) {
68443 ff = CAP_SHIFT;
68444 } else {
68445 ff = 0.5;
68446 }
68447 }
68448
68449 if(insideTickLabels) {
68450 var ang = isNumeric(tickangle) ? +tickangle : 0;
68451 if(ang !== 0) {
68452 var rA = Lib.deg2rad(ang);
68453 xQ = Math.abs(Math.sin(rA)) * CAP_SHIFT * flipIt;
68454 ff = 0;
68455 }
68456 }
68457
68458 out.xFn = function(d) { return d.dx + shift - (x0 + d.fontSize * ff) * flipIt + xQ * d.fontSize; };
68459 out.yFn = function(d) { return d.dy + y0 + d.fontSize * MID_SHIFT; };
68460 out.anchorFn = function(d, a) {
68461 if(isNumeric(a) && Math.abs(a) === 90) {
68462 return 'middle';
68463 }
68464
68465 return endSide ? 'end' : 'start';
68466 };
68467 out.heightFn = function(d, a, h) {
68468 if(ax.side === 'right') a *= -1;
68469
68470 return a < -30 ? -h :
68471 a < 30 ? -0.5 * h :
68472 0;
68473 };
68474 }
68475
68476 return out;
68477};
68478
68479function tickDataFn(d) {
68480 return [d.text, d.x, d.axInfo, d.font, d.fontSize, d.fontColor].join('_');
68481}
68482
68483/**
68484 * Draw axis ticks
68485 *
68486 * @param {DOM element} gd
68487 * @param {object} ax (full) axis object
68488 * - {string} _id
68489 * - {string} ticks
68490 * - {number} linewidth
68491 * - {string} tickcolor
68492 * @param {object} opts
68493 * - {array of object} vals (calcTicks output-like)
68494 * - {d3 selection} layer
68495 * - {string or fn} path
68496 * - {fn} transFn
68497 * - {boolean} crisp (set to false to unset crisp-edge SVG rendering)
68498 */
68499axes.drawTicks = function(gd, ax, opts) {
68500 opts = opts || {};
68501
68502 var cls = ax._id + 'tick';
68503
68504 var vals = opts.vals;
68505 if(
68506 ax.ticklabelmode === 'period'
68507 ) {
68508 // drop very first tick that we added to handle period
68509 vals = vals.slice();
68510 vals.shift();
68511 }
68512
68513 var ticks = opts.layer.selectAll('path.' + cls)
68514 .data(ax.ticks ? vals : [], tickDataFn);
68515
68516 ticks.exit().remove();
68517
68518 ticks.enter().append('path')
68519 .classed(cls, 1)
68520 .classed('ticks', 1)
68521 .classed('crisp', opts.crisp !== false)
68522 .call(Color.stroke, ax.tickcolor)
68523 .style('stroke-width', Drawing.crispRound(gd, ax.tickwidth, 1) + 'px')
68524 .attr('d', opts.path)
68525 .style('display', null); // visible
68526
68527 hideCounterAxisInsideTickLabels(ax, [TICK_PATH]);
68528
68529 ticks.attr('transform', opts.transFn);
68530};
68531
68532/**
68533 * Draw axis grid
68534 *
68535 * @param {DOM element} gd
68536 * @param {object} ax (full) axis object
68537 * - {string} _id
68538 * - {boolean} showgrid
68539 * - {string} gridcolor
68540 * - {string} gridwidth
68541 * - {boolean} zeroline
68542 * - {string} type
68543 * - {string} dtick
68544 * @param {object} opts
68545 * - {array of object} vals (calcTicks output-like)
68546 * - {d3 selection} layer
68547 * - {object} counterAxis (full axis object corresponding to counter axis)
68548 * optional - only required if this axis supports zero lines
68549 * - {string or fn} path
68550 * - {fn} transFn
68551 * - {boolean} crisp (set to false to unset crisp-edge SVG rendering)
68552 */
68553axes.drawGrid = function(gd, ax, opts) {
68554 opts = opts || {};
68555
68556 var cls = ax._id + 'grid';
68557 var vals = opts.vals;
68558 var counterAx = opts.counterAxis;
68559 if(ax.showgrid === false) {
68560 vals = [];
68561 } else if(counterAx && axes.shouldShowZeroLine(gd, ax, counterAx)) {
68562 var isArrayMode = ax.tickmode === 'array';
68563 for(var i = 0; i < vals.length; i++) {
68564 var xi = vals[i].x;
68565 if(isArrayMode ? !xi : (Math.abs(xi) < ax.dtick / 100)) {
68566 vals = vals.slice(0, i).concat(vals.slice(i + 1));
68567 // In array mode you can in principle have multiple
68568 // ticks at 0, so test them all. Otherwise once we found
68569 // one we can stop.
68570 if(isArrayMode) i--;
68571 else break;
68572 }
68573 }
68574 }
68575
68576 var grid = opts.layer.selectAll('path.' + cls)
68577 .data(vals, tickDataFn);
68578
68579 grid.exit().remove();
68580
68581 grid.enter().append('path')
68582 .classed(cls, 1)
68583 .classed('crisp', opts.crisp !== false);
68584
68585 ax._gw = Drawing.crispRound(gd, ax.gridwidth, 1);
68586
68587 grid.attr('transform', opts.transFn)
68588 .attr('d', opts.path)
68589 .call(Color.stroke, ax.gridcolor || '#ddd')
68590 .style('stroke-width', ax._gw + 'px')
68591 .style('display', null); // visible
68592
68593 hideCounterAxisInsideTickLabels(ax, [GRID_PATH]);
68594
68595 if(typeof opts.path === 'function') grid.attr('d', opts.path);
68596};
68597
68598/**
68599 * Draw axis zero-line
68600 *
68601 * @param {DOM element} gd
68602 * @param {object} ax (full) axis object
68603 * - {string} _id
68604 * - {boolean} zeroline
68605 * - {number} zerolinewidth
68606 * - {string} zerolinecolor
68607 * - {number (optional)} _gridWidthCrispRound
68608 * @param {object} opts
68609 * - {d3 selection} layer
68610 * - {object} counterAxis (full axis object corresponding to counter axis)
68611 * - {string or fn} path
68612 * - {fn} transFn
68613 * - {boolean} crisp (set to false to unset crisp-edge SVG rendering)
68614 */
68615axes.drawZeroLine = function(gd, ax, opts) {
68616 opts = opts || opts;
68617
68618 var cls = ax._id + 'zl';
68619 var show = axes.shouldShowZeroLine(gd, ax, opts.counterAxis);
68620
68621 var zl = opts.layer.selectAll('path.' + cls)
68622 .data(show ? [{x: 0, id: ax._id}] : []);
68623
68624 zl.exit().remove();
68625
68626 zl.enter().append('path')
68627 .classed(cls, 1)
68628 .classed('zl', 1)
68629 .classed('crisp', opts.crisp !== false)
68630 .each(function() {
68631 // use the fact that only one element can enter to trigger a sort.
68632 // If several zerolines enter at the same time we will sort once per,
68633 // but generally this should be a minimal overhead.
68634 opts.layer.selectAll('path').sort(function(da, db) {
68635 return idSort(da.id, db.id);
68636 });
68637 });
68638
68639 zl.attr('transform', opts.transFn)
68640 .attr('d', opts.path)
68641 .call(Color.stroke, ax.zerolinecolor || Color.defaultLine)
68642 .style('stroke-width', Drawing.crispRound(gd, ax.zerolinewidth, ax._gw || 1) + 'px')
68643 .style('display', null); // visible
68644
68645 hideCounterAxisInsideTickLabels(ax, [ZERO_PATH]);
68646};
68647
68648/**
68649 * Draw axis tick labels
68650 *
68651 * @param {DOM element} gd
68652 * @param {object} ax (full) axis object
68653 * - {string} _id
68654 * - {boolean} showticklabels
68655 * - {number} tickangle
68656 * - {object (optional)} _selections
68657 * - {object} (optional)} _tickAngles
68658 * - {object} (optional)} _prevTickAngles
68659 * @param {object} opts
68660 * - {array of object} vals (calcTicks output-like)
68661 * - {d3 selection} layer
68662 * - {string (optional)} cls (node className)
68663 * - {boolean} repositionOnUpdate (set to true to reposition update selection)
68664 * - {boolean} secondary
68665 * - {fn} transFn
68666 * - {object} labelFns
68667 * + {fn} xFn
68668 * + {fn} yFn
68669 * + {fn} anchorFn
68670 * + {fn} heightFn
68671 */
68672axes.drawLabels = function(gd, ax, opts) {
68673 opts = opts || {};
68674
68675 var fullLayout = gd._fullLayout;
68676 var axId = ax._id;
68677 var axLetter = axId.charAt(0);
68678 var cls = opts.cls || axId + 'tick';
68679 var vals = opts.vals;
68680
68681 var labelFns = opts.labelFns;
68682 var tickAngle = opts.secondary ? 0 : ax.tickangle;
68683 var prevAngle = (ax._prevTickAngles || {})[cls];
68684
68685 var tickLabels = opts.layer.selectAll('g.' + cls)
68686 .data(ax.showticklabels ? vals : [], tickDataFn);
68687
68688 var labelsReady = [];
68689
68690 tickLabels.enter().append('g')
68691 .classed(cls, 1)
68692 .append('text')
68693 // only so tex has predictable alignment that we can
68694 // alter later
68695 .attr('text-anchor', 'middle')
68696 .each(function(d) {
68697 var thisLabel = d3.select(this);
68698 var newPromise = gd._promises.length;
68699
68700 thisLabel
68701 .call(svgTextUtils.positionText, labelFns.xFn(d), labelFns.yFn(d))
68702 .call(Drawing.font, d.font, d.fontSize, d.fontColor)
68703 .text(d.text)
68704 .call(svgTextUtils.convertToTspans, gd);
68705
68706 if(gd._promises[newPromise]) {
68707 // if we have an async label, we'll deal with that
68708 // all here so take it out of gd._promises and
68709 // instead position the label and promise this in
68710 // labelsReady
68711 labelsReady.push(gd._promises.pop().then(function() {
68712 positionLabels(thisLabel, tickAngle);
68713 }));
68714 } else {
68715 // sync label: just position it now.
68716 positionLabels(thisLabel, tickAngle);
68717 }
68718 });
68719
68720 hideCounterAxisInsideTickLabels(ax, [TICK_TEXT]);
68721
68722 tickLabels.exit().remove();
68723
68724 if(opts.repositionOnUpdate) {
68725 tickLabels.each(function(d) {
68726 d3.select(this).select('text')
68727 .call(svgTextUtils.positionText, labelFns.xFn(d), labelFns.yFn(d));
68728 });
68729 }
68730
68731 function positionLabels(s, angle) {
68732 s.each(function(d) {
68733 var thisLabel = d3.select(this);
68734 var mathjaxGroup = thisLabel.select('.text-math-group');
68735 var anchor = labelFns.anchorFn(d, angle);
68736
68737 var transform = opts.transFn.call(thisLabel.node(), d) +
68738 ((isNumeric(angle) && +angle !== 0) ?
68739 (' rotate(' + angle + ',' + labelFns.xFn(d) + ',' +
68740 (labelFns.yFn(d) - d.fontSize / 2) + ')') :
68741 '');
68742
68743 // how much to shift a multi-line label to center it vertically.
68744 var nLines = svgTextUtils.lineCount(thisLabel);
68745 var lineHeight = LINE_SPACING * d.fontSize;
68746 var anchorHeight = labelFns.heightFn(d, isNumeric(angle) ? +angle : 0, (nLines - 1) * lineHeight);
68747
68748 if(anchorHeight) {
68749 transform += strTranslate(0, anchorHeight);
68750 }
68751
68752 if(mathjaxGroup.empty()) {
68753 var thisText = thisLabel.select('text');
68754 thisText.attr({
68755 transform: transform,
68756 'text-anchor': anchor
68757 });
68758
68759 thisText.style('opacity', 1); // visible
68760
68761 if(ax._adjustTickLabelsOverflow) {
68762 ax._adjustTickLabelsOverflow();
68763 }
68764 } else {
68765 var mjWidth = Drawing.bBox(mathjaxGroup.node()).width;
68766 var mjShift = mjWidth * {end: -0.5, start: 0.5}[anchor];
68767 mathjaxGroup.attr('transform', transform + strTranslate(mjShift, 0));
68768 }
68769 });
68770 }
68771
68772 ax._adjustTickLabelsOverflow = function() {
68773 var ticklabeloverflow = ax.ticklabeloverflow;
68774 if(!ticklabeloverflow || ticklabeloverflow === 'allow') return;
68775
68776 var hideOverflow = ticklabeloverflow.indexOf('hide') !== -1;
68777
68778 var isX = ax._id.charAt(0) === 'x';
68779 // div positions
68780 var p0 = 0;
68781 var p1 = isX ?
68782 gd._fullLayout.width :
68783 gd._fullLayout.height;
68784
68785 if(ticklabeloverflow.indexOf('domain') !== -1) {
68786 // domain positions
68787 var rl = Lib.simpleMap(ax.range, ax.r2l);
68788 p0 = ax.l2p(rl[0]) + ax._offset;
68789 p1 = ax.l2p(rl[1]) + ax._offset;
68790 }
68791
68792 var min = Math.min(p0, p1);
68793 var max = Math.max(p0, p1);
68794
68795 var side = ax.side;
68796
68797 var visibleLabelMin = Infinity;
68798 var visibleLabelMax = -Infinity;
68799
68800 tickLabels.each(function(d) {
68801 var thisLabel = d3.select(this);
68802 var mathjaxGroup = thisLabel.select('.text-math-group');
68803
68804 if(mathjaxGroup.empty()) {
68805 var bb = Drawing.bBox(thisLabel.node());
68806 var adjust = 0;
68807 if(isX) {
68808 if(bb.right > max) adjust = 1;
68809 else if(bb.left < min) adjust = 1;
68810 } else {
68811 if(bb.bottom > max) adjust = 1;
68812 else if(bb.top + (ax.tickangle ? 0 : d.fontSize / 4) < min) adjust = 1;
68813 }
68814
68815 var t = thisLabel.select('text');
68816 if(adjust) {
68817 if(hideOverflow) t.style('opacity', 0); // hidden
68818 } else {
68819 t.style('opacity', 1); // visible
68820
68821 if(side === 'bottom' || side === 'right') {
68822 visibleLabelMin = Math.min(visibleLabelMin, isX ? bb.top : bb.left);
68823 } else {
68824 visibleLabelMin = -Infinity;
68825 }
68826
68827 if(side === 'top' || side === 'left') {
68828 visibleLabelMax = Math.max(visibleLabelMax, isX ? bb.bottom : bb.right);
68829 } else {
68830 visibleLabelMax = Infinity;
68831 }
68832 }
68833 } // TODO: hide mathjax?
68834 });
68835
68836 for(var subplot in fullLayout._plots) {
68837 var plotinfo = fullLayout._plots[subplot];
68838 if(ax._id !== plotinfo.xaxis._id && ax._id !== plotinfo.yaxis._id) continue;
68839 var anchorAx = isX ? plotinfo.yaxis : plotinfo.xaxis;
68840 if(anchorAx) {
68841 anchorAx['_visibleLabelMin_' + ax._id] = visibleLabelMin;
68842 anchorAx['_visibleLabelMax_' + ax._id] = visibleLabelMax;
68843 }
68844 }
68845 };
68846
68847 ax._hideCounterAxisInsideTickLabels = function(partialOpts) {
68848 var isX = ax._id.charAt(0) === 'x';
68849
68850 var anchoredAxes = [];
68851 for(var subplot in fullLayout._plots) {
68852 var plotinfo = fullLayout._plots[subplot];
68853 if(ax._id !== plotinfo.xaxis._id && ax._id !== plotinfo.yaxis._id) continue;
68854 anchoredAxes.push(isX ? plotinfo.yaxis : plotinfo.xaxis);
68855 }
68856
68857 anchoredAxes.forEach(function(anchorAx, idx) {
68858 if(anchorAx && insideTicklabelposition(anchorAx)) {
68859 (partialOpts || [
68860 ZERO_PATH,
68861 GRID_PATH,
68862 TICK_PATH,
68863 TICK_TEXT
68864 ]).forEach(function(e) {
68865 var isPeriodLabel =
68866 e.K === 'tick' &&
68867 e.L === 'text' &&
68868 ax.ticklabelmode === 'period';
68869
68870 var mainPlotinfo = fullLayout._plots[ax._mainSubplot];
68871
68872 var sel;
68873 if(e.K === ZERO_PATH.K) sel = mainPlotinfo.zerolinelayer.selectAll('.' + ax._id + 'zl');
68874 else if(e.K === GRID_PATH.K) sel = mainPlotinfo.gridlayer.selectAll('.' + ax._id);
68875 else sel = mainPlotinfo[ax._id.charAt(0) + 'axislayer'];
68876
68877 sel.each(function() {
68878 var w = d3.select(this);
68879 if(e.L) w = w.selectAll(e.L);
68880
68881 w.each(function(d) {
68882 var q = ax.l2p(
68883 isPeriodLabel ? getPosX(d) : d.x
68884 ) + ax._offset;
68885
68886 var t = d3.select(this);
68887 if(
68888 q < ax['_visibleLabelMax_' + anchorAx._id] &&
68889 q > ax['_visibleLabelMin_' + anchorAx._id]
68890 ) {
68891 t.style('display', 'none'); // hidden
68892 } else if(e.K === 'tick' && !idx) {
68893 t.style('display', null); // visible
68894 }
68895 });
68896 });
68897 });
68898 }
68899 });
68900 };
68901
68902 // make sure all labels are correctly positioned at their base angle
68903 // the positionLabels call above is only for newly drawn labels.
68904 // do this without waiting, using the last calculated angle to
68905 // minimize flicker, then do it again when we know all labels are
68906 // there, putting back the prescribed angle to check for overlaps.
68907 positionLabels(tickLabels, (prevAngle + 1) ? prevAngle : tickAngle);
68908
68909 function allLabelsReady() {
68910 return labelsReady.length && Promise.all(labelsReady);
68911 }
68912
68913 var autoangle = null;
68914
68915 function fixLabelOverlaps() {
68916 positionLabels(tickLabels, tickAngle);
68917
68918 // check for auto-angling if x labels overlap
68919 // don't auto-angle at all for log axes with
68920 // base and digit format
68921 if(vals.length && axLetter === 'x' && !isNumeric(tickAngle) &&
68922 (ax.type !== 'log' || String(ax.dtick).charAt(0) !== 'D')
68923 ) {
68924 autoangle = 0;
68925
68926 var maxFontSize = 0;
68927 var lbbArray = [];
68928 var i;
68929
68930 tickLabels.each(function(d) {
68931 maxFontSize = Math.max(maxFontSize, d.fontSize);
68932
68933 var x = ax.l2p(d.x);
68934 var thisLabel = selectTickLabel(this);
68935 var bb = Drawing.bBox(thisLabel.node());
68936
68937 lbbArray.push({
68938 // ignore about y, just deal with x overlaps
68939 top: 0,
68940 bottom: 10,
68941 height: 10,
68942 left: x - bb.width / 2,
68943 // impose a 2px gap
68944 right: x + bb.width / 2 + 2,
68945 width: bb.width + 2
68946 });
68947 });
68948
68949 if((ax.tickson === 'boundaries' || ax.showdividers) && !opts.secondary) {
68950 var gap = 2;
68951 if(ax.ticks) gap += ax.tickwidth / 2;
68952
68953 // TODO should secondary labels also fall into this fix-overlap regime?
68954
68955 for(i = 0; i < lbbArray.length; i++) {
68956 var xbnd = vals[i].xbnd;
68957 var lbb = lbbArray[i];
68958 if(
68959 (xbnd[0] !== null && (lbb.left - ax.l2p(xbnd[0])) < gap) ||
68960 (xbnd[1] !== null && (ax.l2p(xbnd[1]) - lbb.right) < gap)
68961 ) {
68962 autoangle = 90;
68963 break;
68964 }
68965 }
68966 } else {
68967 var vLen = vals.length;
68968 var tickSpacing = Math.abs((vals[vLen - 1].x - vals[0].x) * ax._m) / (vLen - 1);
68969
68970 var ticklabelposition = ax.ticklabelposition || '';
68971 var has = function(str) {
68972 return ticklabelposition.indexOf(str) !== -1;
68973 };
68974 var isTop = has('top');
68975 var isLeft = has('left');
68976 var isRight = has('right');
68977 var isBottom = has('bottom');
68978 var isAligned = isBottom || isLeft || isTop || isRight;
68979 var pad = !isAligned ? 0 :
68980 (ax.tickwidth || 0) + 2 * TEXTPAD;
68981
68982 var rotate90 = (tickSpacing < maxFontSize * 2.5) || ax.type === 'multicategory';
68983
68984 // any overlap at all - set 30 degrees or 90 degrees
68985 for(i = 0; i < lbbArray.length - 1; i++) {
68986 if(Lib.bBoxIntersect(lbbArray[i], lbbArray[i + 1], pad)) {
68987 autoangle = rotate90 ? 90 : 30;
68988 break;
68989 }
68990 }
68991 }
68992
68993 if(autoangle) {
68994 positionLabels(tickLabels, autoangle);
68995 }
68996 }
68997 }
68998
68999 if(ax._selections) {
69000 ax._selections[cls] = tickLabels;
69001 }
69002
69003 var seq = [allLabelsReady];
69004
69005 // N.B. during auto-margin redraws, if the axis fixed its label overlaps
69006 // by rotating 90 degrees, do not attempt to re-fix its label overlaps
69007 // as this can lead to infinite redraw loops!
69008 if(ax.automargin && fullLayout._redrawFromAutoMarginCount && prevAngle === 90) {
69009 autoangle = 90;
69010 seq.push(function() {
69011 positionLabels(tickLabels, prevAngle);
69012 });
69013 } else {
69014 seq.push(fixLabelOverlaps);
69015 }
69016
69017 // save current tick angle for future redraws
69018 if(ax._tickAngles) {
69019 seq.push(function() {
69020 ax._tickAngles[cls] = autoangle === null ?
69021 (isNumeric(tickAngle) ? tickAngle : 0) :
69022 autoangle;
69023 });
69024 }
69025
69026 var anchorAx = ax._anchorAxis;
69027 if(
69028 anchorAx && anchorAx.autorange &&
69029 insideTicklabelposition(ax) &&
69030 !isLinked(fullLayout, ax._id)
69031 ) {
69032 if(!fullLayout._insideTickLabelsAutorange) {
69033 fullLayout._insideTickLabelsAutorange = {};
69034 }
69035 fullLayout._insideTickLabelsAutorange[anchorAx._name + '.autorange'] = anchorAx.autorange;
69036
69037 seq.push(
69038 function computeFinalTickLabelBoundingBoxes() {
69039 tickLabels.each(function(d, i) {
69040 var thisLabel = selectTickLabel(this);
69041 var mathjaxGroup = thisLabel.select('.text-math-group');
69042 if(mathjaxGroup.empty()) {
69043 ax._vals[i].bb = Drawing.bBox(thisLabel.node());
69044 }
69045 });
69046 }
69047 );
69048 }
69049
69050 var done = Lib.syncOrAsync(seq);
69051 if(done && done.then) gd._promises.push(done);
69052 return done;
69053};
69054
69055/**
69056 * Draw axis dividers
69057 *
69058 * @param {DOM element} gd
69059 * @param {object} ax (full) axis object
69060 * - {string} _id
69061 * - {string} showdividers
69062 * - {number} dividerwidth
69063 * - {string} dividercolor
69064 * @param {object} opts
69065 * - {array of object} vals (calcTicks output-like)
69066 * - {d3 selection} layer
69067 * - {fn} path
69068 * - {fn} transFn
69069 */
69070function drawDividers(gd, ax, opts) {
69071 var cls = ax._id + 'divider';
69072 var vals = opts.vals;
69073
69074 var dividers = opts.layer.selectAll('path.' + cls)
69075 .data(vals, tickDataFn);
69076
69077 dividers.exit().remove();
69078
69079 dividers.enter().insert('path', ':first-child')
69080 .classed(cls, 1)
69081 .classed('crisp', 1)
69082 .call(Color.stroke, ax.dividercolor)
69083 .style('stroke-width', Drawing.crispRound(gd, ax.dividerwidth, 1) + 'px');
69084
69085 dividers
69086 .attr('transform', opts.transFn)
69087 .attr('d', opts.path);
69088}
69089
69090/**
69091 * Get axis position in px, that is the distance for the graph's
69092 * top (left) edge for x (y) axes.
69093 *
69094 * @param {DOM element} gd
69095 * @param {object} ax (full) axis object
69096 * - {string} _id
69097 * - {string} side
69098 * if anchored:
69099 * - {object} _anchorAxis
69100 * Otherwise:
69101 * - {number} position
69102 * @return {number}
69103 */
69104axes.getPxPosition = function(gd, ax) {
69105 var gs = gd._fullLayout._size;
69106 var axLetter = ax._id.charAt(0);
69107 var side = ax.side;
69108 var anchorAxis;
69109
69110 if(ax.anchor !== 'free') {
69111 anchorAxis = ax._anchorAxis;
69112 } else if(axLetter === 'x') {
69113 anchorAxis = {
69114 _offset: gs.t + (1 - (ax.position || 0)) * gs.h,
69115 _length: 0
69116 };
69117 } else if(axLetter === 'y') {
69118 anchorAxis = {
69119 _offset: gs.l + (ax.position || 0) * gs.w,
69120 _length: 0
69121 };
69122 }
69123
69124 if(side === 'top' || side === 'left') {
69125 return anchorAxis._offset;
69126 } else if(side === 'bottom' || side === 'right') {
69127 return anchorAxis._offset + anchorAxis._length;
69128 }
69129};
69130
69131/**
69132 * Approximate axis title depth (w/o computing its bounding box)
69133 *
69134 * @param {object} ax (full) axis object
69135 * - {string} title.text
69136 * - {number} title.font.size
69137 * - {number} title.standoff
69138 * @return {number} (in px)
69139 */
69140function approxTitleDepth(ax) {
69141 var fontSize = ax.title.font.size;
69142 var extraLines = (ax.title.text.match(svgTextUtils.BR_TAG_ALL) || []).length;
69143 if(ax.title.hasOwnProperty('standoff')) {
69144 return extraLines ?
69145 fontSize * (CAP_SHIFT + (extraLines * LINE_SPACING)) :
69146 fontSize * CAP_SHIFT;
69147 } else {
69148 return extraLines ?
69149 fontSize * (extraLines + 1) * LINE_SPACING :
69150 fontSize;
69151 }
69152}
69153
69154/**
69155 * Draw axis title, compute default standoff if necessary
69156 *
69157 * @param {DOM element} gd
69158 * @param {object} ax (full) axis object
69159 * - {string} _id
69160 * - {string} _name
69161 * - {string} side
69162 * - {number} title.font.size
69163 * - {object} _selections
69164 *
69165 * - {number} _depth
69166 * - {number} title.standoff
69167 * OR
69168 * - {number} linewidth
69169 * - {boolean} showticklabels
69170 */
69171function drawTitle(gd, ax) {
69172 var fullLayout = gd._fullLayout;
69173 var axId = ax._id;
69174 var axLetter = axId.charAt(0);
69175 var fontSize = ax.title.font.size;
69176 var titleStandoff;
69177
69178 if(ax.title.hasOwnProperty('standoff')) {
69179 titleStandoff = ax._depth + ax.title.standoff + approxTitleDepth(ax);
69180 } else {
69181 var isInside = insideTicklabelposition(ax);
69182
69183 if(ax.type === 'multicategory') {
69184 titleStandoff = ax._depth;
69185 } else {
69186 var offsetBase = 1.5 * fontSize;
69187 if(isInside) {
69188 offsetBase = 0.5 * fontSize;
69189 if(ax.ticks === 'outside') {
69190 offsetBase += ax.ticklen;
69191 }
69192 }
69193 titleStandoff = 10 + offsetBase + (ax.linewidth ? ax.linewidth - 1 : 0);
69194 }
69195
69196 if(!isInside) {
69197 if(axLetter === 'x') {
69198 titleStandoff += ax.side === 'top' ?
69199 fontSize * (ax.showticklabels ? 1 : 0) :
69200 fontSize * (ax.showticklabels ? 1.5 : 0.5);
69201 } else {
69202 titleStandoff += ax.side === 'right' ?
69203 fontSize * (ax.showticklabels ? 1 : 0.5) :
69204 fontSize * (ax.showticklabels ? 0.5 : 0);
69205 }
69206 }
69207 }
69208
69209 var pos = axes.getPxPosition(gd, ax);
69210 var transform, x, y;
69211
69212 if(axLetter === 'x') {
69213 x = ax._offset + ax._length / 2;
69214 y = (ax.side === 'top') ? pos - titleStandoff : pos + titleStandoff;
69215 } else {
69216 y = ax._offset + ax._length / 2;
69217 x = (ax.side === 'right') ? pos + titleStandoff : pos - titleStandoff;
69218 transform = {rotate: '-90', offset: 0};
69219 }
69220
69221 var avoid;
69222
69223 if(ax.type !== 'multicategory') {
69224 var tickLabels = ax._selections[ax._id + 'tick'];
69225
69226 avoid = {
69227 selection: tickLabels,
69228 side: ax.side
69229 };
69230
69231 if(tickLabels && tickLabels.node() && tickLabels.node().parentNode) {
69232 var translation = Drawing.getTranslate(tickLabels.node().parentNode);
69233 avoid.offsetLeft = translation.x;
69234 avoid.offsetTop = translation.y;
69235 }
69236
69237 if(ax.title.hasOwnProperty('standoff')) {
69238 avoid.pad = 0;
69239 }
69240 }
69241
69242 return Titles.draw(gd, axId + 'title', {
69243 propContainer: ax,
69244 propName: ax._name + '.title.text',
69245 placeholder: fullLayout._dfltTitle[axLetter],
69246 avoid: avoid,
69247 transform: transform,
69248 attributes: {x: x, y: y, 'text-anchor': 'middle'}
69249 });
69250}
69251
69252axes.shouldShowZeroLine = function(gd, ax, counterAxis) {
69253 var rng = Lib.simpleMap(ax.range, ax.r2l);
69254 return (
69255 (rng[0] * rng[1] <= 0) &&
69256 ax.zeroline &&
69257 (ax.type === 'linear' || ax.type === '-') &&
69258 !(ax.rangebreaks && ax.maskBreaks(0) === BADNUM) &&
69259 (
69260 clipEnds(ax, 0) ||
69261 !anyCounterAxLineAtZero(gd, ax, counterAxis, rng) ||
69262 hasBarsOrFill(gd, ax)
69263 )
69264 );
69265};
69266
69267axes.clipEnds = function(ax, vals) {
69268 return vals.filter(function(d) { return clipEnds(ax, d.x); });
69269};
69270
69271function clipEnds(ax, l) {
69272 var p = ax.l2p(l);
69273 return (p > 1 && p < ax._length - 1);
69274}
69275
69276function anyCounterAxLineAtZero(gd, ax, counterAxis, rng) {
69277 var mainCounterAxis = counterAxis._mainAxis;
69278 if(!mainCounterAxis) return;
69279
69280 var fullLayout = gd._fullLayout;
69281 var axLetter = ax._id.charAt(0);
69282 var counterLetter = axes.counterLetter(ax._id);
69283
69284 var zeroPosition = ax._offset + (
69285 ((Math.abs(rng[0]) < Math.abs(rng[1])) === (axLetter === 'x')) ?
69286 0 : ax._length
69287 );
69288
69289 function lineNearZero(ax2) {
69290 if(!ax2.showline || !ax2.linewidth) return false;
69291 var tolerance = Math.max((ax2.linewidth + ax.zerolinewidth) / 2, 1);
69292
69293 function closeEnough(pos2) {
69294 return typeof pos2 === 'number' && Math.abs(pos2 - zeroPosition) < tolerance;
69295 }
69296
69297 if(closeEnough(ax2._mainLinePosition) || closeEnough(ax2._mainMirrorPosition)) {
69298 return true;
69299 }
69300 var linePositions = ax2._linepositions || {};
69301 for(var k in linePositions) {
69302 if(closeEnough(linePositions[k][0]) || closeEnough(linePositions[k][1])) {
69303 return true;
69304 }
69305 }
69306 }
69307
69308 var plotinfo = fullLayout._plots[counterAxis._mainSubplot];
69309 if(!(plotinfo.mainplotinfo || plotinfo).overlays.length) {
69310 return lineNearZero(counterAxis, zeroPosition);
69311 }
69312
69313 var counterLetterAxes = axes.list(gd, counterLetter);
69314 for(var i = 0; i < counterLetterAxes.length; i++) {
69315 var counterAxis2 = counterLetterAxes[i];
69316 if(
69317 counterAxis2._mainAxis === mainCounterAxis &&
69318 lineNearZero(counterAxis2, zeroPosition)
69319 ) {
69320 return true;
69321 }
69322 }
69323}
69324
69325function hasBarsOrFill(gd, ax) {
69326 var fullData = gd._fullData;
69327 var subplot = ax._mainSubplot;
69328 var axLetter = ax._id.charAt(0);
69329
69330 for(var i = 0; i < fullData.length; i++) {
69331 var trace = fullData[i];
69332
69333 if(trace.visible === true && (trace.xaxis + trace.yaxis) === subplot) {
69334 if(
69335 Registry.traceIs(trace, 'bar-like') &&
69336 trace.orientation === {x: 'h', y: 'v'}[axLetter]
69337 ) return true;
69338
69339 if(
69340 trace.fill &&
69341 trace.fill.charAt(trace.fill.length - 1) === axLetter
69342 ) return true;
69343 }
69344 }
69345 return false;
69346}
69347
69348function selectTickLabel(gTick) {
69349 var s = d3.select(gTick);
69350 var mj = s.select('.text-math-group');
69351 return mj.empty() ? s.select('text') : mj;
69352}
69353
69354/**
69355 * Find all margin pushers for 2D axes and reserve them for later use
69356 * Both label and rangeslider automargin calculations happen later so
69357 * we need to explicitly allow their ids in order to not delete them.
69358 *
69359 * TODO: can we pull the actual automargin calls forward to avoid this hack?
69360 * We're probably also doing multiple redraws in this case, would be faster
69361 * if we can just do the whole calculation ahead of time and draw once.
69362 */
69363axes.allowAutoMargin = function(gd) {
69364 var axList = axes.list(gd, '', true);
69365 for(var i = 0; i < axList.length; i++) {
69366 var ax = axList[i];
69367 if(ax.automargin) {
69368 Plots.allowAutoMargin(gd, axAutoMarginID(ax));
69369 if(ax.mirror) {
69370 Plots.allowAutoMargin(gd, axMirrorAutoMarginID(ax));
69371 }
69372 }
69373 if(Registry.getComponentMethod('rangeslider', 'isVisible')(ax)) {
69374 Plots.allowAutoMargin(gd, rangeSliderAutoMarginID(ax));
69375 }
69376 }
69377};
69378
69379function axAutoMarginID(ax) { return ax._id + '.automargin'; }
69380function axMirrorAutoMarginID(ax) { return axAutoMarginID(ax) + '.mirror'; }
69381function rangeSliderAutoMarginID(ax) { return ax._id + '.rangeslider'; }
69382
69383// swap all the presentation attributes of the axes showing these traces
69384axes.swap = function(gd, traces) {
69385 var axGroups = makeAxisGroups(gd, traces);
69386
69387 for(var i = 0; i < axGroups.length; i++) {
69388 swapAxisGroup(gd, axGroups[i].x, axGroups[i].y);
69389 }
69390};
69391
69392function makeAxisGroups(gd, traces) {
69393 var groups = [];
69394 var i, j;
69395
69396 for(i = 0; i < traces.length; i++) {
69397 var groupsi = [];
69398 var xi = gd._fullData[traces[i]].xaxis;
69399 var yi = gd._fullData[traces[i]].yaxis;
69400 if(!xi || !yi) continue; // not a 2D cartesian trace?
69401
69402 for(j = 0; j < groups.length; j++) {
69403 if(groups[j].x.indexOf(xi) !== -1 || groups[j].y.indexOf(yi) !== -1) {
69404 groupsi.push(j);
69405 }
69406 }
69407
69408 if(!groupsi.length) {
69409 groups.push({x: [xi], y: [yi]});
69410 continue;
69411 }
69412
69413 var group0 = groups[groupsi[0]];
69414 var groupj;
69415
69416 if(groupsi.length > 1) {
69417 for(j = 1; j < groupsi.length; j++) {
69418 groupj = groups[groupsi[j]];
69419 mergeAxisGroups(group0.x, groupj.x);
69420 mergeAxisGroups(group0.y, groupj.y);
69421 }
69422 }
69423 mergeAxisGroups(group0.x, [xi]);
69424 mergeAxisGroups(group0.y, [yi]);
69425 }
69426
69427 return groups;
69428}
69429
69430function mergeAxisGroups(intoSet, fromSet) {
69431 for(var i = 0; i < fromSet.length; i++) {
69432 if(intoSet.indexOf(fromSet[i]) === -1) intoSet.push(fromSet[i]);
69433 }
69434}
69435
69436function swapAxisGroup(gd, xIds, yIds) {
69437 var xFullAxes = [];
69438 var yFullAxes = [];
69439 var layout = gd.layout;
69440 var i, j;
69441
69442 for(i = 0; i < xIds.length; i++) xFullAxes.push(axes.getFromId(gd, xIds[i]));
69443 for(i = 0; i < yIds.length; i++) yFullAxes.push(axes.getFromId(gd, yIds[i]));
69444
69445 var allAxKeys = Object.keys(axAttrs);
69446
69447 var noSwapAttrs = [
69448 'anchor', 'domain', 'overlaying', 'position', 'side', 'tickangle', 'editType'
69449 ];
69450 var numericTypes = ['linear', 'log'];
69451
69452 for(i = 0; i < allAxKeys.length; i++) {
69453 var keyi = allAxKeys[i];
69454 var xVal = xFullAxes[0][keyi];
69455 var yVal = yFullAxes[0][keyi];
69456 var allEqual = true;
69457 var coerceLinearX = false;
69458 var coerceLinearY = false;
69459 if(keyi.charAt(0) === '_' || typeof xVal === 'function' ||
69460 noSwapAttrs.indexOf(keyi) !== -1) {
69461 continue;
69462 }
69463 for(j = 1; j < xFullAxes.length && allEqual; j++) {
69464 var xVali = xFullAxes[j][keyi];
69465 if(keyi === 'type' && numericTypes.indexOf(xVal) !== -1 &&
69466 numericTypes.indexOf(xVali) !== -1 && xVal !== xVali) {
69467 // type is special - if we find a mixture of linear and log,
69468 // coerce them all to linear on flipping
69469 coerceLinearX = true;
69470 } else if(xVali !== xVal) allEqual = false;
69471 }
69472 for(j = 1; j < yFullAxes.length && allEqual; j++) {
69473 var yVali = yFullAxes[j][keyi];
69474 if(keyi === 'type' && numericTypes.indexOf(yVal) !== -1 &&
69475 numericTypes.indexOf(yVali) !== -1 && yVal !== yVali) {
69476 // type is special - if we find a mixture of linear and log,
69477 // coerce them all to linear on flipping
69478 coerceLinearY = true;
69479 } else if(yFullAxes[j][keyi] !== yVal) allEqual = false;
69480 }
69481 if(allEqual) {
69482 if(coerceLinearX) layout[xFullAxes[0]._name].type = 'linear';
69483 if(coerceLinearY) layout[yFullAxes[0]._name].type = 'linear';
69484 swapAxisAttrs(layout, keyi, xFullAxes, yFullAxes, gd._fullLayout._dfltTitle);
69485 }
69486 }
69487
69488 // now swap x&y for any annotations anchored to these x & y
69489 for(i = 0; i < gd._fullLayout.annotations.length; i++) {
69490 var ann = gd._fullLayout.annotations[i];
69491 if(xIds.indexOf(ann.xref) !== -1 &&
69492 yIds.indexOf(ann.yref) !== -1) {
69493 Lib.swapAttrs(layout.annotations[i], ['?']);
69494 }
69495 }
69496}
69497
69498function swapAxisAttrs(layout, key, xFullAxes, yFullAxes, dfltTitle) {
69499 // in case the value is the default for either axis,
69500 // look at the first axis in each list and see if
69501 // this key's value is undefined
69502 var np = Lib.nestedProperty;
69503 var xVal = np(layout[xFullAxes[0]._name], key).get();
69504 var yVal = np(layout[yFullAxes[0]._name], key).get();
69505 var i;
69506
69507 if(key === 'title') {
69508 // special handling of placeholder titles
69509 if(xVal && xVal.text === dfltTitle.x) {
69510 xVal.text = dfltTitle.y;
69511 }
69512 if(yVal && yVal.text === dfltTitle.y) {
69513 yVal.text = dfltTitle.x;
69514 }
69515 }
69516
69517 for(i = 0; i < xFullAxes.length; i++) {
69518 np(layout, xFullAxes[i]._name + '.' + key).set(yVal);
69519 }
69520 for(i = 0; i < yFullAxes.length; i++) {
69521 np(layout, yFullAxes[i]._name + '.' + key).set(xVal);
69522 }
69523}
69524
69525function isAngular(ax) {
69526 return ax._id === 'angularaxis';
69527}
69528
69529function moveOutsideBreak(v, ax) {
69530 var len = ax._rangebreaks.length;
69531 for(var k = 0; k < len; k++) {
69532 var brk = ax._rangebreaks[k];
69533 if(v >= brk.min && v < brk.max) {
69534 return brk.max;
69535 }
69536 }
69537 return v;
69538}
69539
69540function insideTicklabelposition(ax) {
69541 return ((ax.ticklabelposition || '').indexOf('inside') !== -1);
69542}
69543
69544function hideCounterAxisInsideTickLabels(ax, opts) {
69545 if(insideTicklabelposition(ax._anchorAxis || {})) {
69546 if(ax._hideCounterAxisInsideTickLabels) {
69547 ax._hideCounterAxisInsideTickLabels(opts);
69548 }
69549 }
69550}
69551
69552},{"../../components/color":157,"../../components/drawing":179,"../../components/titles":255,"../../constants/alignment":262,"../../constants/numerical":267,"../../lib":287,"../../lib/svg_text_utils":310,"../../plots/plots":369,"../../registry":376,"./autorange":333,"./axis_autotype":335,"./axis_ids":338,"./clean_ticks":340,"./layout_attributes":349,"./set_convert":355,"@plotly/d3":20,"fast-isnumeric":33}],335:[function(_dereq_,module,exports){
69553'use strict';
69554
69555var isNumeric = _dereq_('fast-isnumeric');
69556
69557var Lib = _dereq_('../../lib');
69558var BADNUM = _dereq_('../../constants/numerical').BADNUM;
69559
69560var isArrayOrTypedArray = Lib.isArrayOrTypedArray;
69561var isDateTime = Lib.isDateTime;
69562var cleanNumber = Lib.cleanNumber;
69563var round = Math.round;
69564
69565module.exports = function autoType(array, calendar, opts) {
69566 var a = array;
69567
69568 var noMultiCategory = opts.noMultiCategory;
69569 if(isArrayOrTypedArray(a) && !a.length) return '-';
69570 if(!noMultiCategory && multiCategory(a)) return 'multicategory';
69571 if(noMultiCategory && Array.isArray(a[0])) { // no need to flat typed arrays here
69572 var b = [];
69573 for(var i = 0; i < a.length; i++) {
69574 if(isArrayOrTypedArray(a[i])) {
69575 for(var j = 0; j < a[i].length; j++) {
69576 b.push(a[i][j]);
69577 }
69578 }
69579 }
69580 a = b;
69581 }
69582
69583 if(moreDates(a, calendar)) return 'date';
69584
69585 var convertNumeric = opts.autotypenumbers !== 'strict'; // compare against strict, just in case autotypenumbers was not provided in opts
69586 if(category(a, convertNumeric)) return 'category';
69587 if(linearOK(a, convertNumeric)) return 'linear';
69588
69589 return '-';
69590};
69591
69592function hasTypeNumber(v, convertNumeric) {
69593 return convertNumeric ? isNumeric(v) : typeof v === 'number';
69594}
69595
69596// is there at least one number in array? If not, we should leave
69597// ax.type empty so it can be autoset later
69598function linearOK(a, convertNumeric) {
69599 var len = a.length;
69600
69601 for(var i = 0; i < len; i++) {
69602 if(hasTypeNumber(a[i], convertNumeric)) return true;
69603 }
69604
69605 return false;
69606}
69607
69608// does the array a have mostly dates rather than numbers?
69609// note: some values can be neither (such as blanks, text)
69610// 2- or 4-digit integers can be both, so require twice as many
69611// dates as non-dates, to exclude cases with mostly 2 & 4 digit
69612// numbers and a few dates
69613// as with categories, consider DISTINCT values only.
69614function moreDates(a, calendar) {
69615 var len = a.length;
69616
69617 var inc = getIncrement(len);
69618 var dats = 0;
69619 var nums = 0;
69620 var seen = {};
69621
69622 for(var f = 0; f < len; f += inc) {
69623 var i = round(f);
69624 var ai = a[i];
69625 var stri = String(ai);
69626 if(seen[stri]) continue;
69627 seen[stri] = 1;
69628
69629 if(isDateTime(ai, calendar)) dats++;
69630 if(isNumeric(ai)) nums++;
69631 }
69632
69633 return dats > nums * 2;
69634}
69635
69636// return increment to test at most 1000 points, evenly spaced
69637function getIncrement(len) {
69638 return Math.max(1, (len - 1) / 1000);
69639}
69640
69641// are the (x,y)-values in gd.data mostly text?
69642// require twice as many DISTINCT categories as distinct numbers
69643function category(a, convertNumeric) {
69644 var len = a.length;
69645
69646 var inc = getIncrement(len);
69647 var nums = 0;
69648 var cats = 0;
69649 var seen = {};
69650
69651 for(var f = 0; f < len; f += inc) {
69652 var i = round(f);
69653 var ai = a[i];
69654 var stri = String(ai);
69655 if(seen[stri]) continue;
69656 seen[stri] = 1;
69657
69658 var t = typeof ai;
69659 if(t === 'boolean') cats++;
69660 else if(convertNumeric ? cleanNumber(ai) !== BADNUM : t === 'number') nums++;
69661 else if(t === 'string') cats++;
69662 }
69663
69664 return cats > nums * 2;
69665}
69666
69667// very-loose requirements for multicategory,
69668// trace modules that should never auto-type to multicategory
69669// should be declared with 'noMultiCategory'
69670function multiCategory(a) {
69671 return isArrayOrTypedArray(a[0]) && isArrayOrTypedArray(a[1]);
69672}
69673
69674},{"../../constants/numerical":267,"../../lib":287,"fast-isnumeric":33}],336:[function(_dereq_,module,exports){
69675'use strict';
69676
69677var isNumeric = _dereq_('fast-isnumeric');
69678
69679var Registry = _dereq_('../../registry');
69680var Lib = _dereq_('../../lib');
69681
69682var handleArrayContainerDefaults = _dereq_('../array_container_defaults');
69683
69684var layoutAttributes = _dereq_('./layout_attributes');
69685var handleTickValueDefaults = _dereq_('./tick_value_defaults');
69686var handleTickMarkDefaults = _dereq_('./tick_mark_defaults');
69687var handleTickLabelDefaults = _dereq_('./tick_label_defaults');
69688var handleCategoryOrderDefaults = _dereq_('./category_order_defaults');
69689var handleLineGridDefaults = _dereq_('./line_grid_defaults');
69690var setConvert = _dereq_('./set_convert');
69691
69692var DAY_OF_WEEK = _dereq_('./constants').WEEKDAY_PATTERN;
69693var HOUR = _dereq_('./constants').HOUR_PATTERN;
69694
69695/**
69696 * options: object containing:
69697 *
69698 * letter: 'x' or 'y'
69699 * title: name of the axis (ie 'Colorbar') to go in default title
69700 * font: the default font to inherit
69701 * outerTicks: boolean, should ticks default to outside?
69702 * showGrid: boolean, should gridlines be shown by default?
69703 * noHover: boolean, this axis doesn't support hover effects?
69704 * noTickson: boolean, this axis doesn't support 'tickson'
69705 * data: the plot data, used to manage categories
69706 * bgColor: the plot background color, to calculate default gridline colors
69707 * calendar:
69708 * splomStash:
69709 * visibleDflt: boolean
69710 * reverseDflt: boolean
69711 * automargin: boolean
69712 */
69713module.exports = function handleAxisDefaults(containerIn, containerOut, coerce, options, layoutOut) {
69714 var letter = options.letter;
69715 var font = options.font || {};
69716 var splomStash = options.splomStash || {};
69717
69718 var visible = coerce('visible', !options.visibleDflt);
69719
69720 var axTemplate = containerOut._template || {};
69721 var axType = containerOut.type || axTemplate.type || '-';
69722
69723 var ticklabelmode;
69724 if(axType === 'date') {
69725 var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleDefaults');
69726 handleCalendarDefaults(containerIn, containerOut, 'calendar', options.calendar);
69727
69728 if(!options.noTicklabelmode) {
69729 ticklabelmode = coerce('ticklabelmode');
69730 }
69731 }
69732
69733 var ticklabelposition = '';
69734 if(!options.noTicklabelposition || axType === 'multicategory') {
69735 ticklabelposition = Lib.coerce(containerIn, containerOut, {
69736 ticklabelposition: {
69737 valType: 'enumerated',
69738 dflt: 'outside',
69739 values: ticklabelmode === 'period' ? ['outside', 'inside'] :
69740 letter === 'x' ? [
69741 'outside', 'inside',
69742 'outside left', 'inside left',
69743 'outside right', 'inside right'
69744 ] : [
69745 'outside', 'inside',
69746 'outside top', 'inside top',
69747 'outside bottom', 'inside bottom'
69748 ]
69749 }
69750 }, 'ticklabelposition');
69751 }
69752
69753 if(!options.noTicklabeloverflow) {
69754 coerce('ticklabeloverflow',
69755 ticklabelposition.indexOf('inside') !== -1 ?
69756 'hide past domain' :
69757 axType === 'category' ||
69758 axType === 'multicategory' ?
69759 'allow' :
69760 'hide past div'
69761 );
69762 }
69763
69764 setConvert(containerOut, layoutOut);
69765
69766 var autorangeDflt = !containerOut.isValidRange(containerIn.range);
69767 if(autorangeDflt && options.reverseDflt) autorangeDflt = 'reversed';
69768 var autoRange = coerce('autorange', autorangeDflt);
69769 if(autoRange && (axType === 'linear' || axType === '-')) coerce('rangemode');
69770
69771 coerce('range');
69772 containerOut.cleanRange();
69773
69774 handleCategoryOrderDefaults(containerIn, containerOut, coerce, options);
69775
69776 if(axType !== 'category' && !options.noHover) coerce('hoverformat');
69777
69778 var dfltColor = coerce('color');
69779 // if axis.color was provided, use it for fonts too; otherwise,
69780 // inherit from global font color in case that was provided.
69781 // Compare to dflt rather than to containerIn, so we can provide color via
69782 // template too.
69783 var dfltFontColor = (dfltColor !== layoutAttributes.color.dflt) ? dfltColor : font.color;
69784 // try to get default title from splom trace, fallback to graph-wide value
69785 var dfltTitle = splomStash.label || layoutOut._dfltTitle[letter];
69786
69787 handleTickLabelDefaults(containerIn, containerOut, coerce, axType, options, {pass: 1});
69788 if(!visible) return containerOut;
69789
69790 coerce('title.text', dfltTitle);
69791 Lib.coerceFont(coerce, 'title.font', {
69792 family: font.family,
69793 size: Lib.bigFont(font.size),
69794 color: dfltFontColor
69795 });
69796
69797 handleTickValueDefaults(containerIn, containerOut, coerce, axType);
69798 handleTickLabelDefaults(containerIn, containerOut, coerce, axType, options, {pass: 2});
69799 handleTickMarkDefaults(containerIn, containerOut, coerce, options);
69800 handleLineGridDefaults(containerIn, containerOut, coerce, {
69801 dfltColor: dfltColor,
69802 bgColor: options.bgColor,
69803 showGrid: options.showGrid,
69804 attributes: layoutAttributes
69805 });
69806
69807 if(containerOut.showline || containerOut.ticks) coerce('mirror');
69808
69809 if(options.automargin) coerce('automargin');
69810
69811 var isMultiCategory = axType === 'multicategory';
69812
69813 if(!options.noTickson &&
69814 (axType === 'category' || isMultiCategory) &&
69815 (containerOut.ticks || containerOut.showgrid)
69816 ) {
69817 var ticksonDflt;
69818 if(isMultiCategory) ticksonDflt = 'boundaries';
69819 var tickson = coerce('tickson', ticksonDflt);
69820 if(tickson === 'boundaries') {
69821 delete containerOut.ticklabelposition;
69822 }
69823 }
69824
69825 if(isMultiCategory) {
69826 var showDividers = coerce('showdividers');
69827 if(showDividers) {
69828 coerce('dividercolor');
69829 coerce('dividerwidth');
69830 }
69831 }
69832
69833 if(axType === 'date') {
69834 handleArrayContainerDefaults(containerIn, containerOut, {
69835 name: 'rangebreaks',
69836 inclusionAttr: 'enabled',
69837 handleItemDefaults: rangebreaksDefaults
69838 });
69839
69840 if(!containerOut.rangebreaks.length) {
69841 delete containerOut.rangebreaks;
69842 } else {
69843 for(var k = 0; k < containerOut.rangebreaks.length; k++) {
69844 if(containerOut.rangebreaks[k].pattern === DAY_OF_WEEK) {
69845 containerOut._hasDayOfWeekBreaks = true;
69846 break;
69847 }
69848 }
69849
69850 setConvert(containerOut, layoutOut);
69851
69852 if(layoutOut._has('scattergl') || layoutOut._has('splom')) {
69853 for(var i = 0; i < options.data.length; i++) {
69854 var trace = options.data[i];
69855 if(trace.type === 'scattergl' || trace.type === 'splom') {
69856 trace.visible = false;
69857 Lib.warn(trace.type +
69858 ' traces do not work on axes with rangebreaks.' +
69859 ' Setting trace ' + trace.index + ' to `visible: false`.');
69860 }
69861 }
69862 }
69863 }
69864 }
69865
69866 return containerOut;
69867};
69868
69869function rangebreaksDefaults(itemIn, itemOut, containerOut) {
69870 function coerce(attr, dflt) {
69871 return Lib.coerce(itemIn, itemOut, layoutAttributes.rangebreaks, attr, dflt);
69872 }
69873
69874 var enabled = coerce('enabled');
69875
69876 if(enabled) {
69877 var bnds = coerce('bounds');
69878 if(bnds && bnds.length >= 2) {
69879 var dfltPattern = '';
69880 var i, q;
69881 if(bnds.length === 2) {
69882 for(i = 0; i < 2; i++) {
69883 q = indexOfDay(bnds[i]);
69884 if(q) {
69885 dfltPattern = DAY_OF_WEEK;
69886 break;
69887 }
69888 }
69889 }
69890 var pattern = coerce('pattern', dfltPattern);
69891 if(pattern === DAY_OF_WEEK) {
69892 for(i = 0; i < 2; i++) {
69893 q = indexOfDay(bnds[i]);
69894 if(q) {
69895 // convert to integers i.e 'Sunday' --> 0
69896 itemOut.bounds[i] = bnds[i] = q - 1;
69897 }
69898 }
69899 }
69900 if(pattern) {
69901 // ensure types and ranges
69902 for(i = 0; i < 2; i++) {
69903 q = bnds[i];
69904 switch(pattern) {
69905 case DAY_OF_WEEK :
69906 if(!isNumeric(q)) {
69907 itemOut.enabled = false;
69908 return;
69909 }
69910 q = +q;
69911
69912 if(
69913 q !== Math.floor(q) || // don't accept fractional days for mow
69914 q < 0 || q >= 7
69915 ) {
69916 itemOut.enabled = false;
69917 return;
69918 }
69919 // use number
69920 itemOut.bounds[i] = bnds[i] = q;
69921 break;
69922
69923 case HOUR :
69924 if(!isNumeric(q)) {
69925 itemOut.enabled = false;
69926 return;
69927 }
69928 q = +q;
69929
69930 if(q < 0 || q > 24) { // accept 24
69931 itemOut.enabled = false;
69932 return;
69933 }
69934 // use number
69935 itemOut.bounds[i] = bnds[i] = q;
69936 break;
69937 }
69938 }
69939 }
69940
69941 if(containerOut.autorange === false) {
69942 var rng = containerOut.range;
69943
69944 // if bounds are bigger than the (set) range, disable break
69945 if(rng[0] < rng[1]) {
69946 if(bnds[0] < rng[0] && bnds[1] > rng[1]) {
69947 itemOut.enabled = false;
69948 return;
69949 }
69950 } else if(bnds[0] > rng[0] && bnds[1] < rng[1]) {
69951 itemOut.enabled = false;
69952 return;
69953 }
69954 }
69955 } else {
69956 var values = coerce('values');
69957
69958 if(values && values.length) {
69959 coerce('dvalue');
69960 } else {
69961 itemOut.enabled = false;
69962 return;
69963 }
69964 }
69965 }
69966}
69967
69968// these numbers are one more than what bounds would be mapped to
69969var dayStrToNum = {
69970 sun: 1,
69971 mon: 2,
69972 tue: 3,
69973 wed: 4,
69974 thu: 5,
69975 fri: 6,
69976 sat: 7
69977};
69978
69979function indexOfDay(v) {
69980 if(typeof v !== 'string') return;
69981 return dayStrToNum[
69982 v.substr(0, 3).toLowerCase()
69983 ];
69984}
69985
69986},{"../../lib":287,"../../registry":376,"../array_container_defaults":329,"./category_order_defaults":339,"./constants":341,"./layout_attributes":349,"./line_grid_defaults":351,"./set_convert":355,"./tick_label_defaults":356,"./tick_mark_defaults":357,"./tick_value_defaults":358,"fast-isnumeric":33}],337:[function(_dereq_,module,exports){
69987'use strict';
69988
69989var docs = _dereq_('../../constants/docs');
69990var FORMAT_LINK = docs.FORMAT_LINK;
69991var DATE_FORMAT_LINK = docs.DATE_FORMAT_LINK;
69992
69993function axisHoverFormat(x, noDates) {
69994 return {
69995 valType: 'string',
69996 dflt: '',
69997 editType: 'none',
69998 description: (
69999 noDates ? descriptionOnlyNumbers : descriptionWithDates
70000 )('hover text', x) + [
70001 'By default the values are formatted using ' + (
70002 noDates ?
70003 'generic number format' :
70004 ('`' + x + 'axis.hoverformat`')
70005 ) + '.',
70006 ].join(' ')
70007 };
70008}
70009
70010function descriptionOnlyNumbers(label, x) {
70011 return [
70012 'Sets the ' + label + ' formatting rule' + (x ? 'for `' + x + '` ' : ''),
70013 'using d3 formatting mini-languages',
70014 'which are very similar to those in Python. For numbers, see: ' + FORMAT_LINK + '.'
70015 ].join(' ');
70016}
70017
70018function descriptionWithDates(label, x) {
70019 return descriptionOnlyNumbers(label, x) + [
70020 ' And for dates see: ' + DATE_FORMAT_LINK + '.',
70021 'We add two items to d3\'s date formatter:',
70022 '*%h* for half of the year as a decimal number as well as',
70023 '*%{n}f* for fractional seconds',
70024 'with n digits. For example, *2016-10-13 09:15:23.456* with tickformat',
70025 '*%H~%M~%S.%2f* would display *09~15~23.46*'
70026 ].join(' ');
70027}
70028
70029module.exports = {
70030 axisHoverFormat: axisHoverFormat,
70031 descriptionOnlyNumbers: descriptionOnlyNumbers,
70032 descriptionWithDates: descriptionWithDates
70033};
70034
70035},{"../../constants/docs":264}],338:[function(_dereq_,module,exports){
70036'use strict';
70037
70038var Registry = _dereq_('../../registry');
70039
70040var constants = _dereq_('./constants');
70041
70042
70043// convert between axis names (xaxis, xaxis2, etc, elements of gd.layout)
70044// and axis id's (x, x2, etc). Would probably have ditched 'xaxis'
70045// completely in favor of just 'x' if it weren't ingrained in the API etc.
70046exports.id2name = function id2name(id) {
70047 if(typeof id !== 'string' || !id.match(constants.AX_ID_PATTERN)) return;
70048 var axNum = id.split(' ')[0].substr(1);
70049 if(axNum === '1') axNum = '';
70050 return id.charAt(0) + 'axis' + axNum;
70051};
70052
70053exports.name2id = function name2id(name) {
70054 if(!name.match(constants.AX_NAME_PATTERN)) return;
70055 var axNum = name.substr(5);
70056 if(axNum === '1') axNum = '';
70057 return name.charAt(0) + axNum;
70058};
70059
70060/*
70061 * Cleans up the number of an axis, e.g., 'x002'->'x2', 'x0'->'x', 'x1' -> 'x',
70062 * etc.
70063 * If domainId is true, then id could be a domain reference and if it is, the
70064 * ' domain' part is kept at the end of the axis ID string.
70065 */
70066exports.cleanId = function cleanId(id, axLetter, domainId) {
70067 var domainTest = /( domain)$/.test(id);
70068 if(typeof id !== 'string' || !id.match(constants.AX_ID_PATTERN)) return;
70069 if(axLetter && id.charAt(0) !== axLetter) return;
70070 if(domainTest && (!domainId)) return;
70071 var axNum = id.split(' ')[0].substr(1).replace(/^0+/, '');
70072 if(axNum === '1') axNum = '';
70073 return id.charAt(0) + axNum + (domainTest && domainId ? ' domain' : '');
70074};
70075
70076// get all axis objects, as restricted in listNames
70077exports.list = function(gd, axLetter, only2d) {
70078 var fullLayout = gd._fullLayout;
70079 if(!fullLayout) return [];
70080
70081 var idList = exports.listIds(gd, axLetter);
70082 var out = new Array(idList.length);
70083 var i;
70084
70085 for(i = 0; i < idList.length; i++) {
70086 var idi = idList[i];
70087 out[i] = fullLayout[idi.charAt(0) + 'axis' + idi.substr(1)];
70088 }
70089
70090 if(!only2d) {
70091 var sceneIds3D = fullLayout._subplots.gl3d || [];
70092
70093 for(i = 0; i < sceneIds3D.length; i++) {
70094 var scene = fullLayout[sceneIds3D[i]];
70095
70096 if(axLetter) out.push(scene[axLetter + 'axis']);
70097 else out.push(scene.xaxis, scene.yaxis, scene.zaxis);
70098 }
70099 }
70100
70101 return out;
70102};
70103
70104// get all axis ids, optionally restricted by letter
70105// this only makes sense for 2d axes
70106exports.listIds = function(gd, axLetter) {
70107 var fullLayout = gd._fullLayout;
70108 if(!fullLayout) return [];
70109
70110 var subplotLists = fullLayout._subplots;
70111 if(axLetter) return subplotLists[axLetter + 'axis'];
70112 return subplotLists.xaxis.concat(subplotLists.yaxis);
70113};
70114
70115// get an axis object from its id 'x','x2' etc
70116// optionally, id can be a subplot (ie 'x2y3') and type gets x or y from it
70117exports.getFromId = function(gd, id, type) {
70118 var fullLayout = gd._fullLayout;
70119 // remove "domain" suffix
70120 id = ((id === undefined) || (typeof(id) !== 'string')) ? id : id.replace(' domain', '');
70121
70122 if(type === 'x') id = id.replace(/y[0-9]*/, '');
70123 else if(type === 'y') id = id.replace(/x[0-9]*/, '');
70124
70125 return fullLayout[exports.id2name(id)];
70126};
70127
70128// get an axis object of specified type from the containing trace
70129exports.getFromTrace = function(gd, fullTrace, type) {
70130 var fullLayout = gd._fullLayout;
70131 var ax = null;
70132
70133 if(Registry.traceIs(fullTrace, 'gl3d')) {
70134 var scene = fullTrace.scene;
70135 if(scene.substr(0, 5) === 'scene') {
70136 ax = fullLayout[scene][type + 'axis'];
70137 }
70138 } else {
70139 ax = exports.getFromId(gd, fullTrace[type + 'axis'] || type);
70140 }
70141
70142 return ax;
70143};
70144
70145// sort x, x2, x10, y, y2, y10...
70146exports.idSort = function(id1, id2) {
70147 var letter1 = id1.charAt(0);
70148 var letter2 = id2.charAt(0);
70149 if(letter1 !== letter2) return letter1 > letter2 ? 1 : -1;
70150 return +(id1.substr(1) || 1) - +(id2.substr(1) || 1);
70151};
70152
70153/*
70154 * An axis reference (e.g., the contents at the 'xref' key of an object) might
70155 * have extra information appended. Extract the axis ID only.
70156 *
70157 * ar: the axis reference string
70158 *
70159 */
70160exports.ref2id = function(ar) {
70161 // This assumes ar has been coerced via coerceRef, and uses the shortcut of
70162 // checking if the first letter matches [xyz] to determine if it should
70163 // return the axis ID. Otherwise it returns false.
70164 return (/^[xyz]/.test(ar)) ? ar.split(' ')[0] : false;
70165};
70166
70167function isFound(axId, list) {
70168 if(list && list.length) {
70169 for(var i = 0; i < list.length; i++) {
70170 if(list[i][axId]) return true;
70171 }
70172 }
70173 return false;
70174}
70175
70176exports.isLinked = function(fullLayout, axId) {
70177 return (
70178 isFound(axId, fullLayout._axisMatchGroups) ||
70179 isFound(axId, fullLayout._axisConstraintGroups)
70180 );
70181};
70182
70183},{"../../registry":376,"./constants":341}],339:[function(_dereq_,module,exports){
70184'use strict';
70185
70186function findCategories(ax, opts) {
70187 var dataAttr = opts.dataAttr || ax._id.charAt(0);
70188 var lookup = {};
70189 var axData;
70190 var i, j;
70191
70192 if(opts.axData) {
70193 // non-x/y case
70194 axData = opts.axData;
70195 } else {
70196 // x/y case
70197 axData = [];
70198 for(i = 0; i < opts.data.length; i++) {
70199 var trace = opts.data[i];
70200 if(trace[dataAttr + 'axis'] === ax._id) {
70201 axData.push(trace);
70202 }
70203 }
70204 }
70205
70206 for(i = 0; i < axData.length; i++) {
70207 var vals = axData[i][dataAttr];
70208 for(j = 0; j < vals.length; j++) {
70209 var v = vals[j];
70210 if(v !== null && v !== undefined) {
70211 lookup[v] = 1;
70212 }
70213 }
70214 }
70215
70216 return Object.keys(lookup);
70217}
70218
70219/**
70220 * Fills in category* default and initial categories.
70221 *
70222 * @param {object} containerIn : input axis object
70223 * @param {object} containerOut : full axis object
70224 * @param {function} coerce : Lib.coerce fn wrapper
70225 * @param {object} opts :
70226 * - data {array} : (full) data trace
70227 * OR
70228 * - axData {array} : (full) data associated with axis being coerced here
70229 * - dataAttr {string} : attribute name corresponding to coordinate array
70230 */
70231module.exports = function handleCategoryOrderDefaults(containerIn, containerOut, coerce, opts) {
70232 if(containerOut.type !== 'category') return;
70233
70234 var arrayIn = containerIn.categoryarray;
70235 var isValidArray = (Array.isArray(arrayIn) && arrayIn.length > 0);
70236
70237 // override default 'categoryorder' value when non-empty array is supplied
70238 var orderDefault;
70239 if(isValidArray) orderDefault = 'array';
70240
70241 var order = coerce('categoryorder', orderDefault);
70242 var array;
70243
70244 // coerce 'categoryarray' only in array order case
70245 if(order === 'array') {
70246 array = coerce('categoryarray');
70247 }
70248
70249 // cannot set 'categoryorder' to 'array' with an invalid 'categoryarray'
70250 if(!isValidArray && order === 'array') {
70251 order = containerOut.categoryorder = 'trace';
70252 }
70253
70254 // set up things for makeCalcdata
70255 if(order === 'trace') {
70256 containerOut._initialCategories = [];
70257 } else if(order === 'array') {
70258 containerOut._initialCategories = array.slice();
70259 } else {
70260 array = findCategories(containerOut, opts).sort();
70261 if(order === 'category ascending') {
70262 containerOut._initialCategories = array;
70263 } else if(order === 'category descending') {
70264 containerOut._initialCategories = array.reverse();
70265 }
70266 }
70267};
70268
70269},{}],340:[function(_dereq_,module,exports){
70270'use strict';
70271
70272var isNumeric = _dereq_('fast-isnumeric');
70273var Lib = _dereq_('../../lib');
70274var constants = _dereq_('../../constants/numerical');
70275var ONEDAY = constants.ONEDAY;
70276var ONEWEEK = constants.ONEWEEK;
70277
70278/**
70279 * Return a validated dtick value for this axis
70280 *
70281 * @param {any} dtick: the candidate dtick. valid values are numbers and strings,
70282 * and further constrained depending on the axis type.
70283 * @param {string} axType: the axis type
70284 */
70285exports.dtick = function(dtick, axType) {
70286 var isLog = axType === 'log';
70287 var isDate = axType === 'date';
70288 var isCat = axType === 'category';
70289 var dtickDflt = isDate ? ONEDAY : 1;
70290
70291 if(!dtick) return dtickDflt;
70292
70293 if(isNumeric(dtick)) {
70294 dtick = Number(dtick);
70295 if(dtick <= 0) return dtickDflt;
70296 if(isCat) {
70297 // category dtick must be positive integers
70298 return Math.max(1, Math.round(dtick));
70299 }
70300 if(isDate) {
70301 // date dtick must be at least 0.1ms (our current precision)
70302 return Math.max(0.1, dtick);
70303 }
70304 return dtick;
70305 }
70306
70307 if(typeof dtick !== 'string' || !(isDate || isLog)) {
70308 return dtickDflt;
70309 }
70310
70311 var prefix = dtick.charAt(0);
70312 var dtickNum = dtick.substr(1);
70313 dtickNum = isNumeric(dtickNum) ? Number(dtickNum) : 0;
70314
70315 if((dtickNum <= 0) || !(
70316 // "M<n>" gives ticks every (integer) n months
70317 (isDate && prefix === 'M' && dtickNum === Math.round(dtickNum)) ||
70318 // "L<f>" gives ticks linearly spaced in data (not in position) every (float) f
70319 (isLog && prefix === 'L') ||
70320 // "D1" gives powers of 10 with all small digits between, "D2" gives only 2 and 5
70321 (isLog && prefix === 'D' && (dtickNum === 1 || dtickNum === 2))
70322 )) {
70323 return dtickDflt;
70324 }
70325
70326 return dtick;
70327};
70328
70329/**
70330 * Return a validated tick0 for this axis
70331 *
70332 * @param {any} tick0: the candidate tick0. Valid values are numbers and strings,
70333 * further constrained depending on the axis type
70334 * @param {string} axType: the axis type
70335 * @param {string} calendar: for date axes, the calendar to validate/convert with
70336 * @param {any} dtick: an already valid dtick. Only used for D1 and D2 log dticks,
70337 * which do not support tick0 at all.
70338 */
70339exports.tick0 = function(tick0, axType, calendar, dtick) {
70340 if(axType === 'date') {
70341 return Lib.cleanDate(tick0,
70342 Lib.dateTick0(calendar, (dtick % ONEWEEK === 0) ? 1 : 0)
70343 );
70344 }
70345 if(dtick === 'D1' || dtick === 'D2') {
70346 // D1 and D2 modes ignore tick0 entirely
70347 return undefined;
70348 }
70349 // Aside from date axes, tick0 must be numeric
70350 return isNumeric(tick0) ? Number(tick0) : 0;
70351};
70352
70353},{"../../constants/numerical":267,"../../lib":287,"fast-isnumeric":33}],341:[function(_dereq_,module,exports){
70354'use strict';
70355
70356var counterRegex = _dereq_('../../lib/regex').counter;
70357
70358module.exports = {
70359 idRegex: {
70360 x: counterRegex('x', '( domain)?'),
70361 y: counterRegex('y', '( domain)?')
70362 },
70363
70364 attrRegex: counterRegex('[xy]axis'),
70365
70366 // axis match regular expression
70367 xAxisMatch: counterRegex('xaxis'),
70368 yAxisMatch: counterRegex('yaxis'),
70369
70370 // pattern matching axis ids and names
70371 // note that this is more permissive than counterRegex, as
70372 // id2name, name2id, and cleanId accept "x1" etc
70373 AX_ID_PATTERN: /^[xyz][0-9]*( domain)?$/,
70374 AX_NAME_PATTERN: /^[xyz]axis[0-9]*$/,
70375
70376 // and for 2D subplots
70377 SUBPLOT_PATTERN: /^x([0-9]*)y([0-9]*)$/,
70378
70379 HOUR_PATTERN: 'hour',
70380 WEEKDAY_PATTERN: 'day of week',
70381
70382 // pixels to move mouse before you stop clamping to starting point
70383 MINDRAG: 8,
70384
70385 // smallest dimension allowed for a select box
70386 MINSELECT: 12,
70387
70388 // smallest dimension allowed for a zoombox
70389 MINZOOM: 20,
70390
70391 // width of axis drag regions
70392 DRAGGERSIZE: 20,
70393
70394 // max pixels off straight before a lasso select line counts as bent
70395 BENDPX: 1.5,
70396
70397 // delay before a redraw (relayout) after smooth panning and zooming
70398 REDRAWDELAY: 50,
70399
70400 // throttling limit (ms) for selectPoints calls
70401 SELECTDELAY: 100,
70402
70403 // cache ID suffix for throttle
70404 SELECTID: '-select',
70405
70406 // last resort axis ranges for x and y axes if we have no data
70407 DFLTRANGEX: [-1, 6],
70408 DFLTRANGEY: [-1, 4],
70409
70410 // Layers to keep trace types in the right order
70411 // N.B. each 'unique' plot method must have its own layer
70412 traceLayerClasses: [
70413 'imagelayer',
70414 'heatmaplayer',
70415 'contourcarpetlayer', 'contourlayer',
70416 'funnellayer', 'waterfalllayer', 'barlayer',
70417 'carpetlayer',
70418 'violinlayer',
70419 'boxlayer',
70420 'ohlclayer',
70421 'scattercarpetlayer', 'scatterlayer'
70422 ],
70423
70424 clipOnAxisFalseQuery: [
70425 '.scatterlayer',
70426 '.barlayer',
70427 '.funnellayer',
70428 '.waterfalllayer'
70429 ],
70430
70431 layerValue2layerClass: {
70432 'above traces': 'above',
70433 'below traces': 'below'
70434 }
70435};
70436
70437},{"../../lib/regex":303}],342:[function(_dereq_,module,exports){
70438'use strict';
70439
70440var Lib = _dereq_('../../lib');
70441
70442var autorange = _dereq_('./autorange');
70443var id2name = _dereq_('./axis_ids').id2name;
70444var layoutAttributes = _dereq_('./layout_attributes');
70445var scaleZoom = _dereq_('./scale_zoom');
70446var setConvert = _dereq_('./set_convert');
70447
70448var ALMOST_EQUAL = _dereq_('../../constants/numerical').ALMOST_EQUAL;
70449var FROM_BL = _dereq_('../../constants/alignment').FROM_BL;
70450
70451exports.handleDefaults = function(layoutIn, layoutOut, opts) {
70452 var axIds = opts.axIds;
70453 var axHasImage = opts.axHasImage;
70454
70455 // sets of axes linked by `scaleanchor` OR `matches` along with the
70456 // scaleratios compounded together, populated in handleConstraintDefaults
70457 var constraintGroups = layoutOut._axisConstraintGroups = [];
70458 // similar to _axisConstraintGroups, but only matching axes
70459 var matchGroups = layoutOut._axisMatchGroups = [];
70460
70461 var i, group, axId, axName, axIn, axOut, attr, val;
70462
70463 for(i = 0; i < axIds.length; i++) {
70464 axName = id2name(axIds[i]);
70465 axIn = layoutIn[axName];
70466 axOut = layoutOut[axName];
70467
70468 handleOneAxDefaults(axIn, axOut, {
70469 axIds: axIds,
70470 layoutOut: layoutOut,
70471 hasImage: axHasImage[axName]
70472 });
70473 }
70474
70475 // save matchGroup on each matching axis
70476 function stash(groups, stashAttr) {
70477 for(i = 0; i < groups.length; i++) {
70478 group = groups[i];
70479 for(axId in group) {
70480 layoutOut[id2name(axId)][stashAttr] = group;
70481 }
70482 }
70483 }
70484 stash(matchGroups, '_matchGroup');
70485
70486 // If any axis in a constraint group is fixedrange, they all get fixed
70487 // This covers matches axes, as they're now in the constraintgroup too
70488 // and have not yet been removed (if the group is *only* matching)
70489 for(i = 0; i < constraintGroups.length; i++) {
70490 group = constraintGroups[i];
70491 for(axId in group) {
70492 axOut = layoutOut[id2name(axId)];
70493 if(axOut.fixedrange) {
70494 for(var axId2 in group) {
70495 var axName2 = id2name(axId2);
70496 if((layoutIn[axName2] || {}).fixedrange === false) {
70497 Lib.warn(
70498 'fixedrange was specified as false for axis ' +
70499 axName2 + ' but was overridden because another ' +
70500 'axis in its constraint group has fixedrange true'
70501 );
70502 }
70503 layoutOut[axName2].fixedrange = true;
70504 }
70505 break;
70506 }
70507 }
70508 }
70509
70510 // remove constraint groups that simply duplicate match groups
70511 i = 0;
70512 while(i < constraintGroups.length) {
70513 group = constraintGroups[i];
70514 for(axId in group) {
70515 axOut = layoutOut[id2name(axId)];
70516 if(axOut._matchGroup && Object.keys(axOut._matchGroup).length === Object.keys(group).length) {
70517 constraintGroups.splice(i, 1);
70518 i--;
70519 }
70520 break;
70521 }
70522 i++;
70523 }
70524
70525 // save constraintGroup on each constrained axis
70526 stash(constraintGroups, '_constraintGroup');
70527
70528 // make sure `matching` axes share values of necessary attributes
70529 // Precedence (base axis is the one that doesn't list a `matches`, ie others
70530 // all point to it):
70531 // (1) explicitly defined value in the base axis
70532 // (2) explicitly defined in another axis (arbitrary order)
70533 // (3) default in the base axis
70534 var matchAttrs = [
70535 'constrain',
70536 'range',
70537 'autorange',
70538 'rangemode',
70539 'rangebreaks',
70540 'categoryorder',
70541 'categoryarray'
70542 ];
70543 var hasRange = false;
70544 var hasDayOfWeekBreaks = false;
70545
70546 function setAttrVal() {
70547 val = axOut[attr];
70548 if(attr === 'rangebreaks') {
70549 hasDayOfWeekBreaks = axOut._hasDayOfWeekBreaks;
70550 }
70551 }
70552
70553 for(i = 0; i < matchGroups.length; i++) {
70554 group = matchGroups[i];
70555
70556 // find 'matching' range attrs
70557 for(var j = 0; j < matchAttrs.length; j++) {
70558 attr = matchAttrs[j];
70559 val = null;
70560 var baseAx;
70561 for(axId in group) {
70562 axName = id2name(axId);
70563 axIn = layoutIn[axName];
70564 axOut = layoutOut[axName];
70565 if(!(attr in axOut)) {
70566 continue;
70567 }
70568 if(!axOut.matches) {
70569 baseAx = axOut;
70570 // top priority: explicit value in base axis
70571 if(attr in axIn) {
70572 setAttrVal();
70573 break;
70574 }
70575 }
70576 if(val === null && attr in axIn) {
70577 // second priority: first explicit value in another axis
70578 setAttrVal();
70579 }
70580 }
70581
70582 // special logic for coupling of range and autorange
70583 // if nobody explicitly specifies autorange, but someone does
70584 // explicitly specify range, autorange must be disabled.
70585 if(attr === 'range' && val) {
70586 hasRange = true;
70587 }
70588 if(attr === 'autorange' && val === null && hasRange) {
70589 val = false;
70590 }
70591
70592 if(val === null && attr in baseAx) {
70593 // fallback: default value in base axis
70594 val = baseAx[attr];
70595 }
70596 // but we still might not have a value, which is fine.
70597 if(val !== null) {
70598 for(axId in group) {
70599 axOut = layoutOut[id2name(axId)];
70600 axOut[attr] = attr === 'range' ? val.slice() : val;
70601
70602 if(attr === 'rangebreaks') {
70603 axOut._hasDayOfWeekBreaks = hasDayOfWeekBreaks;
70604 setConvert(axOut, layoutOut);
70605 }
70606 }
70607 }
70608 }
70609 }
70610};
70611
70612function handleOneAxDefaults(axIn, axOut, opts) {
70613 var axIds = opts.axIds;
70614 var layoutOut = opts.layoutOut;
70615 var hasImage = opts.hasImage;
70616 var constraintGroups = layoutOut._axisConstraintGroups;
70617 var matchGroups = layoutOut._axisMatchGroups;
70618 var axId = axOut._id;
70619 var axLetter = axId.charAt(0);
70620 var splomStash = ((layoutOut._splomAxes || {})[axLetter] || {})[axId] || {};
70621 var thisID = axOut._id;
70622 var isX = thisID.charAt(0) === 'x';
70623
70624 // Clear _matchGroup & _constraintGroup so relinkPrivateKeys doesn't keep
70625 // an old one around. If this axis is in a group we'll set this again later
70626 axOut._matchGroup = null;
70627 axOut._constraintGroup = null;
70628
70629 function coerce(attr, dflt) {
70630 return Lib.coerce(axIn, axOut, layoutAttributes, attr, dflt);
70631 }
70632
70633 // coerce the constraint mechanics even if this axis has no scaleanchor
70634 // because it may be the anchor of another axis.
70635 coerce('constrain', hasImage ? 'domain' : 'range');
70636 Lib.coerce(axIn, axOut, {
70637 constraintoward: {
70638 valType: 'enumerated',
70639 values: isX ? ['left', 'center', 'right'] : ['bottom', 'middle', 'top'],
70640 dflt: isX ? 'center' : 'middle'
70641 }
70642 }, 'constraintoward');
70643
70644 // If this axis is already part of a constraint group, we can't
70645 // scaleanchor any other axis in that group, or we'd make a loop.
70646 // Filter axIds to enforce this, also matching axis types.
70647 var thisType = axOut.type;
70648 var i, idi;
70649
70650 var linkableAxes = [];
70651 for(i = 0; i < axIds.length; i++) {
70652 idi = axIds[i];
70653 if(idi === thisID) continue;
70654
70655 var axi = layoutOut[id2name(idi)];
70656 if(axi.type === thisType) {
70657 linkableAxes.push(idi);
70658 }
70659 }
70660
70661 var thisGroup = getConstraintGroup(constraintGroups, thisID);
70662 if(thisGroup) {
70663 var linkableAxesNoLoops = [];
70664 for(i = 0; i < linkableAxes.length; i++) {
70665 idi = linkableAxes[i];
70666 if(!thisGroup[idi]) linkableAxesNoLoops.push(idi);
70667 }
70668 linkableAxes = linkableAxesNoLoops;
70669 }
70670
70671 var canLink = linkableAxes.length;
70672
70673 var matches, scaleanchor;
70674
70675 if(canLink && (axIn.matches || splomStash.matches)) {
70676 matches = Lib.coerce(axIn, axOut, {
70677 matches: {
70678 valType: 'enumerated',
70679 values: linkableAxes,
70680 dflt: linkableAxes.indexOf(splomStash.matches) !== -1 ? splomStash.matches : undefined
70681 }
70682 }, 'matches');
70683 }
70684
70685 // 'matches' wins over 'scaleanchor' - each axis can only specify one
70686 // constraint, but you can chain matches and scaleanchor constraints by
70687 // specifying them in separate axes.
70688 var scaleanchorDflt = hasImage && !isX ? axOut.anchor : undefined;
70689 if(canLink && !matches && (axIn.scaleanchor || scaleanchorDflt)) {
70690 scaleanchor = Lib.coerce(axIn, axOut, {
70691 scaleanchor: {
70692 valType: 'enumerated',
70693 values: linkableAxes
70694 }
70695 }, 'scaleanchor', scaleanchorDflt);
70696 }
70697
70698 if(matches) {
70699 axOut._matchGroup = updateConstraintGroups(matchGroups, thisID, matches, 1);
70700
70701 // Also include match constraints in the scale groups
70702 var matchedAx = layoutOut[id2name(matches)];
70703 var matchRatio = extent(layoutOut, axOut) / extent(layoutOut, matchedAx);
70704 if(isX !== (matches.charAt(0) === 'x')) {
70705 // We don't yet know the actual scale ratio of x/y matches constraints,
70706 // due to possible automargins, so just leave a placeholder for this:
70707 // 'x' means "x size over y size", 'y' means the inverse.
70708 // in principle in the constraint group you could get multiple of these.
70709 matchRatio = (isX ? 'x' : 'y') + matchRatio;
70710 }
70711 updateConstraintGroups(constraintGroups, thisID, matches, matchRatio);
70712 } else if(axIn.matches && axIds.indexOf(axIn.matches) !== -1) {
70713 Lib.warn('ignored ' + axOut._name + '.matches: "' +
70714 axIn.matches + '" to avoid an infinite loop');
70715 }
70716
70717 if(scaleanchor) {
70718 var scaleratio = coerce('scaleratio');
70719
70720 // TODO: I suppose I could do attribute.min: Number.MIN_VALUE to avoid zero,
70721 // but that seems hacky. Better way to say "must be a positive number"?
70722 // Of course if you use several super-tiny values you could eventually
70723 // force a product of these to zero and all hell would break loose...
70724 // Likewise with super-huge values.
70725 if(!scaleratio) scaleratio = axOut.scaleratio = 1;
70726
70727 updateConstraintGroups(constraintGroups, thisID, scaleanchor, scaleratio);
70728 } else if(axIn.scaleanchor && axIds.indexOf(axIn.scaleanchor) !== -1) {
70729 Lib.warn('ignored ' + axOut._name + '.scaleanchor: "' +
70730 axIn.scaleanchor + '" to avoid either an infinite loop ' +
70731 'and possibly inconsistent scaleratios, or because this axis ' +
70732 'declares a *matches* constraint.');
70733 }
70734}
70735
70736function extent(layoutOut, ax) {
70737 var domain = ax.domain;
70738 if(!domain) {
70739 // at this point overlaying axes haven't yet inherited their main domains
70740 // TODO: constrain: domain with overlaying axes is likely a bug.
70741 domain = layoutOut[id2name(ax.overlaying)].domain;
70742 }
70743 return domain[1] - domain[0];
70744}
70745
70746function getConstraintGroup(groups, thisID) {
70747 for(var i = 0; i < groups.length; i++) {
70748 if(groups[i][thisID]) {
70749 return groups[i];
70750 }
70751 }
70752 return null;
70753}
70754
70755/*
70756 * Add this axis to the axis constraint groups, which is the collection
70757 * of axes that are all constrained together on scale (or matching).
70758 *
70759 * constraintGroups: a list of objects. each object is
70760 * {axis_id: scale_within_group}, where scale_within_group is
70761 * only important relative to the rest of the group, and defines
70762 * the relative scales between all axes in the group
70763 *
70764 * thisGroup: the group the current axis is already in
70765 * thisID: the id if the current axis
70766 * thatID: the id of the axis to scale it with
70767 * scaleratio: the ratio of this axis to the thatID axis
70768 */
70769function updateConstraintGroups(constraintGroups, thisID, thatID, scaleratio) {
70770 var i, j, groupi, keyj, thisGroupIndex;
70771
70772 var thisGroup = getConstraintGroup(constraintGroups, thisID);
70773
70774 if(thisGroup === null) {
70775 thisGroup = {};
70776 thisGroup[thisID] = 1;
70777 thisGroupIndex = constraintGroups.length;
70778 constraintGroups.push(thisGroup);
70779 } else {
70780 thisGroupIndex = constraintGroups.indexOf(thisGroup);
70781 }
70782
70783 var thisGroupKeys = Object.keys(thisGroup);
70784
70785 // we know that this axis isn't in any other groups, but we don't know
70786 // about the thatID axis. If it is, we need to merge the groups.
70787 for(i = 0; i < constraintGroups.length; i++) {
70788 groupi = constraintGroups[i];
70789 if(i !== thisGroupIndex && groupi[thatID]) {
70790 var baseScale = groupi[thatID];
70791 for(j = 0; j < thisGroupKeys.length; j++) {
70792 keyj = thisGroupKeys[j];
70793 groupi[keyj] = multiplyScales(baseScale, multiplyScales(scaleratio, thisGroup[keyj]));
70794 }
70795 constraintGroups.splice(thisGroupIndex, 1);
70796 return;
70797 }
70798 }
70799
70800 // otherwise, we insert the new thatID axis as the base scale (1)
70801 // in its group, and scale the rest of the group to it
70802 if(scaleratio !== 1) {
70803 for(j = 0; j < thisGroupKeys.length; j++) {
70804 var key = thisGroupKeys[j];
70805 thisGroup[key] = multiplyScales(scaleratio, thisGroup[key]);
70806 }
70807 }
70808 thisGroup[thatID] = 1;
70809}
70810
70811// scales may be numbers or 'x1.3', 'yy4.5' etc to multiply by as-yet-unknown
70812// ratios between x and y plot sizes n times
70813function multiplyScales(a, b) {
70814 var aPrefix = '';
70815 var bPrefix = '';
70816 var aLen, bLen;
70817
70818 if(typeof a === 'string') {
70819 aPrefix = a.match(/^[xy]*/)[0];
70820 aLen = aPrefix.length;
70821 a = +a.substr(aLen);
70822 }
70823
70824 if(typeof b === 'string') {
70825 bPrefix = b.match(/^[xy]*/)[0];
70826 bLen = bPrefix.length;
70827 b = +b.substr(bLen);
70828 }
70829
70830 var c = a * b;
70831
70832 // just two numbers
70833 if(!aLen && !bLen) {
70834 return c;
70835 }
70836
70837 // one or more prefixes of the same type
70838 if(!aLen || !bLen || aPrefix.charAt(0) === bPrefix.charAt(0)) {
70839 return aPrefix + bPrefix + (a * b);
70840 }
70841
70842 // x and y cancel each other out exactly - back to a number
70843 if(aLen === bLen) {
70844 return c;
70845 }
70846
70847 // partial cancelation of prefixes
70848 return (aLen > bLen ? aPrefix.substr(bLen) : bPrefix.substr(aLen)) + c;
70849}
70850
70851function finalRatios(group, fullLayout) {
70852 var size = fullLayout._size;
70853 var yRatio = size.h / size.w;
70854 var out = {};
70855 var keys = Object.keys(group);
70856 for(var i = 0; i < keys.length; i++) {
70857 var key = keys[i];
70858 var val = group[key];
70859
70860 if(typeof val === 'string') {
70861 var prefix = val.match(/^[xy]*/)[0];
70862 var pLen = prefix.length;
70863 val = +val.substr(pLen);
70864 var mult = prefix.charAt(0) === 'y' ? yRatio : (1 / yRatio);
70865 for(var j = 0; j < pLen; j++) {
70866 val *= mult;
70867 }
70868 }
70869
70870 out[key] = val;
70871 }
70872 return out;
70873}
70874
70875exports.enforce = function enforce(gd) {
70876 var fullLayout = gd._fullLayout;
70877 var constraintGroups = fullLayout._axisConstraintGroups || [];
70878
70879 var i, j, group, axisID, ax, normScale, mode, factor;
70880
70881 // matching constraints are handled in the autorange code when autoranged,
70882 // or in the supplyDefaults code when explicitly ranged.
70883 // now we just need to handle scaleanchor constraints
70884 // matches constraints that chain with scaleanchor constraints are included
70885 // here too, but because matches has already been satisfied,
70886 // any changes here should preserve that.
70887 for(i = 0; i < constraintGroups.length; i++) {
70888 group = finalRatios(constraintGroups[i], fullLayout);
70889 var axisIDs = Object.keys(group);
70890
70891 var minScale = Infinity;
70892 var maxScale = 0;
70893 // mostly matchScale will be the same as minScale
70894 // ie we expand axis ranges to encompass *everything*
70895 // that's currently in any of their ranges, but during
70896 // autorange of a subset of axes we will ignore other
70897 // axes for this purpose.
70898 var matchScale = Infinity;
70899 var normScales = {};
70900 var axes = {};
70901 var hasAnyDomainConstraint = false;
70902
70903 // find the (normalized) scale of each axis in the group
70904 for(j = 0; j < axisIDs.length; j++) {
70905 axisID = axisIDs[j];
70906 axes[axisID] = ax = fullLayout[id2name(axisID)];
70907
70908 if(ax._inputDomain) ax.domain = ax._inputDomain.slice();
70909 else ax._inputDomain = ax.domain.slice();
70910
70911 if(!ax._inputRange) ax._inputRange = ax.range.slice();
70912
70913 // set axis scale here so we can use _m rather than
70914 // having to calculate it from length and range
70915 ax.setScale();
70916
70917 // abs: inverted scales still satisfy the constraint
70918 normScales[axisID] = normScale = Math.abs(ax._m) / group[axisID];
70919 minScale = Math.min(minScale, normScale);
70920 if(ax.constrain === 'domain' || !ax._constraintShrinkable) {
70921 matchScale = Math.min(matchScale, normScale);
70922 }
70923
70924 // this has served its purpose, so remove it
70925 delete ax._constraintShrinkable;
70926 maxScale = Math.max(maxScale, normScale);
70927
70928 if(ax.constrain === 'domain') hasAnyDomainConstraint = true;
70929 }
70930
70931 // Do we have a constraint mismatch? Give a small buffer for rounding errors
70932 if(minScale > ALMOST_EQUAL * maxScale && !hasAnyDomainConstraint) continue;
70933
70934 // now increase any ranges we need to until all normalized scales are equal
70935 for(j = 0; j < axisIDs.length; j++) {
70936 axisID = axisIDs[j];
70937 normScale = normScales[axisID];
70938 ax = axes[axisID];
70939 mode = ax.constrain;
70940
70941 // even if the scale didn't change, if we're shrinking domain
70942 // we need to recalculate in case `constraintoward` changed
70943 if(normScale !== matchScale || mode === 'domain') {
70944 factor = normScale / matchScale;
70945
70946 if(mode === 'range') {
70947 scaleZoom(ax, factor);
70948 } else {
70949 // mode === 'domain'
70950
70951 var inputDomain = ax._inputDomain;
70952 var domainShrunk = (ax.domain[1] - ax.domain[0]) /
70953 (inputDomain[1] - inputDomain[0]);
70954 var rangeShrunk = (ax.r2l(ax.range[1]) - ax.r2l(ax.range[0])) /
70955 (ax.r2l(ax._inputRange[1]) - ax.r2l(ax._inputRange[0]));
70956
70957 factor /= domainShrunk;
70958
70959 if(factor * rangeShrunk < 1) {
70960 // we've asked to magnify the axis more than we can just by
70961 // enlarging the domain - so we need to constrict range
70962 ax.domain = ax._input.domain = inputDomain.slice();
70963 scaleZoom(ax, factor);
70964 continue;
70965 }
70966
70967 if(rangeShrunk < 1) {
70968 // the range has previously been constricted by ^^, but we've
70969 // switched to the domain-constricted regime, so reset range
70970 ax.range = ax._input.range = ax._inputRange.slice();
70971 factor *= rangeShrunk;
70972 }
70973
70974 if(ax.autorange) {
70975 /*
70976 * range & factor may need to change because range was
70977 * calculated for the larger scaling, so some pixel
70978 * paddings may get cut off when we reduce the domain.
70979 *
70980 * This is easier than the regular autorange calculation
70981 * because we already know the scaling `m`, but we still
70982 * need to cut out impossible constraints (like
70983 * annotations with super-long arrows). That's what
70984 * outerMin/Max are for - if the expansion was going to
70985 * go beyond the original domain, it must be impossible
70986 */
70987 var rl0 = ax.r2l(ax.range[0]);
70988 var rl1 = ax.r2l(ax.range[1]);
70989 var rangeCenter = (rl0 + rl1) / 2;
70990 var rangeMin = rangeCenter;
70991 var rangeMax = rangeCenter;
70992 var halfRange = Math.abs(rl1 - rangeCenter);
70993 // extra tiny bit for rounding errors, in case we actually
70994 // *are* expanding to the full domain
70995 var outerMin = rangeCenter - halfRange * factor * 1.0001;
70996 var outerMax = rangeCenter + halfRange * factor * 1.0001;
70997 var getPadMin = autorange.makePadFn(fullLayout, ax, 0);
70998 var getPadMax = autorange.makePadFn(fullLayout, ax, 1);
70999
71000 updateDomain(ax, factor);
71001 var m = Math.abs(ax._m);
71002 var extremes = autorange.concatExtremes(gd, ax);
71003 var minArray = extremes.min;
71004 var maxArray = extremes.max;
71005 var newVal;
71006 var k;
71007
71008 for(k = 0; k < minArray.length; k++) {
71009 newVal = minArray[k].val - getPadMin(minArray[k]) / m;
71010 if(newVal > outerMin && newVal < rangeMin) {
71011 rangeMin = newVal;
71012 }
71013 }
71014
71015 for(k = 0; k < maxArray.length; k++) {
71016 newVal = maxArray[k].val + getPadMax(maxArray[k]) / m;
71017 if(newVal < outerMax && newVal > rangeMax) {
71018 rangeMax = newVal;
71019 }
71020 }
71021
71022 var domainExpand = (rangeMax - rangeMin) / (2 * halfRange);
71023 factor /= domainExpand;
71024
71025 rangeMin = ax.l2r(rangeMin);
71026 rangeMax = ax.l2r(rangeMax);
71027 ax.range = ax._input.range = (rl0 < rl1) ?
71028 [rangeMin, rangeMax] : [rangeMax, rangeMin];
71029 }
71030
71031 updateDomain(ax, factor);
71032 }
71033 }
71034 }
71035 }
71036};
71037
71038exports.getAxisGroup = function getAxisGroup(fullLayout, axId) {
71039 var matchGroups = fullLayout._axisMatchGroups;
71040
71041 for(var i = 0; i < matchGroups.length; i++) {
71042 var group = matchGroups[i];
71043 if(group[axId]) return 'g' + i;
71044 }
71045 return axId;
71046};
71047
71048// For use before autoranging, check if this axis was previously constrained
71049// by domain but no longer is
71050exports.clean = function clean(gd, ax) {
71051 if(ax._inputDomain) {
71052 var isConstrained = false;
71053 var axId = ax._id;
71054 var constraintGroups = gd._fullLayout._axisConstraintGroups;
71055 for(var j = 0; j < constraintGroups.length; j++) {
71056 if(constraintGroups[j][axId]) {
71057 isConstrained = true;
71058 break;
71059 }
71060 }
71061 if(!isConstrained || ax.constrain !== 'domain') {
71062 ax._input.domain = ax.domain = ax._inputDomain;
71063 delete ax._inputDomain;
71064 }
71065 }
71066};
71067
71068function updateDomain(ax, factor) {
71069 var inputDomain = ax._inputDomain;
71070 var centerFraction = FROM_BL[ax.constraintoward];
71071 var center = inputDomain[0] + (inputDomain[1] - inputDomain[0]) * centerFraction;
71072
71073 ax.domain = ax._input.domain = [
71074 center + (inputDomain[0] - center) / factor,
71075 center + (inputDomain[1] - center) / factor
71076 ];
71077 ax.setScale();
71078}
71079
71080},{"../../constants/alignment":262,"../../constants/numerical":267,"../../lib":287,"./autorange":333,"./axis_ids":338,"./layout_attributes":349,"./scale_zoom":353,"./set_convert":355}],343:[function(_dereq_,module,exports){
71081'use strict';
71082
71083var d3 = _dereq_('@plotly/d3');
71084var Lib = _dereq_('../../lib');
71085var numberFormat = Lib.numberFormat;
71086var tinycolor = _dereq_('tinycolor2');
71087var supportsPassive = _dereq_('has-passive-events');
71088
71089var Registry = _dereq_('../../registry');
71090var strTranslate = Lib.strTranslate;
71091var svgTextUtils = _dereq_('../../lib/svg_text_utils');
71092var Color = _dereq_('../../components/color');
71093var Drawing = _dereq_('../../components/drawing');
71094var Fx = _dereq_('../../components/fx');
71095var Axes = _dereq_('./axes');
71096var setCursor = _dereq_('../../lib/setcursor');
71097var dragElement = _dereq_('../../components/dragelement');
71098var helpers = _dereq_('../../components/dragelement/helpers');
71099var selectingOrDrawing = helpers.selectingOrDrawing;
71100var freeMode = helpers.freeMode;
71101
71102var FROM_TL = _dereq_('../../constants/alignment').FROM_TL;
71103var clearGlCanvases = _dereq_('../../lib/clear_gl_canvases');
71104var redrawReglTraces = _dereq_('../../plot_api/subroutines').redrawReglTraces;
71105
71106var Plots = _dereq_('../plots');
71107
71108var getFromId = _dereq_('./axis_ids').getFromId;
71109var prepSelect = _dereq_('./select').prepSelect;
71110var clearSelect = _dereq_('./select').clearSelect;
71111var selectOnClick = _dereq_('./select').selectOnClick;
71112var scaleZoom = _dereq_('./scale_zoom');
71113
71114var constants = _dereq_('./constants');
71115var MINDRAG = constants.MINDRAG;
71116var MINZOOM = constants.MINZOOM;
71117
71118// flag for showing "doubleclick to zoom out" only at the beginning
71119var SHOWZOOMOUTTIP = true;
71120
71121// dragBox: create an element to drag one or more axis ends
71122// inputs:
71123// plotinfo - which subplot are we making dragboxes on?
71124// x,y,w,h - left, top, width, height of the box
71125// ns - how does this drag the vertical axis?
71126// 'n' - top only
71127// 's' - bottom only
71128// 'ns' - top and bottom together, difference unchanged
71129// ew - same for horizontal axis
71130function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) {
71131 // mouseDown stores ms of first mousedown event in the last
71132 // `gd._context.doubleClickDelay` ms on the drag bars
71133 // numClicks stores how many mousedowns have been seen
71134 // within `gd._context.doubleClickDelay` so we can check for click or doubleclick events
71135 // dragged stores whether a drag has occurred, so we don't have to
71136 // redraw unnecessarily, ie if no move bigger than MINDRAG or MINZOOM px
71137 var zoomlayer = gd._fullLayout._zoomlayer;
71138 var isMainDrag = (ns + ew === 'nsew');
71139 var singleEnd = (ns + ew).length === 1;
71140
71141 // main subplot x and y (i.e. found in plotinfo - the main ones)
71142 var xa0, ya0;
71143 // {ax._id: ax} hash objects
71144 var xaHash, yaHash;
71145 // xaHash/yaHash values (arrays)
71146 var xaxes, yaxes;
71147 // main axis offsets
71148 var xs, ys;
71149 // main axis lengths
71150 var pw, ph;
71151 // contains keys 'xaHash', 'yaHash', 'xaxes', and 'yaxes'
71152 // which are the x/y {ax._id: ax} hash objects and their values
71153 // for linked axis relative to this subplot
71154 var links;
71155 // similar to `links` but for matching axes
71156 var matches;
71157 // set to ew/ns val when active, set to '' when inactive
71158 var xActive, yActive;
71159 // are all axes in this subplot are fixed?
71160 var allFixedRanges;
71161 // do we need to edit x/y ranges?
71162 var editX, editY;
71163 // graph-wide optimization flags
71164 var hasScatterGl, hasSplom, hasSVG;
71165 // collected changes to be made to the plot by relayout at the end
71166 var updates;
71167 // scaling factors from css transform
71168 var scaleX;
71169 var scaleY;
71170
71171 function recomputeAxisLists() {
71172 xa0 = plotinfo.xaxis;
71173 ya0 = plotinfo.yaxis;
71174 pw = xa0._length;
71175 ph = ya0._length;
71176 xs = xa0._offset;
71177 ys = ya0._offset;
71178
71179 xaHash = {};
71180 xaHash[xa0._id] = xa0;
71181 yaHash = {};
71182 yaHash[ya0._id] = ya0;
71183
71184 // if we're dragging two axes at once, also drag overlays
71185 if(ns && ew) {
71186 var overlays = plotinfo.overlays;
71187 for(var i = 0; i < overlays.length; i++) {
71188 var xa = overlays[i].xaxis;
71189 xaHash[xa._id] = xa;
71190 var ya = overlays[i].yaxis;
71191 yaHash[ya._id] = ya;
71192 }
71193 }
71194
71195 xaxes = hashValues(xaHash);
71196 yaxes = hashValues(yaHash);
71197 xActive = isDirectionActive(xaxes, ew);
71198 yActive = isDirectionActive(yaxes, ns);
71199 allFixedRanges = !yActive && !xActive;
71200
71201 matches = calcLinks(gd, gd._fullLayout._axisMatchGroups, xaHash, yaHash);
71202 links = calcLinks(gd, gd._fullLayout._axisConstraintGroups, xaHash, yaHash, matches);
71203 var spConstrained = links.isSubplotConstrained || matches.isSubplotConstrained;
71204 editX = ew || spConstrained;
71205 editY = ns || spConstrained;
71206
71207 var fullLayout = gd._fullLayout;
71208 hasScatterGl = fullLayout._has('scattergl');
71209 hasSplom = fullLayout._has('splom');
71210 hasSVG = fullLayout._has('svg');
71211 }
71212
71213 recomputeAxisLists();
71214
71215 var cursor = getDragCursor(yActive + xActive, gd._fullLayout.dragmode, isMainDrag);
71216 var dragger = makeRectDragger(plotinfo, ns + ew + 'drag', cursor, x, y, w, h);
71217
71218 // still need to make the element if the axes are disabled
71219 // but nuke its events (except for maindrag which needs them for hover)
71220 // and stop there
71221 if(allFixedRanges && !isMainDrag) {
71222 dragger.onmousedown = null;
71223 dragger.style.pointerEvents = 'none';
71224 return dragger;
71225 }
71226
71227 var dragOptions = {
71228 element: dragger,
71229 gd: gd,
71230 plotinfo: plotinfo
71231 };
71232
71233 dragOptions.prepFn = function(e, startX, startY) {
71234 var dragModePrev = dragOptions.dragmode;
71235 var dragModeNow = gd._fullLayout.dragmode;
71236 if(dragModeNow !== dragModePrev) {
71237 dragOptions.dragmode = dragModeNow;
71238 }
71239
71240 recomputeAxisLists();
71241
71242 scaleX = gd._fullLayout._invScaleX;
71243 scaleY = gd._fullLayout._invScaleY;
71244
71245 if(!allFixedRanges) {
71246 if(isMainDrag) {
71247 // main dragger handles all drag modes, and changes
71248 // to pan (or to zoom if it already is pan) on shift
71249 if(e.shiftKey) {
71250 if(dragModeNow === 'pan') dragModeNow = 'zoom';
71251 else if(!selectingOrDrawing(dragModeNow)) dragModeNow = 'pan';
71252 } else if(e.ctrlKey) {
71253 dragModeNow = 'pan';
71254 }
71255 } else {
71256 // all other draggers just pan
71257 dragModeNow = 'pan';
71258 }
71259 }
71260
71261 if(freeMode(dragModeNow)) dragOptions.minDrag = 1;
71262 else dragOptions.minDrag = undefined;
71263
71264 if(selectingOrDrawing(dragModeNow)) {
71265 dragOptions.xaxes = xaxes;
71266 dragOptions.yaxes = yaxes;
71267 // this attaches moveFn, clickFn, doneFn on dragOptions
71268 prepSelect(e, startX, startY, dragOptions, dragModeNow);
71269 } else {
71270 dragOptions.clickFn = clickFn;
71271 if(selectingOrDrawing(dragModePrev)) {
71272 // TODO Fix potential bug
71273 // Note: clearing / resetting selection state only happens, when user
71274 // triggers at least one interaction in pan/zoom mode. Otherwise, the
71275 // select/lasso outlines are deleted (in plots.js.cleanPlot) but the selection
71276 // cache isn't cleared. So when the user switches back to select/lasso and
71277 // 'adds to a selection' with Shift, the "old", seemingly removed outlines
71278 // are redrawn again because the selection cache still holds their coordinates.
71279 // However, this isn't easily solved, since plots.js would need
71280 // to have a reference to the dragOptions object (which holds the
71281 // selection cache).
71282 clearAndResetSelect();
71283 }
71284
71285 if(!allFixedRanges) {
71286 if(dragModeNow === 'zoom') {
71287 dragOptions.moveFn = zoomMove;
71288 dragOptions.doneFn = zoomDone;
71289
71290 // zoomMove takes care of the threshold, but we need to
71291 // minimize this so that constrained zoom boxes will flip
71292 // orientation at the right place
71293 dragOptions.minDrag = 1;
71294
71295 zoomPrep(e, startX, startY);
71296 } else if(dragModeNow === 'pan') {
71297 dragOptions.moveFn = plotDrag;
71298 dragOptions.doneFn = dragTail;
71299 }
71300 }
71301 }
71302
71303 gd._fullLayout._redrag = function() {
71304 var dragDataNow = gd._dragdata;
71305
71306 if(dragDataNow && dragDataNow.element === dragger) {
71307 var dragModeNow = gd._fullLayout.dragmode;
71308
71309 if(!selectingOrDrawing(dragModeNow)) {
71310 recomputeAxisLists();
71311 updateSubplots([0, 0, pw, ph]);
71312 dragOptions.moveFn(dragDataNow.dx, dragDataNow.dy);
71313 }
71314
71315 // TODO should we try to "re-select" under select/lasso modes?
71316 // probably best to wait for https://github.com/plotly/plotly.js/issues/1851
71317 }
71318 };
71319 };
71320
71321 function clearAndResetSelect() {
71322 // clear selection polygon cache (if any)
71323 dragOptions.plotinfo.selection = false;
71324 // clear selection outlines
71325 clearSelect(gd);
71326 }
71327
71328 function clickFn(numClicks, evt) {
71329 var gd = dragOptions.gd;
71330 if(gd._fullLayout._activeShapeIndex >= 0) {
71331 gd._fullLayout._deactivateShape(gd);
71332 return;
71333 }
71334
71335 var clickmode = gd._fullLayout.clickmode;
71336
71337 removeZoombox(gd);
71338
71339 if(numClicks === 2 && !singleEnd) doubleClick();
71340
71341 if(isMainDrag) {
71342 if(clickmode.indexOf('select') > -1) {
71343 selectOnClick(evt, gd, xaxes, yaxes, plotinfo.id, dragOptions);
71344 }
71345
71346 if(clickmode.indexOf('event') > -1) {
71347 Fx.click(gd, evt, plotinfo.id);
71348 }
71349 } else if(numClicks === 1 && singleEnd) {
71350 var ax = ns ? ya0 : xa0;
71351 var end = (ns === 's' || ew === 'w') ? 0 : 1;
71352 var attrStr = ax._name + '.range[' + end + ']';
71353 var initialText = getEndText(ax, end);
71354 var hAlign = 'left';
71355 var vAlign = 'middle';
71356
71357 if(ax.fixedrange) return;
71358
71359 if(ns) {
71360 vAlign = (ns === 'n') ? 'top' : 'bottom';
71361 if(ax.side === 'right') hAlign = 'right';
71362 } else if(ew === 'e') hAlign = 'right';
71363
71364 if(gd._context.showAxisRangeEntryBoxes) {
71365 d3.select(dragger)
71366 .call(svgTextUtils.makeEditable, {
71367 gd: gd,
71368 immediate: true,
71369 background: gd._fullLayout.paper_bgcolor,
71370 text: String(initialText),
71371 fill: ax.tickfont ? ax.tickfont.color : '#444',
71372 horizontalAlign: hAlign,
71373 verticalAlign: vAlign
71374 })
71375 .on('edit', function(text) {
71376 var v = ax.d2r(text);
71377 if(v !== undefined) {
71378 Registry.call('_guiRelayout', gd, attrStr, v);
71379 }
71380 });
71381 }
71382 }
71383 }
71384
71385 dragElement.init(dragOptions);
71386
71387 // x/y px position at start of drag
71388 var x0, y0;
71389 // bbox object of the zoombox
71390 var box;
71391 // luminance of bg behind zoombox
71392 var lum;
71393 // zoombox path outline
71394 var path0;
71395 // is zoombox dimmed (during drag)
71396 var dimmed;
71397 // 'x'-only, 'y' or 'xy' zooming
71398 var zoomMode;
71399 // zoombox d3 selection
71400 var zb;
71401 // zoombox corner d3 selection
71402 var corners;
71403 // zoom takes over minDrag, so it also has to take over gd._dragged
71404 var zoomDragged;
71405
71406 function zoomPrep(e, startX, startY) {
71407 var dragBBox = dragger.getBoundingClientRect();
71408 x0 = startX - dragBBox.left;
71409 y0 = startY - dragBBox.top;
71410
71411 gd._fullLayout._calcInverseTransform(gd);
71412 var transformedCoords = Lib.apply3DTransform(gd._fullLayout._invTransform)(x0, y0);
71413 x0 = transformedCoords[0];
71414 y0 = transformedCoords[1];
71415
71416 box = {l: x0, r: x0, w: 0, t: y0, b: y0, h: 0};
71417 lum = gd._hmpixcount ?
71418 (gd._hmlumcount / gd._hmpixcount) :
71419 tinycolor(gd._fullLayout.plot_bgcolor).getLuminance();
71420 path0 = 'M0,0H' + pw + 'V' + ph + 'H0V0';
71421 dimmed = false;
71422 zoomMode = 'xy';
71423 zoomDragged = false;
71424 zb = makeZoombox(zoomlayer, lum, xs, ys, path0);
71425 corners = makeCorners(zoomlayer, xs, ys);
71426 }
71427
71428 function zoomMove(dx0, dy0) {
71429 if(gd._transitioningWithDuration) {
71430 return false;
71431 }
71432
71433 var x1 = Math.max(0, Math.min(pw, scaleX * dx0 + x0));
71434 var y1 = Math.max(0, Math.min(ph, scaleY * dy0 + y0));
71435 var dx = Math.abs(x1 - x0);
71436 var dy = Math.abs(y1 - y0);
71437
71438 box.l = Math.min(x0, x1);
71439 box.r = Math.max(x0, x1);
71440 box.t = Math.min(y0, y1);
71441 box.b = Math.max(y0, y1);
71442
71443 function noZoom() {
71444 zoomMode = '';
71445 box.r = box.l;
71446 box.t = box.b;
71447 corners.attr('d', 'M0,0Z');
71448 }
71449
71450 if(links.isSubplotConstrained) {
71451 if(dx > MINZOOM || dy > MINZOOM) {
71452 zoomMode = 'xy';
71453 if(dx / pw > dy / ph) {
71454 dy = dx * ph / pw;
71455 if(y0 > y1) box.t = y0 - dy;
71456 else box.b = y0 + dy;
71457 } else {
71458 dx = dy * pw / ph;
71459 if(x0 > x1) box.l = x0 - dx;
71460 else box.r = x0 + dx;
71461 }
71462 corners.attr('d', xyCorners(box));
71463 } else {
71464 noZoom();
71465 }
71466 } else if(matches.isSubplotConstrained) {
71467 if(dx > MINZOOM || dy > MINZOOM) {
71468 zoomMode = 'xy';
71469
71470 var r0 = Math.min(box.l / pw, (ph - box.b) / ph);
71471 var r1 = Math.max(box.r / pw, (ph - box.t) / ph);
71472
71473 box.l = r0 * pw;
71474 box.r = r1 * pw;
71475 box.b = (1 - r0) * ph;
71476 box.t = (1 - r1) * ph;
71477 corners.attr('d', xyCorners(box));
71478 } else {
71479 noZoom();
71480 }
71481 } else if(!yActive || dy < Math.min(Math.max(dx * 0.6, MINDRAG), MINZOOM)) {
71482 // look for small drags in one direction or the other,
71483 // and only drag the other axis
71484
71485 if(dx < MINDRAG || !xActive) {
71486 noZoom();
71487 } else {
71488 box.t = 0;
71489 box.b = ph;
71490 zoomMode = 'x';
71491 corners.attr('d', xCorners(box, y0));
71492 }
71493 } else if(!xActive || dx < Math.min(dy * 0.6, MINZOOM)) {
71494 box.l = 0;
71495 box.r = pw;
71496 zoomMode = 'y';
71497 corners.attr('d', yCorners(box, x0));
71498 } else {
71499 zoomMode = 'xy';
71500 corners.attr('d', xyCorners(box));
71501 }
71502 box.w = box.r - box.l;
71503 box.h = box.b - box.t;
71504
71505 if(zoomMode) zoomDragged = true;
71506 gd._dragged = zoomDragged;
71507
71508 updateZoombox(zb, corners, box, path0, dimmed, lum);
71509 computeZoomUpdates();
71510 gd.emit('plotly_relayouting', updates);
71511 dimmed = true;
71512 }
71513
71514 function computeZoomUpdates() {
71515 updates = {};
71516
71517 // TODO: edit linked axes in zoomAxRanges and in dragTail
71518 if(zoomMode === 'xy' || zoomMode === 'x') {
71519 zoomAxRanges(xaxes, box.l / pw, box.r / pw, updates, links.xaxes);
71520 updateMatchedAxRange('x', updates);
71521 }
71522 if(zoomMode === 'xy' || zoomMode === 'y') {
71523 zoomAxRanges(yaxes, (ph - box.b) / ph, (ph - box.t) / ph, updates, links.yaxes);
71524 updateMatchedAxRange('y', updates);
71525 }
71526 }
71527
71528 function zoomDone() {
71529 computeZoomUpdates();
71530 removeZoombox(gd);
71531 dragTail();
71532 showDoubleClickNotifier(gd);
71533 }
71534
71535 // scroll zoom, on all draggers except corners
71536 var scrollViewBox = [0, 0, pw, ph];
71537 // wait a little after scrolling before redrawing
71538 var redrawTimer = null;
71539 var REDRAWDELAY = constants.REDRAWDELAY;
71540 var mainplot = plotinfo.mainplot ? gd._fullLayout._plots[plotinfo.mainplot] : plotinfo;
71541
71542 function zoomWheel(e) {
71543 // deactivate mousewheel scrolling on embedded graphs
71544 // devs can override this with layout._enablescrollzoom,
71545 // but _ ensures this setting won't leave their page
71546 if(!gd._context._scrollZoom.cartesian && !gd._fullLayout._enablescrollzoom) {
71547 return;
71548 }
71549
71550 clearAndResetSelect();
71551
71552 // If a transition is in progress, then disable any behavior:
71553 if(gd._transitioningWithDuration) {
71554 e.preventDefault();
71555 e.stopPropagation();
71556 return;
71557 }
71558
71559 recomputeAxisLists();
71560
71561 clearTimeout(redrawTimer);
71562
71563 var wheelDelta = -e.deltaY;
71564 if(!isFinite(wheelDelta)) wheelDelta = e.wheelDelta / 10;
71565 if(!isFinite(wheelDelta)) {
71566 Lib.log('Did not find wheel motion attributes: ', e);
71567 return;
71568 }
71569
71570 var zoom = Math.exp(-Math.min(Math.max(wheelDelta, -20), 20) / 200);
71571 var gbb = mainplot.draglayer.select('.nsewdrag').node().getBoundingClientRect();
71572 var xfrac = (e.clientX - gbb.left) / gbb.width;
71573 var yfrac = (gbb.bottom - e.clientY) / gbb.height;
71574 var i;
71575
71576 function zoomWheelOneAxis(ax, centerFraction, zoom) {
71577 if(ax.fixedrange) return;
71578
71579 var axRange = Lib.simpleMap(ax.range, ax.r2l);
71580 var v0 = axRange[0] + (axRange[1] - axRange[0]) * centerFraction;
71581 function doZoom(v) { return ax.l2r(v0 + (v - v0) * zoom); }
71582 ax.range = axRange.map(doZoom);
71583 }
71584
71585 if(editX) {
71586 // if we're only zooming this axis because of constraints,
71587 // zoom it about the center
71588 if(!ew) xfrac = 0.5;
71589
71590 for(i = 0; i < xaxes.length; i++) {
71591 zoomWheelOneAxis(xaxes[i], xfrac, zoom);
71592 }
71593 updateMatchedAxRange('x');
71594
71595 scrollViewBox[2] *= zoom;
71596 scrollViewBox[0] += scrollViewBox[2] * xfrac * (1 / zoom - 1);
71597 }
71598 if(editY) {
71599 if(!ns) yfrac = 0.5;
71600
71601 for(i = 0; i < yaxes.length; i++) {
71602 zoomWheelOneAxis(yaxes[i], yfrac, zoom);
71603 }
71604 updateMatchedAxRange('y');
71605
71606 scrollViewBox[3] *= zoom;
71607 scrollViewBox[1] += scrollViewBox[3] * (1 - yfrac) * (1 / zoom - 1);
71608 }
71609
71610 // viewbox redraw at first
71611 updateSubplots(scrollViewBox);
71612 ticksAndAnnotations();
71613
71614 gd.emit('plotly_relayouting', updates);
71615
71616 // then replot after a delay to make sure
71617 // no more scrolling is coming
71618 redrawTimer = setTimeout(function() {
71619 if(!gd._fullLayout) return;
71620 scrollViewBox = [0, 0, pw, ph];
71621 dragTail();
71622 }, REDRAWDELAY);
71623
71624 e.preventDefault();
71625 return;
71626 }
71627
71628 // everything but the corners gets wheel zoom
71629 if(ns.length * ew.length !== 1) {
71630 attachWheelEventHandler(dragger, zoomWheel);
71631 }
71632
71633 // plotDrag: move the plot in response to a drag
71634 function plotDrag(dx, dy) {
71635 dx = dx * scaleX;
71636 dy = dy * scaleY;
71637 // If a transition is in progress, then disable any behavior:
71638 if(gd._transitioningWithDuration) {
71639 return;
71640 }
71641
71642 // prevent axis drawing from monkeying with margins until we're done
71643 gd._fullLayout._replotting = true;
71644
71645 if(xActive === 'ew' || yActive === 'ns') {
71646 var spDx = xActive ? -dx : 0;
71647 var spDy = yActive ? -dy : 0;
71648 if(matches.isSubplotConstrained) {
71649 if(xActive && yActive) {
71650 var frac = (dx / pw - dy / ph) / 2;
71651 dx = frac * pw;
71652 dy = -frac * ph;
71653 spDx = -dx;
71654 spDy = -dy;
71655 }
71656 if(yActive) {
71657 spDx = -spDy * pw / ph;
71658 } else {
71659 spDy = -spDx * ph / pw;
71660 }
71661 }
71662 if(xActive) {
71663 dragAxList(xaxes, dx);
71664 updateMatchedAxRange('x');
71665 }
71666 if(yActive) {
71667 dragAxList(yaxes, dy);
71668 updateMatchedAxRange('y');
71669 }
71670 updateSubplots([spDx, spDy, pw, ph]);
71671 ticksAndAnnotations();
71672 gd.emit('plotly_relayouting', updates);
71673 return;
71674 }
71675
71676 // dz: set a new value for one end (0 or 1) of an axis array axArray,
71677 // and return a pixel shift for that end for the viewbox
71678 // based on pixel drag distance d
71679 // TODO: this makes (generally non-fatal) errors when you get
71680 // near floating point limits
71681 function dz(axArray, end, d) {
71682 var otherEnd = 1 - end;
71683 var movedAx;
71684 var newLinearizedEnd;
71685 for(var i = 0; i < axArray.length; i++) {
71686 var axi = axArray[i];
71687 if(axi.fixedrange) continue;
71688 movedAx = axi;
71689 newLinearizedEnd = axi._rl[otherEnd] +
71690 (axi._rl[end] - axi._rl[otherEnd]) / dZoom(d / axi._length);
71691 var newEnd = axi.l2r(newLinearizedEnd);
71692
71693 // if l2r comes back false or undefined, it means we've dragged off
71694 // the end of valid ranges - so stop.
71695 if(newEnd !== false && newEnd !== undefined) axi.range[end] = newEnd;
71696 }
71697 return movedAx._length * (movedAx._rl[end] - newLinearizedEnd) /
71698 (movedAx._rl[end] - movedAx._rl[otherEnd]);
71699 }
71700
71701 var dxySign = ((xActive === 'w') === (yActive === 'n')) ? 1 : -1;
71702 if(xActive && yActive && (links.isSubplotConstrained || matches.isSubplotConstrained)) {
71703 // dragging a corner of a constrained subplot:
71704 // respect the fixed corner, but harmonize dx and dy
71705 var dxyFraction = (dx / pw + dxySign * dy / ph) / 2;
71706 dx = dxyFraction * pw;
71707 dy = dxySign * dxyFraction * ph;
71708 }
71709
71710 var xStart, yStart;
71711
71712 if(xActive === 'w') dx = dz(xaxes, 0, dx);
71713 else if(xActive === 'e') dx = dz(xaxes, 1, -dx);
71714 else if(!xActive) dx = 0;
71715
71716 if(yActive === 'n') dy = dz(yaxes, 1, dy);
71717 else if(yActive === 's') dy = dz(yaxes, 0, -dy);
71718 else if(!yActive) dy = 0;
71719
71720 xStart = (xActive === 'w') ? dx : 0;
71721 yStart = (yActive === 'n') ? dy : 0;
71722
71723 if(
71724 (links.isSubplotConstrained && !matches.isSubplotConstrained) ||
71725 // NW or SE on matching axes - create a symmetric zoom
71726 (matches.isSubplotConstrained && xActive && yActive && dxySign > 0)
71727 ) {
71728 var i;
71729 if(matches.isSubplotConstrained || (!xActive && yActive.length === 1)) {
71730 // dragging one end of the y axis of a constrained subplot
71731 // scale the other axis the same about its middle
71732 for(i = 0; i < xaxes.length; i++) {
71733 xaxes[i].range = xaxes[i]._r.slice();
71734 scaleZoom(xaxes[i], 1 - dy / ph);
71735 }
71736 dx = dy * pw / ph;
71737 xStart = dx / 2;
71738 }
71739 if(matches.isSubplotConstrained || (!yActive && xActive.length === 1)) {
71740 for(i = 0; i < yaxes.length; i++) {
71741 yaxes[i].range = yaxes[i]._r.slice();
71742 scaleZoom(yaxes[i], 1 - dx / pw);
71743 }
71744 dy = dx * ph / pw;
71745 yStart = dy / 2;
71746 }
71747 }
71748
71749 if(!matches.isSubplotConstrained || !yActive) {
71750 updateMatchedAxRange('x');
71751 }
71752 if(!matches.isSubplotConstrained || !xActive) {
71753 updateMatchedAxRange('y');
71754 }
71755 var xSize = pw - dx;
71756 var ySize = ph - dy;
71757 if(matches.isSubplotConstrained && !(xActive && yActive)) {
71758 if(xActive) {
71759 yStart = xStart ? 0 : (dx * ph / pw);
71760 ySize = xSize * ph / pw;
71761 } else {
71762 xStart = yStart ? 0 : (dy * pw / ph);
71763 xSize = ySize * pw / ph;
71764 }
71765 }
71766 updateSubplots([xStart, yStart, xSize, ySize]);
71767 ticksAndAnnotations();
71768 gd.emit('plotly_relayouting', updates);
71769 }
71770
71771 function updateMatchedAxRange(axLetter, out) {
71772 var matchedAxes = matches.isSubplotConstrained ?
71773 {x: yaxes, y: xaxes}[axLetter] :
71774 matches[axLetter + 'axes'];
71775
71776 var constrainedAxes = matches.isSubplotConstrained ?
71777 {x: xaxes, y: yaxes}[axLetter] :
71778 [];
71779
71780 for(var i = 0; i < matchedAxes.length; i++) {
71781 var ax = matchedAxes[i];
71782 var axId = ax._id;
71783 var axId2 = matches.xLinks[axId] || matches.yLinks[axId];
71784 var ax2 = constrainedAxes[0] || xaHash[axId2] || yaHash[axId2];
71785
71786 if(ax2) {
71787 if(out) {
71788 // zoombox case - don't mutate 'range', just add keys in 'updates'
71789 out[ax._name + '.range[0]'] = out[ax2._name + '.range[0]'];
71790 out[ax._name + '.range[1]'] = out[ax2._name + '.range[1]'];
71791 } else {
71792 ax.range = ax2.range.slice();
71793 }
71794 }
71795 }
71796 }
71797
71798 // Draw ticks and annotations (and other components) when ranges change.
71799 // Also records the ranges that have changed for use by update at the end.
71800 function ticksAndAnnotations() {
71801 var activeAxIds = [];
71802 var i;
71803
71804 function pushActiveAxIds(axList) {
71805 for(i = 0; i < axList.length; i++) {
71806 if(!axList[i].fixedrange) activeAxIds.push(axList[i]._id);
71807 }
71808 }
71809
71810 if(editX) {
71811 pushActiveAxIds(xaxes);
71812 pushActiveAxIds(links.xaxes);
71813 pushActiveAxIds(matches.xaxes);
71814 }
71815 if(editY) {
71816 pushActiveAxIds(yaxes);
71817 pushActiveAxIds(links.yaxes);
71818 pushActiveAxIds(matches.yaxes);
71819 }
71820
71821 updates = {};
71822 for(i = 0; i < activeAxIds.length; i++) {
71823 var axId = activeAxIds[i];
71824 var ax = getFromId(gd, axId);
71825 Axes.drawOne(gd, ax, {skipTitle: true});
71826 updates[ax._name + '.range[0]'] = ax.range[0];
71827 updates[ax._name + '.range[1]'] = ax.range[1];
71828 }
71829
71830 Axes.redrawComponents(gd, activeAxIds);
71831 }
71832
71833 function doubleClick() {
71834 if(gd._transitioningWithDuration) return;
71835
71836 var doubleClickConfig = gd._context.doubleClick;
71837
71838 var axList = [];
71839 if(xActive) axList = axList.concat(xaxes);
71840 if(yActive) axList = axList.concat(yaxes);
71841 if(matches.xaxes) axList = axList.concat(matches.xaxes);
71842 if(matches.yaxes) axList = axList.concat(matches.yaxes);
71843
71844 var attrs = {};
71845 var ax, i, rangeInitial;
71846
71847 // For reset+autosize mode:
71848 // If *any* of the main axes is not at its initial range
71849 // (or autoranged, if we have no initial range, to match the logic in
71850 // doubleClickConfig === 'reset' below), we reset.
71851 // If they are *all* at their initial ranges, then we autosize.
71852 if(doubleClickConfig === 'reset+autosize') {
71853 doubleClickConfig = 'autosize';
71854
71855 for(i = 0; i < axList.length; i++) {
71856 ax = axList[i];
71857 if((ax._rangeInitial && (
71858 ax.range[0] !== ax._rangeInitial[0] ||
71859 ax.range[1] !== ax._rangeInitial[1]
71860 )) ||
71861 (!ax._rangeInitial && !ax.autorange)
71862 ) {
71863 doubleClickConfig = 'reset';
71864 break;
71865 }
71866 }
71867 }
71868
71869 if(doubleClickConfig === 'autosize') {
71870 // don't set the linked axes here, so relayout marks them as shrinkable
71871 // and we autosize just to the requested axis/axes
71872 for(i = 0; i < axList.length; i++) {
71873 ax = axList[i];
71874 if(!ax.fixedrange) attrs[ax._name + '.autorange'] = true;
71875 }
71876 } else if(doubleClickConfig === 'reset') {
71877 // when we're resetting, reset all linked axes too, so we get back
71878 // to the fully-auto-with-constraints situation
71879 if(xActive || links.isSubplotConstrained) axList = axList.concat(links.xaxes);
71880 if(yActive && !links.isSubplotConstrained) axList = axList.concat(links.yaxes);
71881
71882 if(links.isSubplotConstrained) {
71883 if(!xActive) axList = axList.concat(xaxes);
71884 else if(!yActive) axList = axList.concat(yaxes);
71885 }
71886
71887 for(i = 0; i < axList.length; i++) {
71888 ax = axList[i];
71889
71890 if(!ax.fixedrange) {
71891 if(!ax._rangeInitial) {
71892 attrs[ax._name + '.autorange'] = true;
71893 } else {
71894 rangeInitial = ax._rangeInitial;
71895 attrs[ax._name + '.range[0]'] = rangeInitial[0];
71896 attrs[ax._name + '.range[1]'] = rangeInitial[1];
71897 }
71898 }
71899 }
71900 }
71901
71902 gd.emit('plotly_doubleclick', null);
71903 Registry.call('_guiRelayout', gd, attrs);
71904 }
71905
71906 // dragTail - finish a drag event with a redraw
71907 function dragTail() {
71908 // put the subplot viewboxes back to default (Because we're going to)
71909 // be repositioning the data in the relayout. But DON'T call
71910 // ticksAndAnnotations again - it's unnecessary and would overwrite `updates`
71911 updateSubplots([0, 0, pw, ph]);
71912
71913 // since we may have been redrawing some things during the drag, we may have
71914 // accumulated MathJax promises - wait for them before we relayout.
71915 Lib.syncOrAsync([
71916 Plots.previousPromises,
71917 function() {
71918 gd._fullLayout._replotting = false;
71919 Registry.call('_guiRelayout', gd, updates);
71920 }
71921 ], gd);
71922 }
71923
71924 // updateSubplots - find all plot viewboxes that should be
71925 // affected by this drag, and update them. look for all plots
71926 // sharing an affected axis (including the one being dragged),
71927 // includes also scattergl and splom logic.
71928 function updateSubplots(viewBox) {
71929 var fullLayout = gd._fullLayout;
71930 var plotinfos = fullLayout._plots;
71931 var subplots = fullLayout._subplots.cartesian;
71932 var i, sp, xa, ya;
71933
71934 if(hasSplom) {
71935 Registry.subplotsRegistry.splom.drag(gd);
71936 }
71937
71938 if(hasScatterGl) {
71939 for(i = 0; i < subplots.length; i++) {
71940 sp = plotinfos[subplots[i]];
71941 xa = sp.xaxis;
71942 ya = sp.yaxis;
71943
71944 if(sp._scene) {
71945 var xrng = Lib.simpleMap(xa.range, xa.r2l);
71946 var yrng = Lib.simpleMap(ya.range, ya.r2l);
71947 sp._scene.update({range: [xrng[0], yrng[0], xrng[1], yrng[1]]});
71948 }
71949 }
71950 }
71951
71952 if(hasSplom || hasScatterGl) {
71953 clearGlCanvases(gd);
71954 redrawReglTraces(gd);
71955 }
71956
71957 if(hasSVG) {
71958 var xScaleFactor = viewBox[2] / xa0._length;
71959 var yScaleFactor = viewBox[3] / ya0._length;
71960
71961 for(i = 0; i < subplots.length; i++) {
71962 sp = plotinfos[subplots[i]];
71963 xa = sp.xaxis;
71964 ya = sp.yaxis;
71965
71966 var editX2 = (editX || matches.isSubplotConstrained) && !xa.fixedrange && xaHash[xa._id];
71967 var editY2 = (editY || matches.isSubplotConstrained) && !ya.fixedrange && yaHash[ya._id];
71968
71969 var xScaleFactor2, yScaleFactor2;
71970 var clipDx, clipDy;
71971
71972 if(editX2) {
71973 xScaleFactor2 = xScaleFactor;
71974 clipDx = ew || matches.isSubplotConstrained ? viewBox[0] : getShift(xa, xScaleFactor2);
71975 } else if(matches.xaHash[xa._id]) {
71976 xScaleFactor2 = xScaleFactor;
71977 clipDx = viewBox[0] * xa._length / xa0._length;
71978 } else if(matches.yaHash[xa._id]) {
71979 xScaleFactor2 = yScaleFactor;
71980 clipDx = yActive === 'ns' ?
71981 -viewBox[1] * xa._length / ya0._length :
71982 getShift(xa, xScaleFactor2, {n: 'top', s: 'bottom'}[yActive]);
71983 } else {
71984 xScaleFactor2 = getLinkedScaleFactor(xa, xScaleFactor, yScaleFactor);
71985 clipDx = scaleAndGetShift(xa, xScaleFactor2);
71986 }
71987
71988 if(editY2) {
71989 yScaleFactor2 = yScaleFactor;
71990 clipDy = ns || matches.isSubplotConstrained ? viewBox[1] : getShift(ya, yScaleFactor2);
71991 } else if(matches.yaHash[ya._id]) {
71992 yScaleFactor2 = yScaleFactor;
71993 clipDy = viewBox[1] * ya._length / ya0._length;
71994 } else if(matches.xaHash[ya._id]) {
71995 yScaleFactor2 = xScaleFactor;
71996 clipDy = xActive === 'ew' ?
71997 -viewBox[0] * ya._length / xa0._length :
71998 getShift(ya, yScaleFactor2, {e: 'right', w: 'left'}[xActive]);
71999 } else {
72000 yScaleFactor2 = getLinkedScaleFactor(ya, xScaleFactor, yScaleFactor);
72001 clipDy = scaleAndGetShift(ya, yScaleFactor2);
72002 }
72003
72004 // don't scale at all if neither axis is scalable here
72005 if(!xScaleFactor2 && !yScaleFactor2) {
72006 continue;
72007 }
72008
72009 // but if only one is, reset the other axis scaling
72010 if(!xScaleFactor2) xScaleFactor2 = 1;
72011 if(!yScaleFactor2) yScaleFactor2 = 1;
72012
72013 var plotDx = xa._offset - clipDx / xScaleFactor2;
72014 var plotDy = ya._offset - clipDy / yScaleFactor2;
72015
72016 // TODO could be more efficient here:
72017 // setTranslate and setScale do a lot of extra work
72018 // when working independently, should perhaps combine
72019 // them into a single routine.
72020 sp.clipRect
72021 .call(Drawing.setTranslate, clipDx, clipDy)
72022 .call(Drawing.setScale, xScaleFactor2, yScaleFactor2);
72023
72024 sp.plot
72025 .call(Drawing.setTranslate, plotDx, plotDy)
72026 .call(Drawing.setScale, 1 / xScaleFactor2, 1 / yScaleFactor2);
72027
72028 // apply an inverse scale to individual points to counteract
72029 // the scale of the trace group.
72030 // apply only when scale changes, as adjusting the scale of
72031 // all the points can be expansive.
72032 if(xScaleFactor2 !== sp.xScaleFactor || yScaleFactor2 !== sp.yScaleFactor) {
72033 Drawing.setPointGroupScale(sp.zoomScalePts, xScaleFactor2, yScaleFactor2);
72034 Drawing.setTextPointsScale(sp.zoomScaleTxt, xScaleFactor2, yScaleFactor2);
72035 }
72036
72037 Drawing.hideOutsideRangePoints(sp.clipOnAxisFalseTraces, sp);
72038
72039 // update x/y scaleFactor stash
72040 sp.xScaleFactor = xScaleFactor2;
72041 sp.yScaleFactor = yScaleFactor2;
72042 }
72043 }
72044 }
72045
72046 // Find the appropriate scaling for this axis, if it's linked to the
72047 // dragged axes by constraints. 0 is special, it means this axis shouldn't
72048 // ever be scaled (will be converted to 1 if the other axis is scaled)
72049 function getLinkedScaleFactor(ax, xScaleFactor, yScaleFactor) {
72050 if(ax.fixedrange) return 0;
72051
72052 if(editX && links.xaHash[ax._id]) {
72053 return xScaleFactor;
72054 }
72055 if(editY && (links.isSubplotConstrained ? links.xaHash : links.yaHash)[ax._id]) {
72056 return yScaleFactor;
72057 }
72058 return 0;
72059 }
72060
72061 function scaleAndGetShift(ax, scaleFactor) {
72062 if(scaleFactor) {
72063 ax.range = ax._r.slice();
72064 scaleZoom(ax, scaleFactor);
72065 return getShift(ax, scaleFactor);
72066 }
72067 return 0;
72068 }
72069
72070 function getShift(ax, scaleFactor, from) {
72071 return ax._length * (1 - scaleFactor) * FROM_TL[from || ax.constraintoward || 'middle'];
72072 }
72073
72074 return dragger;
72075}
72076
72077function makeDragger(plotinfo, nodeName, dragClass, cursor) {
72078 var dragger3 = Lib.ensureSingle(plotinfo.draglayer, nodeName, dragClass, function(s) {
72079 s.classed('drag', true)
72080 .style({fill: 'transparent', 'stroke-width': 0})
72081 .attr('data-subplot', plotinfo.id);
72082 });
72083
72084 dragger3.call(setCursor, cursor);
72085
72086 return dragger3.node();
72087}
72088
72089function makeRectDragger(plotinfo, dragClass, cursor, x, y, w, h) {
72090 var dragger = makeDragger(plotinfo, 'rect', dragClass, cursor);
72091 d3.select(dragger).call(Drawing.setRect, x, y, w, h);
72092 return dragger;
72093}
72094
72095function isDirectionActive(axList, activeVal) {
72096 for(var i = 0; i < axList.length; i++) {
72097 if(!axList[i].fixedrange) return activeVal;
72098 }
72099 return '';
72100}
72101
72102function getEndText(ax, end) {
72103 var initialVal = ax.range[end];
72104 var diff = Math.abs(initialVal - ax.range[1 - end]);
72105 var dig;
72106
72107 // TODO: this should basically be ax.r2d but we're doing extra
72108 // rounding here... can we clean up at all?
72109 if(ax.type === 'date') {
72110 return initialVal;
72111 } else if(ax.type === 'log') {
72112 dig = Math.ceil(Math.max(0, -Math.log(diff) / Math.LN10)) + 3;
72113 return numberFormat('.' + dig + 'g')(Math.pow(10, initialVal));
72114 } else { // linear numeric (or category... but just show numbers here)
72115 dig = Math.floor(Math.log(Math.abs(initialVal)) / Math.LN10) -
72116 Math.floor(Math.log(diff) / Math.LN10) + 4;
72117 return numberFormat('.' + String(dig) + 'g')(initialVal);
72118 }
72119}
72120
72121function zoomAxRanges(axList, r0Fraction, r1Fraction, updates, linkedAxes) {
72122 for(var i = 0; i < axList.length; i++) {
72123 var axi = axList[i];
72124 if(axi.fixedrange) continue;
72125
72126 if(axi.rangebreaks) {
72127 var isY = axi._id.charAt(0) === 'y';
72128 var r0F = isY ? (1 - r0Fraction) : r0Fraction;
72129 var r1F = isY ? (1 - r1Fraction) : r1Fraction;
72130
72131 updates[axi._name + '.range[0]'] = axi.l2r(axi.p2l(r0F * axi._length));
72132 updates[axi._name + '.range[1]'] = axi.l2r(axi.p2l(r1F * axi._length));
72133 } else {
72134 var axRangeLinear0 = axi._rl[0];
72135 var axRangeLinearSpan = axi._rl[1] - axRangeLinear0;
72136 updates[axi._name + '.range[0]'] = axi.l2r(axRangeLinear0 + axRangeLinearSpan * r0Fraction);
72137 updates[axi._name + '.range[1]'] = axi.l2r(axRangeLinear0 + axRangeLinearSpan * r1Fraction);
72138 }
72139 }
72140
72141 // zoom linked axes about their centers
72142 if(linkedAxes && linkedAxes.length) {
72143 var linkedR0Fraction = (r0Fraction + (1 - r1Fraction)) / 2;
72144 zoomAxRanges(linkedAxes, linkedR0Fraction, 1 - linkedR0Fraction, updates, []);
72145 }
72146}
72147
72148function dragAxList(axList, pix) {
72149 for(var i = 0; i < axList.length; i++) {
72150 var axi = axList[i];
72151 if(!axi.fixedrange) {
72152 if(axi.rangebreaks) {
72153 var p0 = 0;
72154 var p1 = axi._length;
72155 var d0 = axi.p2l(p0 + pix) - axi.p2l(p0);
72156 var d1 = axi.p2l(p1 + pix) - axi.p2l(p1);
72157 var delta = (d0 + d1) / 2;
72158
72159 axi.range = [
72160 axi.l2r(axi._rl[0] - delta),
72161 axi.l2r(axi._rl[1] - delta)
72162 ];
72163 } else {
72164 axi.range = [
72165 axi.l2r(axi._rl[0] - pix / axi._m),
72166 axi.l2r(axi._rl[1] - pix / axi._m)
72167 ];
72168 }
72169 }
72170 }
72171}
72172
72173// common transform for dragging one end of an axis
72174// d>0 is compressing scale (cursor is over the plot,
72175// the axis end should move with the cursor)
72176// d<0 is expanding (cursor is off the plot, axis end moves
72177// nonlinearly so you can expand far)
72178function dZoom(d) {
72179 return 1 - ((d >= 0) ? Math.min(d, 0.9) :
72180 1 / (1 / Math.max(d, -0.3) + 3.222));
72181}
72182
72183function getDragCursor(nsew, dragmode, isMainDrag) {
72184 if(!nsew) return 'pointer';
72185 if(nsew === 'nsew') {
72186 // in this case here, clear cursor and
72187 // use the cursor style set on <g .draglayer>
72188 if(isMainDrag) return '';
72189 if(dragmode === 'pan') return 'move';
72190 return 'crosshair';
72191 }
72192 return nsew.toLowerCase() + '-resize';
72193}
72194
72195function makeZoombox(zoomlayer, lum, xs, ys, path0) {
72196 return zoomlayer.append('path')
72197 .attr('class', 'zoombox')
72198 .style({
72199 'fill': lum > 0.2 ? 'rgba(0,0,0,0)' : 'rgba(255,255,255,0)',
72200 'stroke-width': 0
72201 })
72202 .attr('transform', strTranslate(xs, ys))
72203 .attr('d', path0 + 'Z');
72204}
72205
72206function makeCorners(zoomlayer, xs, ys) {
72207 return zoomlayer.append('path')
72208 .attr('class', 'zoombox-corners')
72209 .style({
72210 fill: Color.background,
72211 stroke: Color.defaultLine,
72212 'stroke-width': 1,
72213 opacity: 0
72214 })
72215 .attr('transform', strTranslate(xs, ys))
72216 .attr('d', 'M0,0Z');
72217}
72218
72219function updateZoombox(zb, corners, box, path0, dimmed, lum) {
72220 zb.attr('d',
72221 path0 + 'M' + (box.l) + ',' + (box.t) + 'v' + (box.h) +
72222 'h' + (box.w) + 'v-' + (box.h) + 'h-' + (box.w) + 'Z');
72223 transitionZoombox(zb, corners, dimmed, lum);
72224}
72225
72226function transitionZoombox(zb, corners, dimmed, lum) {
72227 if(!dimmed) {
72228 zb.transition()
72229 .style('fill', lum > 0.2 ? 'rgba(0,0,0,0.4)' :
72230 'rgba(255,255,255,0.3)')
72231 .duration(200);
72232 corners.transition()
72233 .style('opacity', 1)
72234 .duration(200);
72235 }
72236}
72237
72238function removeZoombox(gd) {
72239 d3.select(gd)
72240 .selectAll('.zoombox,.js-zoombox-backdrop,.js-zoombox-menu,.zoombox-corners')
72241 .remove();
72242}
72243
72244function showDoubleClickNotifier(gd) {
72245 if(SHOWZOOMOUTTIP && gd.data && gd._context.showTips) {
72246 Lib.notifier(Lib._(gd, 'Double-click to zoom back out'), 'long');
72247 SHOWZOOMOUTTIP = false;
72248 }
72249}
72250
72251function xCorners(box, y0) {
72252 return 'M' +
72253 (box.l - 0.5) + ',' + (y0 - MINZOOM - 0.5) +
72254 'h-3v' + (2 * MINZOOM + 1) + 'h3ZM' +
72255 (box.r + 0.5) + ',' + (y0 - MINZOOM - 0.5) +
72256 'h3v' + (2 * MINZOOM + 1) + 'h-3Z';
72257}
72258
72259function yCorners(box, x0) {
72260 return 'M' +
72261 (x0 - MINZOOM - 0.5) + ',' + (box.t - 0.5) +
72262 'v-3h' + (2 * MINZOOM + 1) + 'v3ZM' +
72263 (x0 - MINZOOM - 0.5) + ',' + (box.b + 0.5) +
72264 'v3h' + (2 * MINZOOM + 1) + 'v-3Z';
72265}
72266
72267function xyCorners(box) {
72268 var clen = Math.floor(Math.min(box.b - box.t, box.r - box.l, MINZOOM) / 2);
72269 return 'M' +
72270 (box.l - 3.5) + ',' + (box.t - 0.5 + clen) + 'h3v' + (-clen) +
72271 'h' + clen + 'v-3h-' + (clen + 3) + 'ZM' +
72272 (box.r + 3.5) + ',' + (box.t - 0.5 + clen) + 'h-3v' + (-clen) +
72273 'h' + (-clen) + 'v-3h' + (clen + 3) + 'ZM' +
72274 (box.r + 3.5) + ',' + (box.b + 0.5 - clen) + 'h-3v' + clen +
72275 'h' + (-clen) + 'v3h' + (clen + 3) + 'ZM' +
72276 (box.l - 3.5) + ',' + (box.b + 0.5 - clen) + 'h3v' + clen +
72277 'h' + clen + 'v3h-' + (clen + 3) + 'Z';
72278}
72279
72280function calcLinks(gd, groups, xaHash, yaHash, exclude) {
72281 var isSubplotConstrained = false;
72282 var xLinks = {};
72283 var yLinks = {};
72284 var xID, yID, xLinkID, yLinkID;
72285 var xExclude = (exclude || {}).xaHash;
72286 var yExclude = (exclude || {}).yaHash;
72287
72288 for(var i = 0; i < groups.length; i++) {
72289 var group = groups[i];
72290 // check if any of the x axes we're dragging is in this constraint group
72291 for(xID in xaHash) {
72292 if(group[xID]) {
72293 // put the rest of these axes into xLinks, if we're not already
72294 // dragging them, so we know to scale these axes automatically too
72295 // to match the changes in the dragged x axes
72296 for(xLinkID in group) {
72297 if(
72298 !(exclude && (xExclude[xLinkID] || yExclude[xLinkID])) &&
72299 !(xLinkID.charAt(0) === 'x' ? xaHash : yaHash)[xLinkID]
72300 ) {
72301 xLinks[xLinkID] = xID;
72302 }
72303 }
72304
72305 // check if the x and y axes of THIS drag are linked
72306 for(yID in yaHash) {
72307 if(
72308 !(exclude && (xExclude[yID] || yExclude[yID])) &&
72309 group[yID]
72310 ) {
72311 isSubplotConstrained = true;
72312 }
72313 }
72314 }
72315 }
72316
72317 // now check if any of the y axes we're dragging is in this constraint group
72318 // only look for outside links, as we've already checked for links within the dragger
72319 for(yID in yaHash) {
72320 if(group[yID]) {
72321 for(yLinkID in group) {
72322 if(
72323 !(exclude && (xExclude[yLinkID] || yExclude[yLinkID])) &&
72324 !(yLinkID.charAt(0) === 'x' ? xaHash : yaHash)[yLinkID]
72325 ) {
72326 yLinks[yLinkID] = yID;
72327 }
72328 }
72329 }
72330 }
72331 }
72332
72333 if(isSubplotConstrained) {
72334 // merge xLinks and yLinks if the subplot is constrained,
72335 // since we'll always apply both anyway and the two will contain
72336 // duplicates
72337 Lib.extendFlat(xLinks, yLinks);
72338 yLinks = {};
72339 }
72340
72341 var xaHashLinked = {};
72342 var xaxesLinked = [];
72343 for(xLinkID in xLinks) {
72344 var xa = getFromId(gd, xLinkID);
72345 xaxesLinked.push(xa);
72346 xaHashLinked[xa._id] = xa;
72347 }
72348
72349 var yaHashLinked = {};
72350 var yaxesLinked = [];
72351 for(yLinkID in yLinks) {
72352 var ya = getFromId(gd, yLinkID);
72353 yaxesLinked.push(ya);
72354 yaHashLinked[ya._id] = ya;
72355 }
72356
72357 return {
72358 xaHash: xaHashLinked,
72359 yaHash: yaHashLinked,
72360 xaxes: xaxesLinked,
72361 yaxes: yaxesLinked,
72362 xLinks: xLinks,
72363 yLinks: yLinks,
72364 isSubplotConstrained: isSubplotConstrained
72365 };
72366}
72367
72368// still seems to be some confusion about onwheel vs onmousewheel...
72369function attachWheelEventHandler(element, handler) {
72370 if(!supportsPassive) {
72371 if(element.onwheel !== undefined) element.onwheel = handler;
72372 else if(element.onmousewheel !== undefined) element.onmousewheel = handler;
72373 else if(!element.isAddedWheelEvent) {
72374 element.isAddedWheelEvent = true;
72375 element.addEventListener('wheel', handler, {passive: false});
72376 }
72377 } else {
72378 var wheelEventName = element.onwheel !== undefined ? 'wheel' : 'mousewheel';
72379
72380 if(element._onwheel) {
72381 element.removeEventListener(wheelEventName, element._onwheel);
72382 }
72383 element._onwheel = handler;
72384
72385 element.addEventListener(wheelEventName, handler, {passive: false});
72386 }
72387}
72388
72389function hashValues(hash) {
72390 var out = [];
72391 for(var k in hash) out.push(hash[k]);
72392 return out;
72393}
72394
72395module.exports = {
72396 makeDragBox: makeDragBox,
72397
72398 makeDragger: makeDragger,
72399 makeRectDragger: makeRectDragger,
72400 makeZoombox: makeZoombox,
72401 makeCorners: makeCorners,
72402
72403 updateZoombox: updateZoombox,
72404 xyCorners: xyCorners,
72405 transitionZoombox: transitionZoombox,
72406 removeZoombox: removeZoombox,
72407 showDoubleClickNotifier: showDoubleClickNotifier,
72408
72409 attachWheelEventHandler: attachWheelEventHandler
72410};
72411
72412},{"../../components/color":157,"../../components/dragelement":176,"../../components/dragelement/helpers":175,"../../components/drawing":179,"../../components/fx":197,"../../constants/alignment":262,"../../lib":287,"../../lib/clear_gl_canvases":275,"../../lib/setcursor":307,"../../lib/svg_text_utils":310,"../../plot_api/subroutines":324,"../../registry":376,"../plots":369,"./axes":334,"./axis_ids":338,"./constants":341,"./scale_zoom":353,"./select":354,"@plotly/d3":20,"has-passive-events":65,"tinycolor2":121}],344:[function(_dereq_,module,exports){
72413'use strict';
72414
72415var d3 = _dereq_('@plotly/d3');
72416
72417var Fx = _dereq_('../../components/fx');
72418var dragElement = _dereq_('../../components/dragelement');
72419var setCursor = _dereq_('../../lib/setcursor');
72420
72421var makeDragBox = _dereq_('./dragbox').makeDragBox;
72422var DRAGGERSIZE = _dereq_('./constants').DRAGGERSIZE;
72423
72424exports.initInteractions = function initInteractions(gd) {
72425 var fullLayout = gd._fullLayout;
72426
72427 if(gd._context.staticPlot) {
72428 // this sweeps up more than just cartesian drag elements...
72429 d3.select(gd).selectAll('.drag').remove();
72430 return;
72431 }
72432
72433 if(!fullLayout._has('cartesian') && !fullLayout._has('splom')) return;
72434
72435 var subplots = Object.keys(fullLayout._plots || {}).sort(function(a, b) {
72436 // sort overlays last, then by x axis number, then y axis number
72437 if((fullLayout._plots[a].mainplot && true) ===
72438 (fullLayout._plots[b].mainplot && true)) {
72439 var aParts = a.split('y');
72440 var bParts = b.split('y');
72441 return (aParts[0] === bParts[0]) ?
72442 (Number(aParts[1] || 1) - Number(bParts[1] || 1)) :
72443 (Number(aParts[0] || 1) - Number(bParts[0] || 1));
72444 }
72445 return fullLayout._plots[a].mainplot ? 1 : -1;
72446 });
72447
72448 subplots.forEach(function(subplot) {
72449 var plotinfo = fullLayout._plots[subplot];
72450 var xa = plotinfo.xaxis;
72451 var ya = plotinfo.yaxis;
72452
72453 // main and corner draggers need not be repeated for
72454 // overlaid subplots - these draggers drag them all
72455 if(!plotinfo.mainplot) {
72456 // main dragger goes over the grids and data, so we use its
72457 // mousemove events for all data hover effects
72458 var maindrag = makeDragBox(gd, plotinfo, xa._offset, ya._offset,
72459 xa._length, ya._length, 'ns', 'ew');
72460
72461 maindrag.onmousemove = function(evt) {
72462 // This is on `gd._fullLayout`, *not* fullLayout because the reference
72463 // changes by the time this is called again.
72464 gd._fullLayout._rehover = function() {
72465 if((gd._fullLayout._hoversubplot === subplot) && gd._fullLayout._plots[subplot]) {
72466 Fx.hover(gd, evt, subplot);
72467 }
72468 };
72469
72470 Fx.hover(gd, evt, subplot);
72471
72472 // Note that we have *not* used the cached fullLayout variable here
72473 // since that may be outdated when this is called as a callback later on
72474 gd._fullLayout._lasthover = maindrag;
72475 gd._fullLayout._hoversubplot = subplot;
72476 };
72477
72478 /*
72479 * IMPORTANT:
72480 * We must check for the presence of the drag cover here.
72481 * If we don't, a 'mouseout' event is triggered on the
72482 * maindrag before each 'click' event, which has the effect
72483 * of clearing the hoverdata; thus, cancelling the click event.
72484 */
72485 maindrag.onmouseout = function(evt) {
72486 if(gd._dragging) return;
72487
72488 // When the mouse leaves this maindrag, unset the hovered subplot.
72489 // This may cause problems if it leaves the subplot directly *onto*
72490 // another subplot, but that's a tiny corner case at the moment.
72491 gd._fullLayout._hoversubplot = null;
72492
72493 dragElement.unhover(gd, evt);
72494 };
72495
72496 // corner draggers
72497 if(gd._context.showAxisDragHandles) {
72498 makeDragBox(gd, plotinfo, xa._offset - DRAGGERSIZE, ya._offset - DRAGGERSIZE,
72499 DRAGGERSIZE, DRAGGERSIZE, 'n', 'w');
72500 makeDragBox(gd, plotinfo, xa._offset + xa._length, ya._offset - DRAGGERSIZE,
72501 DRAGGERSIZE, DRAGGERSIZE, 'n', 'e');
72502 makeDragBox(gd, plotinfo, xa._offset - DRAGGERSIZE, ya._offset + ya._length,
72503 DRAGGERSIZE, DRAGGERSIZE, 's', 'w');
72504 makeDragBox(gd, plotinfo, xa._offset + xa._length, ya._offset + ya._length,
72505 DRAGGERSIZE, DRAGGERSIZE, 's', 'e');
72506 }
72507 }
72508 if(gd._context.showAxisDragHandles) {
72509 // x axis draggers - if you have overlaid plots,
72510 // these drag each axis separately
72511 if(subplot === xa._mainSubplot) {
72512 // the y position of the main x axis line
72513 var y0 = xa._mainLinePosition;
72514 if(xa.side === 'top') y0 -= DRAGGERSIZE;
72515 makeDragBox(gd, plotinfo, xa._offset + xa._length * 0.1, y0,
72516 xa._length * 0.8, DRAGGERSIZE, '', 'ew');
72517 makeDragBox(gd, plotinfo, xa._offset, y0,
72518 xa._length * 0.1, DRAGGERSIZE, '', 'w');
72519 makeDragBox(gd, plotinfo, xa._offset + xa._length * 0.9, y0,
72520 xa._length * 0.1, DRAGGERSIZE, '', 'e');
72521 }
72522 // y axis draggers
72523 if(subplot === ya._mainSubplot) {
72524 // the x position of the main y axis line
72525 var x0 = ya._mainLinePosition;
72526 if(ya.side !== 'right') x0 -= DRAGGERSIZE;
72527 makeDragBox(gd, plotinfo, x0, ya._offset + ya._length * 0.1,
72528 DRAGGERSIZE, ya._length * 0.8, 'ns', '');
72529 makeDragBox(gd, plotinfo, x0, ya._offset + ya._length * 0.9,
72530 DRAGGERSIZE, ya._length * 0.1, 's', '');
72531 makeDragBox(gd, plotinfo, x0, ya._offset,
72532 DRAGGERSIZE, ya._length * 0.1, 'n', '');
72533 }
72534 }
72535 });
72536
72537 // In case you mousemove over some hovertext, send it to Fx.hover too
72538 // we do this so that we can put the hover text in front of everything,
72539 // but still be able to interact with everything as if it isn't there
72540 var hoverLayer = fullLayout._hoverlayer.node();
72541
72542 hoverLayer.onmousemove = function(evt) {
72543 evt.target = gd._fullLayout._lasthover;
72544 Fx.hover(gd, evt, fullLayout._hoversubplot);
72545 };
72546
72547 hoverLayer.onclick = function(evt) {
72548 evt.target = gd._fullLayout._lasthover;
72549 Fx.click(gd, evt);
72550 };
72551
72552 // also delegate mousedowns... TODO: does this actually work?
72553 hoverLayer.onmousedown = function(evt) {
72554 gd._fullLayout._lasthover.onmousedown(evt);
72555 };
72556
72557 exports.updateFx(gd);
72558};
72559
72560// Minimal set of update needed on 'modebar' edits.
72561// We only need to update the <g .draglayer> cursor style.
72562//
72563// Note that changing the axis configuration and/or the fixedrange attribute
72564// should trigger a full initInteractions.
72565exports.updateFx = function(gd) {
72566 var fullLayout = gd._fullLayout;
72567 var cursor = fullLayout.dragmode === 'pan' ? 'move' : 'crosshair';
72568 setCursor(fullLayout._draggers, cursor);
72569};
72570
72571},{"../../components/dragelement":176,"../../components/fx":197,"../../lib/setcursor":307,"./constants":341,"./dragbox":343,"@plotly/d3":20}],345:[function(_dereq_,module,exports){
72572'use strict';
72573
72574function clearOutlineControllers(gd) {
72575 var zoomLayer = gd._fullLayout._zoomlayer;
72576 if(zoomLayer) {
72577 zoomLayer.selectAll('.outline-controllers').remove();
72578 }
72579}
72580
72581function clearSelect(gd) {
72582 var zoomLayer = gd._fullLayout._zoomlayer;
72583 if(zoomLayer) {
72584 // until we get around to persistent selections, remove the outline
72585 // here. The selection itself will be removed when the plot redraws
72586 // at the end.
72587 zoomLayer.selectAll('.select-outline').remove();
72588 }
72589
72590 gd._fullLayout._drawing = false;
72591}
72592
72593module.exports = {
72594 clearOutlineControllers: clearOutlineControllers,
72595 clearSelect: clearSelect
72596};
72597
72598},{}],346:[function(_dereq_,module,exports){
72599'use strict';
72600
72601var strTranslate = _dereq_('../../lib').strTranslate;
72602
72603// in v3 (once log ranges are fixed),
72604// we'll be able to p2r here for all axis types
72605function p2r(ax, v) {
72606 switch(ax.type) {
72607 case 'log':
72608 return ax.p2d(v);
72609 case 'date':
72610 return ax.p2r(v, 0, ax.calendar);
72611 default:
72612 return ax.p2r(v);
72613 }
72614}
72615
72616function r2p(ax, v) {
72617 switch(ax.type) {
72618 case 'log':
72619 return ax.d2p(v);
72620 case 'date':
72621 return ax.r2p(v, 0, ax.calendar);
72622 default:
72623 return ax.r2p(v);
72624 }
72625}
72626
72627function axValue(ax) {
72628 var index = (ax._id.charAt(0) === 'y') ? 1 : 0;
72629 return function(v) { return p2r(ax, v[index]); };
72630}
72631
72632function getTransform(plotinfo) {
72633 return strTranslate(
72634 plotinfo.xaxis._offset,
72635 plotinfo.yaxis._offset
72636 );
72637}
72638
72639module.exports = {
72640 p2r: p2r,
72641 r2p: r2p,
72642 axValue: axValue,
72643 getTransform: getTransform
72644};
72645
72646},{"../../lib":287}],347:[function(_dereq_,module,exports){
72647'use strict';
72648
72649var Registry = _dereq_('../../registry');
72650var Lib = _dereq_('../../lib');
72651var axisIds = _dereq_('./axis_ids');
72652
72653/**
72654 * Factory function for checking component arrays for subplot references.
72655 *
72656 * @param {string} containerArrayName: the top-level array in gd.layout to check
72657 * If an item in this container is found that references a cartesian x and/or y axis,
72658 * ensure cartesian is marked as a base plot module and record the axes (and subplot
72659 * if both refs are axes) in gd._fullLayout
72660 *
72661 * @return {function}: with args layoutIn (gd.layout) and layoutOut (gd._fullLayout)
72662 * as expected of a component includeBasePlot method
72663 */
72664module.exports = function makeIncludeComponents(containerArrayName) {
72665 return function includeComponents(layoutIn, layoutOut) {
72666 var array = layoutIn[containerArrayName];
72667 if(!Array.isArray(array)) return;
72668
72669 var Cartesian = Registry.subplotsRegistry.cartesian;
72670 var idRegex = Cartesian.idRegex;
72671 var subplots = layoutOut._subplots;
72672 var xaList = subplots.xaxis;
72673 var yaList = subplots.yaxis;
72674 var cartesianList = subplots.cartesian;
72675 var hasCartesianOrGL2D = layoutOut._has('cartesian') || layoutOut._has('gl2d');
72676
72677 for(var i = 0; i < array.length; i++) {
72678 var itemi = array[i];
72679 if(!Lib.isPlainObject(itemi)) continue;
72680
72681 // call cleanId because if xref, or yref has something appended
72682 // (e.g., ' domain') this will get removed.
72683 var xref = axisIds.cleanId(itemi.xref, 'x', false);
72684 var yref = axisIds.cleanId(itemi.yref, 'y', false);
72685
72686 var hasXref = idRegex.x.test(xref);
72687 var hasYref = idRegex.y.test(yref);
72688 if(hasXref || hasYref) {
72689 if(!hasCartesianOrGL2D) Lib.pushUnique(layoutOut._basePlotModules, Cartesian);
72690
72691 var newAxis = false;
72692 if(hasXref && xaList.indexOf(xref) === -1) {
72693 xaList.push(xref);
72694 newAxis = true;
72695 }
72696 if(hasYref && yaList.indexOf(yref) === -1) {
72697 yaList.push(yref);
72698 newAxis = true;
72699 }
72700
72701 /*
72702 * Notice the logic here: only add a subplot for a component if
72703 * it's referencing both x and y axes AND it's creating a new axis
72704 * so for example if your plot already has xy and x2y2, an annotation
72705 * on x2y or xy2 will not create a new subplot.
72706 */
72707 if(newAxis && hasXref && hasYref) {
72708 cartesianList.push(xref + yref);
72709 }
72710 }
72711 }
72712 };
72713};
72714
72715},{"../../lib":287,"../../registry":376,"./axis_ids":338}],348:[function(_dereq_,module,exports){
72716'use strict';
72717
72718var d3 = _dereq_('@plotly/d3');
72719
72720var Registry = _dereq_('../../registry');
72721var Lib = _dereq_('../../lib');
72722var Plots = _dereq_('../plots');
72723var Drawing = _dereq_('../../components/drawing');
72724
72725var getModuleCalcData = _dereq_('../get_data').getModuleCalcData;
72726var axisIds = _dereq_('./axis_ids');
72727var constants = _dereq_('./constants');
72728var xmlnsNamespaces = _dereq_('../../constants/xmlns_namespaces');
72729
72730var ensureSingle = Lib.ensureSingle;
72731
72732function ensureSingleAndAddDatum(parent, nodeType, className) {
72733 return Lib.ensureSingle(parent, nodeType, className, function(s) {
72734 s.datum(className);
72735 });
72736}
72737
72738exports.name = 'cartesian';
72739
72740exports.attr = ['xaxis', 'yaxis'];
72741
72742exports.idRoot = ['x', 'y'];
72743
72744exports.idRegex = constants.idRegex;
72745
72746exports.attrRegex = constants.attrRegex;
72747
72748exports.attributes = _dereq_('./attributes');
72749
72750exports.layoutAttributes = _dereq_('./layout_attributes');
72751
72752exports.supplyLayoutDefaults = _dereq_('./layout_defaults');
72753
72754exports.transitionAxes = _dereq_('./transition_axes');
72755
72756exports.finalizeSubplots = function(layoutIn, layoutOut) {
72757 var subplots = layoutOut._subplots;
72758 var xList = subplots.xaxis;
72759 var yList = subplots.yaxis;
72760 var spSVG = subplots.cartesian;
72761 var spAll = spSVG.concat(subplots.gl2d || []);
72762 var allX = {};
72763 var allY = {};
72764 var i, xi, yi;
72765
72766 for(i = 0; i < spAll.length; i++) {
72767 var parts = spAll[i].split('y');
72768 allX[parts[0]] = 1;
72769 allY['y' + parts[1]] = 1;
72770 }
72771
72772 // check for x axes with no subplot, and make one from the anchor of that x axis
72773 for(i = 0; i < xList.length; i++) {
72774 xi = xList[i];
72775 if(!allX[xi]) {
72776 yi = (layoutIn[axisIds.id2name(xi)] || {}).anchor;
72777 if(!constants.idRegex.y.test(yi)) yi = 'y';
72778 spSVG.push(xi + yi);
72779 spAll.push(xi + yi);
72780
72781 if(!allY[yi]) {
72782 allY[yi] = 1;
72783 Lib.pushUnique(yList, yi);
72784 }
72785 }
72786 }
72787
72788 // same for y axes with no subplot
72789 for(i = 0; i < yList.length; i++) {
72790 yi = yList[i];
72791 if(!allY[yi]) {
72792 xi = (layoutIn[axisIds.id2name(yi)] || {}).anchor;
72793 if(!constants.idRegex.x.test(xi)) xi = 'x';
72794 spSVG.push(xi + yi);
72795 spAll.push(xi + yi);
72796
72797 if(!allX[xi]) {
72798 allX[xi] = 1;
72799 Lib.pushUnique(xList, xi);
72800 }
72801 }
72802 }
72803
72804 // finally, if we've gotten here we're supposed to show cartesian...
72805 // so if there are NO subplots at all, make one from the first
72806 // x & y axes in the input layout
72807 if(!spAll.length) {
72808 xi = '';
72809 yi = '';
72810 for(var ki in layoutIn) {
72811 if(constants.attrRegex.test(ki)) {
72812 var axLetter = ki.charAt(0);
72813 if(axLetter === 'x') {
72814 if(!xi || (+ki.substr(5) < +xi.substr(5))) {
72815 xi = ki;
72816 }
72817 } else if(!yi || (+ki.substr(5) < +yi.substr(5))) {
72818 yi = ki;
72819 }
72820 }
72821 }
72822 xi = xi ? axisIds.name2id(xi) : 'x';
72823 yi = yi ? axisIds.name2id(yi) : 'y';
72824 xList.push(xi);
72825 yList.push(yi);
72826 spSVG.push(xi + yi);
72827 }
72828};
72829
72830/**
72831 * Cartesian.plot
72832 *
72833 * @param {DOM div | object} gd
72834 * @param {array (optional)} traces
72835 * array of traces indices to plot
72836 * if undefined, plots all cartesian traces,
72837 * @param {object} (optional) transitionOpts
72838 * transition option object
72839 * @param {function} (optional) makeOnCompleteCallback
72840 * transition make callback function from Plots.transition
72841 */
72842exports.plot = function(gd, traces, transitionOpts, makeOnCompleteCallback) {
72843 var fullLayout = gd._fullLayout;
72844 var subplots = fullLayout._subplots.cartesian;
72845 var calcdata = gd.calcdata;
72846 var i;
72847
72848 if(!Array.isArray(traces)) {
72849 // If traces is not provided, then it's a complete replot and missing
72850 // traces are removed
72851 traces = [];
72852 for(i = 0; i < calcdata.length; i++) traces.push(i);
72853 }
72854
72855 for(i = 0; i < subplots.length; i++) {
72856 var subplot = subplots[i];
72857 var subplotInfo = fullLayout._plots[subplot];
72858
72859 // Get all calcdata for this subplot:
72860 var cdSubplot = [];
72861 var pcd;
72862
72863 for(var j = 0; j < calcdata.length; j++) {
72864 var cd = calcdata[j];
72865 var trace = cd[0].trace;
72866
72867 // Skip trace if whitelist provided and it's not whitelisted:
72868 // if (Array.isArray(traces) && traces.indexOf(i) === -1) continue;
72869 if(trace.xaxis + trace.yaxis === subplot) {
72870 // XXX: Should trace carpet dependencies. Only replot all carpet plots if the carpet
72871 // axis has actually changed:
72872 //
72873 // If this trace is specifically requested, add it to the list:
72874 if(traces.indexOf(trace.index) !== -1 || trace.carpet) {
72875 // Okay, so example: traces 0, 1, and 2 have fill = tonext. You animate
72876 // traces 0 and 2. Trace 1 also needs to be updated, otherwise its fill
72877 // is outdated. So this retroactively adds the previous trace if the
72878 // traces are interdependent.
72879 if(
72880 pcd &&
72881 pcd[0].trace.xaxis + pcd[0].trace.yaxis === subplot &&
72882 ['tonextx', 'tonexty', 'tonext'].indexOf(trace.fill) !== -1 &&
72883 cdSubplot.indexOf(pcd) === -1
72884 ) {
72885 cdSubplot.push(pcd);
72886 }
72887
72888 cdSubplot.push(cd);
72889 }
72890
72891 // Track the previous trace on this subplot for the retroactive-add step
72892 // above:
72893 pcd = cd;
72894 }
72895 }
72896
72897 plotOne(gd, subplotInfo, cdSubplot, transitionOpts, makeOnCompleteCallback);
72898 }
72899};
72900
72901function plotOne(gd, plotinfo, cdSubplot, transitionOpts, makeOnCompleteCallback) {
72902 var traceLayerClasses = constants.traceLayerClasses;
72903 var fullLayout = gd._fullLayout;
72904 var modules = fullLayout._modules;
72905 var _module, cdModuleAndOthers, cdModule;
72906
72907 var layerData = [];
72908 var zoomScaleQueryParts = [];
72909
72910 for(var i = 0; i < modules.length; i++) {
72911 _module = modules[i];
72912 var name = _module.name;
72913 var categories = Registry.modules[name].categories;
72914
72915 if(categories.svg) {
72916 var className = (_module.layerName || name + 'layer');
72917 var plotMethod = _module.plot;
72918
72919 // plot all visible traces of this type on this subplot at once
72920 cdModuleAndOthers = getModuleCalcData(cdSubplot, plotMethod);
72921 cdModule = cdModuleAndOthers[0];
72922 // don't need to search the found traces again - in fact we need to NOT
72923 // so that if two modules share the same plotter we don't double-plot
72924 cdSubplot = cdModuleAndOthers[1];
72925
72926 if(cdModule.length) {
72927 layerData.push({
72928 i: traceLayerClasses.indexOf(className),
72929 className: className,
72930 plotMethod: plotMethod,
72931 cdModule: cdModule
72932 });
72933 }
72934
72935 if(categories.zoomScale) {
72936 zoomScaleQueryParts.push('.' + className);
72937 }
72938 }
72939 }
72940
72941 layerData.sort(function(a, b) { return a.i - b.i; });
72942
72943 var layers = plotinfo.plot.selectAll('g.mlayer')
72944 .data(layerData, function(d) { return d.className; });
72945
72946 layers.enter().append('g')
72947 .attr('class', function(d) { return d.className; })
72948 .classed('mlayer', true)
72949 .classed('rangeplot', plotinfo.isRangePlot);
72950
72951 layers.exit().remove();
72952
72953 layers.order();
72954
72955 layers.each(function(d) {
72956 var sel = d3.select(this);
72957 var className = d.className;
72958
72959 d.plotMethod(
72960 gd, plotinfo, d.cdModule, sel,
72961 transitionOpts, makeOnCompleteCallback
72962 );
72963
72964 // layers that allow `cliponaxis: false`
72965 if(constants.clipOnAxisFalseQuery.indexOf('.' + className) === -1) {
72966 Drawing.setClipUrl(sel, plotinfo.layerClipId, gd);
72967 }
72968 });
72969
72970 // call Scattergl.plot separately
72971 if(fullLayout._has('scattergl')) {
72972 _module = Registry.getModule('scattergl');
72973 cdModule = getModuleCalcData(cdSubplot, _module)[0];
72974 _module.plot(gd, plotinfo, cdModule);
72975 }
72976
72977 // stash "hot" selections for faster interaction on drag and scroll
72978 if(!gd._context.staticPlot) {
72979 if(plotinfo._hasClipOnAxisFalse) {
72980 plotinfo.clipOnAxisFalseTraces = plotinfo.plot
72981 .selectAll(constants.clipOnAxisFalseQuery.join(','))
72982 .selectAll('.trace');
72983 }
72984
72985 if(zoomScaleQueryParts.length) {
72986 var traces = plotinfo.plot
72987 .selectAll(zoomScaleQueryParts.join(','))
72988 .selectAll('.trace');
72989
72990 plotinfo.zoomScalePts = traces.selectAll('path.point');
72991 plotinfo.zoomScaleTxt = traces.selectAll('.textpoint');
72992 }
72993 }
72994}
72995
72996exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) {
72997 var oldPlots = oldFullLayout._plots || {};
72998 var newPlots = newFullLayout._plots || {};
72999 var oldSubplotList = oldFullLayout._subplots || {};
73000 var plotinfo;
73001 var i, k;
73002
73003 // when going from a large splom graph to something else,
73004 // we need to clear <g subplot> so that the new cartesian subplot
73005 // can have the correct layer ordering
73006 if(oldFullLayout._hasOnlyLargeSploms && !newFullLayout._hasOnlyLargeSploms) {
73007 for(k in oldPlots) {
73008 plotinfo = oldPlots[k];
73009 if(plotinfo.plotgroup) plotinfo.plotgroup.remove();
73010 }
73011 }
73012
73013 var hadGl = (oldFullLayout._has && oldFullLayout._has('gl'));
73014 var hasGl = (newFullLayout._has && newFullLayout._has('gl'));
73015
73016 if(hadGl && !hasGl) {
73017 for(k in oldPlots) {
73018 plotinfo = oldPlots[k];
73019 if(plotinfo._scene) plotinfo._scene.destroy();
73020 }
73021 }
73022
73023 // delete any titles we don't need anymore
73024 // check if axis list has changed, and if so clear old titles
73025 if(oldSubplotList.xaxis && oldSubplotList.yaxis) {
73026 var oldAxIDs = axisIds.listIds({_fullLayout: oldFullLayout});
73027 for(i = 0; i < oldAxIDs.length; i++) {
73028 var oldAxId = oldAxIDs[i];
73029 if(!newFullLayout[axisIds.id2name(oldAxId)]) {
73030 oldFullLayout._infolayer.selectAll('.g-' + oldAxId + 'title').remove();
73031 }
73032 }
73033 }
73034
73035 var hadCartesian = (oldFullLayout._has && oldFullLayout._has('cartesian'));
73036 var hasCartesian = (newFullLayout._has && newFullLayout._has('cartesian'));
73037
73038 if(hadCartesian && !hasCartesian) {
73039 // if we've gotten rid of all cartesian traces, remove all the subplot svg items
73040
73041 purgeSubplotLayers(oldFullLayout._cartesianlayer.selectAll('.subplot'), oldFullLayout);
73042 oldFullLayout._defs.selectAll('.axesclip').remove();
73043 delete oldFullLayout._axisConstraintGroups;
73044 delete oldFullLayout._axisMatchGroups;
73045 } else if(oldSubplotList.cartesian) {
73046 // otherwise look for subplots we need to remove
73047
73048 for(i = 0; i < oldSubplotList.cartesian.length; i++) {
73049 var oldSubplotId = oldSubplotList.cartesian[i];
73050 if(!newPlots[oldSubplotId]) {
73051 var selector = '.' + oldSubplotId + ',.' + oldSubplotId + '-x,.' + oldSubplotId + '-y';
73052 oldFullLayout._cartesianlayer.selectAll(selector).remove();
73053 removeSubplotExtras(oldSubplotId, oldFullLayout);
73054 }
73055 }
73056 }
73057};
73058
73059exports.drawFramework = function(gd) {
73060 var fullLayout = gd._fullLayout;
73061 var subplotData = makeSubplotData(gd);
73062
73063 var subplotLayers = fullLayout._cartesianlayer.selectAll('.subplot')
73064 .data(subplotData, String);
73065
73066 subplotLayers.enter().append('g')
73067 .attr('class', function(d) { return 'subplot ' + d[0]; });
73068
73069 subplotLayers.order();
73070
73071 subplotLayers.exit()
73072 .call(purgeSubplotLayers, fullLayout);
73073
73074 subplotLayers.each(function(d) {
73075 var id = d[0];
73076 var plotinfo = fullLayout._plots[id];
73077
73078 plotinfo.plotgroup = d3.select(this);
73079 makeSubplotLayer(gd, plotinfo);
73080
73081 // make separate drag layers for each subplot,
73082 // but append them to paper rather than the plot groups,
73083 // so they end up on top of the rest
73084 plotinfo.draglayer = ensureSingle(fullLayout._draggers, 'g', id);
73085 });
73086};
73087
73088exports.rangePlot = function(gd, plotinfo, cdSubplot) {
73089 makeSubplotLayer(gd, plotinfo);
73090 plotOne(gd, plotinfo, cdSubplot);
73091 Plots.style(gd);
73092};
73093
73094function makeSubplotData(gd) {
73095 var fullLayout = gd._fullLayout;
73096 var ids = fullLayout._subplots.cartesian;
73097 var len = ids.length;
73098 var i, j, id, plotinfo, xa, ya;
73099
73100 // split 'regular' and 'overlaying' subplots
73101 var regulars = [];
73102 var overlays = [];
73103
73104 for(i = 0; i < len; i++) {
73105 id = ids[i];
73106 plotinfo = fullLayout._plots[id];
73107 xa = plotinfo.xaxis;
73108 ya = plotinfo.yaxis;
73109
73110 var xa2 = xa._mainAxis;
73111 var ya2 = ya._mainAxis;
73112 var mainplot = xa2._id + ya2._id;
73113 var mainplotinfo = fullLayout._plots[mainplot];
73114 plotinfo.overlays = [];
73115
73116 if(mainplot !== id && mainplotinfo) {
73117 plotinfo.mainplot = mainplot;
73118 plotinfo.mainplotinfo = mainplotinfo;
73119 overlays.push(id);
73120 } else {
73121 plotinfo.mainplot = undefined;
73122 plotinfo.mainplotinfo = undefined;
73123 regulars.push(id);
73124 }
73125 }
73126
73127 // fill in list of overlaying subplots in 'main plot'
73128 for(i = 0; i < overlays.length; i++) {
73129 id = overlays[i];
73130 plotinfo = fullLayout._plots[id];
73131 plotinfo.mainplotinfo.overlays.push(plotinfo);
73132 }
73133
73134 // put 'regular' subplot data before 'overlaying'
73135 var subplotIds = regulars.concat(overlays);
73136 var subplotData = new Array(len);
73137
73138 for(i = 0; i < len; i++) {
73139 id = subplotIds[i];
73140 plotinfo = fullLayout._plots[id];
73141 xa = plotinfo.xaxis;
73142 ya = plotinfo.yaxis;
73143
73144 // use info about axis layer and overlaying pattern
73145 // to clean what need to be cleaned up in exit selection
73146 var d = [id, xa.layer, ya.layer, xa.overlaying || '', ya.overlaying || ''];
73147 for(j = 0; j < plotinfo.overlays.length; j++) {
73148 d.push(plotinfo.overlays[j].id);
73149 }
73150 subplotData[i] = d;
73151 }
73152
73153 return subplotData;
73154}
73155
73156function makeSubplotLayer(gd, plotinfo) {
73157 var plotgroup = plotinfo.plotgroup;
73158 var id = plotinfo.id;
73159 var xLayer = constants.layerValue2layerClass[plotinfo.xaxis.layer];
73160 var yLayer = constants.layerValue2layerClass[plotinfo.yaxis.layer];
73161 var hasOnlyLargeSploms = gd._fullLayout._hasOnlyLargeSploms;
73162
73163 if(!plotinfo.mainplot) {
73164 if(hasOnlyLargeSploms) {
73165 // TODO could do even better
73166 // - we don't need plot (but we would have to mock it in lsInner
73167 // and other places
73168 // - we don't (x|y)lines and (x|y)axislayer for most subplots
73169 // usually just the bottom x and left y axes.
73170 plotinfo.xlines = ensureSingle(plotgroup, 'path', 'xlines-above');
73171 plotinfo.ylines = ensureSingle(plotgroup, 'path', 'ylines-above');
73172 plotinfo.xaxislayer = ensureSingle(plotgroup, 'g', 'xaxislayer-above');
73173 plotinfo.yaxislayer = ensureSingle(plotgroup, 'g', 'yaxislayer-above');
73174 } else {
73175 var backLayer = ensureSingle(plotgroup, 'g', 'layer-subplot');
73176 plotinfo.shapelayer = ensureSingle(backLayer, 'g', 'shapelayer');
73177 plotinfo.imagelayer = ensureSingle(backLayer, 'g', 'imagelayer');
73178
73179 plotinfo.gridlayer = ensureSingle(plotgroup, 'g', 'gridlayer');
73180 plotinfo.zerolinelayer = ensureSingle(plotgroup, 'g', 'zerolinelayer');
73181
73182 ensureSingle(plotgroup, 'path', 'xlines-below');
73183 ensureSingle(plotgroup, 'path', 'ylines-below');
73184 plotinfo.overlinesBelow = ensureSingle(plotgroup, 'g', 'overlines-below');
73185
73186 ensureSingle(plotgroup, 'g', 'xaxislayer-below');
73187 ensureSingle(plotgroup, 'g', 'yaxislayer-below');
73188 plotinfo.overaxesBelow = ensureSingle(plotgroup, 'g', 'overaxes-below');
73189
73190 plotinfo.plot = ensureSingle(plotgroup, 'g', 'plot');
73191 plotinfo.overplot = ensureSingle(plotgroup, 'g', 'overplot');
73192
73193 plotinfo.xlines = ensureSingle(plotgroup, 'path', 'xlines-above');
73194 plotinfo.ylines = ensureSingle(plotgroup, 'path', 'ylines-above');
73195 plotinfo.overlinesAbove = ensureSingle(plotgroup, 'g', 'overlines-above');
73196
73197 ensureSingle(plotgroup, 'g', 'xaxislayer-above');
73198 ensureSingle(plotgroup, 'g', 'yaxislayer-above');
73199 plotinfo.overaxesAbove = ensureSingle(plotgroup, 'g', 'overaxes-above');
73200
73201 // set refs to correct layers as determined by 'axis.layer'
73202 plotinfo.xlines = plotgroup.select('.xlines-' + xLayer);
73203 plotinfo.ylines = plotgroup.select('.ylines-' + yLayer);
73204 plotinfo.xaxislayer = plotgroup.select('.xaxislayer-' + xLayer);
73205 plotinfo.yaxislayer = plotgroup.select('.yaxislayer-' + yLayer);
73206 }
73207 } else {
73208 var mainplotinfo = plotinfo.mainplotinfo;
73209 var mainplotgroup = mainplotinfo.plotgroup;
73210 var xId = id + '-x';
73211 var yId = id + '-y';
73212
73213 // now make the components of overlaid subplots
73214 // overlays don't have backgrounds, and append all
73215 // their other components to the corresponding
73216 // extra groups of their main plots.
73217
73218 plotinfo.gridlayer = mainplotinfo.gridlayer;
73219 plotinfo.zerolinelayer = mainplotinfo.zerolinelayer;
73220
73221 ensureSingle(mainplotinfo.overlinesBelow, 'path', xId);
73222 ensureSingle(mainplotinfo.overlinesBelow, 'path', yId);
73223 ensureSingle(mainplotinfo.overaxesBelow, 'g', xId);
73224 ensureSingle(mainplotinfo.overaxesBelow, 'g', yId);
73225
73226 plotinfo.plot = ensureSingle(mainplotinfo.overplot, 'g', id);
73227
73228 ensureSingle(mainplotinfo.overlinesAbove, 'path', xId);
73229 ensureSingle(mainplotinfo.overlinesAbove, 'path', yId);
73230 ensureSingle(mainplotinfo.overaxesAbove, 'g', xId);
73231 ensureSingle(mainplotinfo.overaxesAbove, 'g', yId);
73232
73233 // set refs to correct layers as determined by 'abovetraces'
73234 plotinfo.xlines = mainplotgroup.select('.overlines-' + xLayer).select('.' + xId);
73235 plotinfo.ylines = mainplotgroup.select('.overlines-' + yLayer).select('.' + yId);
73236 plotinfo.xaxislayer = mainplotgroup.select('.overaxes-' + xLayer).select('.' + xId);
73237 plotinfo.yaxislayer = mainplotgroup.select('.overaxes-' + yLayer).select('.' + yId);
73238 }
73239
73240 // common attributes for all subplots, overlays or not
73241
73242 if(!hasOnlyLargeSploms) {
73243 ensureSingleAndAddDatum(plotinfo.gridlayer, 'g', plotinfo.xaxis._id);
73244 ensureSingleAndAddDatum(plotinfo.gridlayer, 'g', plotinfo.yaxis._id);
73245 plotinfo.gridlayer.selectAll('g')
73246 .map(function(d) { return d[0]; })
73247 .sort(axisIds.idSort);
73248 }
73249
73250 plotinfo.xlines
73251 .style('fill', 'none')
73252 .classed('crisp', true);
73253
73254 plotinfo.ylines
73255 .style('fill', 'none')
73256 .classed('crisp', true);
73257}
73258
73259function purgeSubplotLayers(layers, fullLayout) {
73260 if(!layers) return;
73261
73262 var overlayIdsToRemove = {};
73263
73264 layers.each(function(d) {
73265 var id = d[0];
73266 var plotgroup = d3.select(this);
73267
73268 plotgroup.remove();
73269 removeSubplotExtras(id, fullLayout);
73270 overlayIdsToRemove[id] = true;
73271
73272 // do not remove individual axis <clipPath>s here
73273 // as other subplots may need them
73274 });
73275
73276 // must remove overlaid subplot trace layers 'manually'
73277
73278 for(var k in fullLayout._plots) {
73279 var subplotInfo = fullLayout._plots[k];
73280 var overlays = subplotInfo.overlays || [];
73281
73282 for(var j = 0; j < overlays.length; j++) {
73283 var overlayInfo = overlays[j];
73284
73285 if(overlayIdsToRemove[overlayInfo.id]) {
73286 overlayInfo.plot.selectAll('.trace').remove();
73287 }
73288 }
73289 }
73290}
73291
73292function removeSubplotExtras(subplotId, fullLayout) {
73293 fullLayout._draggers.selectAll('g.' + subplotId).remove();
73294 fullLayout._defs.select('#clip' + fullLayout._uid + subplotId + 'plot').remove();
73295}
73296
73297exports.toSVG = function(gd) {
73298 var imageRoot = gd._fullLayout._glimages;
73299 var root = d3.select(gd).selectAll('.svg-container');
73300 var canvases = root.filter(function(d, i) {return i === root.size() - 1;})
73301 .selectAll('.gl-canvas-context, .gl-canvas-focus');
73302
73303 function canvasToImage() {
73304 var canvas = this;
73305 var imageData = canvas.toDataURL('image/png');
73306 var image = imageRoot.append('svg:image');
73307
73308 image.attr({
73309 xmlns: xmlnsNamespaces.svg,
73310 'xlink:href': imageData,
73311 preserveAspectRatio: 'none',
73312 x: 0,
73313 y: 0,
73314 width: canvas.style.width,
73315 height: canvas.style.height
73316 });
73317 }
73318
73319 canvases.each(canvasToImage);
73320};
73321
73322exports.updateFx = _dereq_('./graph_interact').updateFx;
73323
73324},{"../../components/drawing":179,"../../constants/xmlns_namespaces":268,"../../lib":287,"../../registry":376,"../get_data":365,"../plots":369,"./attributes":332,"./axis_ids":338,"./constants":341,"./graph_interact":344,"./layout_attributes":349,"./layout_defaults":350,"./transition_axes":359,"@plotly/d3":20}],349:[function(_dereq_,module,exports){
73325'use strict';
73326
73327var fontAttrs = _dereq_('../font_attributes');
73328var colorAttrs = _dereq_('../../components/color/attributes');
73329var dash = _dereq_('../../components/drawing/attributes').dash;
73330var extendFlat = _dereq_('../../lib/extend').extendFlat;
73331var templatedArray = _dereq_('../../plot_api/plot_template').templatedArray;
73332var descriptionWithDates = _dereq_('../../plots/cartesian/axis_format_attributes').descriptionWithDates;
73333
73334var ONEDAY = _dereq_('../../constants/numerical').ONEDAY;
73335var constants = _dereq_('./constants');
73336var HOUR = constants.HOUR_PATTERN;
73337var DAY_OF_WEEK = constants.WEEKDAY_PATTERN;
73338
73339module.exports = {
73340 visible: {
73341 valType: 'boolean',
73342 editType: 'plot',
73343 },
73344 color: {
73345 valType: 'color',
73346 dflt: colorAttrs.defaultLine,
73347 editType: 'ticks',
73348 },
73349 title: {
73350 text: {
73351 valType: 'string',
73352 editType: 'ticks',
73353 },
73354 font: fontAttrs({
73355 editType: 'ticks',
73356 }),
73357 standoff: {
73358 valType: 'number',
73359 min: 0,
73360 editType: 'ticks',
73361 },
73362 editType: 'ticks'
73363 },
73364 type: {
73365 valType: 'enumerated',
73366 // '-' means we haven't yet run autotype or couldn't find any data
73367 // it gets turned into linear in gd._fullLayout but not copied back
73368 // to gd.data like the others are.
73369 values: ['-', 'linear', 'log', 'date', 'category', 'multicategory'],
73370 dflt: '-',
73371 editType: 'calc',
73372 // we forget when an axis has been autotyped, just writing the auto
73373 // value back to the input - so it doesn't make sense to template this.
73374 // Note: we do NOT prohibit this in `coerce`, so if someone enters a
73375 // type in the template explicitly it will be honored as the default.
73376 _noTemplating: true,
73377 },
73378 autotypenumbers: {
73379 valType: 'enumerated',
73380 values: ['convert types', 'strict'],
73381 dflt: 'convert types',
73382 editType: 'calc',
73383 },
73384 autorange: {
73385 valType: 'enumerated',
73386 values: [true, false, 'reversed'],
73387 dflt: true,
73388 editType: 'axrange',
73389 impliedEdits: {'range[0]': undefined, 'range[1]': undefined},
73390 },
73391 rangemode: {
73392 valType: 'enumerated',
73393 values: ['normal', 'tozero', 'nonnegative'],
73394 dflt: 'normal',
73395 editType: 'plot',
73396 },
73397 range: {
73398 valType: 'info_array',
73399 items: [
73400 {valType: 'any', editType: 'axrange', impliedEdits: {'^autorange': false}, anim: true},
73401 {valType: 'any', editType: 'axrange', impliedEdits: {'^autorange': false}, anim: true}
73402 ],
73403 editType: 'axrange',
73404 impliedEdits: {'autorange': false},
73405 anim: true,
73406 },
73407 fixedrange: {
73408 valType: 'boolean',
73409 dflt: false,
73410 editType: 'calc',
73411 },
73412 // scaleanchor: not used directly, just put here for reference
73413 // values are any opposite-letter axis id
73414 scaleanchor: {
73415 valType: 'enumerated',
73416 values: [
73417 constants.idRegex.x.toString(),
73418 constants.idRegex.y.toString()
73419 ],
73420 editType: 'plot',
73421 },
73422 scaleratio: {
73423 valType: 'number',
73424 min: 0,
73425 dflt: 1,
73426 editType: 'plot',
73427 },
73428 constrain: {
73429 valType: 'enumerated',
73430 values: ['range', 'domain'],
73431 editType: 'plot',
73432 },
73433 // constraintoward: not used directly, just put here for reference
73434 constraintoward: {
73435 valType: 'enumerated',
73436 values: ['left', 'center', 'right', 'top', 'middle', 'bottom'],
73437 editType: 'plot',
73438 },
73439 matches: {
73440 valType: 'enumerated',
73441 values: [
73442 constants.idRegex.x.toString(),
73443 constants.idRegex.y.toString()
73444 ],
73445 editType: 'calc',
73446 },
73447
73448 rangebreaks: templatedArray('rangebreak', {
73449 enabled: {
73450 valType: 'boolean',
73451 dflt: true,
73452 editType: 'calc',
73453 },
73454
73455 bounds: {
73456 valType: 'info_array',
73457 items: [
73458 {valType: 'any', editType: 'calc'},
73459 {valType: 'any', editType: 'calc'}
73460 ],
73461 editType: 'calc',
73462 },
73463
73464 pattern: {
73465 valType: 'enumerated',
73466 values: [DAY_OF_WEEK, HOUR, ''],
73467 editType: 'calc',
73468 },
73469
73470 values: {
73471 valType: 'info_array',
73472 freeLength: true,
73473 editType: 'calc',
73474 items: {
73475 valType: 'any',
73476 editType: 'calc'
73477 },
73478 },
73479 dvalue: {
73480 // TODO could become 'any' to add support for 'months', 'years'
73481 valType: 'number',
73482 editType: 'calc',
73483 min: 0,
73484 dflt: ONEDAY,
73485 },
73486
73487 /*
73488 gap: {
73489 valType: 'number',
73490 min: 0,
73491 dflt: 0, // for *date* axes, maybe something else for *linear*
73492 editType: 'calc',
73493 },
73494 gapmode: {
73495 valType: 'enumerated',
73496 values: ['pixels', 'fraction'],
73497 dflt: 'pixels',
73498 editType: 'calc',
73499 },
73500 */
73501
73502 // To complete https://github.com/plotly/plotly.js/issues/4210
73503 // we additionally need `gap` and make this work on *linear*, and
73504 // possibly all other cartesian axis types. We possibly would also need
73505 // some style attributes controlling the zig-zag on the corresponding
73506 // axis.
73507
73508 editType: 'calc'
73509 }),
73510
73511 // ticks
73512 tickmode: {
73513 valType: 'enumerated',
73514 values: ['auto', 'linear', 'array'],
73515 editType: 'ticks',
73516 impliedEdits: {tick0: undefined, dtick: undefined},
73517 },
73518 nticks: {
73519 valType: 'integer',
73520 min: 0,
73521 dflt: 0,
73522 editType: 'ticks',
73523 },
73524 tick0: {
73525 valType: 'any',
73526 editType: 'ticks',
73527 impliedEdits: {tickmode: 'linear'},
73528 },
73529 dtick: {
73530 valType: 'any',
73531 editType: 'ticks',
73532 impliedEdits: {tickmode: 'linear'},
73533 },
73534 tickvals: {
73535 valType: 'data_array',
73536 editType: 'ticks',
73537 },
73538 ticktext: {
73539 valType: 'data_array',
73540 editType: 'ticks',
73541 },
73542 ticks: {
73543 valType: 'enumerated',
73544 values: ['outside', 'inside', ''],
73545 editType: 'ticks',
73546 },
73547 tickson: {
73548 valType: 'enumerated',
73549 values: ['labels', 'boundaries'],
73550 dflt: 'labels',
73551 editType: 'ticks',
73552 },
73553 ticklabelmode: {
73554 valType: 'enumerated',
73555 values: ['instant', 'period'],
73556 dflt: 'instant',
73557 editType: 'ticks',
73558 },
73559 // ticklabelposition: not used directly, as values depend on direction (similar to side)
73560 // left/right options are for x axes, and top/bottom options are for y axes
73561 ticklabelposition: {
73562 valType: 'enumerated',
73563 values: [
73564 'outside', 'inside',
73565 'outside top', 'inside top',
73566 'outside left', 'inside left',
73567 'outside right', 'inside right',
73568 'outside bottom', 'inside bottom'
73569 ],
73570 dflt: 'outside',
73571 editType: 'calc',
73572 },
73573 ticklabeloverflow: {
73574 valType: 'enumerated',
73575 values: [
73576 'allow',
73577 'hide past div',
73578 'hide past domain'
73579 ],
73580 editType: 'calc',
73581 },
73582 mirror: {
73583 valType: 'enumerated',
73584 values: [true, 'ticks', false, 'all', 'allticks'],
73585 dflt: false,
73586 editType: 'ticks+layoutstyle',
73587 },
73588 ticklen: {
73589 valType: 'number',
73590 min: 0,
73591 dflt: 5,
73592 editType: 'ticks',
73593 },
73594 tickwidth: {
73595 valType: 'number',
73596 min: 0,
73597 dflt: 1,
73598 editType: 'ticks',
73599 },
73600 tickcolor: {
73601 valType: 'color',
73602 dflt: colorAttrs.defaultLine,
73603 editType: 'ticks',
73604 },
73605 showticklabels: {
73606 valType: 'boolean',
73607 dflt: true,
73608 editType: 'ticks',
73609 },
73610 automargin: {
73611 valType: 'boolean',
73612 dflt: false,
73613 editType: 'ticks',
73614 },
73615 showspikes: {
73616 valType: 'boolean',
73617 dflt: false,
73618 editType: 'modebar',
73619 },
73620 spikecolor: {
73621 valType: 'color',
73622 dflt: null,
73623 editType: 'none',
73624 },
73625 spikethickness: {
73626 valType: 'number',
73627 dflt: 3,
73628 editType: 'none',
73629 },
73630 spikedash: extendFlat({}, dash, {dflt: 'dash', editType: 'none'}),
73631 spikemode: {
73632 valType: 'flaglist',
73633 flags: ['toaxis', 'across', 'marker'],
73634 dflt: 'toaxis',
73635 editType: 'none',
73636 },
73637 spikesnap: {
73638 valType: 'enumerated',
73639 values: ['data', 'cursor', 'hovered data'],
73640 dflt: 'hovered data',
73641 editType: 'none',
73642 },
73643 tickfont: fontAttrs({
73644 editType: 'ticks',
73645 }),
73646 tickangle: {
73647 valType: 'angle',
73648 dflt: 'auto',
73649 editType: 'ticks',
73650 },
73651 tickprefix: {
73652 valType: 'string',
73653 dflt: '',
73654 editType: 'ticks',
73655 },
73656 showtickprefix: {
73657 valType: 'enumerated',
73658 values: ['all', 'first', 'last', 'none'],
73659 dflt: 'all',
73660 editType: 'ticks',
73661 },
73662 ticksuffix: {
73663 valType: 'string',
73664 dflt: '',
73665 editType: 'ticks',
73666 },
73667 showticksuffix: {
73668 valType: 'enumerated',
73669 values: ['all', 'first', 'last', 'none'],
73670 dflt: 'all',
73671 editType: 'ticks',
73672 },
73673 showexponent: {
73674 valType: 'enumerated',
73675 values: ['all', 'first', 'last', 'none'],
73676 dflt: 'all',
73677 editType: 'ticks',
73678 },
73679 exponentformat: {
73680 valType: 'enumerated',
73681 values: ['none', 'e', 'E', 'power', 'SI', 'B'],
73682 dflt: 'B',
73683 editType: 'ticks',
73684 },
73685 minexponent: {
73686 valType: 'number',
73687 dflt: 3,
73688 min: 0,
73689 editType: 'ticks',
73690 },
73691 separatethousands: {
73692 valType: 'boolean',
73693 dflt: false,
73694 editType: 'ticks',
73695 },
73696 tickformat: {
73697 valType: 'string',
73698 dflt: '',
73699 editType: 'ticks',
73700 description: descriptionWithDates('tick label')
73701 },
73702 tickformatstops: templatedArray('tickformatstop', {
73703 enabled: {
73704 valType: 'boolean',
73705 dflt: true,
73706 editType: 'ticks',
73707 },
73708 dtickrange: {
73709 valType: 'info_array',
73710 items: [
73711 {valType: 'any', editType: 'ticks'},
73712 {valType: 'any', editType: 'ticks'}
73713 ],
73714 editType: 'ticks',
73715 },
73716 value: {
73717 valType: 'string',
73718 dflt: '',
73719 editType: 'ticks',
73720 },
73721 editType: 'ticks'
73722 }),
73723 hoverformat: {
73724 valType: 'string',
73725 dflt: '',
73726 editType: 'none',
73727 description: descriptionWithDates('hover text')
73728 },
73729 // lines and grids
73730 showline: {
73731 valType: 'boolean',
73732 dflt: false,
73733 editType: 'ticks+layoutstyle',
73734 },
73735 linecolor: {
73736 valType: 'color',
73737 dflt: colorAttrs.defaultLine,
73738 editType: 'layoutstyle',
73739 },
73740 linewidth: {
73741 valType: 'number',
73742 min: 0,
73743 dflt: 1,
73744 editType: 'ticks+layoutstyle',
73745 },
73746 showgrid: {
73747 valType: 'boolean',
73748 editType: 'ticks',
73749 },
73750 gridcolor: {
73751 valType: 'color',
73752 dflt: colorAttrs.lightLine,
73753 editType: 'ticks',
73754 },
73755 gridwidth: {
73756 valType: 'number',
73757 min: 0,
73758 dflt: 1,
73759 editType: 'ticks',
73760 },
73761 zeroline: {
73762 valType: 'boolean',
73763 editType: 'ticks',
73764 },
73765 zerolinecolor: {
73766 valType: 'color',
73767 dflt: colorAttrs.defaultLine,
73768 editType: 'ticks',
73769 },
73770 zerolinewidth: {
73771 valType: 'number',
73772 dflt: 1,
73773 editType: 'ticks',
73774 },
73775
73776 showdividers: {
73777 valType: 'boolean',
73778 dflt: true,
73779 editType: 'ticks',
73780 },
73781 dividercolor: {
73782 valType: 'color',
73783 dflt: colorAttrs.defaultLine,
73784 editType: 'ticks',
73785 },
73786 dividerwidth: {
73787 valType: 'number',
73788 dflt: 1,
73789 editType: 'ticks',
73790 },
73791 // TODO dividerlen: that would override "to label base" length?
73792
73793 // positioning attributes
73794 // anchor: not used directly, just put here for reference
73795 // values are any opposite-letter axis id
73796 anchor: {
73797 valType: 'enumerated',
73798 values: [
73799 'free',
73800 constants.idRegex.x.toString(),
73801 constants.idRegex.y.toString()
73802 ],
73803 editType: 'plot',
73804 },
73805 // side: not used directly, as values depend on direction
73806 // values are top, bottom for x axes, and left, right for y
73807 side: {
73808 valType: 'enumerated',
73809 values: ['top', 'bottom', 'left', 'right'],
73810 editType: 'plot',
73811 },
73812 // overlaying: not used directly, just put here for reference
73813 // values are false and any other same-letter axis id that's not
73814 // itself overlaying anything
73815 overlaying: {
73816 valType: 'enumerated',
73817 values: [
73818 'free',
73819 constants.idRegex.x.toString(),
73820 constants.idRegex.y.toString()
73821 ],
73822 editType: 'plot',
73823 },
73824 layer: {
73825 valType: 'enumerated',
73826 values: ['above traces', 'below traces'],
73827 dflt: 'above traces',
73828 editType: 'plot',
73829 },
73830 domain: {
73831 valType: 'info_array',
73832 items: [
73833 {valType: 'number', min: 0, max: 1, editType: 'plot'},
73834 {valType: 'number', min: 0, max: 1, editType: 'plot'}
73835 ],
73836 dflt: [0, 1],
73837 editType: 'plot',
73838 },
73839 position: {
73840 valType: 'number',
73841 min: 0,
73842 max: 1,
73843 dflt: 0,
73844 editType: 'plot',
73845 },
73846 categoryorder: {
73847 valType: 'enumerated',
73848 values: [
73849 'trace', 'category ascending', 'category descending', 'array',
73850 'total ascending', 'total descending',
73851 'min ascending', 'min descending',
73852 'max ascending', 'max descending',
73853 'sum ascending', 'sum descending',
73854 'mean ascending', 'mean descending',
73855 'median ascending', 'median descending'
73856 ],
73857 dflt: 'trace',
73858 editType: 'calc',
73859 },
73860 categoryarray: {
73861 valType: 'data_array',
73862 editType: 'calc',
73863 },
73864 uirevision: {
73865 valType: 'any',
73866 editType: 'none',
73867 },
73868 editType: 'calc',
73869
73870 _deprecated: {
73871 autotick: {
73872 valType: 'boolean',
73873 editType: 'ticks',
73874 },
73875 title: {
73876 valType: 'string',
73877 editType: 'ticks',
73878 },
73879 titlefont: fontAttrs({
73880 editType: 'ticks',
73881 })
73882 }
73883};
73884
73885},{"../../components/color/attributes":156,"../../components/drawing/attributes":178,"../../constants/numerical":267,"../../lib/extend":281,"../../plot_api/plot_template":323,"../../plots/cartesian/axis_format_attributes":337,"../font_attributes":363,"./constants":341}],350:[function(_dereq_,module,exports){
73886'use strict';
73887
73888var Lib = _dereq_('../../lib');
73889var Color = _dereq_('../../components/color');
73890var isUnifiedHover = _dereq_('../../components/fx/helpers').isUnifiedHover;
73891var handleHoverModeDefaults = _dereq_('../../components/fx/hovermode_defaults');
73892var Template = _dereq_('../../plot_api/plot_template');
73893var basePlotLayoutAttributes = _dereq_('../layout_attributes');
73894
73895var layoutAttributes = _dereq_('./layout_attributes');
73896var handleTypeDefaults = _dereq_('./type_defaults');
73897var handleAxisDefaults = _dereq_('./axis_defaults');
73898var constraints = _dereq_('./constraints');
73899var handlePositionDefaults = _dereq_('./position_defaults');
73900
73901var axisIds = _dereq_('./axis_ids');
73902var id2name = axisIds.id2name;
73903var name2id = axisIds.name2id;
73904
73905var AX_ID_PATTERN = _dereq_('./constants').AX_ID_PATTERN;
73906
73907var Registry = _dereq_('../../registry');
73908var traceIs = Registry.traceIs;
73909var getComponentMethod = Registry.getComponentMethod;
73910
73911function appendList(cont, k, item) {
73912 if(Array.isArray(cont[k])) cont[k].push(item);
73913 else cont[k] = [item];
73914}
73915
73916module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
73917 var autotypenumbersDflt = layoutOut.autotypenumbers;
73918
73919 var ax2traces = {};
73920 var xaMayHide = {};
73921 var yaMayHide = {};
73922 var xaMustDisplay = {};
73923 var yaMustDisplay = {};
73924 var yaMustNotReverse = {};
73925 var yaMayReverse = {};
73926 var axHasImage = {};
73927 var outerTicks = {};
73928 var noGrids = {};
73929 var i, j;
73930
73931 // look for axes in the data
73932 for(i = 0; i < fullData.length; i++) {
73933 var trace = fullData[i];
73934 if(!traceIs(trace, 'cartesian') && !traceIs(trace, 'gl2d')) continue;
73935
73936 var xaName;
73937 if(trace.xaxis) {
73938 xaName = id2name(trace.xaxis);
73939 appendList(ax2traces, xaName, trace);
73940 } else if(trace.xaxes) {
73941 for(j = 0; j < trace.xaxes.length; j++) {
73942 appendList(ax2traces, id2name(trace.xaxes[j]), trace);
73943 }
73944 }
73945
73946 var yaName;
73947 if(trace.yaxis) {
73948 yaName = id2name(trace.yaxis);
73949 appendList(ax2traces, yaName, trace);
73950 } else if(trace.yaxes) {
73951 for(j = 0; j < trace.yaxes.length; j++) {
73952 appendList(ax2traces, id2name(trace.yaxes[j]), trace);
73953 }
73954 }
73955
73956 // logic for funnels
73957 if(trace.type === 'funnel') {
73958 if(trace.orientation === 'h') {
73959 if(xaName) xaMayHide[xaName] = true;
73960 if(yaName) yaMayReverse[yaName] = true;
73961 } else {
73962 if(yaName) yaMayHide[yaName] = true;
73963 }
73964 } else if(trace.type === 'image') {
73965 if(yaName) axHasImage[yaName] = true;
73966 if(xaName) axHasImage[xaName] = true;
73967 } else {
73968 if(yaName) {
73969 yaMustDisplay[yaName] = true;
73970 yaMustNotReverse[yaName] = true;
73971 }
73972
73973 if(!traceIs(trace, 'carpet') || (trace.type === 'carpet' && !trace._cheater)) {
73974 if(xaName) xaMustDisplay[xaName] = true;
73975 }
73976 }
73977
73978 // Two things trigger axis visibility:
73979 // 1. is not carpet
73980 // 2. carpet that's not cheater
73981
73982 // The above check for definitely-not-cheater is not adequate. This
73983 // second list tracks which axes *could* be a cheater so that the
73984 // full condition triggering hiding is:
73985 // *could* be a cheater and *is not definitely visible*
73986 if(trace.type === 'carpet' && trace._cheater) {
73987 if(xaName) xaMayHide[xaName] = true;
73988 }
73989
73990 // check for default formatting tweaks
73991 if(traceIs(trace, '2dMap')) {
73992 outerTicks[xaName] = true;
73993 outerTicks[yaName] = true;
73994 }
73995
73996 if(traceIs(trace, 'oriented')) {
73997 var positionAxis = trace.orientation === 'h' ? yaName : xaName;
73998 noGrids[positionAxis] = true;
73999 }
74000 }
74001
74002 var subplots = layoutOut._subplots;
74003 var xIds = subplots.xaxis;
74004 var yIds = subplots.yaxis;
74005 var xNames = Lib.simpleMap(xIds, id2name);
74006 var yNames = Lib.simpleMap(yIds, id2name);
74007 var axNames = xNames.concat(yNames);
74008
74009 // plot_bgcolor only makes sense if there's a (2D) plot!
74010 // TODO: bgcolor for each subplot, to inherit from the main one
74011 var plotBgColor = Color.background;
74012 if(xIds.length && yIds.length) {
74013 plotBgColor = Lib.coerce(layoutIn, layoutOut, basePlotLayoutAttributes, 'plot_bgcolor');
74014 }
74015
74016 var bgColor = Color.combine(plotBgColor, layoutOut.paper_bgcolor);
74017
74018 // name of single axis (e.g. 'xaxis', 'yaxis2')
74019 var axName;
74020 // id of single axis (e.g. 'y', 'x5')
74021 var axId;
74022 // 'x' or 'y'
74023 var axLetter;
74024 // input layout axis container
74025 var axLayoutIn;
74026 // full layout axis container
74027 var axLayoutOut;
74028
74029 function newAxLayoutOut() {
74030 var traces = ax2traces[axName] || [];
74031 axLayoutOut._traceIndices = traces.map(function(t) { return t._expandedIndex; });
74032 axLayoutOut._annIndices = [];
74033 axLayoutOut._shapeIndices = [];
74034 axLayoutOut._imgIndices = [];
74035 axLayoutOut._subplotsWith = [];
74036 axLayoutOut._counterAxes = [];
74037 axLayoutOut._name = axLayoutOut._attr = axName;
74038 axLayoutOut._id = axId;
74039 }
74040
74041 function coerce(attr, dflt) {
74042 return Lib.coerce(axLayoutIn, axLayoutOut, layoutAttributes, attr, dflt);
74043 }
74044
74045 function coerce2(attr, dflt) {
74046 return Lib.coerce2(axLayoutIn, axLayoutOut, layoutAttributes, attr, dflt);
74047 }
74048
74049 function getCounterAxes(axLetter) {
74050 return (axLetter === 'x') ? yIds : xIds;
74051 }
74052
74053 function getOverlayableAxes(axLetter, axName) {
74054 var list = (axLetter === 'x') ? xNames : yNames;
74055 var out = [];
74056
74057 for(var j = 0; j < list.length; j++) {
74058 var axName2 = list[j];
74059
74060 if(axName2 !== axName && !(layoutIn[axName2] || {}).overlaying) {
74061 out.push(name2id(axName2));
74062 }
74063 }
74064
74065 return out;
74066 }
74067
74068 // list of available counter axis names
74069 var counterAxes = {x: getCounterAxes('x'), y: getCounterAxes('y')};
74070 // list of all x AND y axis ids
74071 var allAxisIds = counterAxes.x.concat(counterAxes.y);
74072 // lookup and list of axis ids that axes in axNames have a reference to,
74073 // even though they are missing from allAxisIds
74074 var missingMatchedAxisIdsLookup = {};
74075 var missingMatchedAxisIds = [];
74076
74077 // fill in 'missing' axis lookup when an axis is set to match an axis
74078 // not part of the allAxisIds list, save axis type so that we can propagate
74079 // it to the missing axes
74080 function addMissingMatchedAxis() {
74081 var matchesIn = axLayoutIn.matches;
74082 if(AX_ID_PATTERN.test(matchesIn) && allAxisIds.indexOf(matchesIn) === -1) {
74083 missingMatchedAxisIdsLookup[matchesIn] = axLayoutIn.type;
74084 missingMatchedAxisIds = Object.keys(missingMatchedAxisIdsLookup);
74085 }
74086 }
74087
74088 var hovermode = handleHoverModeDefaults(layoutIn, layoutOut);
74089 var unifiedHover = isUnifiedHover(hovermode);
74090
74091 // first pass creates the containers, determines types, and handles most of the settings
74092 for(i = 0; i < axNames.length; i++) {
74093 axName = axNames[i];
74094 axId = name2id(axName);
74095 axLetter = axName.charAt(0);
74096
74097 if(!Lib.isPlainObject(layoutIn[axName])) {
74098 layoutIn[axName] = {};
74099 }
74100
74101 axLayoutIn = layoutIn[axName];
74102 axLayoutOut = Template.newContainer(layoutOut, axName, axLetter + 'axis');
74103 newAxLayoutOut();
74104
74105 var visibleDflt =
74106 (axLetter === 'x' && !xaMustDisplay[axName] && xaMayHide[axName]) ||
74107 (axLetter === 'y' && !yaMustDisplay[axName] && yaMayHide[axName]);
74108
74109 var reverseDflt =
74110 (axLetter === 'y' &&
74111 (
74112 (!yaMustNotReverse[axName] && yaMayReverse[axName]) ||
74113 axHasImage[axName]
74114 ));
74115
74116 var defaultOptions = {
74117 letter: axLetter,
74118 font: layoutOut.font,
74119 outerTicks: outerTicks[axName],
74120 showGrid: !noGrids[axName],
74121 data: ax2traces[axName] || [],
74122 bgColor: bgColor,
74123 calendar: layoutOut.calendar,
74124 automargin: true,
74125 visibleDflt: visibleDflt,
74126 reverseDflt: reverseDflt,
74127 autotypenumbersDflt: autotypenumbersDflt,
74128 splomStash: ((layoutOut._splomAxes || {})[axLetter] || {})[axId]
74129 };
74130
74131 coerce('uirevision', layoutOut.uirevision);
74132
74133 handleTypeDefaults(axLayoutIn, axLayoutOut, coerce, defaultOptions);
74134 handleAxisDefaults(axLayoutIn, axLayoutOut, coerce, defaultOptions, layoutOut);
74135
74136 var unifiedSpike = unifiedHover && axLetter === hovermode.charAt(0);
74137 var spikecolor = coerce2('spikecolor', unifiedHover ? axLayoutOut.color : undefined);
74138 var spikethickness = coerce2('spikethickness', unifiedHover ? 1.5 : undefined);
74139 var spikedash = coerce2('spikedash', unifiedHover ? 'dot' : undefined);
74140 var spikemode = coerce2('spikemode', unifiedHover ? 'across' : undefined);
74141 var spikesnap = coerce2('spikesnap');
74142 var showSpikes = coerce('showspikes', !!unifiedSpike || !!spikecolor || !!spikethickness || !!spikedash || !!spikemode || !!spikesnap);
74143
74144 if(!showSpikes) {
74145 delete axLayoutOut.spikecolor;
74146 delete axLayoutOut.spikethickness;
74147 delete axLayoutOut.spikedash;
74148 delete axLayoutOut.spikemode;
74149 delete axLayoutOut.spikesnap;
74150 }
74151
74152 handlePositionDefaults(axLayoutIn, axLayoutOut, coerce, {
74153 letter: axLetter,
74154 counterAxes: counterAxes[axLetter],
74155 overlayableAxes: getOverlayableAxes(axLetter, axName),
74156 grid: layoutOut.grid
74157 });
74158
74159 coerce('title.standoff');
74160
74161 addMissingMatchedAxis();
74162
74163 axLayoutOut._input = axLayoutIn;
74164 }
74165
74166 // coerce the 'missing' axes
74167 i = 0;
74168 while(i < missingMatchedAxisIds.length) {
74169 axId = missingMatchedAxisIds[i++];
74170 axName = id2name(axId);
74171 axLetter = axName.charAt(0);
74172
74173 if(!Lib.isPlainObject(layoutIn[axName])) {
74174 layoutIn[axName] = {};
74175 }
74176
74177 axLayoutIn = layoutIn[axName];
74178 axLayoutOut = Template.newContainer(layoutOut, axName, axLetter + 'axis');
74179 newAxLayoutOut();
74180
74181 var defaultOptions2 = {
74182 letter: axLetter,
74183 font: layoutOut.font,
74184 outerTicks: outerTicks[axName],
74185 showGrid: !noGrids[axName],
74186 data: [],
74187 bgColor: bgColor,
74188 calendar: layoutOut.calendar,
74189 automargin: true,
74190 visibleDflt: false,
74191 reverseDflt: false,
74192 autotypenumbersDflt: autotypenumbersDflt,
74193 splomStash: ((layoutOut._splomAxes || {})[axLetter] || {})[axId]
74194 };
74195
74196 coerce('uirevision', layoutOut.uirevision);
74197
74198 axLayoutOut.type = missingMatchedAxisIdsLookup[axId] || 'linear';
74199
74200 handleAxisDefaults(axLayoutIn, axLayoutOut, coerce, defaultOptions2, layoutOut);
74201
74202 handlePositionDefaults(axLayoutIn, axLayoutOut, coerce, {
74203 letter: axLetter,
74204 counterAxes: counterAxes[axLetter],
74205 overlayableAxes: getOverlayableAxes(axLetter, axName),
74206 grid: layoutOut.grid
74207 });
74208
74209 coerce('fixedrange');
74210
74211 addMissingMatchedAxis();
74212
74213 axLayoutOut._input = axLayoutIn;
74214 }
74215
74216 // quick second pass for range slider and selector defaults
74217 var rangeSliderDefaults = getComponentMethod('rangeslider', 'handleDefaults');
74218 var rangeSelectorDefaults = getComponentMethod('rangeselector', 'handleDefaults');
74219
74220 for(i = 0; i < xNames.length; i++) {
74221 axName = xNames[i];
74222 axLayoutIn = layoutIn[axName];
74223 axLayoutOut = layoutOut[axName];
74224
74225 rangeSliderDefaults(layoutIn, layoutOut, axName);
74226
74227 if(axLayoutOut.type === 'date') {
74228 rangeSelectorDefaults(
74229 axLayoutIn,
74230 axLayoutOut,
74231 layoutOut,
74232 yNames,
74233 axLayoutOut.calendar
74234 );
74235 }
74236
74237 coerce('fixedrange');
74238 }
74239
74240 for(i = 0; i < yNames.length; i++) {
74241 axName = yNames[i];
74242 axLayoutIn = layoutIn[axName];
74243 axLayoutOut = layoutOut[axName];
74244
74245 var anchoredAxis = layoutOut[id2name(axLayoutOut.anchor)];
74246
74247 var fixedRangeDflt = getComponentMethod('rangeslider', 'isVisible')(anchoredAxis);
74248
74249 coerce('fixedrange', fixedRangeDflt);
74250 }
74251
74252 // Finally, handle scale constraints and matching axes.
74253 //
74254 // We need to do this after all axes have coerced both `type`
74255 // (so we link only axes of the same type) and
74256 // `fixedrange` (so we can avoid linking from OR TO a fixed axis).
74257 constraints.handleDefaults(layoutIn, layoutOut, {
74258 axIds: allAxisIds.concat(missingMatchedAxisIds).sort(axisIds.idSort),
74259 axHasImage: axHasImage
74260 });
74261};
74262
74263},{"../../components/color":157,"../../components/fx/helpers":193,"../../components/fx/hovermode_defaults":196,"../../lib":287,"../../plot_api/plot_template":323,"../../registry":376,"../layout_attributes":367,"./axis_defaults":336,"./axis_ids":338,"./constants":341,"./constraints":342,"./layout_attributes":349,"./position_defaults":352,"./type_defaults":360}],351:[function(_dereq_,module,exports){
74264'use strict';
74265
74266var colorMix = _dereq_('tinycolor2').mix;
74267var lightFraction = _dereq_('../../components/color/attributes').lightFraction;
74268var Lib = _dereq_('../../lib');
74269
74270/**
74271 * @param {object} opts :
74272 * - dfltColor {string} : default axis color
74273 * - bgColor {string} : combined subplot bg color
74274 * - blend {number, optional} : blend percentage (to compute dflt grid color)
74275 * - showLine {boolean} : show line by default
74276 * - showGrid {boolean} : show grid by default
74277 * - noZeroLine {boolean} : don't coerce zeroline* attributes
74278 * - attributes {object} : attribute object associated with input containers
74279 */
74280module.exports = function handleLineGridDefaults(containerIn, containerOut, coerce, opts) {
74281 opts = opts || {};
74282
74283 var dfltColor = opts.dfltColor;
74284
74285 function coerce2(attr, dflt) {
74286 return Lib.coerce2(containerIn, containerOut, opts.attributes, attr, dflt);
74287 }
74288
74289 var lineColor = coerce2('linecolor', dfltColor);
74290 var lineWidth = coerce2('linewidth');
74291 var showLine = coerce('showline', opts.showLine || !!lineColor || !!lineWidth);
74292
74293 if(!showLine) {
74294 delete containerOut.linecolor;
74295 delete containerOut.linewidth;
74296 }
74297
74298 var gridColorDflt = colorMix(dfltColor, opts.bgColor, opts.blend || lightFraction).toRgbString();
74299 var gridColor = coerce2('gridcolor', gridColorDflt);
74300 var gridWidth = coerce2('gridwidth');
74301 var showGridLines = coerce('showgrid', opts.showGrid || !!gridColor || !!gridWidth);
74302
74303 if(!showGridLines) {
74304 delete containerOut.gridcolor;
74305 delete containerOut.gridwidth;
74306 }
74307
74308 if(!opts.noZeroLine) {
74309 var zeroLineColor = coerce2('zerolinecolor', dfltColor);
74310 var zeroLineWidth = coerce2('zerolinewidth');
74311 var showZeroLine = coerce('zeroline', opts.showGrid || !!zeroLineColor || !!zeroLineWidth);
74312
74313 if(!showZeroLine) {
74314 delete containerOut.zerolinecolor;
74315 delete containerOut.zerolinewidth;
74316 }
74317 }
74318};
74319
74320},{"../../components/color/attributes":156,"../../lib":287,"tinycolor2":121}],352:[function(_dereq_,module,exports){
74321'use strict';
74322
74323var isNumeric = _dereq_('fast-isnumeric');
74324
74325var Lib = _dereq_('../../lib');
74326
74327
74328module.exports = function handlePositionDefaults(containerIn, containerOut, coerce, options) {
74329 var counterAxes = options.counterAxes || [];
74330 var overlayableAxes = options.overlayableAxes || [];
74331 var letter = options.letter;
74332 var grid = options.grid;
74333
74334 var dfltAnchor, dfltDomain, dfltSide, dfltPosition;
74335
74336 if(grid) {
74337 dfltDomain = grid._domains[letter][grid._axisMap[containerOut._id]];
74338 dfltAnchor = grid._anchors[containerOut._id];
74339 if(dfltDomain) {
74340 dfltSide = grid[letter + 'side'].split(' ')[0];
74341 dfltPosition = grid.domain[letter][dfltSide === 'right' || dfltSide === 'top' ? 1 : 0];
74342 }
74343 }
74344
74345 // Even if there's a grid, this axis may not be in it - fall back on non-grid defaults
74346 dfltDomain = dfltDomain || [0, 1];
74347 dfltAnchor = dfltAnchor || (isNumeric(containerIn.position) ? 'free' : (counterAxes[0] || 'free'));
74348 dfltSide = dfltSide || (letter === 'x' ? 'bottom' : 'left');
74349 dfltPosition = dfltPosition || 0;
74350
74351 var anchor = Lib.coerce(containerIn, containerOut, {
74352 anchor: {
74353 valType: 'enumerated',
74354 values: ['free'].concat(counterAxes),
74355 dflt: dfltAnchor
74356 }
74357 }, 'anchor');
74358
74359 if(anchor === 'free') coerce('position', dfltPosition);
74360
74361 Lib.coerce(containerIn, containerOut, {
74362 side: {
74363 valType: 'enumerated',
74364 values: letter === 'x' ? ['bottom', 'top'] : ['left', 'right'],
74365 dflt: dfltSide
74366 }
74367 }, 'side');
74368
74369 var overlaying = false;
74370 if(overlayableAxes.length) {
74371 overlaying = Lib.coerce(containerIn, containerOut, {
74372 overlaying: {
74373 valType: 'enumerated',
74374 values: [false].concat(overlayableAxes),
74375 dflt: false
74376 }
74377 }, 'overlaying');
74378 }
74379
74380 if(!overlaying) {
74381 // TODO: right now I'm copying this domain over to overlaying axes
74382 // in ax.setscale()... but this means we still need (imperfect) logic
74383 // in the axes popover to hide domain for the overlaying axis.
74384 // perhaps I should make a private version _domain that all axes get???
74385 var domain = coerce('domain', dfltDomain);
74386
74387 // according to https://www.npmjs.com/package/canvas-size
74388 // the minimum value of max canvas width across browsers and devices is 4096
74389 // which applied in the calculation below:
74390 if(domain[0] > domain[1] - 1 / 4096) containerOut.domain = dfltDomain;
74391 Lib.noneOrAll(containerIn.domain, containerOut.domain, dfltDomain);
74392 }
74393
74394 coerce('layer');
74395
74396 return containerOut;
74397};
74398
74399},{"../../lib":287,"fast-isnumeric":33}],353:[function(_dereq_,module,exports){
74400'use strict';
74401
74402var FROM_BL = _dereq_('../../constants/alignment').FROM_BL;
74403
74404module.exports = function scaleZoom(ax, factor, centerFraction) {
74405 if(centerFraction === undefined) {
74406 centerFraction = FROM_BL[ax.constraintoward || 'center'];
74407 }
74408
74409 var rangeLinear = [ax.r2l(ax.range[0]), ax.r2l(ax.range[1])];
74410 var center = rangeLinear[0] + (rangeLinear[1] - rangeLinear[0]) * centerFraction;
74411
74412 ax.range = ax._input.range = [
74413 ax.l2r(center + (rangeLinear[0] - center) * factor),
74414 ax.l2r(center + (rangeLinear[1] - center) * factor)
74415 ];
74416 ax.setScale();
74417};
74418
74419},{"../../constants/alignment":262}],354:[function(_dereq_,module,exports){
74420'use strict';
74421
74422var polybool = _dereq_('polybooljs');
74423
74424var Registry = _dereq_('../../registry');
74425var dashStyle = _dereq_('../../components/drawing').dashStyle;
74426var Color = _dereq_('../../components/color');
74427var Fx = _dereq_('../../components/fx');
74428var makeEventData = _dereq_('../../components/fx/helpers').makeEventData;
74429var dragHelpers = _dereq_('../../components/dragelement/helpers');
74430var freeMode = dragHelpers.freeMode;
74431var rectMode = dragHelpers.rectMode;
74432var drawMode = dragHelpers.drawMode;
74433var openMode = dragHelpers.openMode;
74434var selectMode = dragHelpers.selectMode;
74435
74436var displayOutlines = _dereq_('../../components/shapes/draw_newshape/display_outlines');
74437var handleEllipse = _dereq_('../../components/shapes/draw_newshape/helpers').handleEllipse;
74438var newShapes = _dereq_('../../components/shapes/draw_newshape/newshapes');
74439
74440var Lib = _dereq_('../../lib');
74441var polygon = _dereq_('../../lib/polygon');
74442var throttle = _dereq_('../../lib/throttle');
74443var getFromId = _dereq_('./axis_ids').getFromId;
74444var clearGlCanvases = _dereq_('../../lib/clear_gl_canvases');
74445
74446var redrawReglTraces = _dereq_('../../plot_api/subroutines').redrawReglTraces;
74447
74448var constants = _dereq_('./constants');
74449var MINSELECT = constants.MINSELECT;
74450
74451var filteredPolygon = polygon.filter;
74452var polygonTester = polygon.tester;
74453
74454var clearSelect = _dereq_('./handle_outline').clearSelect;
74455
74456var helpers = _dereq_('./helpers');
74457var p2r = helpers.p2r;
74458var axValue = helpers.axValue;
74459var getTransform = helpers.getTransform;
74460
74461function prepSelect(e, startX, startY, dragOptions, mode) {
74462 var isFreeMode = freeMode(mode);
74463 var isRectMode = rectMode(mode);
74464 var isOpenMode = openMode(mode);
74465 var isDrawMode = drawMode(mode);
74466 var isSelectMode = selectMode(mode);
74467
74468 var isLine = mode === 'drawline';
74469 var isEllipse = mode === 'drawcircle';
74470 var isLineOrEllipse = isLine || isEllipse; // cases with two start & end positions
74471
74472 var gd = dragOptions.gd;
74473 var fullLayout = gd._fullLayout;
74474 var zoomLayer = fullLayout._zoomlayer;
74475 var dragBBox = dragOptions.element.getBoundingClientRect();
74476 var plotinfo = dragOptions.plotinfo;
74477 var transform = getTransform(plotinfo);
74478 var x0 = startX - dragBBox.left;
74479 var y0 = startY - dragBBox.top;
74480
74481 fullLayout._calcInverseTransform(gd);
74482 var transformedCoords = Lib.apply3DTransform(fullLayout._invTransform)(x0, y0);
74483 x0 = transformedCoords[0];
74484 y0 = transformedCoords[1];
74485 var scaleX = fullLayout._invScaleX;
74486 var scaleY = fullLayout._invScaleY;
74487
74488 var x1 = x0;
74489 var y1 = y0;
74490 var path0 = 'M' + x0 + ',' + y0;
74491 var pw = dragOptions.xaxes[0]._length;
74492 var ph = dragOptions.yaxes[0]._length;
74493 var allAxes = dragOptions.xaxes.concat(dragOptions.yaxes);
74494 var subtract = e.altKey &&
74495 !(drawMode(mode) && isOpenMode);
74496
74497 var filterPoly, selectionTester, mergedPolygons, currentPolygon;
74498 var i, searchInfo, eventData;
74499
74500 coerceSelectionsCache(e, gd, dragOptions);
74501
74502 if(isFreeMode) {
74503 filterPoly = filteredPolygon([[x0, y0]], constants.BENDPX);
74504 }
74505
74506 var outlines = zoomLayer.selectAll('path.select-outline-' + plotinfo.id).data(isDrawMode ? [0] : [1, 2]);
74507 var drwStyle = fullLayout.newshape;
74508
74509 outlines.enter()
74510 .append('path')
74511 .attr('class', function(d) { return 'select-outline select-outline-' + d + ' select-outline-' + plotinfo.id; })
74512 .style(isDrawMode ? {
74513 opacity: drwStyle.opacity / 2,
74514 fill: isOpenMode ? undefined : drwStyle.fillcolor,
74515 stroke: drwStyle.line.color,
74516 'stroke-dasharray': dashStyle(drwStyle.line.dash, drwStyle.line.width),
74517 'stroke-width': drwStyle.line.width + 'px'
74518 } : {})
74519 .attr('fill-rule', drwStyle.fillrule)
74520 .classed('cursor-move', isDrawMode ? true : false)
74521 .attr('transform', transform)
74522 .attr('d', path0 + 'Z');
74523
74524 var corners = zoomLayer.append('path')
74525 .attr('class', 'zoombox-corners')
74526 .style({
74527 fill: Color.background,
74528 stroke: Color.defaultLine,
74529 'stroke-width': 1
74530 })
74531 .attr('transform', transform)
74532 .attr('d', 'M0,0Z');
74533
74534
74535 var throttleID = fullLayout._uid + constants.SELECTID;
74536 var selection = [];
74537
74538 // find the traces to search for selection points
74539 var searchTraces = determineSearchTraces(gd, dragOptions.xaxes,
74540 dragOptions.yaxes, dragOptions.subplot);
74541
74542 function ascending(a, b) { return a - b; }
74543
74544 // allow subplots to override fillRangeItems routine
74545 var fillRangeItems;
74546
74547 if(plotinfo.fillRangeItems) {
74548 fillRangeItems = plotinfo.fillRangeItems;
74549 } else {
74550 if(isRectMode) {
74551 fillRangeItems = function(eventData, poly) {
74552 var ranges = eventData.range = {};
74553
74554 for(i = 0; i < allAxes.length; i++) {
74555 var ax = allAxes[i];
74556 var axLetter = ax._id.charAt(0);
74557
74558 ranges[ax._id] = [
74559 p2r(ax, poly[axLetter + 'min']),
74560 p2r(ax, poly[axLetter + 'max'])
74561 ].sort(ascending);
74562 }
74563 };
74564 } else { // case of isFreeMode
74565 fillRangeItems = function(eventData, poly, filterPoly) {
74566 var dataPts = eventData.lassoPoints = {};
74567
74568 for(i = 0; i < allAxes.length; i++) {
74569 var ax = allAxes[i];
74570 dataPts[ax._id] = filterPoly.filtered.map(axValue(ax));
74571 }
74572 };
74573 }
74574 }
74575
74576 dragOptions.moveFn = function(dx0, dy0) {
74577 x1 = Math.max(0, Math.min(pw, scaleX * dx0 + x0));
74578 y1 = Math.max(0, Math.min(ph, scaleY * dy0 + y0));
74579
74580 var dx = Math.abs(x1 - x0);
74581 var dy = Math.abs(y1 - y0);
74582
74583 if(isRectMode) {
74584 var direction;
74585 var start, end;
74586
74587 if(isSelectMode) {
74588 var q = fullLayout.selectdirection;
74589
74590 if(q === 'any') {
74591 if(dy < Math.min(dx * 0.6, MINSELECT)) {
74592 direction = 'h';
74593 } else if(dx < Math.min(dy * 0.6, MINSELECT)) {
74594 direction = 'v';
74595 } else {
74596 direction = 'd';
74597 }
74598 } else {
74599 direction = q;
74600 }
74601
74602 switch(direction) {
74603 case 'h':
74604 start = isEllipse ? ph / 2 : 0;
74605 end = ph;
74606 break;
74607 case 'v':
74608 start = isEllipse ? pw / 2 : 0;
74609 end = pw;
74610 break;
74611 }
74612 }
74613
74614 if(isDrawMode) {
74615 switch(fullLayout.newshape.drawdirection) {
74616 case 'vertical':
74617 direction = 'h';
74618 start = isEllipse ? ph / 2 : 0;
74619 end = ph;
74620 break;
74621 case 'horizontal':
74622 direction = 'v';
74623 start = isEllipse ? pw / 2 : 0;
74624 end = pw;
74625 break;
74626 case 'ortho':
74627 if(dx < dy) {
74628 direction = 'h';
74629 start = y0;
74630 end = y1;
74631 } else {
74632 direction = 'v';
74633 start = x0;
74634 end = x1;
74635 }
74636 break;
74637 default: // i.e. case of 'diagonal'
74638 direction = 'd';
74639 }
74640 }
74641
74642 if(direction === 'h') {
74643 // horizontal motion
74644 currentPolygon = isLineOrEllipse ?
74645 handleEllipse(isEllipse, [x1, start], [x1, end]) : // using x1 instead of x0 allows adjusting the line while drawing
74646 [[x0, start], [x0, end], [x1, end], [x1, start]]; // make a vertical box
74647
74648 currentPolygon.xmin = isLineOrEllipse ? x1 : Math.min(x0, x1);
74649 currentPolygon.xmax = isLineOrEllipse ? x1 : Math.max(x0, x1);
74650 currentPolygon.ymin = Math.min(start, end);
74651 currentPolygon.ymax = Math.max(start, end);
74652 // extras to guide users in keeping a straight selection
74653 corners.attr('d', 'M' + currentPolygon.xmin + ',' + (y0 - MINSELECT) +
74654 'h-4v' + (2 * MINSELECT) + 'h4Z' +
74655 'M' + (currentPolygon.xmax - 1) + ',' + (y0 - MINSELECT) +
74656 'h4v' + (2 * MINSELECT) + 'h-4Z');
74657 } else if(direction === 'v') {
74658 // vertical motion
74659 currentPolygon = isLineOrEllipse ?
74660 handleEllipse(isEllipse, [start, y1], [end, y1]) : // using y1 instead of y0 allows adjusting the line while drawing
74661 [[start, y0], [start, y1], [end, y1], [end, y0]]; // make a horizontal box
74662
74663 currentPolygon.xmin = Math.min(start, end);
74664 currentPolygon.xmax = Math.max(start, end);
74665 currentPolygon.ymin = isLineOrEllipse ? y1 : Math.min(y0, y1);
74666 currentPolygon.ymax = isLineOrEllipse ? y1 : Math.max(y0, y1);
74667 corners.attr('d', 'M' + (x0 - MINSELECT) + ',' + currentPolygon.ymin +
74668 'v-4h' + (2 * MINSELECT) + 'v4Z' +
74669 'M' + (x0 - MINSELECT) + ',' + (currentPolygon.ymax - 1) +
74670 'v4h' + (2 * MINSELECT) + 'v-4Z');
74671 } else if(direction === 'd') {
74672 // diagonal motion
74673 currentPolygon = isLineOrEllipse ?
74674 handleEllipse(isEllipse, [x0, y0], [x1, y1]) :
74675 [[x0, y0], [x0, y1], [x1, y1], [x1, y0]];
74676
74677 currentPolygon.xmin = Math.min(x0, x1);
74678 currentPolygon.xmax = Math.max(x0, x1);
74679 currentPolygon.ymin = Math.min(y0, y1);
74680 currentPolygon.ymax = Math.max(y0, y1);
74681 corners.attr('d', 'M0,0Z');
74682 }
74683 } else if(isFreeMode) {
74684 filterPoly.addPt([x1, y1]);
74685 currentPolygon = filterPoly.filtered;
74686 }
74687
74688 // create outline & tester
74689 if(dragOptions.selectionDefs && dragOptions.selectionDefs.length) {
74690 mergedPolygons = mergePolygons(dragOptions.mergedPolygons, currentPolygon, subtract);
74691 currentPolygon.subtract = subtract;
74692 selectionTester = multiTester(dragOptions.selectionDefs.concat([currentPolygon]));
74693 } else {
74694 mergedPolygons = [currentPolygon];
74695 selectionTester = polygonTester(currentPolygon);
74696 }
74697
74698 // display polygons on the screen
74699 displayOutlines(convertPoly(mergedPolygons, isOpenMode), outlines, dragOptions);
74700
74701 if(isSelectMode) {
74702 throttle.throttle(
74703 throttleID,
74704 constants.SELECTDELAY,
74705 function() {
74706 selection = [];
74707
74708 var thisSelection;
74709 var traceSelections = [];
74710 var traceSelection;
74711 for(i = 0; i < searchTraces.length; i++) {
74712 searchInfo = searchTraces[i];
74713
74714 traceSelection = searchInfo._module.selectPoints(searchInfo, selectionTester);
74715 traceSelections.push(traceSelection);
74716
74717 thisSelection = fillSelectionItem(traceSelection, searchInfo);
74718
74719 if(selection.length) {
74720 for(var j = 0; j < thisSelection.length; j++) {
74721 selection.push(thisSelection[j]);
74722 }
74723 } else selection = thisSelection;
74724 }
74725
74726 eventData = {points: selection};
74727 updateSelectedState(gd, searchTraces, eventData);
74728 fillRangeItems(eventData, currentPolygon, filterPoly);
74729 dragOptions.gd.emit('plotly_selecting', eventData);
74730 }
74731 );
74732 }
74733 };
74734
74735 dragOptions.clickFn = function(numClicks, evt) {
74736 corners.remove();
74737
74738 if(gd._fullLayout._activeShapeIndex >= 0) {
74739 gd._fullLayout._deactivateShape(gd);
74740 return;
74741 }
74742 if(isDrawMode) return;
74743
74744 var clickmode = fullLayout.clickmode;
74745
74746 throttle.done(throttleID).then(function() {
74747 throttle.clear(throttleID);
74748 if(numClicks === 2) {
74749 // clear selection on doubleclick
74750 outlines.remove();
74751 for(i = 0; i < searchTraces.length; i++) {
74752 searchInfo = searchTraces[i];
74753 searchInfo._module.selectPoints(searchInfo, false);
74754 }
74755
74756 updateSelectedState(gd, searchTraces);
74757
74758 clearSelectionsCache(dragOptions);
74759
74760 gd.emit('plotly_deselect', null);
74761 } else {
74762 if(clickmode.indexOf('select') > -1) {
74763 selectOnClick(evt, gd, dragOptions.xaxes, dragOptions.yaxes,
74764 dragOptions.subplot, dragOptions, outlines);
74765 }
74766
74767 if(clickmode === 'event') {
74768 // TODO: remove in v3 - this was probably never intended to work as it does,
74769 // but in case anyone depends on it we don't want to break it now.
74770 // Note that click-to-select introduced pre v3 also emitts proper
74771 // event data when clickmode is having 'select' in its flag list.
74772 gd.emit('plotly_selected', undefined);
74773 }
74774 }
74775
74776 Fx.click(gd, evt);
74777 }).catch(Lib.error);
74778 };
74779
74780 dragOptions.doneFn = function() {
74781 corners.remove();
74782
74783 throttle.done(throttleID).then(function() {
74784 throttle.clear(throttleID);
74785 dragOptions.gd.emit('plotly_selected', eventData);
74786
74787 if(currentPolygon && dragOptions.selectionDefs) {
74788 // save last polygons
74789 currentPolygon.subtract = subtract;
74790 dragOptions.selectionDefs.push(currentPolygon);
74791
74792 // we have to keep reference to arrays container
74793 dragOptions.mergedPolygons.length = 0;
74794 [].push.apply(dragOptions.mergedPolygons, mergedPolygons);
74795 }
74796
74797 if(dragOptions.doneFnCompleted) {
74798 dragOptions.doneFnCompleted(selection);
74799 }
74800 }).catch(Lib.error);
74801
74802 if(isDrawMode) {
74803 clearSelectionsCache(dragOptions);
74804 }
74805 };
74806}
74807
74808function selectOnClick(evt, gd, xAxes, yAxes, subplot, dragOptions, polygonOutlines) {
74809 var hoverData = gd._hoverdata;
74810 var fullLayout = gd._fullLayout;
74811 var clickmode = fullLayout.clickmode;
74812 var sendEvents = clickmode.indexOf('event') > -1;
74813 var selection = [];
74814 var searchTraces, searchInfo, currentSelectionDef, selectionTester, traceSelection;
74815 var thisTracesSelection, pointOrBinSelected, subtract, eventData, i;
74816
74817 if(isHoverDataSet(hoverData)) {
74818 coerceSelectionsCache(evt, gd, dragOptions);
74819 searchTraces = determineSearchTraces(gd, xAxes, yAxes, subplot);
74820 var clickedPtInfo = extractClickedPtInfo(hoverData, searchTraces);
74821 var isBinnedTrace = clickedPtInfo.pointNumbers.length > 0;
74822
74823
74824 // Note: potentially costly operation isPointOrBinSelected is
74825 // called as late as possible through the use of an assignment
74826 // in an if condition.
74827 if(isBinnedTrace ?
74828 isOnlyThisBinSelected(searchTraces, clickedPtInfo) :
74829 isOnlyOnePointSelected(searchTraces) &&
74830 (pointOrBinSelected = isPointOrBinSelected(clickedPtInfo))) {
74831 if(polygonOutlines) polygonOutlines.remove();
74832 for(i = 0; i < searchTraces.length; i++) {
74833 searchInfo = searchTraces[i];
74834 searchInfo._module.selectPoints(searchInfo, false);
74835 }
74836
74837 updateSelectedState(gd, searchTraces);
74838
74839 clearSelectionsCache(dragOptions);
74840
74841 if(sendEvents) {
74842 gd.emit('plotly_deselect', null);
74843 }
74844 } else {
74845 subtract = evt.shiftKey &&
74846 (pointOrBinSelected !== undefined ?
74847 pointOrBinSelected :
74848 isPointOrBinSelected(clickedPtInfo));
74849 currentSelectionDef = newPointSelectionDef(clickedPtInfo.pointNumber, clickedPtInfo.searchInfo, subtract);
74850
74851 var allSelectionDefs = dragOptions.selectionDefs.concat([currentSelectionDef]);
74852 selectionTester = multiTester(allSelectionDefs);
74853
74854 for(i = 0; i < searchTraces.length; i++) {
74855 traceSelection = searchTraces[i]._module.selectPoints(searchTraces[i], selectionTester);
74856 thisTracesSelection = fillSelectionItem(traceSelection, searchTraces[i]);
74857
74858 if(selection.length) {
74859 for(var j = 0; j < thisTracesSelection.length; j++) {
74860 selection.push(thisTracesSelection[j]);
74861 }
74862 } else selection = thisTracesSelection;
74863 }
74864
74865 eventData = {points: selection};
74866 updateSelectedState(gd, searchTraces, eventData);
74867
74868 if(currentSelectionDef && dragOptions) {
74869 dragOptions.selectionDefs.push(currentSelectionDef);
74870 }
74871
74872 if(polygonOutlines) {
74873 var polygons = dragOptions.mergedPolygons;
74874 var isOpenMode = openMode(dragOptions.dragmode);
74875
74876 // display polygons on the screen
74877 displayOutlines(convertPoly(polygons, isOpenMode), polygonOutlines, dragOptions);
74878 }
74879
74880 if(sendEvents) {
74881 gd.emit('plotly_selected', eventData);
74882 }
74883 }
74884 }
74885}
74886
74887/**
74888 * Constructs a new point selection definition object.
74889 */
74890function newPointSelectionDef(pointNumber, searchInfo, subtract) {
74891 return {
74892 pointNumber: pointNumber,
74893 searchInfo: searchInfo,
74894 subtract: subtract
74895 };
74896}
74897
74898function isPointSelectionDef(o) {
74899 return 'pointNumber' in o && 'searchInfo' in o;
74900}
74901
74902/*
74903 * Constructs a new point number tester.
74904 */
74905function newPointNumTester(pointSelectionDef) {
74906 return {
74907 xmin: 0,
74908 xmax: 0,
74909 ymin: 0,
74910 ymax: 0,
74911 pts: [],
74912 contains: function(pt, omitFirstEdge, pointNumber, searchInfo) {
74913 var idxWantedTrace = pointSelectionDef.searchInfo.cd[0].trace._expandedIndex;
74914 var idxActualTrace = searchInfo.cd[0].trace._expandedIndex;
74915 return idxActualTrace === idxWantedTrace &&
74916 pointNumber === pointSelectionDef.pointNumber;
74917 },
74918 isRect: false,
74919 degenerate: false,
74920 subtract: pointSelectionDef.subtract
74921 };
74922}
74923
74924/**
74925 * Wraps multiple selection testers.
74926 *
74927 * @param {Array} list - An array of selection testers.
74928 *
74929 * @return a selection tester object with a contains function
74930 * that can be called to evaluate a point against all wrapped
74931 * selection testers that were passed in list.
74932 */
74933function multiTester(list) {
74934 var testers = [];
74935 var xmin = isPointSelectionDef(list[0]) ? 0 : list[0][0][0];
74936 var xmax = xmin;
74937 var ymin = isPointSelectionDef(list[0]) ? 0 : list[0][0][1];
74938 var ymax = ymin;
74939
74940 for(var i = 0; i < list.length; i++) {
74941 if(isPointSelectionDef(list[i])) {
74942 testers.push(newPointNumTester(list[i]));
74943 } else {
74944 var tester = polygon.tester(list[i]);
74945 tester.subtract = list[i].subtract;
74946 testers.push(tester);
74947 xmin = Math.min(xmin, tester.xmin);
74948 xmax = Math.max(xmax, tester.xmax);
74949 ymin = Math.min(ymin, tester.ymin);
74950 ymax = Math.max(ymax, tester.ymax);
74951 }
74952 }
74953
74954 /**
74955 * Tests if the given point is within this tester.
74956 *
74957 * @param {Array} pt - [0] is the x coordinate, [1] is the y coordinate of the point.
74958 * @param {*} arg - An optional parameter to pass down to wrapped testers.
74959 * @param {number} pointNumber - The point number of the point within the underlying data array.
74960 * @param {number} searchInfo - An object identifying the trace the point is contained in.
74961 *
74962 * @return {boolean} true if point is considered to be selected, false otherwise.
74963 */
74964 function contains(pt, arg, pointNumber, searchInfo) {
74965 var contained = false;
74966 for(var i = 0; i < testers.length; i++) {
74967 if(testers[i].contains(pt, arg, pointNumber, searchInfo)) {
74968 // if contained by subtract tester - exclude the point
74969 contained = testers[i].subtract === false;
74970 }
74971 }
74972
74973 return contained;
74974 }
74975
74976 return {
74977 xmin: xmin,
74978 xmax: xmax,
74979 ymin: ymin,
74980 ymax: ymax,
74981 pts: [],
74982 contains: contains,
74983 isRect: false,
74984 degenerate: false
74985 };
74986}
74987
74988function coerceSelectionsCache(evt, gd, dragOptions) {
74989 gd._fullLayout._drawing = false;
74990
74991 var fullLayout = gd._fullLayout;
74992 var plotinfo = dragOptions.plotinfo;
74993 var dragmode = dragOptions.dragmode;
74994
74995 var selectingOnSameSubplot = (
74996 fullLayout._lastSelectedSubplot &&
74997 fullLayout._lastSelectedSubplot === plotinfo.id
74998 );
74999
75000 var hasModifierKey = (evt.shiftKey || evt.altKey) &&
75001 !(drawMode(dragmode) && openMode(dragmode));
75002
75003 if(selectingOnSameSubplot && hasModifierKey &&
75004 (plotinfo.selection && plotinfo.selection.selectionDefs) && !dragOptions.selectionDefs) {
75005 // take over selection definitions from prev mode, if any
75006 dragOptions.selectionDefs = plotinfo.selection.selectionDefs;
75007 dragOptions.mergedPolygons = plotinfo.selection.mergedPolygons;
75008 } else if(!hasModifierKey || !plotinfo.selection) {
75009 clearSelectionsCache(dragOptions);
75010 }
75011
75012 // clear selection outline when selecting a different subplot
75013 if(!selectingOnSameSubplot) {
75014 clearSelect(gd);
75015 fullLayout._lastSelectedSubplot = plotinfo.id;
75016 }
75017}
75018
75019function clearSelectionsCache(dragOptions) {
75020 var dragmode = dragOptions.dragmode;
75021 var plotinfo = dragOptions.plotinfo;
75022
75023 var gd = dragOptions.gd;
75024 if(gd._fullLayout._activeShapeIndex >= 0) {
75025 gd._fullLayout._deactivateShape(gd);
75026 }
75027
75028 if(drawMode(dragmode)) {
75029 var fullLayout = gd._fullLayout;
75030 var zoomLayer = fullLayout._zoomlayer;
75031
75032 var outlines = zoomLayer.selectAll('.select-outline-' + plotinfo.id);
75033 if(outlines && gd._fullLayout._drawing) {
75034 // add shape
75035 var shapes = newShapes(outlines, dragOptions);
75036 if(shapes) {
75037 Registry.call('_guiRelayout', gd, {
75038 shapes: shapes
75039 });
75040 }
75041
75042 gd._fullLayout._drawing = false;
75043 }
75044 }
75045
75046 plotinfo.selection = {};
75047 plotinfo.selection.selectionDefs = dragOptions.selectionDefs = [];
75048 plotinfo.selection.mergedPolygons = dragOptions.mergedPolygons = [];
75049}
75050
75051function determineSearchTraces(gd, xAxes, yAxes, subplot) {
75052 var searchTraces = [];
75053 var xAxisIds = xAxes.map(function(ax) { return ax._id; });
75054 var yAxisIds = yAxes.map(function(ax) { return ax._id; });
75055 var cd, trace, i;
75056
75057 for(i = 0; i < gd.calcdata.length; i++) {
75058 cd = gd.calcdata[i];
75059 trace = cd[0].trace;
75060
75061 if(trace.visible !== true || !trace._module || !trace._module.selectPoints) continue;
75062
75063 if(subplot && (trace.subplot === subplot || trace.geo === subplot)) {
75064 searchTraces.push(createSearchInfo(trace._module, cd, xAxes[0], yAxes[0]));
75065 } else if(
75066 trace.type === 'splom' &&
75067 // FIXME: make sure we don't have more than single axis for splom
75068 trace._xaxes[xAxisIds[0]] && trace._yaxes[yAxisIds[0]]
75069 ) {
75070 var info = createSearchInfo(trace._module, cd, xAxes[0], yAxes[0]);
75071 info.scene = gd._fullLayout._splomScenes[trace.uid];
75072 searchTraces.push(info);
75073 } else if(
75074 trace.type === 'sankey'
75075 ) {
75076 var sankeyInfo = createSearchInfo(trace._module, cd, xAxes[0], yAxes[0]);
75077 searchTraces.push(sankeyInfo);
75078 } else {
75079 if(xAxisIds.indexOf(trace.xaxis) === -1) continue;
75080 if(yAxisIds.indexOf(trace.yaxis) === -1) continue;
75081
75082 searchTraces.push(createSearchInfo(trace._module, cd,
75083 getFromId(gd, trace.xaxis), getFromId(gd, trace.yaxis)));
75084 }
75085 }
75086
75087 return searchTraces;
75088
75089 function createSearchInfo(module, calcData, xaxis, yaxis) {
75090 return {
75091 _module: module,
75092 cd: calcData,
75093 xaxis: xaxis,
75094 yaxis: yaxis
75095 };
75096 }
75097}
75098
75099function isHoverDataSet(hoverData) {
75100 return hoverData &&
75101 Array.isArray(hoverData) &&
75102 hoverData[0].hoverOnBox !== true;
75103}
75104
75105function extractClickedPtInfo(hoverData, searchTraces) {
75106 var hoverDatum = hoverData[0];
75107 var pointNumber = -1;
75108 var pointNumbers = [];
75109 var searchInfo, i;
75110
75111 for(i = 0; i < searchTraces.length; i++) {
75112 searchInfo = searchTraces[i];
75113 if(hoverDatum.fullData._expandedIndex === searchInfo.cd[0].trace._expandedIndex) {
75114 // Special case for box (and violin)
75115 if(hoverDatum.hoverOnBox === true) {
75116 break;
75117 }
75118
75119 // Hint: in some traces like histogram, one graphical element
75120 // doesn't correspond to one particular data point, but to
75121 // bins of data points. Thus, hoverDatum can have a binNumber
75122 // property instead of pointNumber.
75123 if(hoverDatum.pointNumber !== undefined) {
75124 pointNumber = hoverDatum.pointNumber;
75125 } else if(hoverDatum.binNumber !== undefined) {
75126 pointNumber = hoverDatum.binNumber;
75127 pointNumbers = hoverDatum.pointNumbers;
75128 }
75129
75130 break;
75131 }
75132 }
75133
75134 return {
75135 pointNumber: pointNumber,
75136 pointNumbers: pointNumbers,
75137 searchInfo: searchInfo
75138 };
75139}
75140
75141function isPointOrBinSelected(clickedPtInfo) {
75142 var trace = clickedPtInfo.searchInfo.cd[0].trace;
75143 var ptNum = clickedPtInfo.pointNumber;
75144 var ptNums = clickedPtInfo.pointNumbers;
75145 var ptNumsSet = ptNums.length > 0;
75146
75147 // When pointsNumbers is set (e.g. histogram's binning),
75148 // it is assumed that when the first point of
75149 // a bin is selected, all others are as well
75150 var ptNumToTest = ptNumsSet ? ptNums[0] : ptNum;
75151
75152 // TODO potential performance improvement
75153 // Primarily we need this function to determine if a click adds
75154 // or subtracts from a selection.
75155 // In cases `trace.selectedpoints` is a huge array, indexOf
75156 // might be slow. One remedy would be to introduce a hash somewhere.
75157 return trace.selectedpoints ? trace.selectedpoints.indexOf(ptNumToTest) > -1 : false;
75158}
75159
75160function isOnlyThisBinSelected(searchTraces, clickedPtInfo) {
75161 var tracesWithSelectedPts = [];
75162 var searchInfo, trace, isSameTrace, i;
75163
75164 for(i = 0; i < searchTraces.length; i++) {
75165 searchInfo = searchTraces[i];
75166 if(searchInfo.cd[0].trace.selectedpoints && searchInfo.cd[0].trace.selectedpoints.length > 0) {
75167 tracesWithSelectedPts.push(searchInfo);
75168 }
75169 }
75170
75171 if(tracesWithSelectedPts.length === 1) {
75172 isSameTrace = tracesWithSelectedPts[0] === clickedPtInfo.searchInfo;
75173 if(isSameTrace) {
75174 trace = clickedPtInfo.searchInfo.cd[0].trace;
75175 if(trace.selectedpoints.length === clickedPtInfo.pointNumbers.length) {
75176 for(i = 0; i < clickedPtInfo.pointNumbers.length; i++) {
75177 if(trace.selectedpoints.indexOf(clickedPtInfo.pointNumbers[i]) < 0) {
75178 return false;
75179 }
75180 }
75181 return true;
75182 }
75183 }
75184 }
75185
75186 return false;
75187}
75188
75189function isOnlyOnePointSelected(searchTraces) {
75190 var len = 0;
75191 var searchInfo, trace, i;
75192
75193 for(i = 0; i < searchTraces.length; i++) {
75194 searchInfo = searchTraces[i];
75195 trace = searchInfo.cd[0].trace;
75196 if(trace.selectedpoints) {
75197 if(trace.selectedpoints.length > 1) return false;
75198
75199 len += trace.selectedpoints.length;
75200 if(len > 1) return false;
75201 }
75202 }
75203
75204 return len === 1;
75205}
75206
75207function updateSelectedState(gd, searchTraces, eventData) {
75208 var i, searchInfo, cd, trace;
75209
75210 // before anything else, update preGUI if necessary
75211 for(i = 0; i < searchTraces.length; i++) {
75212 var fullInputTrace = searchTraces[i].cd[0].trace._fullInput;
75213 var tracePreGUI = gd._fullLayout._tracePreGUI[fullInputTrace.uid] || {};
75214 if(tracePreGUI.selectedpoints === undefined) {
75215 tracePreGUI.selectedpoints = fullInputTrace._input.selectedpoints || null;
75216 }
75217 }
75218
75219 if(eventData) {
75220 var pts = eventData.points || [];
75221
75222 for(i = 0; i < searchTraces.length; i++) {
75223 trace = searchTraces[i].cd[0].trace;
75224 trace._input.selectedpoints = trace._fullInput.selectedpoints = [];
75225 if(trace._fullInput !== trace) trace.selectedpoints = [];
75226 }
75227
75228 for(i = 0; i < pts.length; i++) {
75229 var pt = pts[i];
75230 var data = pt.data;
75231 var fullData = pt.fullData;
75232
75233 if(pt.pointIndices) {
75234 [].push.apply(data.selectedpoints, pt.pointIndices);
75235 if(trace._fullInput !== trace) {
75236 [].push.apply(fullData.selectedpoints, pt.pointIndices);
75237 }
75238 } else {
75239 data.selectedpoints.push(pt.pointIndex);
75240 if(trace._fullInput !== trace) {
75241 fullData.selectedpoints.push(pt.pointIndex);
75242 }
75243 }
75244 }
75245 } else {
75246 for(i = 0; i < searchTraces.length; i++) {
75247 trace = searchTraces[i].cd[0].trace;
75248 delete trace.selectedpoints;
75249 delete trace._input.selectedpoints;
75250 if(trace._fullInput !== trace) {
75251 delete trace._fullInput.selectedpoints;
75252 }
75253 }
75254 }
75255
75256 var hasRegl = false;
75257
75258 for(i = 0; i < searchTraces.length; i++) {
75259 searchInfo = searchTraces[i];
75260 cd = searchInfo.cd;
75261 trace = cd[0].trace;
75262
75263 if(Registry.traceIs(trace, 'regl')) {
75264 hasRegl = true;
75265 }
75266
75267 var _module = searchInfo._module;
75268 var fn = _module.styleOnSelect || _module.style;
75269 if(fn) {
75270 fn(gd, cd, cd[0].node3);
75271 if(cd[0].nodeRangePlot3) fn(gd, cd, cd[0].nodeRangePlot3);
75272 }
75273 }
75274
75275 if(hasRegl) {
75276 clearGlCanvases(gd);
75277 redrawReglTraces(gd);
75278 }
75279}
75280
75281function mergePolygons(list, poly, subtract) {
75282 var res;
75283
75284 if(subtract) {
75285 res = polybool.difference({
75286 regions: list,
75287 inverted: false
75288 }, {
75289 regions: [poly],
75290 inverted: false
75291 });
75292
75293 return res.regions;
75294 }
75295
75296 res = polybool.union({
75297 regions: list,
75298 inverted: false
75299 }, {
75300 regions: [poly],
75301 inverted: false
75302 });
75303
75304 return res.regions;
75305}
75306
75307function fillSelectionItem(selection, searchInfo) {
75308 if(Array.isArray(selection)) {
75309 var cd = searchInfo.cd;
75310 var trace = searchInfo.cd[0].trace;
75311
75312 for(var i = 0; i < selection.length; i++) {
75313 selection[i] = makeEventData(selection[i], trace, cd);
75314 }
75315 }
75316
75317 return selection;
75318}
75319
75320function convertPoly(polygonsIn, isOpenMode) { // add M and L command to draft positions
75321 var polygonsOut = [];
75322 for(var i = 0; i < polygonsIn.length; i++) {
75323 polygonsOut[i] = [];
75324 for(var j = 0; j < polygonsIn[i].length; j++) {
75325 polygonsOut[i][j] = [];
75326 polygonsOut[i][j][0] = j ? 'L' : 'M';
75327 for(var k = 0; k < polygonsIn[i][j].length; k++) {
75328 polygonsOut[i][j].push(
75329 polygonsIn[i][j][k]
75330 );
75331 }
75332 }
75333
75334 if(!isOpenMode) {
75335 polygonsOut[i].push([
75336 'Z',
75337 polygonsOut[i][0][1], // initial x
75338 polygonsOut[i][0][2] // initial y
75339 ]);
75340 }
75341 }
75342
75343 return polygonsOut;
75344}
75345
75346module.exports = {
75347 prepSelect: prepSelect,
75348 clearSelect: clearSelect,
75349 clearSelectionsCache: clearSelectionsCache,
75350 selectOnClick: selectOnClick
75351};
75352
75353},{"../../components/color":157,"../../components/dragelement/helpers":175,"../../components/drawing":179,"../../components/fx":197,"../../components/fx/helpers":193,"../../components/shapes/draw_newshape/display_outlines":245,"../../components/shapes/draw_newshape/helpers":246,"../../components/shapes/draw_newshape/newshapes":247,"../../lib":287,"../../lib/clear_gl_canvases":275,"../../lib/polygon":299,"../../lib/throttle":311,"../../plot_api/subroutines":324,"../../registry":376,"./axis_ids":338,"./constants":341,"./handle_outline":345,"./helpers":346,"polybooljs":75}],355:[function(_dereq_,module,exports){
75354'use strict';
75355
75356var d3 = _dereq_('@plotly/d3');
75357var utcFormat = _dereq_('d3-time-format').utcFormat;
75358var Lib = _dereq_('../../lib');
75359var numberFormat = Lib.numberFormat;
75360var isNumeric = _dereq_('fast-isnumeric');
75361
75362var cleanNumber = Lib.cleanNumber;
75363var ms2DateTime = Lib.ms2DateTime;
75364var dateTime2ms = Lib.dateTime2ms;
75365var ensureNumber = Lib.ensureNumber;
75366var isArrayOrTypedArray = Lib.isArrayOrTypedArray;
75367
75368var numConstants = _dereq_('../../constants/numerical');
75369var FP_SAFE = numConstants.FP_SAFE;
75370var BADNUM = numConstants.BADNUM;
75371var LOG_CLIP = numConstants.LOG_CLIP;
75372var ONEWEEK = numConstants.ONEWEEK;
75373var ONEDAY = numConstants.ONEDAY;
75374var ONEHOUR = numConstants.ONEHOUR;
75375var ONEMIN = numConstants.ONEMIN;
75376var ONESEC = numConstants.ONESEC;
75377
75378var axisIds = _dereq_('./axis_ids');
75379var constants = _dereq_('./constants');
75380var HOUR_PATTERN = constants.HOUR_PATTERN;
75381var WEEKDAY_PATTERN = constants.WEEKDAY_PATTERN;
75382
75383function fromLog(v) {
75384 return Math.pow(10, v);
75385}
75386
75387function isValidCategory(v) {
75388 return v !== null && v !== undefined;
75389}
75390
75391/**
75392 * Define the conversion functions for an axis data is used in 5 ways:
75393 *
75394 * d: data, in whatever form it's provided
75395 * c: calcdata: turned into numbers, but not linearized
75396 * l: linearized - same as c except for log axes (and other nonlinear
75397 * mappings later?) this is used when we need to know if it's
75398 * *possible* to show some data on this axis, without caring about
75399 * the current range
75400 * p: pixel value - mapped to the screen with current size and zoom
75401 * r: ranges, tick0, and annotation positions match one of the above
75402 * but are handled differently for different types:
75403 * - linear and date: data format (d)
75404 * - category: calcdata format (c), and will stay that way because
75405 * the data format has no continuous mapping
75406 * - log: linearized (l) format
75407 * TODO: in v3.0 we plan to change it to data format. At that point
75408 * shapes will work the same way as ranges, tick0, and annotations
75409 * so they can use this conversion too.
75410 *
75411 * Creates/updates these conversion functions, and a few more utilities
75412 * like cleanRange, and makeCalcdata
75413 *
75414 * also clears the autotick constraints ._minDtick, ._forceTick0
75415 */
75416module.exports = function setConvert(ax, fullLayout) {
75417 fullLayout = fullLayout || {};
75418
75419 var axId = (ax._id || 'x');
75420 var axLetter = axId.charAt(0);
75421
75422 function toLog(v, clip) {
75423 if(v > 0) return Math.log(v) / Math.LN10;
75424
75425 else if(v <= 0 && clip && ax.range && ax.range.length === 2) {
75426 // clip NaN (ie past negative infinity) to LOG_CLIP axis
75427 // length past the negative edge
75428 var r0 = ax.range[0];
75429 var r1 = ax.range[1];
75430 return 0.5 * (r0 + r1 - 2 * LOG_CLIP * Math.abs(r0 - r1));
75431 } else return BADNUM;
75432 }
75433
75434 /*
75435 * wrapped dateTime2ms that:
75436 * - accepts ms numbers for backward compatibility
75437 * - inserts a dummy arg so calendar is the 3rd arg (see notes below).
75438 * - defaults to ax.calendar
75439 */
75440 function dt2ms(v, _, calendar, opts) {
75441 if((opts || {}).msUTC && isNumeric(v)) {
75442 // For now it is only used
75443 // to fix bar length in milliseconds & gl3d ticks
75444 // It could be applied in other places in v3
75445 return +v;
75446 }
75447
75448 // NOTE: Changed this behavior: previously we took any numeric value
75449 // to be a ms, even if it was a string that could be a bare year.
75450 // Now we convert it as a date if at all possible, and only try
75451 // as (local) ms if that fails.
75452 var ms = dateTime2ms(v, calendar || ax.calendar);
75453 if(ms === BADNUM) {
75454 if(isNumeric(v)) {
75455 v = +v;
75456 // keep track of tenths of ms, that `new Date` will drop
75457 // same logic as in Lib.ms2DateTime
75458 var msecTenths = Math.floor(Lib.mod(v + 0.05, 1) * 10);
75459 var msRounded = Math.round(v - msecTenths / 10);
75460 ms = dateTime2ms(new Date(msRounded)) + msecTenths / 10;
75461 } else return BADNUM;
75462 }
75463 return ms;
75464 }
75465
75466 // wrapped ms2DateTime to insert default ax.calendar
75467 function ms2dt(v, r, calendar) {
75468 return ms2DateTime(v, r, calendar || ax.calendar);
75469 }
75470
75471 function getCategoryName(v) {
75472 return ax._categories[Math.round(v)];
75473 }
75474
75475 /*
75476 * setCategoryIndex: return the index of category v,
75477 * inserting it in the list if it's not already there
75478 *
75479 * this will enter the categories in the order it
75480 * encounters them, ie all the categories from the
75481 * first data set, then all the ones from the second
75482 * that aren't in the first etc.
75483 *
75484 * it is assumed that this function is being invoked in the
75485 * already sorted category order; otherwise there would be
75486 * a disconnect between the array and the index returned
75487 */
75488 function setCategoryIndex(v) {
75489 if(isValidCategory(v)) {
75490 if(ax._categoriesMap === undefined) {
75491 ax._categoriesMap = {};
75492 }
75493
75494 if(ax._categoriesMap[v] !== undefined) {
75495 return ax._categoriesMap[v];
75496 } else {
75497 ax._categories.push(typeof v === 'number' ? String(v) : v);
75498
75499 var curLength = ax._categories.length - 1;
75500 ax._categoriesMap[v] = curLength;
75501
75502 return curLength;
75503 }
75504 }
75505 return BADNUM;
75506 }
75507
75508 function setMultiCategoryIndex(arrayIn, len) {
75509 var arrayOut = new Array(len);
75510
75511 for(var i = 0; i < len; i++) {
75512 var v0 = (arrayIn[0] || [])[i];
75513 var v1 = (arrayIn[1] || [])[i];
75514 arrayOut[i] = getCategoryIndex([v0, v1]);
75515 }
75516
75517 return arrayOut;
75518 }
75519
75520 function getCategoryIndex(v) {
75521 if(ax._categoriesMap) {
75522 return ax._categoriesMap[v];
75523 }
75524 }
75525
75526 function getCategoryPosition(v) {
75527 // d2l/d2c variant that that won't add categories but will also
75528 // allow numbers to be mapped to the linearized axis positions
75529 var index = getCategoryIndex(v);
75530 if(index !== undefined) return index;
75531 if(isNumeric(v)) return +v;
75532 }
75533
75534 function getRangePosition(v) {
75535 return isNumeric(v) ? +v : getCategoryIndex(v);
75536 }
75537
75538 // include 2 fractional digits on pixel, for PDF zooming etc
75539 function _l2p(v, m, b) { return d3.round(b + m * v, 2); }
75540
75541 function _p2l(px, m, b) { return (px - b) / m; }
75542
75543 var l2p = function l2p(v) {
75544 if(!isNumeric(v)) return BADNUM;
75545 return _l2p(v, ax._m, ax._b);
75546 };
75547
75548 var p2l = function(px) {
75549 return _p2l(px, ax._m, ax._b);
75550 };
75551
75552 if(ax.rangebreaks) {
75553 var isY = axLetter === 'y';
75554
75555 l2p = function(v) {
75556 if(!isNumeric(v)) return BADNUM;
75557 var len = ax._rangebreaks.length;
75558 if(!len) return _l2p(v, ax._m, ax._b);
75559
75560 var flip = isY;
75561 if(ax.range[0] > ax.range[1]) flip = !flip;
75562 var signAx = flip ? -1 : 1;
75563 var pos = signAx * v;
75564
75565 var q = 0;
75566 for(var i = 0; i < len; i++) {
75567 var min = signAx * ax._rangebreaks[i].min;
75568 var max = signAx * ax._rangebreaks[i].max;
75569
75570 if(pos < min) break;
75571 if(pos > max) q = i + 1;
75572 else {
75573 // when falls into break, pick 'closest' offset
75574 q = pos < (min + max) / 2 ? i : i + 1;
75575 break;
75576 }
75577 }
75578 var b2 = ax._B[q] || 0;
75579 if(!isFinite(b2)) return 0; // avoid NaN translate e.g. in positionLabels if one keep zooming exactly into a break
75580 return _l2p(v, ax._m2, b2);
75581 };
75582
75583 p2l = function(px) {
75584 var len = ax._rangebreaks.length;
75585 if(!len) return _p2l(px, ax._m, ax._b);
75586
75587 var q = 0;
75588 for(var i = 0; i < len; i++) {
75589 if(px < ax._rangebreaks[i].pmin) break;
75590 if(px > ax._rangebreaks[i].pmax) q = i + 1;
75591 }
75592 return _p2l(px, ax._m2, ax._B[q]);
75593 };
75594 }
75595
75596 // conversions among c/l/p are fairly simple - do them together for all axis types
75597 ax.c2l = (ax.type === 'log') ? toLog : ensureNumber;
75598 ax.l2c = (ax.type === 'log') ? fromLog : ensureNumber;
75599
75600 ax.l2p = l2p;
75601 ax.p2l = p2l;
75602
75603 ax.c2p = (ax.type === 'log') ? function(v, clip) { return l2p(toLog(v, clip)); } : l2p;
75604 ax.p2c = (ax.type === 'log') ? function(px) { return fromLog(p2l(px)); } : p2l;
75605
75606 /*
75607 * now type-specific conversions for **ALL** other combinations
75608 * they're all written out, instead of being combinations of each other, for
75609 * both clarity and speed.
75610 */
75611 if(['linear', '-'].indexOf(ax.type) !== -1) {
75612 // all are data vals, but d and r need cleaning
75613 ax.d2r = ax.r2d = ax.d2c = ax.r2c = ax.d2l = ax.r2l = cleanNumber;
75614 ax.c2d = ax.c2r = ax.l2d = ax.l2r = ensureNumber;
75615
75616 ax.d2p = ax.r2p = function(v) { return ax.l2p(cleanNumber(v)); };
75617 ax.p2d = ax.p2r = p2l;
75618
75619 ax.cleanPos = ensureNumber;
75620 } else if(ax.type === 'log') {
75621 // d and c are data vals, r and l are logged (but d and r need cleaning)
75622 ax.d2r = ax.d2l = function(v, clip) { return toLog(cleanNumber(v), clip); };
75623 ax.r2d = ax.r2c = function(v) { return fromLog(cleanNumber(v)); };
75624
75625 ax.d2c = ax.r2l = cleanNumber;
75626 ax.c2d = ax.l2r = ensureNumber;
75627
75628 ax.c2r = toLog;
75629 ax.l2d = fromLog;
75630
75631 ax.d2p = function(v, clip) { return ax.l2p(ax.d2r(v, clip)); };
75632 ax.p2d = function(px) { return fromLog(p2l(px)); };
75633
75634 ax.r2p = function(v) { return ax.l2p(cleanNumber(v)); };
75635 ax.p2r = p2l;
75636
75637 ax.cleanPos = ensureNumber;
75638 } else if(ax.type === 'date') {
75639 // r and d are date strings, l and c are ms
75640
75641 /*
75642 * Any of these functions with r and d on either side, calendar is the
75643 * **3rd** argument. log has reserved the second argument.
75644 *
75645 * Unless you need the special behavior of the second arg (ms2DateTime
75646 * uses this to limit precision, toLog uses true to clip negatives
75647 * to offscreen low rather than undefined), it's safe to pass 0.
75648 */
75649 ax.d2r = ax.r2d = Lib.identity;
75650
75651 ax.d2c = ax.r2c = ax.d2l = ax.r2l = dt2ms;
75652 ax.c2d = ax.c2r = ax.l2d = ax.l2r = ms2dt;
75653
75654 ax.d2p = ax.r2p = function(v, _, calendar) { return ax.l2p(dt2ms(v, 0, calendar)); };
75655 ax.p2d = ax.p2r = function(px, r, calendar) { return ms2dt(p2l(px), r, calendar); };
75656
75657 ax.cleanPos = function(v) { return Lib.cleanDate(v, BADNUM, ax.calendar); };
75658 } else if(ax.type === 'category') {
75659 // d is categories (string)
75660 // c and l are indices (numbers)
75661 // r is categories or numbers
75662
75663 ax.d2c = ax.d2l = setCategoryIndex;
75664 ax.r2d = ax.c2d = ax.l2d = getCategoryName;
75665
75666 ax.d2r = ax.d2l_noadd = getCategoryPosition;
75667
75668 ax.r2c = function(v) {
75669 var index = getRangePosition(v);
75670 return index !== undefined ? index : ax.fraction2r(0.5);
75671 };
75672
75673 ax.l2r = ax.c2r = ensureNumber;
75674 ax.r2l = getRangePosition;
75675
75676 ax.d2p = function(v) { return ax.l2p(ax.r2c(v)); };
75677 ax.p2d = function(px) { return getCategoryName(p2l(px)); };
75678 ax.r2p = ax.d2p;
75679 ax.p2r = p2l;
75680
75681 ax.cleanPos = function(v) {
75682 if(typeof v === 'string' && v !== '') return v;
75683 return ensureNumber(v);
75684 };
75685 } else if(ax.type === 'multicategory') {
75686 // N.B. multicategory axes don't define d2c and d2l,
75687 // as 'data-to-calcdata' conversion needs to take into
75688 // account all data array items as in ax.makeCalcdata.
75689
75690 ax.r2d = ax.c2d = ax.l2d = getCategoryName;
75691 ax.d2r = ax.d2l_noadd = getCategoryPosition;
75692
75693 ax.r2c = function(v) {
75694 var index = getCategoryPosition(v);
75695 return index !== undefined ? index : ax.fraction2r(0.5);
75696 };
75697
75698 ax.r2c_just_indices = getCategoryIndex;
75699
75700 ax.l2r = ax.c2r = ensureNumber;
75701 ax.r2l = getCategoryPosition;
75702
75703 ax.d2p = function(v) { return ax.l2p(ax.r2c(v)); };
75704 ax.p2d = function(px) { return getCategoryName(p2l(px)); };
75705 ax.r2p = ax.d2p;
75706 ax.p2r = p2l;
75707
75708 ax.cleanPos = function(v) {
75709 if(Array.isArray(v) || (typeof v === 'string' && v !== '')) return v;
75710 return ensureNumber(v);
75711 };
75712
75713 ax.setupMultiCategory = function(fullData) {
75714 var traceIndices = ax._traceIndices;
75715 var i, j;
75716
75717 var group = ax._matchGroup;
75718 if(group && ax._categories.length === 0) {
75719 for(var axId2 in group) {
75720 if(axId2 !== axId) {
75721 var ax2 = fullLayout[axisIds.id2name(axId2)];
75722 traceIndices = traceIndices.concat(ax2._traceIndices);
75723 }
75724 }
75725 }
75726
75727 // [ [cnt, {$cat: index}], for 1,2 ]
75728 var seen = [[0, {}], [0, {}]];
75729 // [ [arrayIn[0][i], arrayIn[1][i]], for i .. N ]
75730 var list = [];
75731
75732 for(i = 0; i < traceIndices.length; i++) {
75733 var trace = fullData[traceIndices[i]];
75734
75735 if(axLetter in trace) {
75736 var arrayIn = trace[axLetter];
75737 var len = trace._length || Lib.minRowLength(arrayIn);
75738
75739 if(isArrayOrTypedArray(arrayIn[0]) && isArrayOrTypedArray(arrayIn[1])) {
75740 for(j = 0; j < len; j++) {
75741 var v0 = arrayIn[0][j];
75742 var v1 = arrayIn[1][j];
75743
75744 if(isValidCategory(v0) && isValidCategory(v1)) {
75745 list.push([v0, v1]);
75746
75747 if(!(v0 in seen[0][1])) {
75748 seen[0][1][v0] = seen[0][0]++;
75749 }
75750 if(!(v1 in seen[1][1])) {
75751 seen[1][1][v1] = seen[1][0]++;
75752 }
75753 }
75754 }
75755 }
75756 }
75757 }
75758
75759 list.sort(function(a, b) {
75760 var ind0 = seen[0][1];
75761 var d = ind0[a[0]] - ind0[b[0]];
75762 if(d) return d;
75763
75764 var ind1 = seen[1][1];
75765 return ind1[a[1]] - ind1[b[1]];
75766 });
75767
75768 for(i = 0; i < list.length; i++) {
75769 setCategoryIndex(list[i]);
75770 }
75771 };
75772 }
75773
75774 // find the range value at the specified (linear) fraction of the axis
75775 ax.fraction2r = function(v) {
75776 var rl0 = ax.r2l(ax.range[0]);
75777 var rl1 = ax.r2l(ax.range[1]);
75778 return ax.l2r(rl0 + v * (rl1 - rl0));
75779 };
75780
75781 // find the fraction of the range at the specified range value
75782 ax.r2fraction = function(v) {
75783 var rl0 = ax.r2l(ax.range[0]);
75784 var rl1 = ax.r2l(ax.range[1]);
75785 return (ax.r2l(v) - rl0) / (rl1 - rl0);
75786 };
75787
75788 /*
75789 * cleanRange: make sure range is a couplet of valid & distinct values
75790 * keep numbers away from the limits of floating point numbers,
75791 * and dates away from the ends of our date system (+/- 9999 years)
75792 *
75793 * optional param rangeAttr: operate on a different attribute, like
75794 * ax._r, rather than ax.range
75795 */
75796 ax.cleanRange = function(rangeAttr, opts) {
75797 if(!opts) opts = {};
75798 if(!rangeAttr) rangeAttr = 'range';
75799
75800 var range = Lib.nestedProperty(ax, rangeAttr).get();
75801 var i, dflt;
75802
75803 if(ax.type === 'date') dflt = Lib.dfltRange(ax.calendar);
75804 else if(axLetter === 'y') dflt = constants.DFLTRANGEY;
75805 else dflt = opts.dfltRange || constants.DFLTRANGEX;
75806
75807 // make sure we don't later mutate the defaults
75808 dflt = dflt.slice();
75809
75810 if(ax.rangemode === 'tozero' || ax.rangemode === 'nonnegative') {
75811 dflt[0] = 0;
75812 }
75813
75814 if(!range || range.length !== 2) {
75815 Lib.nestedProperty(ax, rangeAttr).set(dflt);
75816 return;
75817 }
75818
75819 if(ax.type === 'date' && !ax.autorange) {
75820 // check if milliseconds or js date objects are provided for range
75821 // and convert to date strings
75822 range[0] = Lib.cleanDate(range[0], BADNUM, ax.calendar);
75823 range[1] = Lib.cleanDate(range[1], BADNUM, ax.calendar);
75824 }
75825
75826 for(i = 0; i < 2; i++) {
75827 if(ax.type === 'date') {
75828 if(!Lib.isDateTime(range[i], ax.calendar)) {
75829 ax[rangeAttr] = dflt;
75830 break;
75831 }
75832
75833 if(ax.r2l(range[0]) === ax.r2l(range[1])) {
75834 // split by +/- 1 second
75835 var linCenter = Lib.constrain(ax.r2l(range[0]),
75836 Lib.MIN_MS + 1000, Lib.MAX_MS - 1000);
75837 range[0] = ax.l2r(linCenter - 1000);
75838 range[1] = ax.l2r(linCenter + 1000);
75839 break;
75840 }
75841 } else {
75842 if(!isNumeric(range[i])) {
75843 if(isNumeric(range[1 - i])) {
75844 range[i] = range[1 - i] * (i ? 10 : 0.1);
75845 } else {
75846 ax[rangeAttr] = dflt;
75847 break;
75848 }
75849 }
75850
75851 if(range[i] < -FP_SAFE) range[i] = -FP_SAFE;
75852 else if(range[i] > FP_SAFE) range[i] = FP_SAFE;
75853
75854 if(range[0] === range[1]) {
75855 // somewhat arbitrary: split by 1 or 1ppm, whichever is bigger
75856 var inc = Math.max(1, Math.abs(range[0] * 1e-6));
75857 range[0] -= inc;
75858 range[1] += inc;
75859 }
75860 }
75861 }
75862 };
75863
75864 // set scaling to pixels
75865 ax.setScale = function(usePrivateRange) {
75866 var gs = fullLayout._size;
75867
75868 // make sure we have a domain (pull it in from the axis
75869 // this one is overlaying if necessary)
75870 if(ax.overlaying) {
75871 var ax2 = axisIds.getFromId({ _fullLayout: fullLayout }, ax.overlaying);
75872 ax.domain = ax2.domain;
75873 }
75874
75875 // While transitions are occurring, we get a double-transform
75876 // issue if we transform the drawn layer *and* use the new axis range to
75877 // draw the data. This allows us to construct setConvert using the pre-
75878 // interaction values of the range:
75879 var rangeAttr = (usePrivateRange && ax._r) ? '_r' : 'range';
75880 var calendar = ax.calendar;
75881 ax.cleanRange(rangeAttr);
75882
75883 var rl0 = ax.r2l(ax[rangeAttr][0], calendar);
75884 var rl1 = ax.r2l(ax[rangeAttr][1], calendar);
75885
75886 var isY = axLetter === 'y';
75887 if(isY) {
75888 ax._offset = gs.t + (1 - ax.domain[1]) * gs.h;
75889 ax._length = gs.h * (ax.domain[1] - ax.domain[0]);
75890 ax._m = ax._length / (rl0 - rl1);
75891 ax._b = -ax._m * rl1;
75892 } else {
75893 ax._offset = gs.l + ax.domain[0] * gs.w;
75894 ax._length = gs.w * (ax.domain[1] - ax.domain[0]);
75895 ax._m = ax._length / (rl1 - rl0);
75896 ax._b = -ax._m * rl0;
75897 }
75898
75899 // set of "N" disjoint rangebreaks inside the range
75900 ax._rangebreaks = [];
75901 // length of these rangebreaks in value space - negative on reversed axes
75902 ax._lBreaks = 0;
75903 // l2p slope (same for all intervals)
75904 ax._m2 = 0;
75905 // set of l2p offsets (one for each of the (N+1) piecewise intervals)
75906 ax._B = [];
75907
75908 if(ax.rangebreaks) {
75909 var i, brk;
75910
75911 ax._rangebreaks = ax.locateBreaks(
75912 Math.min(rl0, rl1),
75913 Math.max(rl0, rl1)
75914 );
75915
75916 if(ax._rangebreaks.length) {
75917 for(i = 0; i < ax._rangebreaks.length; i++) {
75918 brk = ax._rangebreaks[i];
75919 ax._lBreaks += Math.abs(brk.max - brk.min);
75920 }
75921
75922 var flip = isY;
75923 if(rl0 > rl1) flip = !flip;
75924 if(flip) ax._rangebreaks.reverse();
75925 var sign = flip ? -1 : 1;
75926
75927 ax._m2 = sign * ax._length / (Math.abs(rl1 - rl0) - ax._lBreaks);
75928 ax._B.push(-ax._m2 * (isY ? rl1 : rl0));
75929 for(i = 0; i < ax._rangebreaks.length; i++) {
75930 brk = ax._rangebreaks[i];
75931 ax._B.push(
75932 ax._B[ax._B.length - 1] -
75933 sign * ax._m2 * (brk.max - brk.min)
75934 );
75935 }
75936
75937 // fill pixel (i.e. 'p') min/max here,
75938 // to not have to loop through the _rangebreaks twice during `p2l`
75939 for(i = 0; i < ax._rangebreaks.length; i++) {
75940 brk = ax._rangebreaks[i];
75941 brk.pmin = l2p(brk.min);
75942 brk.pmax = l2p(brk.max);
75943 }
75944 }
75945 }
75946
75947 if(!isFinite(ax._m) || !isFinite(ax._b) || ax._length < 0) {
75948 fullLayout._replotting = false;
75949 throw new Error('Something went wrong with axis scaling');
75950 }
75951 };
75952
75953 ax.maskBreaks = function(v) {
75954 var rangebreaksIn = ax.rangebreaks || [];
75955 var bnds, b0, b1, vb, vDate;
75956
75957
75958 if(!rangebreaksIn._cachedPatterns) {
75959 rangebreaksIn._cachedPatterns = rangebreaksIn.map(function(brk) {
75960 return brk.enabled && brk.bounds ? Lib.simpleMap(brk.bounds, brk.pattern ?
75961 cleanNumber :
75962 ax.d2c // case of pattern: ''
75963 ) : null;
75964 });
75965 }
75966 if(!rangebreaksIn._cachedValues) {
75967 rangebreaksIn._cachedValues = rangebreaksIn.map(function(brk) {
75968 return brk.enabled && brk.values ? Lib.simpleMap(brk.values, ax.d2c).sort(Lib.sorterAsc) : null;
75969 });
75970 }
75971
75972
75973 for(var i = 0; i < rangebreaksIn.length; i++) {
75974 var brk = rangebreaksIn[i];
75975
75976 if(brk.enabled) {
75977 if(brk.bounds) {
75978 var pattern = brk.pattern;
75979 bnds = rangebreaksIn._cachedPatterns[i];
75980 b0 = bnds[0];
75981 b1 = bnds[1];
75982
75983 switch(pattern) {
75984 case WEEKDAY_PATTERN:
75985 vDate = new Date(v);
75986 vb = vDate.getUTCDay();
75987
75988 if(b0 > b1) {
75989 b1 += 7;
75990 if(vb < b0) vb += 7;
75991 }
75992
75993 break;
75994 case HOUR_PATTERN:
75995 vDate = new Date(v);
75996 var hours = vDate.getUTCHours();
75997 var minutes = vDate.getUTCMinutes();
75998 var seconds = vDate.getUTCSeconds();
75999 var milliseconds = vDate.getUTCMilliseconds();
76000
76001 vb = hours + (
76002 minutes / 60 +
76003 seconds / 3600 +
76004 milliseconds / 3600000
76005 );
76006
76007 if(b0 > b1) {
76008 b1 += 24;
76009 if(vb < b0) vb += 24;
76010 }
76011
76012 break;
76013 case '':
76014 // N.B. should work on date axes as well!
76015 // e.g. { bounds: ['2020-01-04', '2020-01-05 23:59'] }
76016 // TODO should work with reversed-range axes
76017 vb = v;
76018 break;
76019 }
76020
76021 if(vb >= b0 && vb < b1) return BADNUM;
76022 } else {
76023 var vals = rangebreaksIn._cachedValues[i];
76024 for(var j = 0; j < vals.length; j++) {
76025 b0 = vals[j];
76026 b1 = b0 + brk.dvalue;
76027 if(v >= b0 && v < b1) return BADNUM;
76028 }
76029 }
76030 }
76031 }
76032 return v;
76033 };
76034
76035 ax.locateBreaks = function(r0, r1) {
76036 var i, bnds, b0, b1;
76037
76038 var rangebreaksOut = [];
76039 if(!ax.rangebreaks) return rangebreaksOut;
76040
76041 var rangebreaksIn = ax.rangebreaks.slice().sort(function(a, b) {
76042 if(a.pattern === WEEKDAY_PATTERN && b.pattern === HOUR_PATTERN) return -1;
76043 if(b.pattern === WEEKDAY_PATTERN && a.pattern === HOUR_PATTERN) return 1;
76044 return 0;
76045 });
76046
76047 var addBreak = function(min, max) {
76048 min = Lib.constrain(min, r0, r1);
76049 max = Lib.constrain(max, r0, r1);
76050 if(min === max) return;
76051
76052 var isNewBreak = true;
76053 for(var j = 0; j < rangebreaksOut.length; j++) {
76054 var brkj = rangebreaksOut[j];
76055 if(min < brkj.max && max >= brkj.min) {
76056 if(min < brkj.min) {
76057 brkj.min = min;
76058 }
76059 if(max > brkj.max) {
76060 brkj.max = max;
76061 }
76062 isNewBreak = false;
76063 }
76064 }
76065 if(isNewBreak) {
76066 rangebreaksOut.push({min: min, max: max});
76067 }
76068 };
76069
76070 for(i = 0; i < rangebreaksIn.length; i++) {
76071 var brk = rangebreaksIn[i];
76072
76073 if(brk.enabled) {
76074 if(brk.bounds) {
76075 var t0 = r0;
76076 var t1 = r1;
76077 if(brk.pattern) {
76078 // to remove decimal (most often found in auto ranges)
76079 t0 = Math.floor(t0);
76080 }
76081
76082 bnds = Lib.simpleMap(brk.bounds, brk.pattern ? cleanNumber : ax.r2l);
76083 b0 = bnds[0];
76084 b1 = bnds[1];
76085
76086 // r0 value as date
76087 var t0Date = new Date(t0);
76088 // r0 value for break pattern
76089 var bndDelta;
76090 // step in ms between rangebreaks
76091 var step;
76092
76093 switch(brk.pattern) {
76094 case WEEKDAY_PATTERN:
76095 step = ONEWEEK;
76096
76097 bndDelta = (
76098 (b1 < b0 ? 7 : 0) +
76099 (b1 - b0)
76100 ) * ONEDAY;
76101
76102 t0 += b0 * ONEDAY - (
76103 t0Date.getUTCDay() * ONEDAY +
76104 t0Date.getUTCHours() * ONEHOUR +
76105 t0Date.getUTCMinutes() * ONEMIN +
76106 t0Date.getUTCSeconds() * ONESEC +
76107 t0Date.getUTCMilliseconds()
76108 );
76109 break;
76110 case HOUR_PATTERN:
76111 step = ONEDAY;
76112
76113 bndDelta = (
76114 (b1 < b0 ? 24 : 0) +
76115 (b1 - b0)
76116 ) * ONEHOUR;
76117
76118 t0 += b0 * ONEHOUR - (
76119 t0Date.getUTCHours() * ONEHOUR +
76120 t0Date.getUTCMinutes() * ONEMIN +
76121 t0Date.getUTCSeconds() * ONESEC +
76122 t0Date.getUTCMilliseconds()
76123 );
76124 break;
76125 default:
76126 t0 = Math.min(bnds[0], bnds[1]);
76127 t1 = Math.max(bnds[0], bnds[1]);
76128 step = t1 - t0;
76129 bndDelta = step;
76130 }
76131
76132 for(var t = t0; t < t1; t += step) {
76133 addBreak(t, t + bndDelta);
76134 }
76135 } else {
76136 var vals = Lib.simpleMap(brk.values, ax.d2c);
76137 for(var j = 0; j < vals.length; j++) {
76138 b0 = vals[j];
76139 b1 = b0 + brk.dvalue;
76140 addBreak(b0, b1);
76141 }
76142 }
76143 }
76144 }
76145
76146 rangebreaksOut.sort(function(a, b) { return a.min - b.min; });
76147
76148 return rangebreaksOut;
76149 };
76150
76151 // makeCalcdata: takes an x or y array and converts it
76152 // to a position on the axis object "ax"
76153 // inputs:
76154 // trace - a data object from gd.data
76155 // axLetter - a string, either 'x' or 'y', for which item
76156 // to convert (TODO: is this now always the same as
76157 // the first letter of ax._id?)
76158 // in case the expected data isn't there, make a list of
76159 // integers based on the opposite data
76160 ax.makeCalcdata = function(trace, axLetter, opts) {
76161 var arrayIn, arrayOut, i, len;
76162
76163 var axType = ax.type;
76164 var cal = axType === 'date' && trace[axLetter + 'calendar'];
76165
76166 if(axLetter in trace) {
76167 arrayIn = trace[axLetter];
76168 len = trace._length || Lib.minRowLength(arrayIn);
76169
76170 if(Lib.isTypedArray(arrayIn) && (axType === 'linear' || axType === 'log')) {
76171 if(len === arrayIn.length) {
76172 return arrayIn;
76173 } else if(arrayIn.subarray) {
76174 return arrayIn.subarray(0, len);
76175 }
76176 }
76177
76178 if(axType === 'multicategory') {
76179 return setMultiCategoryIndex(arrayIn, len);
76180 }
76181
76182 arrayOut = new Array(len);
76183 for(i = 0; i < len; i++) {
76184 arrayOut[i] = ax.d2c(arrayIn[i], 0, cal, opts);
76185 }
76186 } else {
76187 var v0 = ((axLetter + '0') in trace) ? ax.d2c(trace[axLetter + '0'], 0, cal) : 0;
76188 var dv = (trace['d' + axLetter]) ? Number(trace['d' + axLetter]) : 1;
76189
76190 // the opposing data, for size if we have x and dx etc
76191 arrayIn = trace[{x: 'y', y: 'x'}[axLetter]];
76192 len = trace._length || arrayIn.length;
76193 arrayOut = new Array(len);
76194
76195 for(i = 0; i < len; i++) {
76196 arrayOut[i] = v0 + i * dv;
76197 }
76198 }
76199
76200 // mask (i.e. set to BADNUM) coords that fall inside rangebreaks
76201 if(ax.rangebreaks) {
76202 for(i = 0; i < len; i++) {
76203 arrayOut[i] = ax.maskBreaks(arrayOut[i]);
76204 }
76205 }
76206
76207 return arrayOut;
76208 };
76209
76210 ax.isValidRange = function(range) {
76211 return (
76212 Array.isArray(range) &&
76213 range.length === 2 &&
76214 isNumeric(ax.r2l(range[0])) &&
76215 isNumeric(ax.r2l(range[1]))
76216 );
76217 };
76218
76219 ax.isPtWithinRange = function(d, calendar) {
76220 var coord = ax.c2l(d[axLetter], null, calendar);
76221 var r0 = ax.r2l(ax.range[0]);
76222 var r1 = ax.r2l(ax.range[1]);
76223
76224 if(r0 < r1) {
76225 return r0 <= coord && coord <= r1;
76226 } else {
76227 // Reversed axis case.
76228 return r1 <= coord && coord <= r0;
76229 }
76230 };
76231
76232 ax._emptyCategories = function() {
76233 ax._categories = [];
76234 ax._categoriesMap = {};
76235 };
76236
76237 // should skip if not category nor multicategory
76238 ax.clearCalc = function() {
76239 var group = ax._matchGroup;
76240 if(group) {
76241 var categories = null;
76242 var categoriesMap = null;
76243
76244 for(var axId2 in group) {
76245 var ax2 = fullLayout[axisIds.id2name(axId2)];
76246 if(ax2._categories) {
76247 categories = ax2._categories;
76248 categoriesMap = ax2._categoriesMap;
76249 break;
76250 }
76251 }
76252
76253 if(categories && categoriesMap) {
76254 ax._categories = categories;
76255 ax._categoriesMap = categoriesMap;
76256 } else {
76257 ax._emptyCategories();
76258 }
76259 } else {
76260 ax._emptyCategories();
76261 }
76262
76263 if(ax._initialCategories) {
76264 for(var j = 0; j < ax._initialCategories.length; j++) {
76265 setCategoryIndex(ax._initialCategories[j]);
76266 }
76267 }
76268 };
76269
76270 // sort the axis (and all the matching ones) by _initialCategories
76271 // returns the indices of the traces affected by the reordering
76272 ax.sortByInitialCategories = function() {
76273 var affectedTraces = [];
76274
76275 ax._emptyCategories();
76276
76277 if(ax._initialCategories) {
76278 for(var j = 0; j < ax._initialCategories.length; j++) {
76279 setCategoryIndex(ax._initialCategories[j]);
76280 }
76281 }
76282
76283 affectedTraces = affectedTraces.concat(ax._traceIndices);
76284
76285 // Propagate to matching axes
76286 var group = ax._matchGroup;
76287 for(var axId2 in group) {
76288 if(axId === axId2) continue;
76289 var ax2 = fullLayout[axisIds.id2name(axId2)];
76290 ax2._categories = ax._categories;
76291 ax2._categoriesMap = ax._categoriesMap;
76292 affectedTraces = affectedTraces.concat(ax2._traceIndices);
76293 }
76294 return affectedTraces;
76295 };
76296
76297 // Propagate localization into the axis so that
76298 // methods in Axes can use it w/o having to pass fullLayout
76299 // Default (non-d3) number formatting uses separators directly
76300 // dates and d3-formatted numbers use the d3 locale
76301 // Fall back on default format for dummy axes that don't care about formatting
76302 var locale = fullLayout._d3locale;
76303 if(ax.type === 'date') {
76304 ax._dateFormat = locale ? locale.timeFormat : utcFormat;
76305 ax._extraFormat = fullLayout._extraFormat;
76306 }
76307 // occasionally we need _numFormat to pass through
76308 // even though it won't be needed by this axis
76309 ax._separators = fullLayout.separators;
76310 ax._numFormat = locale ? locale.numberFormat : numberFormat;
76311
76312 // and for bar charts and box plots: reset forced minimum tick spacing
76313 delete ax._minDtick;
76314 delete ax._forceTick0;
76315};
76316
76317},{"../../constants/numerical":267,"../../lib":287,"./axis_ids":338,"./constants":341,"@plotly/d3":20,"d3-time-format":30,"fast-isnumeric":33}],356:[function(_dereq_,module,exports){
76318'use strict';
76319
76320var Lib = _dereq_('../../lib');
76321var contrast = _dereq_('../../components/color').contrast;
76322var layoutAttributes = _dereq_('./layout_attributes');
76323var handleArrayContainerDefaults = _dereq_('../array_container_defaults');
76324
76325module.exports = function handleTickLabelDefaults(containerIn, containerOut, coerce, axType, options, config) {
76326 if(!config || config.pass === 1) {
76327 handlePrefixSuffix(containerIn, containerOut, coerce, axType, options);
76328 }
76329
76330 if(!config || config.pass === 2) {
76331 handleOtherDefaults(containerIn, containerOut, coerce, axType, options);
76332 }
76333};
76334
76335function handlePrefixSuffix(containerIn, containerOut, coerce, axType, options) {
76336 var showAttrDflt = getShowAttrDflt(containerIn);
76337
76338 var tickPrefix = coerce('tickprefix');
76339 if(tickPrefix) coerce('showtickprefix', showAttrDflt);
76340
76341 var tickSuffix = coerce('ticksuffix', options.tickSuffixDflt);
76342 if(tickSuffix) coerce('showticksuffix', showAttrDflt);
76343}
76344
76345function handleOtherDefaults(containerIn, containerOut, coerce, axType, options) {
76346 var showAttrDflt = getShowAttrDflt(containerIn);
76347
76348 var tickPrefix = coerce('tickprefix');
76349 if(tickPrefix) coerce('showtickprefix', showAttrDflt);
76350
76351 var tickSuffix = coerce('ticksuffix', options.tickSuffixDflt);
76352 if(tickSuffix) coerce('showticksuffix', showAttrDflt);
76353
76354 var showTickLabels = coerce('showticklabels');
76355 if(showTickLabels) {
76356 var font = options.font || {};
76357 var contColor = containerOut.color;
76358 var position = containerOut.ticklabelposition || '';
76359 var dfltFontColor = position.indexOf('inside') !== -1 ?
76360 contrast(options.bgColor) :
76361 // as with titlefont.color, inherit axis.color only if one was
76362 // explicitly provided
76363 (contColor && contColor !== layoutAttributes.color.dflt) ?
76364 contColor : font.color;
76365
76366 Lib.coerceFont(coerce, 'tickfont', {
76367 family: font.family,
76368 size: font.size,
76369 color: dfltFontColor
76370 });
76371 coerce('tickangle');
76372
76373 if(axType !== 'category') {
76374 var tickFormat = coerce('tickformat');
76375
76376 handleArrayContainerDefaults(containerIn, containerOut, {
76377 name: 'tickformatstops',
76378 inclusionAttr: 'enabled',
76379 handleItemDefaults: tickformatstopDefaults
76380 });
76381 if(!containerOut.tickformatstops.length) {
76382 delete containerOut.tickformatstops;
76383 }
76384
76385 if(!tickFormat && axType !== 'date') {
76386 coerce('showexponent', showAttrDflt);
76387 coerce('exponentformat');
76388 coerce('minexponent');
76389 coerce('separatethousands');
76390 }
76391 }
76392 }
76393}
76394
76395/*
76396 * Attributes 'showexponent', 'showtickprefix' and 'showticksuffix'
76397 * share values.
76398 *
76399 * If only 1 attribute is set,
76400 * the remaining attributes inherit that value.
76401 *
76402 * If 2 attributes are set to the same value,
76403 * the remaining attribute inherits that value.
76404 *
76405 * If 2 attributes are set to different values,
76406 * the remaining is set to its dflt value.
76407 *
76408 */
76409function getShowAttrDflt(containerIn) {
76410 var showAttrsAll = ['showexponent', 'showtickprefix', 'showticksuffix'];
76411 var showAttrs = showAttrsAll.filter(function(a) {
76412 return containerIn[a] !== undefined;
76413 });
76414 var sameVal = function(a) {
76415 return containerIn[a] === containerIn[showAttrs[0]];
76416 };
76417
76418 if(showAttrs.every(sameVal) || showAttrs.length === 1) {
76419 return containerIn[showAttrs[0]];
76420 }
76421}
76422
76423function tickformatstopDefaults(valueIn, valueOut) {
76424 function coerce(attr, dflt) {
76425 return Lib.coerce(valueIn, valueOut, layoutAttributes.tickformatstops, attr, dflt);
76426 }
76427
76428 var enabled = coerce('enabled');
76429 if(enabled) {
76430 coerce('dtickrange');
76431 coerce('value');
76432 }
76433}
76434
76435},{"../../components/color":157,"../../lib":287,"../array_container_defaults":329,"./layout_attributes":349}],357:[function(_dereq_,module,exports){
76436'use strict';
76437
76438var Lib = _dereq_('../../lib');
76439
76440var layoutAttributes = _dereq_('./layout_attributes');
76441
76442
76443/**
76444 * options: inherits outerTicks from axes.handleAxisDefaults
76445 */
76446module.exports = function handleTickDefaults(containerIn, containerOut, coerce, options) {
76447 var tickLen = Lib.coerce2(containerIn, containerOut, layoutAttributes, 'ticklen');
76448 var tickWidth = Lib.coerce2(containerIn, containerOut, layoutAttributes, 'tickwidth');
76449 var tickColor = Lib.coerce2(containerIn, containerOut, layoutAttributes, 'tickcolor', containerOut.color);
76450 var showTicks = coerce('ticks', (options.outerTicks || tickLen || tickWidth || tickColor) ? 'outside' : '');
76451
76452 if(!showTicks) {
76453 delete containerOut.ticklen;
76454 delete containerOut.tickwidth;
76455 delete containerOut.tickcolor;
76456 }
76457};
76458
76459},{"../../lib":287,"./layout_attributes":349}],358:[function(_dereq_,module,exports){
76460'use strict';
76461
76462var cleanTicks = _dereq_('./clean_ticks');
76463var isArrayOrTypedArray = _dereq_('../../lib').isArrayOrTypedArray;
76464
76465module.exports = function handleTickValueDefaults(containerIn, containerOut, coerce, axType) {
76466 function readInput(attr) {
76467 var v = containerIn[attr];
76468 return (
76469 v !== undefined
76470 ) ? v : (containerOut._template || {})[attr];
76471 }
76472
76473 var _tick0 = readInput('tick0');
76474 var _dtick = readInput('dtick');
76475 var _tickvals = readInput('tickvals');
76476
76477 var tickmodeDefault = isArrayOrTypedArray(_tickvals) ? 'array' :
76478 _dtick ? 'linear' :
76479 'auto';
76480 var tickmode = coerce('tickmode', tickmodeDefault);
76481
76482 if(tickmode === 'auto') coerce('nticks');
76483 else if(tickmode === 'linear') {
76484 // dtick is usually a positive number, but there are some
76485 // special strings available for log or date axes
76486 // tick0 also has special logic
76487 var dtick = containerOut.dtick = cleanTicks.dtick(
76488 _dtick, axType);
76489 containerOut.tick0 = cleanTicks.tick0(
76490 _tick0, axType, containerOut.calendar, dtick);
76491 } else if(axType !== 'multicategory') {
76492 var tickvals = coerce('tickvals');
76493 if(tickvals === undefined) containerOut.tickmode = 'auto';
76494 else coerce('ticktext');
76495 }
76496};
76497
76498},{"../../lib":287,"./clean_ticks":340}],359:[function(_dereq_,module,exports){
76499'use strict';
76500
76501var d3 = _dereq_('@plotly/d3');
76502
76503var Registry = _dereq_('../../registry');
76504var Lib = _dereq_('../../lib');
76505var Drawing = _dereq_('../../components/drawing');
76506var Axes = _dereq_('./axes');
76507
76508/**
76509 * transitionAxes
76510 *
76511 * transition axes from one set of ranges to another, using a svg
76512 * transformations, similar to during panning.
76513 *
76514 * @param {DOM element | object} gd
76515 * @param {array} edits : array of 'edits', each item with
76516 * - plotinfo {object} subplot object
76517 * - xr0 {array} initial x-range
76518 * - xr1 {array} end x-range
76519 * - yr0 {array} initial y-range
76520 * - yr1 {array} end y-range
76521 * @param {object} transitionOpts
76522 * @param {function} makeOnCompleteCallback
76523 */
76524module.exports = function transitionAxes(gd, edits, transitionOpts, makeOnCompleteCallback) {
76525 var fullLayout = gd._fullLayout;
76526
76527 // special case for redraw:false Plotly.animate that relies on this
76528 // to update axis-referenced layout components
76529 if(edits.length === 0) {
76530 Axes.redrawComponents(gd);
76531 return;
76532 }
76533
76534 function unsetSubplotTransform(subplot) {
76535 var xa = subplot.xaxis;
76536 var ya = subplot.yaxis;
76537
76538 fullLayout._defs.select('#' + subplot.clipId + '> rect')
76539 .call(Drawing.setTranslate, 0, 0)
76540 .call(Drawing.setScale, 1, 1);
76541
76542 subplot.plot
76543 .call(Drawing.setTranslate, xa._offset, ya._offset)
76544 .call(Drawing.setScale, 1, 1);
76545
76546 var traceGroups = subplot.plot.selectAll('.scatterlayer .trace');
76547
76548 // This is specifically directed at scatter traces, applying an inverse
76549 // scale to individual points to counteract the scale of the trace
76550 // as a whole:
76551 traceGroups.selectAll('.point')
76552 .call(Drawing.setPointGroupScale, 1, 1);
76553 traceGroups.selectAll('.textpoint')
76554 .call(Drawing.setTextPointsScale, 1, 1);
76555 traceGroups
76556 .call(Drawing.hideOutsideRangePoints, subplot);
76557 }
76558
76559 function updateSubplot(edit, progress) {
76560 var plotinfo = edit.plotinfo;
76561 var xa = plotinfo.xaxis;
76562 var ya = plotinfo.yaxis;
76563 var xlen = xa._length;
76564 var ylen = ya._length;
76565 var editX = !!edit.xr1;
76566 var editY = !!edit.yr1;
76567 var viewBox = [];
76568
76569 if(editX) {
76570 var xr0 = Lib.simpleMap(edit.xr0, xa.r2l);
76571 var xr1 = Lib.simpleMap(edit.xr1, xa.r2l);
76572 var dx0 = xr0[1] - xr0[0];
76573 var dx1 = xr1[1] - xr1[0];
76574 viewBox[0] = (xr0[0] * (1 - progress) + progress * xr1[0] - xr0[0]) / (xr0[1] - xr0[0]) * xlen;
76575 viewBox[2] = xlen * ((1 - progress) + progress * dx1 / dx0);
76576 xa.range[0] = xa.l2r(xr0[0] * (1 - progress) + progress * xr1[0]);
76577 xa.range[1] = xa.l2r(xr0[1] * (1 - progress) + progress * xr1[1]);
76578 } else {
76579 viewBox[0] = 0;
76580 viewBox[2] = xlen;
76581 }
76582
76583 if(editY) {
76584 var yr0 = Lib.simpleMap(edit.yr0, ya.r2l);
76585 var yr1 = Lib.simpleMap(edit.yr1, ya.r2l);
76586 var dy0 = yr0[1] - yr0[0];
76587 var dy1 = yr1[1] - yr1[0];
76588 viewBox[1] = (yr0[1] * (1 - progress) + progress * yr1[1] - yr0[1]) / (yr0[0] - yr0[1]) * ylen;
76589 viewBox[3] = ylen * ((1 - progress) + progress * dy1 / dy0);
76590 ya.range[0] = xa.l2r(yr0[0] * (1 - progress) + progress * yr1[0]);
76591 ya.range[1] = ya.l2r(yr0[1] * (1 - progress) + progress * yr1[1]);
76592 } else {
76593 viewBox[1] = 0;
76594 viewBox[3] = ylen;
76595 }
76596
76597 Axes.drawOne(gd, xa, {skipTitle: true});
76598 Axes.drawOne(gd, ya, {skipTitle: true});
76599 Axes.redrawComponents(gd, [xa._id, ya._id]);
76600
76601 var xScaleFactor = editX ? xlen / viewBox[2] : 1;
76602 var yScaleFactor = editY ? ylen / viewBox[3] : 1;
76603 var clipDx = editX ? viewBox[0] : 0;
76604 var clipDy = editY ? viewBox[1] : 0;
76605 var fracDx = editX ? (viewBox[0] / viewBox[2] * xlen) : 0;
76606 var fracDy = editY ? (viewBox[1] / viewBox[3] * ylen) : 0;
76607 var plotDx = xa._offset - fracDx;
76608 var plotDy = ya._offset - fracDy;
76609
76610 plotinfo.clipRect
76611 .call(Drawing.setTranslate, clipDx, clipDy)
76612 .call(Drawing.setScale, 1 / xScaleFactor, 1 / yScaleFactor);
76613
76614 plotinfo.plot
76615 .call(Drawing.setTranslate, plotDx, plotDy)
76616 .call(Drawing.setScale, xScaleFactor, yScaleFactor);
76617
76618 // apply an inverse scale to individual points to counteract
76619 // the scale of the trace group.
76620 Drawing.setPointGroupScale(plotinfo.zoomScalePts, 1 / xScaleFactor, 1 / yScaleFactor);
76621 Drawing.setTextPointsScale(plotinfo.zoomScaleTxt, 1 / xScaleFactor, 1 / yScaleFactor);
76622 }
76623
76624 var onComplete;
76625 if(makeOnCompleteCallback) {
76626 // This module makes the choice whether or not it notifies Plotly.transition
76627 // about completion:
76628 onComplete = makeOnCompleteCallback();
76629 }
76630
76631 function transitionComplete() {
76632 var aobj = {};
76633
76634 for(var i = 0; i < edits.length; i++) {
76635 var edit = edits[i];
76636 var xa = edit.plotinfo.xaxis;
76637 var ya = edit.plotinfo.yaxis;
76638 if(edit.xr1) aobj[xa._name + '.range'] = edit.xr1.slice();
76639 if(edit.yr1) aobj[ya._name + '.range'] = edit.yr1.slice();
76640 }
76641
76642 // Signal that this transition has completed:
76643 onComplete && onComplete();
76644
76645 return Registry.call('relayout', gd, aobj).then(function() {
76646 for(var i = 0; i < edits.length; i++) {
76647 unsetSubplotTransform(edits[i].plotinfo);
76648 }
76649 });
76650 }
76651
76652 function transitionInterrupt() {
76653 var aobj = {};
76654
76655 for(var i = 0; i < edits.length; i++) {
76656 var edit = edits[i];
76657 var xa = edit.plotinfo.xaxis;
76658 var ya = edit.plotinfo.yaxis;
76659 if(edit.xr0) aobj[xa._name + '.range'] = edit.xr0.slice();
76660 if(edit.yr0) aobj[ya._name + '.range'] = edit.yr0.slice();
76661 }
76662
76663 return Registry.call('relayout', gd, aobj).then(function() {
76664 for(var i = 0; i < edits.length; i++) {
76665 unsetSubplotTransform(edits[i].plotinfo);
76666 }
76667 });
76668 }
76669
76670 var t1, t2, raf;
76671 var easeFn = d3.ease(transitionOpts.easing);
76672
76673 gd._transitionData._interruptCallbacks.push(function() {
76674 window.cancelAnimationFrame(raf);
76675 raf = null;
76676 return transitionInterrupt();
76677 });
76678
76679 function doFrame() {
76680 t2 = Date.now();
76681
76682 var tInterp = Math.min(1, (t2 - t1) / transitionOpts.duration);
76683 var progress = easeFn(tInterp);
76684
76685 for(var i = 0; i < edits.length; i++) {
76686 updateSubplot(edits[i], progress);
76687 }
76688
76689 if(t2 - t1 > transitionOpts.duration) {
76690 transitionComplete();
76691 raf = window.cancelAnimationFrame(doFrame);
76692 } else {
76693 raf = window.requestAnimationFrame(doFrame);
76694 }
76695 }
76696
76697 t1 = Date.now();
76698 raf = window.requestAnimationFrame(doFrame);
76699
76700 return Promise.resolve();
76701};
76702
76703},{"../../components/drawing":179,"../../lib":287,"../../registry":376,"./axes":334,"@plotly/d3":20}],360:[function(_dereq_,module,exports){
76704'use strict';
76705
76706var traceIs = _dereq_('../../registry').traceIs;
76707var autoType = _dereq_('./axis_autotype');
76708
76709/*
76710 * data: the plot data to use in choosing auto type
76711 * name: axis object name (ie 'xaxis') if one should be stored
76712 */
76713module.exports = function handleTypeDefaults(containerIn, containerOut, coerce, options) {
76714 coerce('autotypenumbers', options.autotypenumbersDflt);
76715 var axType = coerce('type', (options.splomStash || {}).type);
76716
76717 if(axType === '-') {
76718 setAutoType(containerOut, options.data);
76719
76720 if(containerOut.type === '-') {
76721 containerOut.type = 'linear';
76722 } else {
76723 // copy autoType back to input axis
76724 // note that if this object didn't exist
76725 // in the input layout, we have to put it in
76726 // this happens in the main supplyDefaults function
76727 containerIn.type = containerOut.type;
76728 }
76729 }
76730};
76731
76732function setAutoType(ax, data) {
76733 // new logic: let people specify any type they want,
76734 // only autotype if type is '-'
76735 if(ax.type !== '-') return;
76736
76737 var id = ax._id;
76738 var axLetter = id.charAt(0);
76739 var i;
76740
76741 // support 3d
76742 if(id.indexOf('scene') !== -1) id = axLetter;
76743
76744 var d0 = getFirstNonEmptyTrace(data, id, axLetter);
76745 if(!d0) return;
76746
76747 // first check for histograms, as the count direction
76748 // should always default to a linear axis
76749 if(d0.type === 'histogram' &&
76750 axLetter === {v: 'y', h: 'x'}[d0.orientation || 'v']
76751 ) {
76752 ax.type = 'linear';
76753 return;
76754 }
76755
76756 var calAttr = axLetter + 'calendar';
76757 var calendar = d0[calAttr];
76758 var opts = {noMultiCategory: !traceIs(d0, 'cartesian') || traceIs(d0, 'noMultiCategory')};
76759
76760 // To not confuse 2D x/y used for per-box sample points for multicategory coordinates
76761 if(d0.type === 'box' && d0._hasPreCompStats &&
76762 axLetter === {h: 'x', v: 'y'}[d0.orientation || 'v']
76763 ) {
76764 opts.noMultiCategory = true;
76765 }
76766
76767 opts.autotypenumbers = ax.autotypenumbers;
76768
76769 // check all boxes on this x axis to see
76770 // if they're dates, numbers, or categories
76771 if(isBoxWithoutPositionCoords(d0, axLetter)) {
76772 var posLetter = getBoxPosLetter(d0);
76773 var boxPositions = [];
76774
76775 for(i = 0; i < data.length; i++) {
76776 var trace = data[i];
76777 if(!traceIs(trace, 'box-violin') || (trace[axLetter + 'axis'] || axLetter) !== id) continue;
76778
76779 if(trace[posLetter] !== undefined) boxPositions.push(trace[posLetter][0]);
76780 else if(trace.name !== undefined) boxPositions.push(trace.name);
76781 else boxPositions.push('text');
76782
76783 if(trace[calAttr] !== calendar) calendar = undefined;
76784 }
76785
76786 ax.type = autoType(boxPositions, calendar, opts);
76787 } else if(d0.type === 'splom') {
76788 var dimensions = d0.dimensions;
76789 var dim = dimensions[d0._axesDim[id]];
76790 if(dim.visible) ax.type = autoType(dim.values, calendar, opts);
76791 } else {
76792 ax.type = autoType(d0[axLetter] || [d0[axLetter + '0']], calendar, opts);
76793 }
76794}
76795
76796function getFirstNonEmptyTrace(data, id, axLetter) {
76797 for(var i = 0; i < data.length; i++) {
76798 var trace = data[i];
76799
76800 if(trace.type === 'splom' &&
76801 trace._length > 0 &&
76802 (trace['_' + axLetter + 'axes'] || {})[id]
76803 ) {
76804 return trace;
76805 }
76806
76807 if((trace[axLetter + 'axis'] || axLetter) === id) {
76808 if(isBoxWithoutPositionCoords(trace, axLetter)) {
76809 return trace;
76810 } else if((trace[axLetter] || []).length || trace[axLetter + '0']) {
76811 return trace;
76812 }
76813 }
76814 }
76815}
76816
76817function getBoxPosLetter(trace) {
76818 return {v: 'x', h: 'y'}[trace.orientation || 'v'];
76819}
76820
76821function isBoxWithoutPositionCoords(trace, axLetter) {
76822 var posLetter = getBoxPosLetter(trace);
76823 var isBox = traceIs(trace, 'box-violin');
76824 var isCandlestick = traceIs(trace._fullInput || {}, 'candlestick');
76825
76826 return (
76827 isBox &&
76828 !isCandlestick &&
76829 axLetter === posLetter &&
76830 trace[posLetter] === undefined &&
76831 trace[posLetter + '0'] === undefined
76832 );
76833}
76834
76835},{"../../registry":376,"./axis_autotype":335}],361:[function(_dereq_,module,exports){
76836'use strict';
76837
76838var Registry = _dereq_('../registry');
76839var Lib = _dereq_('../lib');
76840
76841/*
76842 * Create or update an observer. This function is designed to be
76843 * idempotent so that it can be called over and over as the component
76844 * updates, and will attach and detach listeners as needed.
76845 *
76846 * @param {optional object} container
76847 * An object on which the observer is stored. This is the mechanism
76848 * by which it is idempotent. If it already exists, another won't be
76849 * added. Each time it's called, the value lookup table is updated.
76850 * @param {array} commandList
76851 * An array of commands, following either `buttons` of `updatemenus`
76852 * or `steps` of `sliders`.
76853 * @param {function} onchange
76854 * A listener called when the value is changed. Receives data object
76855 * with information about the new state.
76856 */
76857exports.manageCommandObserver = function(gd, container, commandList, onchange) {
76858 var ret = {};
76859 var enabled = true;
76860
76861 if(container && container._commandObserver) {
76862 ret = container._commandObserver;
76863 }
76864
76865 if(!ret.cache) {
76866 ret.cache = {};
76867 }
76868
76869 // Either create or just recompute this:
76870 ret.lookupTable = {};
76871
76872 var binding = exports.hasSimpleAPICommandBindings(gd, commandList, ret.lookupTable);
76873
76874 if(container && container._commandObserver) {
76875 if(!binding) {
76876 // If container exists and there are no longer any bindings,
76877 // remove existing:
76878 if(container._commandObserver.remove) {
76879 container._commandObserver.remove();
76880 container._commandObserver = null;
76881 return ret;
76882 }
76883 } else {
76884 // If container exists and there *are* bindings, then the lookup
76885 // table should have been updated and check is already attached,
76886 // so there's nothing to be done:
76887 return ret;
76888 }
76889 }
76890
76891 // Determine whether there's anything to do for this binding:
76892
76893 if(binding) {
76894 // Build the cache:
76895 bindingValueHasChanged(gd, binding, ret.cache);
76896
76897 ret.check = function check() {
76898 if(!enabled) return;
76899
76900 var update = bindingValueHasChanged(gd, binding, ret.cache);
76901
76902 if(update.changed && onchange) {
76903 // Disable checks for the duration of this command in order to avoid
76904 // infinite loops:
76905 if(ret.lookupTable[update.value] !== undefined) {
76906 ret.disable();
76907 Promise.resolve(onchange({
76908 value: update.value,
76909 type: binding.type,
76910 prop: binding.prop,
76911 traces: binding.traces,
76912 index: ret.lookupTable[update.value]
76913 })).then(ret.enable, ret.enable);
76914 }
76915 }
76916
76917 return update.changed;
76918 };
76919
76920 var checkEvents = [
76921 'plotly_relayout',
76922 'plotly_redraw',
76923 'plotly_restyle',
76924 'plotly_update',
76925 'plotly_animatingframe',
76926 'plotly_afterplot'
76927 ];
76928
76929 for(var i = 0; i < checkEvents.length; i++) {
76930 gd._internalOn(checkEvents[i], ret.check);
76931 }
76932
76933 ret.remove = function() {
76934 for(var i = 0; i < checkEvents.length; i++) {
76935 gd._removeInternalListener(checkEvents[i], ret.check);
76936 }
76937 };
76938 } else {
76939 // TODO: It'd be really neat to actually give a *reason* for this, but at least a warning
76940 // is a start
76941 Lib.log('Unable to automatically bind plot updates to API command');
76942
76943 ret.lookupTable = {};
76944 ret.remove = function() {};
76945 }
76946
76947 ret.disable = function disable() {
76948 enabled = false;
76949 };
76950
76951 ret.enable = function enable() {
76952 enabled = true;
76953 };
76954
76955 if(container) {
76956 container._commandObserver = ret;
76957 }
76958
76959 return ret;
76960};
76961
76962/*
76963 * This function checks to see if an array of objects containing
76964 * method and args properties is compatible with automatic two-way
76965 * binding. The criteria right now are that
76966 *
76967 * 1. multiple traces may be affected
76968 * 2. only one property may be affected
76969 * 3. the same property must be affected by all commands
76970 */
76971exports.hasSimpleAPICommandBindings = function(gd, commandList, bindingsByValue) {
76972 var i;
76973 var n = commandList.length;
76974
76975 var refBinding;
76976
76977 for(i = 0; i < n; i++) {
76978 var binding;
76979 var command = commandList[i];
76980 var method = command.method;
76981 var args = command.args;
76982
76983 if(!Array.isArray(args)) args = [];
76984
76985 // If any command has no method, refuse to bind:
76986 if(!method) {
76987 return false;
76988 }
76989 var bindings = exports.computeAPICommandBindings(gd, method, args);
76990
76991 // Right now, handle one and *only* one property being set:
76992 if(bindings.length !== 1) {
76993 return false;
76994 }
76995
76996 if(!refBinding) {
76997 refBinding = bindings[0];
76998 if(Array.isArray(refBinding.traces)) {
76999 refBinding.traces.sort();
77000 }
77001 } else {
77002 binding = bindings[0];
77003 if(binding.type !== refBinding.type) {
77004 return false;
77005 }
77006 if(binding.prop !== refBinding.prop) {
77007 return false;
77008 }
77009 if(Array.isArray(refBinding.traces)) {
77010 if(Array.isArray(binding.traces)) {
77011 binding.traces.sort();
77012 for(var j = 0; j < refBinding.traces.length; j++) {
77013 if(refBinding.traces[j] !== binding.traces[j]) {
77014 return false;
77015 }
77016 }
77017 } else {
77018 return false;
77019 }
77020 } else {
77021 if(binding.prop !== refBinding.prop) {
77022 return false;
77023 }
77024 }
77025 }
77026
77027 binding = bindings[0];
77028 var value = binding.value;
77029 if(Array.isArray(value)) {
77030 if(value.length === 1) {
77031 value = value[0];
77032 } else {
77033 return false;
77034 }
77035 }
77036 if(bindingsByValue) {
77037 bindingsByValue[value] = i;
77038 }
77039 }
77040
77041 return refBinding;
77042};
77043
77044function bindingValueHasChanged(gd, binding, cache) {
77045 var container, value, obj;
77046 var changed = false;
77047
77048 if(binding.type === 'data') {
77049 // If it's data, we need to get a trace. Based on the limited scope
77050 // of what we cover, we can just take the first trace from the list,
77051 // or otherwise just the first trace:
77052 container = gd._fullData[binding.traces !== null ? binding.traces[0] : 0];
77053 } else if(binding.type === 'layout') {
77054 container = gd._fullLayout;
77055 } else {
77056 return false;
77057 }
77058
77059 value = Lib.nestedProperty(container, binding.prop).get();
77060
77061 obj = cache[binding.type] = cache[binding.type] || {};
77062
77063 if(obj.hasOwnProperty(binding.prop)) {
77064 if(obj[binding.prop] !== value) {
77065 changed = true;
77066 }
77067 }
77068
77069 obj[binding.prop] = value;
77070
77071 return {
77072 changed: changed,
77073 value: value
77074 };
77075}
77076
77077/*
77078 * Execute an API command. There's really not much to this; it just provides
77079 * a common hook so that implementations don't need to be synchronized across
77080 * multiple components with the ability to invoke API commands.
77081 *
77082 * @param {string} method
77083 * The name of the plotly command to execute. Must be one of 'animate',
77084 * 'restyle', 'relayout', 'update'.
77085 * @param {array} args
77086 * A list of arguments passed to the API command
77087 */
77088exports.executeAPICommand = function(gd, method, args) {
77089 if(method === 'skip') return Promise.resolve();
77090
77091 var _method = Registry.apiMethodRegistry[method];
77092 var allArgs = [gd];
77093 if(!Array.isArray(args)) args = [];
77094
77095 for(var i = 0; i < args.length; i++) {
77096 allArgs.push(args[i]);
77097 }
77098
77099 return _method.apply(null, allArgs).catch(function(err) {
77100 Lib.warn('API call to Plotly.' + method + ' rejected.', err);
77101 return Promise.reject(err);
77102 });
77103};
77104
77105exports.computeAPICommandBindings = function(gd, method, args) {
77106 var bindings;
77107
77108 if(!Array.isArray(args)) args = [];
77109
77110 switch(method) {
77111 case 'restyle':
77112 bindings = computeDataBindings(gd, args);
77113 break;
77114 case 'relayout':
77115 bindings = computeLayoutBindings(gd, args);
77116 break;
77117 case 'update':
77118 bindings = computeDataBindings(gd, [args[0], args[2]])
77119 .concat(computeLayoutBindings(gd, [args[1]]));
77120 break;
77121 case 'animate':
77122 bindings = computeAnimateBindings(gd, args);
77123 break;
77124 default:
77125 // This is the case where intelligent logic about what affects
77126 // this command is not implemented. It causes no ill effects.
77127 // For example, addFrames simply won't bind to a control component.
77128 bindings = [];
77129 }
77130 return bindings;
77131};
77132
77133function computeAnimateBindings(gd, args) {
77134 // We'll assume that the only relevant modification an animation
77135 // makes that's meaningfully tracked is the frame:
77136 if(Array.isArray(args[0]) && args[0].length === 1 && ['string', 'number'].indexOf(typeof args[0][0]) !== -1) {
77137 return [{type: 'layout', prop: '_currentFrame', value: args[0][0].toString()}];
77138 } else {
77139 return [];
77140 }
77141}
77142
77143function computeLayoutBindings(gd, args) {
77144 var bindings = [];
77145
77146 var astr = args[0];
77147 var aobj = {};
77148 if(typeof astr === 'string') {
77149 aobj[astr] = args[1];
77150 } else if(Lib.isPlainObject(astr)) {
77151 aobj = astr;
77152 } else {
77153 return bindings;
77154 }
77155
77156 crawl(aobj, function(path, attrName, attr) {
77157 bindings.push({type: 'layout', prop: path, value: attr});
77158 }, '', 0);
77159
77160 return bindings;
77161}
77162
77163function computeDataBindings(gd, args) {
77164 var traces, astr, val, aobj;
77165 var bindings = [];
77166
77167 // Logic copied from Plotly.restyle:
77168 astr = args[0];
77169 val = args[1];
77170 traces = args[2];
77171 aobj = {};
77172 if(typeof astr === 'string') {
77173 aobj[astr] = val;
77174 } else if(Lib.isPlainObject(astr)) {
77175 // the 3-arg form
77176 aobj = astr;
77177
77178 if(traces === undefined) {
77179 traces = val;
77180 }
77181 } else {
77182 return bindings;
77183 }
77184
77185 if(traces === undefined) {
77186 // Explicitly assign this to null instead of undefined:
77187 traces = null;
77188 }
77189
77190 crawl(aobj, function(path, attrName, _attr) {
77191 var thisTraces;
77192 var attr;
77193
77194 if(Array.isArray(_attr)) {
77195 attr = _attr.slice();
77196
77197 var nAttr = Math.min(attr.length, gd.data.length);
77198 if(traces) {
77199 nAttr = Math.min(nAttr, traces.length);
77200 }
77201 thisTraces = [];
77202 for(var j = 0; j < nAttr; j++) {
77203 thisTraces[j] = traces ? traces[j] : j;
77204 }
77205 } else {
77206 attr = _attr;
77207 thisTraces = traces ? traces.slice() : null;
77208 }
77209
77210 // Convert [7] to just 7 when traces is null:
77211 if(thisTraces === null) {
77212 if(Array.isArray(attr)) {
77213 attr = attr[0];
77214 }
77215 } else if(Array.isArray(thisTraces)) {
77216 if(!Array.isArray(attr)) {
77217 var tmp = attr;
77218 attr = [];
77219 for(var i = 0; i < thisTraces.length; i++) {
77220 attr[i] = tmp;
77221 }
77222 }
77223 attr.length = Math.min(thisTraces.length, attr.length);
77224 }
77225
77226 bindings.push({
77227 type: 'data',
77228 prop: path,
77229 traces: thisTraces,
77230 value: attr
77231 });
77232 }, '', 0);
77233
77234 return bindings;
77235}
77236
77237function crawl(attrs, callback, path, depth) {
77238 Object.keys(attrs).forEach(function(attrName) {
77239 var attr = attrs[attrName];
77240
77241 if(attrName[0] === '_') return;
77242
77243 var thisPath = path + (depth > 0 ? '.' : '') + attrName;
77244
77245 if(Lib.isPlainObject(attr)) {
77246 crawl(attr, callback, thisPath, depth + 1);
77247 } else {
77248 // Only execute the callback on leaf nodes:
77249 callback(thisPath, attrName, attr);
77250 }
77251 });
77252}
77253
77254},{"../lib":287,"../registry":376}],362:[function(_dereq_,module,exports){
77255'use strict';
77256
77257var extendFlat = _dereq_('../lib/extend').extendFlat;
77258
77259/**
77260 * Make a xy domain attribute group
77261 *
77262 * @param {object} opts
77263 * @param {string}
77264 * opts.name: name to be inserted in the default description
77265 * @param {boolean}
77266 * opts.trace: set to true for trace containers
77267 * @param {string}
77268 * opts.editType: editType for all pieces
77269 * @param {boolean}
77270 * opts.noGridCell: set to true to omit `row` and `column`
77271 *
77272 * @param {object} extra
77273 * @param {string}
77274 * extra.description: extra description. N.B we use
77275 * a separate extra container to make it compatible with
77276 * the compress_attributes transform.
77277 *
77278 * @return {object} attributes object containing {x,y} as specified
77279 */
77280exports.attributes = function(opts, extra) {
77281 opts = opts || {};
77282 extra = extra || {};
77283
77284 var base = {
77285 valType: 'info_array',
77286 editType: opts.editType,
77287 items: [
77288 {valType: 'number', min: 0, max: 1, editType: opts.editType},
77289 {valType: 'number', min: 0, max: 1, editType: opts.editType}
77290 ],
77291 dflt: [0, 1]
77292 };
77293
77294 var namePart = opts.name ? opts.name + ' ' : '';
77295 var contPart = opts.trace ? 'trace ' : 'subplot ';
77296 var descPart = extra.description ? ' ' + extra.description : '';
77297
77298 var out = {
77299 x: extendFlat({}, base, {
77300 }),
77301 y: extendFlat({}, base, {
77302 }),
77303 editType: opts.editType
77304 };
77305
77306 if(!opts.noGridCell) {
77307 out.row = {
77308 valType: 'integer',
77309 min: 0,
77310 dflt: 0,
77311 editType: opts.editType,
77312 };
77313 out.column = {
77314 valType: 'integer',
77315 min: 0,
77316 dflt: 0,
77317 editType: opts.editType,
77318 };
77319 }
77320
77321 return out;
77322};
77323
77324exports.defaults = function(containerOut, layout, coerce, dfltDomains) {
77325 var dfltX = (dfltDomains && dfltDomains.x) || [0, 1];
77326 var dfltY = (dfltDomains && dfltDomains.y) || [0, 1];
77327
77328 var grid = layout.grid;
77329 if(grid) {
77330 var column = coerce('domain.column');
77331 if(column !== undefined) {
77332 if(column < grid.columns) dfltX = grid._domains.x[column];
77333 else delete containerOut.domain.column;
77334 }
77335
77336 var row = coerce('domain.row');
77337 if(row !== undefined) {
77338 if(row < grid.rows) dfltY = grid._domains.y[row];
77339 else delete containerOut.domain.row;
77340 }
77341 }
77342
77343 var x = coerce('domain.x', dfltX);
77344 var y = coerce('domain.y', dfltY);
77345
77346 // don't accept bad input data
77347 if(!(x[0] < x[1])) containerOut.domain.x = dfltX.slice();
77348 if(!(y[0] < y[1])) containerOut.domain.y = dfltY.slice();
77349};
77350
77351},{"../lib/extend":281}],363:[function(_dereq_,module,exports){
77352'use strict';
77353
77354/*
77355 * make a font attribute group
77356 *
77357 * @param {object} opts
77358 * @param {string}
77359 * opts.description: where & how this font is used
77360 * @param {optional bool} arrayOk:
77361 * should each part (family, size, color) be arrayOk? default false.
77362 * @param {string} editType:
77363 * the editType for all pieces of this font
77364 * @param {optional string} colorEditType:
77365 * a separate editType just for color
77366 *
77367 * @return {object} attributes object containing {family, size, color} as specified
77368 */
77369module.exports = function(opts) {
77370 var editType = opts.editType;
77371 var colorEditType = opts.colorEditType;
77372 if(colorEditType === undefined) colorEditType = editType;
77373 var attrs = {
77374 family: {
77375 valType: 'string',
77376 noBlank: true,
77377 strict: true,
77378 editType: editType,
77379 },
77380 size: {
77381 valType: 'number',
77382 min: 1,
77383 editType: editType
77384 },
77385 color: {
77386 valType: 'color',
77387 editType: colorEditType
77388 },
77389 editType: editType,
77390 // blank strings so compress_attributes can remove
77391 // TODO - that's uber hacky... better solution?
77392 };
77393
77394 if(opts.arrayOk) {
77395 attrs.family.arrayOk = true;
77396 attrs.size.arrayOk = true;
77397 attrs.color.arrayOk = true;
77398 }
77399
77400 return attrs;
77401};
77402
77403},{}],364:[function(_dereq_,module,exports){
77404'use strict';
77405
77406module.exports = {
77407 _isLinkedToArray: 'frames_entry',
77408
77409 group: {
77410 valType: 'string',
77411 },
77412 name: {
77413 valType: 'string',
77414 },
77415 traces: {
77416 valType: 'any',
77417 },
77418 baseframe: {
77419 valType: 'string',
77420 },
77421 data: {
77422 valType: 'any',
77423 },
77424 layout: {
77425 valType: 'any',
77426 }
77427};
77428
77429},{}],365:[function(_dereq_,module,exports){
77430'use strict';
77431
77432var Registry = _dereq_('../registry');
77433var SUBPLOT_PATTERN = _dereq_('./cartesian/constants').SUBPLOT_PATTERN;
77434
77435/**
77436 * Get calcdata trace(s) associated with a given subplot
77437 *
77438 * @param {array} calcData: as in gd.calcdata
77439 * @param {string} type: subplot type
77440 * @param {string} subplotId: subplot id to look for
77441 *
77442 * @return {array} array of calcdata traces
77443 */
77444exports.getSubplotCalcData = function(calcData, type, subplotId) {
77445 var basePlotModule = Registry.subplotsRegistry[type];
77446 if(!basePlotModule) return [];
77447
77448 var attr = basePlotModule.attr;
77449 var subplotCalcData = [];
77450
77451 for(var i = 0; i < calcData.length; i++) {
77452 var calcTrace = calcData[i];
77453 var trace = calcTrace[0].trace;
77454
77455 if(trace[attr] === subplotId) subplotCalcData.push(calcTrace);
77456 }
77457
77458 return subplotCalcData;
77459};
77460/**
77461 * Get calcdata trace(s) that can be plotted with a given module
77462 * NOTE: this isn't necessarily just exactly matching trace type,
77463 * if multiple trace types use the same plotting routine, they will be
77464 * collected here.
77465 * In order to not plot the same thing multiple times, we return two arrays,
77466 * the calcdata we *will* plot with this module, and the ones we *won't*
77467 *
77468 * @param {array} calcdata: as in gd.calcdata
77469 * @param {object|string|fn} arg1:
77470 * the plotting module, or its name, or its plot method
77471 *
77472 * @return {array[array]} [foundCalcdata, remainingCalcdata]
77473 */
77474exports.getModuleCalcData = function(calcdata, arg1) {
77475 var moduleCalcData = [];
77476 var remainingCalcData = [];
77477
77478 var plotMethod;
77479 if(typeof arg1 === 'string') {
77480 plotMethod = Registry.getModule(arg1).plot;
77481 } else if(typeof arg1 === 'function') {
77482 plotMethod = arg1;
77483 } else {
77484 plotMethod = arg1.plot;
77485 }
77486 if(!plotMethod) {
77487 return [moduleCalcData, calcdata];
77488 }
77489
77490 for(var i = 0; i < calcdata.length; i++) {
77491 var cd = calcdata[i];
77492 var trace = cd[0].trace;
77493 // N.B.
77494 // - 'legendonly' traces do not make it past here
77495 // - skip over 'visible' traces that got trimmed completely during calc transforms
77496 if(trace.visible !== true || trace._length === 0) continue;
77497
77498 // group calcdata trace not by 'module' (as the name of this function
77499 // would suggest), but by 'module plot method' so that if some traces
77500 // share the same module plot method (e.g. bar and histogram), we
77501 // only call it one!
77502 if(trace._module.plot === plotMethod) {
77503 moduleCalcData.push(cd);
77504 } else {
77505 remainingCalcData.push(cd);
77506 }
77507 }
77508
77509 return [moduleCalcData, remainingCalcData];
77510};
77511
77512/**
77513 * Get the data trace(s) associated with a given subplot.
77514 *
77515 * @param {array} data plotly full data array.
77516 * @param {string} type subplot type to look for.
77517 * @param {string} subplotId subplot id to look for.
77518 *
77519 * @return {array} list of trace objects.
77520 *
77521 */
77522exports.getSubplotData = function getSubplotData(data, type, subplotId) {
77523 if(!Registry.subplotsRegistry[type]) return [];
77524
77525 var attr = Registry.subplotsRegistry[type].attr;
77526 var subplotData = [];
77527 var trace, subplotX, subplotY;
77528
77529 if(type === 'gl2d') {
77530 var spmatch = subplotId.match(SUBPLOT_PATTERN);
77531 subplotX = 'x' + spmatch[1];
77532 subplotY = 'y' + spmatch[2];
77533 }
77534
77535 for(var i = 0; i < data.length; i++) {
77536 trace = data[i];
77537
77538 if(type === 'gl2d' && Registry.traceIs(trace, 'gl2d')) {
77539 if(trace[attr[0]] === subplotX && trace[attr[1]] === subplotY) {
77540 subplotData.push(trace);
77541 }
77542 } else {
77543 if(trace[attr] === subplotId) subplotData.push(trace);
77544 }
77545 }
77546
77547 return subplotData;
77548};
77549
77550},{"../registry":376,"./cartesian/constants":341}],366:[function(_dereq_,module,exports){
77551'use strict';
77552
77553function xformMatrix(m, v) {
77554 var out = [0, 0, 0, 0];
77555 var i, j;
77556
77557 for(i = 0; i < 4; ++i) {
77558 for(j = 0; j < 4; ++j) {
77559 out[j] += m[4 * i + j] * v[i];
77560 }
77561 }
77562
77563 return out;
77564}
77565
77566function project(camera, v) {
77567 var p = xformMatrix(camera.projection,
77568 xformMatrix(camera.view,
77569 xformMatrix(camera.model, [v[0], v[1], v[2], 1])));
77570 return p;
77571}
77572
77573module.exports = project;
77574
77575},{}],367:[function(_dereq_,module,exports){
77576'use strict';
77577
77578var fontAttrs = _dereq_('./font_attributes');
77579var animationAttrs = _dereq_('./animation_attributes');
77580var colorAttrs = _dereq_('../components/color/attributes');
77581var drawNewShapeAttrs = _dereq_('../components/shapes/draw_newshape/attributes');
77582var padAttrs = _dereq_('./pad_attributes');
77583var extendFlat = _dereq_('../lib/extend').extendFlat;
77584
77585var globalFont = fontAttrs({
77586 editType: 'calc',
77587});
77588globalFont.family.dflt = '"Open Sans", verdana, arial, sans-serif';
77589globalFont.size.dflt = 12;
77590globalFont.color.dflt = colorAttrs.defaultLine;
77591
77592module.exports = {
77593 font: globalFont,
77594 title: {
77595 text: {
77596 valType: 'string',
77597 editType: 'layoutstyle',
77598 },
77599 font: fontAttrs({
77600 editType: 'layoutstyle',
77601 }),
77602 xref: {
77603 valType: 'enumerated',
77604 dflt: 'container',
77605 values: ['container', 'paper'],
77606 editType: 'layoutstyle',
77607 },
77608 yref: {
77609 valType: 'enumerated',
77610 dflt: 'container',
77611 values: ['container', 'paper'],
77612 editType: 'layoutstyle',
77613 },
77614 x: {
77615 valType: 'number',
77616 min: 0,
77617 max: 1,
77618 dflt: 0.5,
77619 editType: 'layoutstyle',
77620 },
77621 y: {
77622 valType: 'number',
77623 min: 0,
77624 max: 1,
77625 dflt: 'auto',
77626 editType: 'layoutstyle',
77627 },
77628 xanchor: {
77629 valType: 'enumerated',
77630 dflt: 'auto',
77631 values: ['auto', 'left', 'center', 'right'],
77632 editType: 'layoutstyle',
77633 },
77634 yanchor: {
77635 valType: 'enumerated',
77636 dflt: 'auto',
77637 values: ['auto', 'top', 'middle', 'bottom'],
77638 editType: 'layoutstyle',
77639 },
77640 pad: extendFlat(padAttrs({editType: 'layoutstyle'}), {
77641 }),
77642 editType: 'layoutstyle'
77643 },
77644 uniformtext: {
77645 mode: {
77646 valType: 'enumerated',
77647 values: [false, 'hide', 'show'],
77648 dflt: false,
77649 editType: 'plot',
77650 },
77651 minsize: {
77652 valType: 'number',
77653 min: 0,
77654 dflt: 0,
77655 editType: 'plot',
77656 },
77657 editType: 'plot'
77658 },
77659 autosize: {
77660 valType: 'boolean',
77661 dflt: false,
77662 // autosize, width, and height get special editType treatment in _relayout
77663 // so we can handle noop resizes more efficiently
77664 editType: 'none',
77665 },
77666 width: {
77667 valType: 'number',
77668 min: 10,
77669 dflt: 700,
77670 editType: 'plot',
77671 },
77672 height: {
77673 valType: 'number',
77674 min: 10,
77675 dflt: 450,
77676 editType: 'plot',
77677 },
77678 margin: {
77679 l: {
77680 valType: 'number',
77681 min: 0,
77682 dflt: 80,
77683 editType: 'plot',
77684 },
77685 r: {
77686 valType: 'number',
77687 min: 0,
77688 dflt: 80,
77689 editType: 'plot',
77690 },
77691 t: {
77692 valType: 'number',
77693 min: 0,
77694 dflt: 100,
77695 editType: 'plot',
77696 },
77697 b: {
77698 valType: 'number',
77699 min: 0,
77700 dflt: 80,
77701 editType: 'plot',
77702 },
77703 pad: {
77704 valType: 'number',
77705 min: 0,
77706 dflt: 0,
77707 editType: 'plot',
77708 },
77709 autoexpand: {
77710 valType: 'boolean',
77711 dflt: true,
77712 editType: 'plot',
77713 },
77714 editType: 'plot'
77715 },
77716 computed: {
77717 valType: 'any',
77718 editType: 'none',
77719 },
77720 paper_bgcolor: {
77721 valType: 'color',
77722 dflt: colorAttrs.background,
77723 editType: 'plot',
77724 },
77725 plot_bgcolor: {
77726 // defined here, but set in cartesian.supplyLayoutDefaults
77727 // because it needs to know if there are (2D) axes or not
77728 valType: 'color',
77729 dflt: colorAttrs.background,
77730 editType: 'layoutstyle',
77731 },
77732 autotypenumbers: {
77733 valType: 'enumerated',
77734 values: ['convert types', 'strict'],
77735 dflt: 'convert types',
77736 editType: 'calc',
77737 },
77738 separators: {
77739 valType: 'string',
77740 editType: 'plot',
77741 },
77742 hidesources: {
77743 valType: 'boolean',
77744 dflt: false,
77745 editType: 'plot',
77746 },
77747 showlegend: {
77748 // handled in legend.supplyLayoutDefaults
77749 // but included here because it's not in the legend object
77750 valType: 'boolean',
77751 editType: 'legend',
77752 },
77753 colorway: {
77754 valType: 'colorlist',
77755 dflt: colorAttrs.defaults,
77756 editType: 'calc',
77757 },
77758 datarevision: {
77759 valType: 'any',
77760 editType: 'calc',
77761 },
77762 uirevision: {
77763 valType: 'any',
77764 editType: 'none',
77765 },
77766 editrevision: {
77767 valType: 'any',
77768 editType: 'none',
77769 },
77770 selectionrevision: {
77771 valType: 'any',
77772 editType: 'none',
77773 },
77774 template: {
77775 valType: 'any',
77776 editType: 'calc',
77777 },
77778
77779 newshape: drawNewShapeAttrs.newshape,
77780 activeshape: drawNewShapeAttrs.activeshape,
77781
77782 meta: {
77783 valType: 'any',
77784 arrayOk: true,
77785 editType: 'plot',
77786 },
77787
77788 transition: extendFlat({}, animationAttrs.transition, {
77789 editType: 'none'
77790 }),
77791 _deprecated: {
77792 title: {
77793 valType: 'string',
77794 editType: 'layoutstyle',
77795 },
77796 titlefont: fontAttrs({
77797 editType: 'layoutstyle',
77798 })
77799 }
77800};
77801
77802},{"../components/color/attributes":156,"../components/shapes/draw_newshape/attributes":242,"../lib/extend":281,"./animation_attributes":328,"./font_attributes":363,"./pad_attributes":368}],368:[function(_dereq_,module,exports){
77803'use strict';
77804
77805/**
77806 * Creates a set of padding attributes.
77807 *
77808 * @param {object} opts
77809 * @param {string} editType:
77810 * the editType for all pieces of this padding definition
77811 *
77812 * @return {object} attributes object containing {t, r, b, l} as specified
77813 */
77814module.exports = function(opts) {
77815 var editType = opts.editType;
77816 return {
77817 t: {
77818 valType: 'number',
77819 dflt: 0,
77820 editType: editType,
77821 },
77822 r: {
77823 valType: 'number',
77824 dflt: 0,
77825 editType: editType,
77826 },
77827 b: {
77828 valType: 'number',
77829 dflt: 0,
77830 editType: editType,
77831 },
77832 l: {
77833 valType: 'number',
77834 dflt: 0,
77835 editType: editType,
77836 },
77837 editType: editType
77838 };
77839};
77840
77841},{}],369:[function(_dereq_,module,exports){
77842'use strict';
77843
77844var d3 = _dereq_('@plotly/d3');
77845var timeFormatLocale = _dereq_('d3-time-format').timeFormatLocale;
77846var formatLocale = _dereq_('d3-format').formatLocale;
77847var isNumeric = _dereq_('fast-isnumeric');
77848
77849var Registry = _dereq_('../registry');
77850var PlotSchema = _dereq_('../plot_api/plot_schema');
77851var Template = _dereq_('../plot_api/plot_template');
77852var Lib = _dereq_('../lib');
77853var Color = _dereq_('../components/color');
77854var BADNUM = _dereq_('../constants/numerical').BADNUM;
77855
77856var axisIDs = _dereq_('./cartesian/axis_ids');
77857var clearSelect = _dereq_('./cartesian/handle_outline').clearSelect;
77858
77859var animationAttrs = _dereq_('./animation_attributes');
77860var frameAttrs = _dereq_('./frame_attributes');
77861
77862var getModuleCalcData = _dereq_('../plots/get_data').getModuleCalcData;
77863
77864var relinkPrivateKeys = Lib.relinkPrivateKeys;
77865var _ = Lib._;
77866
77867var plots = module.exports = {};
77868
77869// Expose registry methods on Plots for backward-compatibility
77870Lib.extendFlat(plots, Registry);
77871
77872plots.attributes = _dereq_('./attributes');
77873plots.attributes.type.values = plots.allTypes;
77874plots.fontAttrs = _dereq_('./font_attributes');
77875plots.layoutAttributes = _dereq_('./layout_attributes');
77876
77877// TODO make this a plot attribute?
77878plots.fontWeight = 'normal';
77879
77880var transformsRegistry = plots.transformsRegistry;
77881
77882var commandModule = _dereq_('./command');
77883plots.executeAPICommand = commandModule.executeAPICommand;
77884plots.computeAPICommandBindings = commandModule.computeAPICommandBindings;
77885plots.manageCommandObserver = commandModule.manageCommandObserver;
77886plots.hasSimpleAPICommandBindings = commandModule.hasSimpleAPICommandBindings;
77887
77888// in some cases the browser doesn't seem to know how big
77889// the text is at first, so it needs to draw it,
77890// then wait a little, then draw it again
77891plots.redrawText = function(gd) {
77892 gd = Lib.getGraphDiv(gd);
77893
77894 return new Promise(function(resolve) {
77895 setTimeout(function() {
77896 if(!gd._fullLayout) return;
77897 Registry.getComponentMethod('annotations', 'draw')(gd);
77898 Registry.getComponentMethod('legend', 'draw')(gd);
77899 Registry.getComponentMethod('colorbar', 'draw')(gd);
77900 resolve(plots.previousPromises(gd));
77901 }, 300);
77902 });
77903};
77904
77905// resize plot about the container size
77906plots.resize = function(gd) {
77907 gd = Lib.getGraphDiv(gd);
77908
77909 var resolveLastResize;
77910 var p = new Promise(function(resolve, reject) {
77911 if(!gd || Lib.isHidden(gd)) {
77912 reject(new Error('Resize must be passed a displayed plot div element.'));
77913 }
77914
77915 if(gd._redrawTimer) clearTimeout(gd._redrawTimer);
77916 if(gd._resolveResize) resolveLastResize = gd._resolveResize;
77917 gd._resolveResize = resolve;
77918
77919 gd._redrawTimer = setTimeout(function() {
77920 // return if there is nothing to resize or is hidden
77921 if(!gd.layout || (gd.layout.width && gd.layout.height) || Lib.isHidden(gd)) {
77922 resolve(gd);
77923 return;
77924 }
77925
77926 delete gd.layout.width;
77927 delete gd.layout.height;
77928
77929 // autosizing doesn't count as a change that needs saving
77930 var oldchanged = gd.changed;
77931
77932 // nor should it be included in the undo queue
77933 gd.autoplay = true;
77934
77935 Registry.call('relayout', gd, {autosize: true}).then(function() {
77936 gd.changed = oldchanged;
77937 // Only resolve if a new call hasn't been made!
77938 if(gd._resolveResize === resolve) {
77939 delete gd._resolveResize;
77940 resolve(gd);
77941 }
77942 });
77943 }, 100);
77944 });
77945
77946 if(resolveLastResize) resolveLastResize(p);
77947 return p;
77948};
77949
77950
77951// for use in Lib.syncOrAsync, check if there are any
77952// pending promises in this plot and wait for them
77953plots.previousPromises = function(gd) {
77954 if((gd._promises || []).length) {
77955 return Promise.all(gd._promises)
77956 .then(function() { gd._promises = []; });
77957 }
77958};
77959
77960/**
77961 * Adds the 'Edit chart' link.
77962 * Note that now _doPlot calls this so it can regenerate whenever it replots
77963 *
77964 * Add source links to your graph inside the 'showSources' config argument.
77965 */
77966plots.addLinks = function(gd) {
77967 // Do not do anything if showLink and showSources are not set to true in config
77968 if(!gd._context.showLink && !gd._context.showSources) return;
77969
77970 var fullLayout = gd._fullLayout;
77971
77972 var linkContainer = Lib.ensureSingle(fullLayout._paper, 'text', 'js-plot-link-container', function(s) {
77973 s.style({
77974 'font-family': '"Open Sans", Arial, sans-serif',
77975 'font-size': '12px',
77976 'fill': Color.defaultLine,
77977 'pointer-events': 'all'
77978 })
77979 .each(function() {
77980 var links = d3.select(this);
77981 links.append('tspan').classed('js-link-to-tool', true);
77982 links.append('tspan').classed('js-link-spacer', true);
77983 links.append('tspan').classed('js-sourcelinks', true);
77984 });
77985 });
77986
77987 // The text node inside svg
77988 var text = linkContainer.node();
77989 var attrs = {y: fullLayout._paper.attr('height') - 9};
77990
77991 // If text's width is bigger than the layout
77992 // Check that text is a child node or document.body
77993 // because otherwise IE/Edge might throw an exception
77994 // when calling getComputedTextLength().
77995 // Apparently offsetParent is null for invisibles.
77996 if(document.body.contains(text) && text.getComputedTextLength() >= (fullLayout.width - 20)) {
77997 // Align the text at the left
77998 attrs['text-anchor'] = 'start';
77999 attrs.x = 5;
78000 } else {
78001 // Align the text at the right
78002 attrs['text-anchor'] = 'end';
78003 attrs.x = fullLayout._paper.attr('width') - 7;
78004 }
78005
78006 linkContainer.attr(attrs);
78007
78008 var toolspan = linkContainer.select('.js-link-to-tool');
78009 var spacespan = linkContainer.select('.js-link-spacer');
78010 var sourcespan = linkContainer.select('.js-sourcelinks');
78011
78012 if(gd._context.showSources) gd._context.showSources(gd);
78013
78014 // 'view in plotly' link for embedded plots
78015 if(gd._context.showLink) positionPlayWithData(gd, toolspan);
78016
78017 // separator if we have both sources and tool link
78018 spacespan.text((toolspan.text() && sourcespan.text()) ? ' - ' : '');
78019};
78020
78021// note that now this function is only adding the brand in
78022// iframes and 3rd-party apps
78023function positionPlayWithData(gd, container) {
78024 container.text('');
78025 var link = container.append('a')
78026 .attr({
78027 'xlink:xlink:href': '#',
78028 'class': 'link--impt link--embedview',
78029 'font-weight': 'bold'
78030 })
78031 .text(gd._context.linkText + ' ' + String.fromCharCode(187));
78032
78033 if(gd._context.sendData) {
78034 link.on('click', function() {
78035 plots.sendDataToCloud(gd);
78036 });
78037 } else {
78038 var path = window.location.pathname.split('/');
78039 var query = window.location.search;
78040 link.attr({
78041 'xlink:xlink:show': 'new',
78042 'xlink:xlink:href': '/' + path[2].split('.')[0] + '/' + path[1] + query
78043 });
78044 }
78045}
78046
78047plots.sendDataToCloud = function(gd) {
78048 var baseUrl = (window.PLOTLYENV || {}).BASE_URL || gd._context.plotlyServerURL;
78049 if(!baseUrl) return;
78050
78051 gd.emit('plotly_beforeexport');
78052
78053 var hiddenformDiv = d3.select(gd)
78054 .append('div')
78055 .attr('id', 'hiddenform')
78056 .style('display', 'none');
78057
78058 var hiddenform = hiddenformDiv
78059 .append('form')
78060 .attr({
78061 action: baseUrl + '/external',
78062 method: 'post',
78063 target: '_blank'
78064 });
78065
78066 var hiddenformInput = hiddenform
78067 .append('input')
78068 .attr({
78069 type: 'text',
78070 name: 'data'
78071 });
78072
78073 hiddenformInput.node().value = plots.graphJson(gd, false, 'keepdata');
78074 hiddenform.node().submit();
78075 hiddenformDiv.remove();
78076
78077 gd.emit('plotly_afterexport');
78078 return false;
78079};
78080
78081var d3FormatKeys = [
78082 'days', 'shortDays', 'months', 'shortMonths', 'periods',
78083 'dateTime', 'date', 'time',
78084 'decimal', 'thousands', 'grouping', 'currency'
78085];
78086
78087var extraFormatKeys = [
78088 'year', 'month', 'dayMonth', 'dayMonthYear'
78089];
78090
78091/*
78092 * Fill in default values
78093 * @param {DOM element} gd
78094 * @param {object} opts
78095 * @param {boolean} opts.skipUpdateCalc: normally if the existing gd.calcdata looks
78096 * compatible with the new gd._fullData we finish by linking the new _fullData traces
78097 * to the old gd.calcdata, so it's correctly set if we're not going to recalc. But also,
78098 * if there are calcTransforms on the trace, we first remap data arrays from the old full
78099 * trace into the new one. Use skipUpdateCalc to defer this (needed by Plotly.react)
78100 *
78101 * gd.data, gd.layout:
78102 * are precisely what the user specified (except as modified by cleanData/cleanLayout),
78103 * these fields shouldn't be modified (except for filling in some auto values)
78104 * nor used directly after the supply defaults step.
78105 *
78106 * gd._fullData, gd._fullLayout:
78107 * are complete descriptions of how to draw the plot,
78108 * use these fields in all required computations.
78109 *
78110 * gd._fullLayout._modules
78111 * is a list of all the trace modules required to draw the plot.
78112 *
78113 * gd._fullLayout._visibleModules
78114 * subset of _modules, a list of modules corresponding to visible:true traces.
78115 *
78116 * gd._fullLayout._basePlotModules
78117 * is a list of all the plot modules required to draw the plot.
78118 *
78119 * gd._fullLayout._transformModules
78120 * is a list of all the transform modules invoked.
78121 *
78122 */
78123plots.supplyDefaults = function(gd, opts) {
78124 var skipUpdateCalc = opts && opts.skipUpdateCalc;
78125 var oldFullLayout = gd._fullLayout || {};
78126
78127 if(oldFullLayout._skipDefaults) {
78128 delete oldFullLayout._skipDefaults;
78129 return;
78130 }
78131
78132 var newFullLayout = gd._fullLayout = {};
78133 var newLayout = gd.layout || {};
78134
78135 var oldFullData = gd._fullData || [];
78136 var newFullData = gd._fullData = [];
78137 var newData = gd.data || [];
78138
78139 var oldCalcdata = gd.calcdata || [];
78140
78141 var context = gd._context || {};
78142
78143 var i;
78144
78145 // Create all the storage space for frames, but only if doesn't already exist
78146 if(!gd._transitionData) plots.createTransitionData(gd);
78147
78148 // So we only need to do this once (and since we have gd here)
78149 // get the translated placeholder titles.
78150 // These ones get used as default values so need to be known at supplyDefaults
78151 // others keep their blank defaults but render the placeholder as desired later
78152 // TODO: make these work the same way, only inserting the placeholder text at draw time?
78153 // The challenge is that this has slightly different behavior right now in editable mode:
78154 // using the placeholder as default makes this text permanently (but lightly) visible,
78155 // but explicit '' for these titles gives you a placeholder that's hidden until you mouse
78156 // over it - so you're not distracted by it if you really don't want a title, but if you do
78157 // and you're new to plotly you may not be able to find it.
78158 // When editable=false the two behave the same, no title is drawn.
78159 newFullLayout._dfltTitle = {
78160 plot: _(gd, 'Click to enter Plot title'),
78161 x: _(gd, 'Click to enter X axis title'),
78162 y: _(gd, 'Click to enter Y axis title'),
78163 colorbar: _(gd, 'Click to enter Colorscale title'),
78164 annotation: _(gd, 'new text')
78165 };
78166 newFullLayout._traceWord = _(gd, 'trace');
78167
78168 var formatObj = getFormatObj(gd, d3FormatKeys);
78169
78170 // stash the token from context so mapbox subplots can use it as default
78171 newFullLayout._mapboxAccessToken = context.mapboxAccessToken;
78172
78173 // first fill in what we can of layout without looking at data
78174 // because fullData needs a few things from layout
78175 if(oldFullLayout._initialAutoSizeIsDone) {
78176 // coerce the updated layout while preserving width and height
78177 var oldWidth = oldFullLayout.width;
78178 var oldHeight = oldFullLayout.height;
78179
78180 plots.supplyLayoutGlobalDefaults(newLayout, newFullLayout, formatObj);
78181
78182 if(!newLayout.width) newFullLayout.width = oldWidth;
78183 if(!newLayout.height) newFullLayout.height = oldHeight;
78184 plots.sanitizeMargins(newFullLayout);
78185 } else {
78186 // coerce the updated layout and autosize if needed
78187 plots.supplyLayoutGlobalDefaults(newLayout, newFullLayout, formatObj);
78188
78189 var missingWidthOrHeight = (!newLayout.width || !newLayout.height);
78190 var autosize = newFullLayout.autosize;
78191 var autosizable = context.autosizable;
78192 var initialAutoSize = missingWidthOrHeight && (autosize || autosizable);
78193
78194 if(initialAutoSize) plots.plotAutoSize(gd, newLayout, newFullLayout);
78195 else if(missingWidthOrHeight) plots.sanitizeMargins(newFullLayout);
78196
78197 // for backwards-compatibility with Plotly v1.x.x
78198 if(!autosize && missingWidthOrHeight) {
78199 newLayout.width = newFullLayout.width;
78200 newLayout.height = newFullLayout.height;
78201 }
78202 }
78203
78204 newFullLayout._d3locale = getFormatter(formatObj, newFullLayout.separators);
78205 newFullLayout._extraFormat = getFormatObj(gd, extraFormatKeys);
78206
78207 newFullLayout._initialAutoSizeIsDone = true;
78208
78209 // keep track of how many traces are inputted
78210 newFullLayout._dataLength = newData.length;
78211
78212 // clear the lists of trace and baseplot modules, and subplots
78213 newFullLayout._modules = [];
78214 newFullLayout._visibleModules = [];
78215 newFullLayout._basePlotModules = [];
78216 var subplots = newFullLayout._subplots = emptySubplotLists();
78217
78218 // initialize axis and subplot hash objects for splom-generated grids
78219 var splomAxes = newFullLayout._splomAxes = {x: {}, y: {}};
78220 var splomSubplots = newFullLayout._splomSubplots = {};
78221 // initialize splom grid defaults
78222 newFullLayout._splomGridDflt = {};
78223
78224 // for stacked area traces to share config across traces
78225 newFullLayout._scatterStackOpts = {};
78226 // for the first scatter trace on each subplot (so it knows tonext->tozero)
78227 newFullLayout._firstScatter = {};
78228 // for grouped bar/box/violin trace to share config across traces
78229 newFullLayout._alignmentOpts = {};
78230 // track color axes referenced in the data
78231 newFullLayout._colorAxes = {};
78232
78233 // for traces to request a default rangeslider on their x axes
78234 // eg set `_requestRangeslider.x2 = true` for xaxis2
78235 newFullLayout._requestRangeslider = {};
78236
78237 // pull uids from old data to use as new defaults
78238 newFullLayout._traceUids = getTraceUids(oldFullData, newData);
78239
78240 // then do the data
78241 newFullLayout._globalTransforms = (gd._context || {}).globalTransforms;
78242 plots.supplyDataDefaults(newData, newFullData, newLayout, newFullLayout);
78243
78244 // redo grid size defaults with info about splom x/y axes,
78245 // and fill in generated cartesian axes and subplots
78246 var splomXa = Object.keys(splomAxes.x);
78247 var splomYa = Object.keys(splomAxes.y);
78248 if(splomXa.length > 1 && splomYa.length > 1) {
78249 Registry.getComponentMethod('grid', 'sizeDefaults')(newLayout, newFullLayout);
78250
78251 for(i = 0; i < splomXa.length; i++) {
78252 Lib.pushUnique(subplots.xaxis, splomXa[i]);
78253 }
78254 for(i = 0; i < splomYa.length; i++) {
78255 Lib.pushUnique(subplots.yaxis, splomYa[i]);
78256 }
78257 for(var k in splomSubplots) {
78258 Lib.pushUnique(subplots.cartesian, k);
78259 }
78260 }
78261
78262 // attach helper method to check whether a plot type is present on graph
78263 newFullLayout._has = plots._hasPlotType.bind(newFullLayout);
78264
78265 if(oldFullData.length === newFullData.length) {
78266 for(i = 0; i < newFullData.length; i++) {
78267 relinkPrivateKeys(newFullData[i], oldFullData[i]);
78268 }
78269 }
78270
78271 // finally, fill in the pieces of layout that may need to look at data
78272 plots.supplyLayoutModuleDefaults(newLayout, newFullLayout, newFullData, gd._transitionData);
78273
78274 // Special cases that introduce interactions between traces.
78275 // This is after relinkPrivateKeys so we can use those in crossTraceDefaults
78276 // and after layout module defaults, so we can use eg barmode
78277 var _modules = newFullLayout._visibleModules;
78278 var crossTraceDefaultsFuncs = [];
78279 for(i = 0; i < _modules.length; i++) {
78280 var funci = _modules[i].crossTraceDefaults;
78281 // some trace types share crossTraceDefaults (ie histogram2d, histogram2dcontour)
78282 if(funci) Lib.pushUnique(crossTraceDefaultsFuncs, funci);
78283 }
78284 for(i = 0; i < crossTraceDefaultsFuncs.length; i++) {
78285 crossTraceDefaultsFuncs[i](newFullData, newFullLayout);
78286 }
78287
78288 // turn on flag to optimize large splom-only graphs
78289 // mostly by omitting SVG layers during Cartesian.drawFramework
78290 newFullLayout._hasOnlyLargeSploms = (
78291 newFullLayout._basePlotModules.length === 1 &&
78292 newFullLayout._basePlotModules[0].name === 'splom' &&
78293 splomXa.length > 15 &&
78294 splomYa.length > 15 &&
78295 newFullLayout.shapes.length === 0 &&
78296 newFullLayout.images.length === 0
78297 );
78298
78299 // relink / initialize subplot axis objects
78300 plots.linkSubplots(newFullData, newFullLayout, oldFullData, oldFullLayout);
78301
78302 // clean subplots and other artifacts from previous plot calls
78303 plots.cleanPlot(newFullData, newFullLayout, oldFullData, oldFullLayout);
78304
78305 var hadGL2D = !!(oldFullLayout._has && oldFullLayout._has('gl2d'));
78306 var hasGL2D = !!(newFullLayout._has && newFullLayout._has('gl2d'));
78307 var hadCartesian = !!(oldFullLayout._has && oldFullLayout._has('cartesian'));
78308 var hasCartesian = !!(newFullLayout._has && newFullLayout._has('cartesian'));
78309 var hadBgLayer = hadCartesian || hadGL2D;
78310 var hasBgLayer = hasCartesian || hasGL2D;
78311 if(hadBgLayer && !hasBgLayer) {
78312 // remove bgLayer
78313 oldFullLayout._bgLayer.remove();
78314 } else if(hasBgLayer && !hadBgLayer) {
78315 // create bgLayer
78316 newFullLayout._shouldCreateBgLayer = true;
78317 }
78318
78319 // clear selection outline until we implement persistent selection,
78320 // don't clear them though when drag handlers (e.g. listening to
78321 // `plotly_selecting`) update the graph.
78322 // we should try to come up with a better solution when implementing
78323 // https://github.com/plotly/plotly.js/issues/1851
78324 if(oldFullLayout._zoomlayer && !gd._dragging) {
78325 clearSelect({ // mock old gd
78326 _fullLayout: oldFullLayout
78327 });
78328 }
78329
78330
78331 // fill in meta helpers
78332 fillMetaTextHelpers(newFullData, newFullLayout);
78333
78334 // relink functions and _ attributes to promote consistency between plots
78335 relinkPrivateKeys(newFullLayout, oldFullLayout);
78336
78337 // colorscale crossTraceDefaults needs newFullLayout with relinked keys
78338 Registry.getComponentMethod('colorscale', 'crossTraceDefaults')(newFullData, newFullLayout);
78339
78340 // For persisting GUI-driven changes in layout
78341 // _preGUI and _tracePreGUI were already copied over in relinkPrivateKeys
78342 if(!newFullLayout._preGUI) newFullLayout._preGUI = {};
78343 // track trace GUI changes by uid rather than by trace index
78344 if(!newFullLayout._tracePreGUI) newFullLayout._tracePreGUI = {};
78345 var tracePreGUI = newFullLayout._tracePreGUI;
78346 var uids = {};
78347 var uid;
78348 for(uid in tracePreGUI) uids[uid] = 'old';
78349 for(i = 0; i < newFullData.length; i++) {
78350 uid = newFullData[i]._fullInput.uid;
78351 if(!uids[uid]) tracePreGUI[uid] = {};
78352 uids[uid] = 'new';
78353 }
78354 for(uid in uids) {
78355 if(uids[uid] === 'old') delete tracePreGUI[uid];
78356 }
78357
78358 // set up containers for margin calculations
78359 initMargins(newFullLayout);
78360
78361 // collect and do some initial calculations for rangesliders
78362 Registry.getComponentMethod('rangeslider', 'makeData')(newFullLayout);
78363
78364 // update object references in calcdata
78365 if(!skipUpdateCalc && oldCalcdata.length === newFullData.length) {
78366 plots.supplyDefaultsUpdateCalc(oldCalcdata, newFullData);
78367 }
78368};
78369
78370plots.supplyDefaultsUpdateCalc = function(oldCalcdata, newFullData) {
78371 for(var i = 0; i < newFullData.length; i++) {
78372 var newTrace = newFullData[i];
78373 var cd0 = (oldCalcdata[i] || [])[0];
78374 if(cd0 && cd0.trace) {
78375 var oldTrace = cd0.trace;
78376 if(oldTrace._hasCalcTransform) {
78377 var arrayAttrs = oldTrace._arrayAttrs;
78378 var j, astr, oldArrayVal;
78379
78380 for(j = 0; j < arrayAttrs.length; j++) {
78381 astr = arrayAttrs[j];
78382 oldArrayVal = Lib.nestedProperty(oldTrace, astr).get().slice();
78383 Lib.nestedProperty(newTrace, astr).set(oldArrayVal);
78384 }
78385 }
78386 cd0.trace = newTrace;
78387 }
78388 }
78389};
78390
78391/**
78392 * Create a list of uid strings satisfying (in this order of importance):
78393 * 1. all unique, all strings
78394 * 2. matches input uids if provided
78395 * 3. matches previous data uids
78396 */
78397function getTraceUids(oldFullData, newData) {
78398 var len = newData.length;
78399 var oldFullInput = [];
78400 var i, prevFullInput;
78401 for(i = 0; i < oldFullData.length; i++) {
78402 var thisFullInput = oldFullData[i]._fullInput;
78403 if(thisFullInput !== prevFullInput) oldFullInput.push(thisFullInput);
78404 prevFullInput = thisFullInput;
78405 }
78406 var oldLen = oldFullInput.length;
78407 var out = new Array(len);
78408 var seenUids = {};
78409
78410 function setUid(uid, i) {
78411 out[i] = uid;
78412 seenUids[uid] = 1;
78413 }
78414
78415 function tryUid(uid, i) {
78416 if(uid && typeof uid === 'string' && !seenUids[uid]) {
78417 setUid(uid, i);
78418 return true;
78419 }
78420 }
78421
78422 for(i = 0; i < len; i++) {
78423 var newUid = newData[i].uid;
78424 if(typeof newUid === 'number') newUid = String(newUid);
78425
78426 if(tryUid(newUid, i)) continue;
78427 if(i < oldLen && tryUid(oldFullInput[i].uid, i)) continue;
78428 setUid(Lib.randstr(seenUids), i);
78429 }
78430
78431 return out;
78432}
78433
78434/**
78435 * Make a container for collecting subplots we need to display.
78436 *
78437 * Finds all subplot types we need to enumerate once and caches it,
78438 * but makes a new output object each time.
78439 * Single-trace subplots (which have no `id`) such as pie, table, etc
78440 * do not need to be collected because we just draw all visible traces.
78441 */
78442function emptySubplotLists() {
78443 var collectableSubplotTypes = Registry.collectableSubplotTypes;
78444 var out = {};
78445 var i, j;
78446
78447 if(!collectableSubplotTypes) {
78448 collectableSubplotTypes = [];
78449
78450 var subplotsRegistry = Registry.subplotsRegistry;
78451
78452 for(var subplotType in subplotsRegistry) {
78453 var subplotModule = subplotsRegistry[subplotType];
78454 var subplotAttr = subplotModule.attr;
78455
78456 if(subplotAttr) {
78457 collectableSubplotTypes.push(subplotType);
78458
78459 // special case, currently just for cartesian:
78460 // we need to enumerate axes, not just subplots
78461 if(Array.isArray(subplotAttr)) {
78462 for(j = 0; j < subplotAttr.length; j++) {
78463 Lib.pushUnique(collectableSubplotTypes, subplotAttr[j]);
78464 }
78465 }
78466 }
78467 }
78468 }
78469
78470 for(i = 0; i < collectableSubplotTypes.length; i++) {
78471 out[collectableSubplotTypes[i]] = [];
78472 }
78473 return out;
78474}
78475
78476/**
78477 * getFormatObj: use _context to get the format object from locale.
78478 * Used to get d3.locale argument object and extraFormat argument object
78479 *
78480 * Regarding d3.locale argument :
78481 * decimal and thousands can be overridden later by layout.separators
78482 * grouping and currency are not presently used by our automatic number
78483 * formatting system but can be used by custom formats.
78484 *
78485 * @returns {object} d3.locale format object
78486 */
78487function getFormatObj(gd, formatKeys) {
78488 var locale = gd._context.locale;
78489 if(!locale) locale = 'en-US';
78490
78491 var formatDone = false;
78492 var formatObj = {};
78493
78494 function includeFormat(newFormat) {
78495 var formatFinished = true;
78496 for(var i = 0; i < formatKeys.length; i++) {
78497 var formatKey = formatKeys[i];
78498 if(!formatObj[formatKey]) {
78499 if(newFormat[formatKey]) {
78500 formatObj[formatKey] = newFormat[formatKey];
78501 } else formatFinished = false;
78502 }
78503 }
78504 if(formatFinished) formatDone = true;
78505 }
78506
78507 // same as localize, look for format parts in each format spec in the chain
78508 for(var i = 0; i < 2; i++) {
78509 var locales = gd._context.locales;
78510 for(var j = 0; j < 2; j++) {
78511 var formatj = (locales[locale] || {}).format;
78512 if(formatj) {
78513 includeFormat(formatj);
78514 if(formatDone) break;
78515 }
78516 locales = Registry.localeRegistry;
78517 }
78518
78519 var baseLocale = locale.split('-')[0];
78520 if(formatDone || baseLocale === locale) break;
78521 locale = baseLocale;
78522 }
78523
78524 // lastly pick out defaults from english (non-US, as DMY is so much more common)
78525 if(!formatDone) includeFormat(Registry.localeRegistry.en.format);
78526
78527 return formatObj;
78528}
78529
78530/**
78531 * getFormatter: combine the final separators with the locale formatting object
78532 * we pulled earlier to generate number and time formatters
78533 * TODO: remove separators in v3, only use locale, so we don't need this step?
78534 *
78535 * @param {object} formatObj: d3.locale format object
78536 * @param {string} separators: length-2 string to override decimal and thousands
78537 * separators in number formatting
78538 *
78539 * @returns {object} {numberFormat, timeFormat} d3 formatter factory functions
78540 * for numbers and time
78541 */
78542function getFormatter(formatObj, separators) {
78543 formatObj.decimal = separators.charAt(0);
78544 formatObj.thousands = separators.charAt(1);
78545
78546 return {
78547 numberFormat: function(formatStr) {
78548 try {
78549 formatStr = formatLocale(formatObj).format(
78550 Lib.adjustFormat(formatStr)
78551 );
78552 } catch(e) {
78553 Lib.warnBadFormat(formatStr);
78554 return Lib.noFormat;
78555 }
78556
78557 return formatStr;
78558 },
78559 timeFormat: timeFormatLocale(formatObj).utcFormat
78560 };
78561}
78562
78563function fillMetaTextHelpers(newFullData, newFullLayout) {
78564 var _meta;
78565 var meta4data = [];
78566
78567 if(newFullLayout.meta) {
78568 _meta = newFullLayout._meta = {
78569 meta: newFullLayout.meta,
78570 layout: {meta: newFullLayout.meta}
78571 };
78572 }
78573
78574 for(var i = 0; i < newFullData.length; i++) {
78575 var trace = newFullData[i];
78576
78577 if(trace.meta) {
78578 meta4data[trace.index] = trace._meta = {meta: trace.meta};
78579 } else if(newFullLayout.meta) {
78580 trace._meta = {meta: newFullLayout.meta};
78581 }
78582 if(newFullLayout.meta) {
78583 trace._meta.layout = {meta: newFullLayout.meta};
78584 }
78585 }
78586
78587 if(meta4data.length) {
78588 if(!_meta) {
78589 _meta = newFullLayout._meta = {};
78590 }
78591 _meta.data = meta4data;
78592 }
78593}
78594
78595// Create storage for all of the data related to frames and transitions:
78596plots.createTransitionData = function(gd) {
78597 // Set up the default keyframe if it doesn't exist:
78598 if(!gd._transitionData) {
78599 gd._transitionData = {};
78600 }
78601
78602 if(!gd._transitionData._frames) {
78603 gd._transitionData._frames = [];
78604 }
78605
78606 if(!gd._transitionData._frameHash) {
78607 gd._transitionData._frameHash = {};
78608 }
78609
78610 if(!gd._transitionData._counter) {
78611 gd._transitionData._counter = 0;
78612 }
78613
78614 if(!gd._transitionData._interruptCallbacks) {
78615 gd._transitionData._interruptCallbacks = [];
78616 }
78617};
78618
78619// helper function to be bound to fullLayout to check
78620// whether a certain plot type is present on plot
78621// or trace has a category
78622plots._hasPlotType = function(category) {
78623 var i;
78624
78625 // check base plot modules
78626 var basePlotModules = this._basePlotModules || [];
78627 for(i = 0; i < basePlotModules.length; i++) {
78628 if(basePlotModules[i].name === category) return true;
78629 }
78630
78631 // check trace modules (including non-visible:true)
78632 var modules = this._modules || [];
78633 for(i = 0; i < modules.length; i++) {
78634 var name = modules[i].name;
78635 if(name === category) return true;
78636 // N.B. this is modules[i] along with 'categories' as a hash object
78637 var _module = Registry.modules[name];
78638 if(_module && _module.categories[category]) return true;
78639 }
78640
78641 return false;
78642};
78643
78644plots.cleanPlot = function(newFullData, newFullLayout, oldFullData, oldFullLayout) {
78645 var i, j;
78646
78647 var basePlotModules = oldFullLayout._basePlotModules || [];
78648 for(i = 0; i < basePlotModules.length; i++) {
78649 var _module = basePlotModules[i];
78650
78651 if(_module.clean) {
78652 _module.clean(newFullData, newFullLayout, oldFullData, oldFullLayout);
78653 }
78654 }
78655
78656 var hadGl = oldFullLayout._has && oldFullLayout._has('gl');
78657 var hasGl = newFullLayout._has && newFullLayout._has('gl');
78658
78659 if(hadGl && !hasGl) {
78660 if(oldFullLayout._glcontainer !== undefined) {
78661 oldFullLayout._glcontainer.selectAll('.gl-canvas').remove();
78662 oldFullLayout._glcontainer.selectAll('.no-webgl').remove();
78663 oldFullLayout._glcanvas = null;
78664 }
78665 }
78666
78667 var hasInfoLayer = !!oldFullLayout._infolayer;
78668
78669 oldLoop:
78670 for(i = 0; i < oldFullData.length; i++) {
78671 var oldTrace = oldFullData[i];
78672 var oldUid = oldTrace.uid;
78673
78674 for(j = 0; j < newFullData.length; j++) {
78675 var newTrace = newFullData[j];
78676
78677 if(oldUid === newTrace.uid) continue oldLoop;
78678 }
78679
78680 // clean old colorbars
78681 if(hasInfoLayer) {
78682 oldFullLayout._infolayer.select('.cb' + oldUid).remove();
78683 }
78684 }
78685};
78686
78687plots.linkSubplots = function(newFullData, newFullLayout, oldFullData, oldFullLayout) {
78688 var i, j;
78689
78690 var oldSubplots = oldFullLayout._plots || {};
78691 var newSubplots = newFullLayout._plots = {};
78692 var newSubplotList = newFullLayout._subplots;
78693
78694 var mockGd = {
78695 _fullData: newFullData,
78696 _fullLayout: newFullLayout
78697 };
78698
78699 var ids = newSubplotList.cartesian.concat(newSubplotList.gl2d || []);
78700
78701 for(i = 0; i < ids.length; i++) {
78702 var id = ids[i];
78703 var oldSubplot = oldSubplots[id];
78704 var xaxis = axisIDs.getFromId(mockGd, id, 'x');
78705 var yaxis = axisIDs.getFromId(mockGd, id, 'y');
78706 var plotinfo;
78707
78708 // link or create subplot object
78709 if(oldSubplot) {
78710 plotinfo = newSubplots[id] = oldSubplot;
78711 } else {
78712 plotinfo = newSubplots[id] = {};
78713 plotinfo.id = id;
78714 }
78715
78716 // add these axis ids to each others' subplot lists
78717 xaxis._counterAxes.push(yaxis._id);
78718 yaxis._counterAxes.push(xaxis._id);
78719 xaxis._subplotsWith.push(id);
78720 yaxis._subplotsWith.push(id);
78721
78722 // update x and y axis layout object refs
78723 plotinfo.xaxis = xaxis;
78724 plotinfo.yaxis = yaxis;
78725
78726 // By default, we clip at the subplot level,
78727 // but if one trace on a given subplot has *cliponaxis* set to false,
78728 // we need to clip at the trace module layer level;
78729 // find this out here, once of for all.
78730 plotinfo._hasClipOnAxisFalse = false;
78731
78732 for(j = 0; j < newFullData.length; j++) {
78733 var trace = newFullData[j];
78734
78735 if(
78736 trace.xaxis === plotinfo.xaxis._id &&
78737 trace.yaxis === plotinfo.yaxis._id &&
78738 trace.cliponaxis === false
78739 ) {
78740 plotinfo._hasClipOnAxisFalse = true;
78741 break;
78742 }
78743 }
78744 }
78745
78746 // while we're at it, link overlaying axes to their main axes and
78747 // anchored axes to the axes they're anchored to
78748 var axList = axisIDs.list(mockGd, null, true);
78749 var ax;
78750 for(i = 0; i < axList.length; i++) {
78751 ax = axList[i];
78752 var mainAx = null;
78753
78754 if(ax.overlaying) {
78755 mainAx = axisIDs.getFromId(mockGd, ax.overlaying);
78756
78757 // you cannot overlay an axis that's already overlaying another
78758 if(mainAx && mainAx.overlaying) {
78759 ax.overlaying = false;
78760 mainAx = null;
78761 }
78762 }
78763 ax._mainAxis = mainAx || ax;
78764
78765 /*
78766 * For now force overlays to overlay completely... so they
78767 * can drag together correctly and share backgrounds.
78768 * Later perhaps we make separate axis domain and
78769 * tick/line domain or something, so they can still share
78770 * the (possibly larger) dragger and background but don't
78771 * have to both be drawn over that whole domain
78772 */
78773 if(mainAx) ax.domain = mainAx.domain.slice();
78774
78775 ax._anchorAxis = ax.anchor === 'free' ?
78776 null :
78777 axisIDs.getFromId(mockGd, ax.anchor);
78778 }
78779
78780 // finally, we can find the main subplot for each axis
78781 // (on which the ticks & labels are drawn)
78782 for(i = 0; i < axList.length; i++) {
78783 ax = axList[i];
78784 ax._counterAxes.sort(axisIDs.idSort);
78785 ax._subplotsWith.sort(Lib.subplotSort);
78786 ax._mainSubplot = findMainSubplot(ax, newFullLayout);
78787
78788 // find "full" domain span of counter axes,
78789 // this loop can be costly, so only compute it when required
78790 if(ax._counterAxes.length && (
78791 (ax.spikemode && ax.spikemode.indexOf('across') !== -1) ||
78792 (ax.automargin && ax.mirror && ax.anchor !== 'free') ||
78793 Registry.getComponentMethod('rangeslider', 'isVisible')(ax)
78794 )) {
78795 var min = 1;
78796 var max = 0;
78797 for(j = 0; j < ax._counterAxes.length; j++) {
78798 var ax2 = axisIDs.getFromId(mockGd, ax._counterAxes[j]);
78799 min = Math.min(min, ax2.domain[0]);
78800 max = Math.max(max, ax2.domain[1]);
78801 }
78802 if(min < max) {
78803 ax._counterDomainMin = min;
78804 ax._counterDomainMax = max;
78805 }
78806 }
78807 }
78808};
78809
78810function findMainSubplot(ax, fullLayout) {
78811 var mockGd = {_fullLayout: fullLayout};
78812
78813 var isX = ax._id.charAt(0) === 'x';
78814 var anchorAx = ax._mainAxis._anchorAxis;
78815 var mainSubplotID = '';
78816 var nextBestMainSubplotID = '';
78817 var anchorID = '';
78818
78819 // First try the main ID with the anchor
78820 if(anchorAx) {
78821 anchorID = anchorAx._mainAxis._id;
78822 mainSubplotID = isX ? (ax._id + anchorID) : (anchorID + ax._id);
78823 }
78824
78825 // Then look for a subplot with the counteraxis overlaying the anchor
78826 // If that fails just use the first subplot including this axis
78827 if(!mainSubplotID || !fullLayout._plots[mainSubplotID]) {
78828 mainSubplotID = '';
78829
78830 var counterIDs = ax._counterAxes;
78831 for(var j = 0; j < counterIDs.length; j++) {
78832 var counterPart = counterIDs[j];
78833 var id = isX ? (ax._id + counterPart) : (counterPart + ax._id);
78834 if(!nextBestMainSubplotID) nextBestMainSubplotID = id;
78835 var counterAx = axisIDs.getFromId(mockGd, counterPart);
78836 if(anchorID && counterAx.overlaying === anchorID) {
78837 mainSubplotID = id;
78838 break;
78839 }
78840 }
78841 }
78842
78843 return mainSubplotID || nextBestMainSubplotID;
78844}
78845
78846// This function clears any trace attributes with valType: color and
78847// no set dflt filed in the plot schema. This is needed because groupby (which
78848// is the only transform for which this currently applies) supplies parent
78849// trace defaults, then expanded trace defaults. The result is that `null`
78850// colors are default-supplied and inherited as a color instead of a null.
78851// The result is that expanded trace default colors have no effect, with
78852// the final result that groups are indistinguishable. This function clears
78853// those colors so that individual groupby groups get unique colors.
78854plots.clearExpandedTraceDefaultColors = function(trace) {
78855 var colorAttrs, path, i;
78856
78857 // This uses weird closure state in order to satisfy the linter rule
78858 // that we can't create functions in a loop.
78859 function locateColorAttrs(attr, attrName, attrs, level) {
78860 path[level] = attrName;
78861 path.length = level + 1;
78862 if(attr.valType === 'color' && attr.dflt === undefined) {
78863 colorAttrs.push(path.join('.'));
78864 }
78865 }
78866
78867 path = [];
78868
78869 // Get the cached colorAttrs:
78870 colorAttrs = trace._module._colorAttrs;
78871
78872 // Or else compute and cache the colorAttrs on the module:
78873 if(!colorAttrs) {
78874 trace._module._colorAttrs = colorAttrs = [];
78875 PlotSchema.crawl(
78876 trace._module.attributes,
78877 locateColorAttrs
78878 );
78879 }
78880
78881 for(i = 0; i < colorAttrs.length; i++) {
78882 var origprop = Lib.nestedProperty(trace, '_input.' + colorAttrs[i]);
78883
78884 if(!origprop.get()) {
78885 Lib.nestedProperty(trace, colorAttrs[i]).set(null);
78886 }
78887 }
78888};
78889
78890
78891plots.supplyDataDefaults = function(dataIn, dataOut, layout, fullLayout) {
78892 var modules = fullLayout._modules;
78893 var visibleModules = fullLayout._visibleModules;
78894 var basePlotModules = fullLayout._basePlotModules;
78895 var cnt = 0;
78896 var colorCnt = 0;
78897
78898 var i, fullTrace, trace;
78899
78900 fullLayout._transformModules = [];
78901
78902 function pushModule(fullTrace) {
78903 dataOut.push(fullTrace);
78904
78905 var _module = fullTrace._module;
78906 if(!_module) return;
78907
78908 Lib.pushUnique(modules, _module);
78909 if(fullTrace.visible === true) Lib.pushUnique(visibleModules, _module);
78910 Lib.pushUnique(basePlotModules, fullTrace._module.basePlotModule);
78911 cnt++;
78912
78913 // TODO: do we really want color not to increment for explicitly invisible traces?
78914 // This logic is weird, but matches previous behavior: traces that you explicitly
78915 // set to visible:false do not increment the color, but traces WE determine to be
78916 // empty or invalid (and thus set to visible:false) DO increment color.
78917 // I kind of think we should just let all traces increment color, visible or not.
78918 // see mock: axes-autotype-empty vs. a test of restyling visible: false that
78919 // I can't find right now...
78920 if(fullTrace._input.visible !== false) colorCnt++;
78921 }
78922
78923 var carpetIndex = {};
78924 var carpetDependents = [];
78925 var dataTemplate = (layout.template || {}).data || {};
78926 var templater = Template.traceTemplater(dataTemplate);
78927
78928 for(i = 0; i < dataIn.length; i++) {
78929 trace = dataIn[i];
78930
78931 // reuse uid we may have pulled out of oldFullData
78932 // Note: templater supplies trace type
78933 fullTrace = templater.newTrace(trace);
78934 fullTrace.uid = fullLayout._traceUids[i];
78935 plots.supplyTraceDefaults(trace, fullTrace, colorCnt, fullLayout, i);
78936
78937 fullTrace.index = i;
78938 fullTrace._input = trace;
78939 fullTrace._expandedIndex = cnt;
78940
78941 if(fullTrace.transforms && fullTrace.transforms.length) {
78942 var sdInvisible = trace.visible !== false && fullTrace.visible === false;
78943
78944 var expandedTraces = applyTransforms(fullTrace, dataOut, layout, fullLayout);
78945
78946 for(var j = 0; j < expandedTraces.length; j++) {
78947 var expandedTrace = expandedTraces[j];
78948
78949 // No further templating during transforms.
78950 var fullExpandedTrace = {
78951 _template: fullTrace._template,
78952 type: fullTrace.type,
78953 // set uid using parent uid and expanded index
78954 // to promote consistency between update calls
78955 uid: fullTrace.uid + j
78956 };
78957
78958 // If the first supplyDefaults created `visible: false`,
78959 // clear it before running supplyDefaults a second time,
78960 // because sometimes there are items we still want to coerce
78961 // inside trace modules before determining that the trace is
78962 // again `visible: false`, for example partial visibilities
78963 // in `splom` traces.
78964 if(sdInvisible && expandedTrace.visible === false) {
78965 delete expandedTrace.visible;
78966 }
78967
78968 plots.supplyTraceDefaults(expandedTrace, fullExpandedTrace, cnt, fullLayout, i);
78969
78970 // relink private (i.e. underscore) keys expanded trace to full expanded trace so
78971 // that transform supply-default methods can set _ keys for future use.
78972 relinkPrivateKeys(fullExpandedTrace, expandedTrace);
78973
78974 // add info about parent data trace
78975 fullExpandedTrace.index = i;
78976 fullExpandedTrace._input = trace;
78977 fullExpandedTrace._fullInput = fullTrace;
78978
78979 // add info about the expanded data
78980 fullExpandedTrace._expandedIndex = cnt;
78981 fullExpandedTrace._expandedInput = expandedTrace;
78982
78983 pushModule(fullExpandedTrace);
78984 }
78985 } else {
78986 // add identify refs for consistency with transformed traces
78987 fullTrace._fullInput = fullTrace;
78988 fullTrace._expandedInput = fullTrace;
78989
78990 pushModule(fullTrace);
78991 }
78992
78993 if(Registry.traceIs(fullTrace, 'carpetAxis')) {
78994 carpetIndex[fullTrace.carpet] = fullTrace;
78995 }
78996
78997 if(Registry.traceIs(fullTrace, 'carpetDependent')) {
78998 carpetDependents.push(i);
78999 }
79000 }
79001
79002 for(i = 0; i < carpetDependents.length; i++) {
79003 fullTrace = dataOut[carpetDependents[i]];
79004
79005 if(!fullTrace.visible) continue;
79006
79007 var carpetAxis = carpetIndex[fullTrace.carpet];
79008 fullTrace._carpet = carpetAxis;
79009
79010 if(!carpetAxis || !carpetAxis.visible) {
79011 fullTrace.visible = false;
79012 continue;
79013 }
79014
79015 fullTrace.xaxis = carpetAxis.xaxis;
79016 fullTrace.yaxis = carpetAxis.yaxis;
79017 }
79018};
79019
79020plots.supplyAnimationDefaults = function(opts) {
79021 opts = opts || {};
79022 var i;
79023 var optsOut = {};
79024
79025 function coerce(attr, dflt) {
79026 return Lib.coerce(opts || {}, optsOut, animationAttrs, attr, dflt);
79027 }
79028
79029 coerce('mode');
79030 coerce('direction');
79031 coerce('fromcurrent');
79032
79033 if(Array.isArray(opts.frame)) {
79034 optsOut.frame = [];
79035 for(i = 0; i < opts.frame.length; i++) {
79036 optsOut.frame[i] = plots.supplyAnimationFrameDefaults(opts.frame[i] || {});
79037 }
79038 } else {
79039 optsOut.frame = plots.supplyAnimationFrameDefaults(opts.frame || {});
79040 }
79041
79042 if(Array.isArray(opts.transition)) {
79043 optsOut.transition = [];
79044 for(i = 0; i < opts.transition.length; i++) {
79045 optsOut.transition[i] = plots.supplyAnimationTransitionDefaults(opts.transition[i] || {});
79046 }
79047 } else {
79048 optsOut.transition = plots.supplyAnimationTransitionDefaults(opts.transition || {});
79049 }
79050
79051 return optsOut;
79052};
79053
79054plots.supplyAnimationFrameDefaults = function(opts) {
79055 var optsOut = {};
79056
79057 function coerce(attr, dflt) {
79058 return Lib.coerce(opts || {}, optsOut, animationAttrs.frame, attr, dflt);
79059 }
79060
79061 coerce('duration');
79062 coerce('redraw');
79063
79064 return optsOut;
79065};
79066
79067plots.supplyAnimationTransitionDefaults = function(opts) {
79068 var optsOut = {};
79069
79070 function coerce(attr, dflt) {
79071 return Lib.coerce(opts || {}, optsOut, animationAttrs.transition, attr, dflt);
79072 }
79073
79074 coerce('duration');
79075 coerce('easing');
79076
79077 return optsOut;
79078};
79079
79080plots.supplyFrameDefaults = function(frameIn) {
79081 var frameOut = {};
79082
79083 function coerce(attr, dflt) {
79084 return Lib.coerce(frameIn, frameOut, frameAttrs, attr, dflt);
79085 }
79086
79087 coerce('group');
79088 coerce('name');
79089 coerce('traces');
79090 coerce('baseframe');
79091 coerce('data');
79092 coerce('layout');
79093
79094 return frameOut;
79095};
79096
79097plots.supplyTraceDefaults = function(traceIn, traceOut, colorIndex, layout, traceInIndex) {
79098 var colorway = layout.colorway || Color.defaults;
79099 var defaultColor = colorway[colorIndex % colorway.length];
79100
79101 var i;
79102
79103 function coerce(attr, dflt) {
79104 return Lib.coerce(traceIn, traceOut, plots.attributes, attr, dflt);
79105 }
79106
79107 var visible = coerce('visible');
79108
79109 coerce('type');
79110 coerce('name', layout._traceWord + ' ' + traceInIndex);
79111
79112 coerce('uirevision', layout.uirevision);
79113
79114 // we want even invisible traces to make their would-be subplots visible
79115 // so coerce the subplot id(s) now no matter what
79116 var _module = plots.getModule(traceOut);
79117
79118 traceOut._module = _module;
79119 if(_module) {
79120 var basePlotModule = _module.basePlotModule;
79121 var subplotAttr = basePlotModule.attr;
79122 var subplotAttrs = basePlotModule.attributes;
79123 if(subplotAttr && subplotAttrs) {
79124 var subplots = layout._subplots;
79125 var subplotId = '';
79126
79127 if(
79128 visible ||
79129 basePlotModule.name !== 'gl2d' // for now just drop empty gl2d subplots
79130 // TODO - currently if we draw an empty gl2d subplot, it draws
79131 // nothing then gets stuck and you can't get it back without newPlot
79132 // sort this out in the regl refactor?
79133 ) {
79134 if(Array.isArray(subplotAttr)) {
79135 for(i = 0; i < subplotAttr.length; i++) {
79136 var attri = subplotAttr[i];
79137 var vali = Lib.coerce(traceIn, traceOut, subplotAttrs, attri);
79138
79139 if(subplots[attri]) Lib.pushUnique(subplots[attri], vali);
79140 subplotId += vali;
79141 }
79142 } else {
79143 subplotId = Lib.coerce(traceIn, traceOut, subplotAttrs, subplotAttr);
79144 }
79145
79146 if(subplots[basePlotModule.name]) {
79147 Lib.pushUnique(subplots[basePlotModule.name], subplotId);
79148 }
79149 }
79150 }
79151 }
79152
79153 if(visible) {
79154 coerce('customdata');
79155 coerce('ids');
79156 coerce('meta');
79157
79158 if(Registry.traceIs(traceOut, 'showLegend')) {
79159 Lib.coerce(traceIn, traceOut,
79160 _module.attributes.showlegend ? _module.attributes : plots.attributes,
79161 'showlegend'
79162 );
79163
79164 coerce('legendgroup');
79165 var titleText = coerce('legendgrouptitle.text');
79166 if(titleText) {
79167 Lib.coerceFont(coerce, 'legendgrouptitle.font', Lib.extendFlat({}, layout.font, {
79168 size: Math.round(layout.font.size * 1.1) // default to larger font size
79169 }));
79170 }
79171
79172 coerce('legendrank');
79173
79174 traceOut._dfltShowLegend = true;
79175 } else {
79176 traceOut._dfltShowLegend = false;
79177 }
79178
79179 if(_module) {
79180 _module.supplyDefaults(traceIn, traceOut, defaultColor, layout);
79181 }
79182
79183 if(!Registry.traceIs(traceOut, 'noOpacity')) {
79184 coerce('opacity');
79185 }
79186
79187 if(Registry.traceIs(traceOut, 'notLegendIsolatable')) {
79188 // This clears out the legendonly state for traces like carpet that
79189 // cannot be isolated in the legend
79190 traceOut.visible = !!traceOut.visible;
79191 }
79192
79193 if(!Registry.traceIs(traceOut, 'noHover')) {
79194 if(!traceOut.hovertemplate) Lib.coerceHoverinfo(traceIn, traceOut, layout);
79195
79196 // parcats support hover, but not hoverlabel stylings (yet)
79197 if(traceOut.type !== 'parcats') {
79198 Registry.getComponentMethod('fx', 'supplyDefaults')(traceIn, traceOut, defaultColor, layout);
79199 }
79200 }
79201
79202 if(_module && _module.selectPoints) {
79203 coerce('selectedpoints');
79204 }
79205
79206 plots.supplyTransformDefaults(traceIn, traceOut, layout);
79207 }
79208
79209 return traceOut;
79210};
79211
79212/**
79213 * hasMakesDataTransform: does this trace have a transform that makes its own
79214 * data, either by grabbing it from somewhere else or by creating it from input
79215 * parameters? If so, we should still keep going with supplyDefaults
79216 * even if the trace is invisible, which may just be because it has no data yet.
79217 */
79218function hasMakesDataTransform(trace) {
79219 var transforms = trace.transforms;
79220 if(Array.isArray(transforms) && transforms.length) {
79221 for(var i = 0; i < transforms.length; i++) {
79222 var ti = transforms[i];
79223 var _module = ti._module || transformsRegistry[ti.type];
79224 if(_module && _module.makesData) return true;
79225 }
79226 }
79227 return false;
79228}
79229
79230plots.hasMakesDataTransform = hasMakesDataTransform;
79231
79232plots.supplyTransformDefaults = function(traceIn, traceOut, layout) {
79233 // For now we only allow transforms on 1D traces, ie those that specify a _length.
79234 // If we were to implement 2D transforms, we'd need to have each transform
79235 // describe its own applicability and disable itself when it doesn't apply.
79236 // Also allow transforms that make their own data, but not in globalTransforms
79237 if(!(traceOut._length || hasMakesDataTransform(traceIn))) return;
79238
79239 var globalTransforms = layout._globalTransforms || [];
79240 var transformModules = layout._transformModules || [];
79241
79242 if(!Array.isArray(traceIn.transforms) && globalTransforms.length === 0) return;
79243
79244 var containerIn = traceIn.transforms || [];
79245 var transformList = globalTransforms.concat(containerIn);
79246 var containerOut = traceOut.transforms = [];
79247
79248 for(var i = 0; i < transformList.length; i++) {
79249 var transformIn = transformList[i];
79250 var type = transformIn.type;
79251 var _module = transformsRegistry[type];
79252 var transformOut;
79253
79254 /*
79255 * Supply defaults may run twice. First pass runs all supply defaults steps
79256 * and adds the _module to any output transforms.
79257 * If transforms exist another pass is run so that any generated traces also
79258 * go through supply defaults. This has the effect of rerunning
79259 * supplyTransformDefaults. If the transform does not have a `transform`
79260 * function it could not have generated any new traces and the second stage
79261 * is unnecessary. We detect this case with the following variables.
79262 */
79263 var isFirstStage = !(transformIn._module && transformIn._module === _module);
79264 var doLaterStages = _module && typeof _module.transform === 'function';
79265
79266 if(!_module) Lib.warn('Unrecognized transform type ' + type + '.');
79267
79268 if(_module && _module.supplyDefaults && (isFirstStage || doLaterStages)) {
79269 transformOut = _module.supplyDefaults(transformIn, traceOut, layout, traceIn);
79270 transformOut.type = type;
79271 transformOut._module = _module;
79272
79273 Lib.pushUnique(transformModules, _module);
79274 } else {
79275 transformOut = Lib.extendFlat({}, transformIn);
79276 }
79277
79278 containerOut.push(transformOut);
79279 }
79280};
79281
79282function applyTransforms(fullTrace, fullData, layout, fullLayout) {
79283 var container = fullTrace.transforms;
79284 var dataOut = [fullTrace];
79285
79286 for(var i = 0; i < container.length; i++) {
79287 var transform = container[i];
79288 var _module = transformsRegistry[transform.type];
79289
79290 if(_module && _module.transform) {
79291 dataOut = _module.transform(dataOut, {
79292 transform: transform,
79293 fullTrace: fullTrace,
79294 fullData: fullData,
79295 layout: layout,
79296 fullLayout: fullLayout,
79297 transformIndex: i
79298 });
79299 }
79300 }
79301
79302 return dataOut;
79303}
79304
79305plots.supplyLayoutGlobalDefaults = function(layoutIn, layoutOut, formatObj) {
79306 function coerce(attr, dflt) {
79307 return Lib.coerce(layoutIn, layoutOut, plots.layoutAttributes, attr, dflt);
79308 }
79309
79310 var template = layoutIn.template;
79311 if(Lib.isPlainObject(template)) {
79312 layoutOut.template = template;
79313 layoutOut._template = template.layout;
79314 layoutOut._dataTemplate = template.data;
79315 }
79316
79317 coerce('autotypenumbers');
79318
79319 var globalFont = Lib.coerceFont(coerce, 'font');
79320
79321 coerce('title.text', layoutOut._dfltTitle.plot);
79322
79323 Lib.coerceFont(coerce, 'title.font', {
79324 family: globalFont.family,
79325 size: Math.round(globalFont.size * 1.4),
79326 color: globalFont.color
79327 });
79328
79329 coerce('title.xref');
79330 coerce('title.yref');
79331 coerce('title.x');
79332 coerce('title.y');
79333 coerce('title.xanchor');
79334 coerce('title.yanchor');
79335 coerce('title.pad.t');
79336 coerce('title.pad.r');
79337 coerce('title.pad.b');
79338 coerce('title.pad.l');
79339
79340 var uniformtextMode = coerce('uniformtext.mode');
79341 if(uniformtextMode) {
79342 coerce('uniformtext.minsize');
79343 }
79344
79345 // Make sure that autosize is defaulted to *true*
79346 // on layouts with no set width and height for backward compatibly,
79347 // in particular https://plotly.com/javascript/responsive-fluid-layout/
79348 //
79349 // Before https://github.com/plotly/plotly.js/pull/635 ,
79350 // layouts with no set width and height were set temporary set to 'initial'
79351 // to pass through the autosize routine
79352 //
79353 // This behavior is subject to change in v3.
79354 coerce('autosize', !(layoutIn.width && layoutIn.height));
79355
79356 coerce('width');
79357 coerce('height');
79358 coerce('margin.l');
79359 coerce('margin.r');
79360 coerce('margin.t');
79361 coerce('margin.b');
79362 coerce('margin.pad');
79363 coerce('margin.autoexpand');
79364
79365 if(layoutIn.width && layoutIn.height) plots.sanitizeMargins(layoutOut);
79366
79367 Registry.getComponentMethod('grid', 'sizeDefaults')(layoutIn, layoutOut);
79368
79369 coerce('paper_bgcolor');
79370
79371 coerce('separators', formatObj.decimal + formatObj.thousands);
79372 coerce('hidesources');
79373
79374 coerce('colorway');
79375
79376 coerce('datarevision');
79377 var uirevision = coerce('uirevision');
79378 coerce('editrevision', uirevision);
79379 coerce('selectionrevision', uirevision);
79380
79381 Registry.getComponentMethod(
79382 'modebar',
79383 'supplyLayoutDefaults'
79384 )(layoutIn, layoutOut);
79385
79386 Registry.getComponentMethod(
79387 'shapes',
79388 'supplyDrawNewShapeDefaults'
79389 )(layoutIn, layoutOut, coerce);
79390
79391 coerce('meta');
79392
79393 // do not include defaults in fullLayout when users do not set transition
79394 if(Lib.isPlainObject(layoutIn.transition)) {
79395 coerce('transition.duration');
79396 coerce('transition.easing');
79397 coerce('transition.ordering');
79398 }
79399
79400 Registry.getComponentMethod(
79401 'calendars',
79402 'handleDefaults'
79403 )(layoutIn, layoutOut, 'calendar');
79404
79405 Registry.getComponentMethod(
79406 'fx',
79407 'supplyLayoutGlobalDefaults'
79408 )(layoutIn, layoutOut, coerce);
79409};
79410
79411function getComputedSize(attr) {
79412 return (
79413 (typeof attr === 'string') &&
79414 (attr.substr(attr.length - 2) === 'px') &&
79415 parseFloat(attr)
79416 );
79417}
79418
79419
79420plots.plotAutoSize = function plotAutoSize(gd, layout, fullLayout) {
79421 var context = gd._context || {};
79422 var frameMargins = context.frameMargins;
79423 var newWidth;
79424 var newHeight;
79425
79426 var isPlotDiv = Lib.isPlotDiv(gd);
79427
79428 if(isPlotDiv) gd.emit('plotly_autosize');
79429
79430 // embedded in an iframe - just take the full iframe size
79431 // if we get to this point, with no aspect ratio restrictions
79432 if(context.fillFrame) {
79433 newWidth = window.innerWidth;
79434 newHeight = window.innerHeight;
79435
79436 // somehow we get a few extra px height sometimes...
79437 // just hide it
79438 document.body.style.overflow = 'hidden';
79439 } else {
79440 // plotly.js - let the developers do what they want, either
79441 // provide height and width for the container div,
79442 // specify size in layout, or take the defaults,
79443 // but don't enforce any ratio restrictions
79444 var computedStyle = isPlotDiv ? window.getComputedStyle(gd) : {};
79445
79446 newWidth = getComputedSize(computedStyle.width) || getComputedSize(computedStyle.maxWidth) || fullLayout.width;
79447 newHeight = getComputedSize(computedStyle.height) || getComputedSize(computedStyle.maxHeight) || fullLayout.height;
79448
79449 if(isNumeric(frameMargins) && frameMargins > 0) {
79450 var factor = 1 - 2 * frameMargins;
79451 newWidth = Math.round(factor * newWidth);
79452 newHeight = Math.round(factor * newHeight);
79453 }
79454 }
79455
79456 var minWidth = plots.layoutAttributes.width.min;
79457 var minHeight = plots.layoutAttributes.height.min;
79458 if(newWidth < minWidth) newWidth = minWidth;
79459 if(newHeight < minHeight) newHeight = minHeight;
79460
79461 var widthHasChanged = !layout.width &&
79462 (Math.abs(fullLayout.width - newWidth) > 1);
79463 var heightHasChanged = !layout.height &&
79464 (Math.abs(fullLayout.height - newHeight) > 1);
79465
79466 if(heightHasChanged || widthHasChanged) {
79467 if(widthHasChanged) fullLayout.width = newWidth;
79468 if(heightHasChanged) fullLayout.height = newHeight;
79469 }
79470
79471 // cache initial autosize value, used in relayout when
79472 // width or height values are set to null
79473 if(!gd._initialAutoSize) {
79474 gd._initialAutoSize = { width: newWidth, height: newHeight };
79475 }
79476
79477 plots.sanitizeMargins(fullLayout);
79478};
79479
79480plots.supplyLayoutModuleDefaults = function(layoutIn, layoutOut, fullData, transitionData) {
79481 var componentsRegistry = Registry.componentsRegistry;
79482 var basePlotModules = layoutOut._basePlotModules;
79483 var component, i, _module;
79484
79485 var Cartesian = Registry.subplotsRegistry.cartesian;
79486
79487 // check if any components need to add more base plot modules
79488 // that weren't captured by traces
79489 for(component in componentsRegistry) {
79490 _module = componentsRegistry[component];
79491
79492 if(_module.includeBasePlot) {
79493 _module.includeBasePlot(layoutIn, layoutOut);
79494 }
79495 }
79496
79497 // make sure we *at least* have some cartesian axes
79498 if(!basePlotModules.length) {
79499 basePlotModules.push(Cartesian);
79500 }
79501
79502 // ensure all cartesian axes have at least one subplot
79503 if(layoutOut._has('cartesian')) {
79504 Registry.getComponentMethod('grid', 'contentDefaults')(layoutIn, layoutOut);
79505 Cartesian.finalizeSubplots(layoutIn, layoutOut);
79506 }
79507
79508 // sort subplot lists
79509 for(var subplotType in layoutOut._subplots) {
79510 layoutOut._subplots[subplotType].sort(Lib.subplotSort);
79511 }
79512
79513 // base plot module layout defaults
79514 for(i = 0; i < basePlotModules.length; i++) {
79515 _module = basePlotModules[i];
79516
79517 // e.g. pie does not have a layout-defaults step
79518 if(_module.supplyLayoutDefaults) {
79519 _module.supplyLayoutDefaults(layoutIn, layoutOut, fullData);
79520 }
79521 }
79522
79523 // trace module layout defaults
79524 // use _modules rather than _visibleModules so that even
79525 // legendonly traces can include settings - eg barmode, which affects
79526 // legend.traceorder default value.
79527 var modules = layoutOut._modules;
79528 for(i = 0; i < modules.length; i++) {
79529 _module = modules[i];
79530
79531 if(_module.supplyLayoutDefaults) {
79532 _module.supplyLayoutDefaults(layoutIn, layoutOut, fullData);
79533 }
79534 }
79535
79536 // transform module layout defaults
79537 var transformModules = layoutOut._transformModules;
79538 for(i = 0; i < transformModules.length; i++) {
79539 _module = transformModules[i];
79540
79541 if(_module.supplyLayoutDefaults) {
79542 _module.supplyLayoutDefaults(layoutIn, layoutOut, fullData, transitionData);
79543 }
79544 }
79545
79546 for(component in componentsRegistry) {
79547 _module = componentsRegistry[component];
79548
79549 if(_module.supplyLayoutDefaults) {
79550 _module.supplyLayoutDefaults(layoutIn, layoutOut, fullData);
79551 }
79552 }
79553};
79554
79555// Remove all plotly attributes from a div so it can be replotted fresh
79556// TODO: these really need to be encapsulated into a much smaller set...
79557plots.purge = function(gd) {
79558 // note: we DO NOT remove _context because it doesn't change when we insert
79559 // a new plot, and may have been set outside of our scope.
79560
79561 var fullLayout = gd._fullLayout || {};
79562 if(fullLayout._glcontainer !== undefined) {
79563 fullLayout._glcontainer.selectAll('.gl-canvas').remove();
79564 fullLayout._glcontainer.remove();
79565 fullLayout._glcanvas = null;
79566 }
79567
79568 // remove modebar
79569 if(fullLayout._modeBar) fullLayout._modeBar.destroy();
79570
79571 if(gd._transitionData) {
79572 // Ensure any dangling callbacks are simply dropped if the plot is purged.
79573 // This is more or less only actually important for testing.
79574 if(gd._transitionData._interruptCallbacks) {
79575 gd._transitionData._interruptCallbacks.length = 0;
79576 }
79577
79578 if(gd._transitionData._animationRaf) {
79579 window.cancelAnimationFrame(gd._transitionData._animationRaf);
79580 }
79581 }
79582
79583 // remove any planned throttles
79584 Lib.clearThrottle();
79585
79586 // remove responsive handler
79587 Lib.clearResponsive(gd);
79588
79589 // data and layout
79590 delete gd.data;
79591 delete gd.layout;
79592 delete gd._fullData;
79593 delete gd._fullLayout;
79594 delete gd.calcdata;
79595 delete gd.empty;
79596
79597 delete gd.fid;
79598
79599 delete gd.undoqueue; // action queue
79600 delete gd.undonum;
79601 delete gd.autoplay; // are we doing an action that doesn't go in undo queue?
79602 delete gd.changed;
79603
79604 // these get recreated on _doPlot anyway, but just to be safe
79605 // (and to have a record of them...)
79606 delete gd._promises;
79607 delete gd._redrawTimer;
79608 delete gd._hmlumcount;
79609 delete gd._hmpixcount;
79610 delete gd._transitionData;
79611 delete gd._transitioning;
79612 delete gd._initialAutoSize;
79613 delete gd._transitioningWithDuration;
79614
79615 // created during certain events, that *should* clean them up
79616 // themselves, but may not if there was an error
79617 delete gd._dragging;
79618 delete gd._dragged;
79619 delete gd._dragdata;
79620 delete gd._hoverdata;
79621 delete gd._snapshotInProgress;
79622 delete gd._editing;
79623 delete gd._mouseDownTime;
79624 delete gd._legendMouseDownTime;
79625
79626 // remove all event listeners
79627 if(gd.removeAllListeners) gd.removeAllListeners();
79628};
79629
79630plots.style = function(gd) {
79631 var _modules = gd._fullLayout._visibleModules;
79632 var styleModules = [];
79633 var i;
79634
79635 // some trace modules reuse the same style method,
79636 // make sure to not unnecessary call them multiple times.
79637
79638 for(i = 0; i < _modules.length; i++) {
79639 var _module = _modules[i];
79640 if(_module.style) {
79641 Lib.pushUnique(styleModules, _module.style);
79642 }
79643 }
79644
79645 for(i = 0; i < styleModules.length; i++) {
79646 styleModules[i](gd);
79647 }
79648};
79649
79650plots.sanitizeMargins = function(fullLayout) {
79651 // polar doesn't do margins...
79652 if(!fullLayout || !fullLayout.margin) return;
79653
79654 var width = fullLayout.width;
79655 var height = fullLayout.height;
79656 var margin = fullLayout.margin;
79657 var plotWidth = width - (margin.l + margin.r);
79658 var plotHeight = height - (margin.t + margin.b);
79659 var correction;
79660
79661 // if margin.l + margin.r = 0 then plotWidth > 0
79662 // as width >= 10 by supplyDefaults
79663 // similarly for margin.t + margin.b
79664
79665 if(plotWidth < 0) {
79666 correction = (width - 1) / (margin.l + margin.r);
79667 margin.l = Math.floor(correction * margin.l);
79668 margin.r = Math.floor(correction * margin.r);
79669 }
79670
79671 if(plotHeight < 0) {
79672 correction = (height - 1) / (margin.t + margin.b);
79673 margin.t = Math.floor(correction * margin.t);
79674 margin.b = Math.floor(correction * margin.b);
79675 }
79676};
79677
79678plots.clearAutoMarginIds = function(gd) {
79679 gd._fullLayout._pushmarginIds = {};
79680};
79681
79682plots.allowAutoMargin = function(gd, id) {
79683 gd._fullLayout._pushmarginIds[id] = 1;
79684};
79685
79686function initMargins(fullLayout) {
79687 var margin = fullLayout.margin;
79688
79689 if(!fullLayout._size) {
79690 var gs = fullLayout._size = {
79691 l: Math.round(margin.l),
79692 r: Math.round(margin.r),
79693 t: Math.round(margin.t),
79694 b: Math.round(margin.b),
79695 p: Math.round(margin.pad)
79696 };
79697 gs.w = Math.round(fullLayout.width) - gs.l - gs.r;
79698 gs.h = Math.round(fullLayout.height) - gs.t - gs.b;
79699 }
79700 if(!fullLayout._pushmargin) fullLayout._pushmargin = {};
79701 if(!fullLayout._pushmarginIds) fullLayout._pushmarginIds = {};
79702}
79703
79704// non-negotiable - this is the smallest height we will allow users to specify via explicit margins
79705var MIN_SPECIFIED_WIDTH = 2;
79706var MIN_SPECIFIED_HEIGHT = 2;
79707
79708// could be exposed as an option - the smallest we will allow automargin to shrink a larger plot
79709var MIN_REDUCED_WIDTH = 64;
79710var MIN_REDUCED_HEIGHT = 64;
79711
79712/**
79713 * autoMargin: called by components that may need to expand the margins to
79714 * be rendered on-plot.
79715 *
79716 * @param {DOM element} gd
79717 * @param {string} id - an identifier unique (within this plot) to this object,
79718 * so we can remove a previous margin expansion from the same object.
79719 * @param {object} o - the margin requirements of this object, or omit to delete
79720 * this entry (like if it's hidden). Keys are:
79721 * x, y: plot fraction of the anchor point.
79722 * xl, xr, yt, yb: if the object has an extent defined in plot fraction,
79723 * you can specify both edges as plot fractions in each dimension
79724 * l, r, t, b: the pixels to pad past the plot fraction x[l|r] and y[t|b]
79725 * pad: extra pixels to add in all directions, default 12 (why?)
79726 */
79727plots.autoMargin = function(gd, id, o) {
79728 var fullLayout = gd._fullLayout;
79729 var width = fullLayout.width;
79730 var height = fullLayout.height;
79731 var margin = fullLayout.margin;
79732
79733 var minFinalWidth = Lib.constrain(
79734 width - margin.l - margin.r,
79735 MIN_SPECIFIED_WIDTH,
79736 MIN_REDUCED_WIDTH
79737 );
79738
79739 var minFinalHeight = Lib.constrain(
79740 height - margin.t - margin.b,
79741 MIN_SPECIFIED_HEIGHT,
79742 MIN_REDUCED_HEIGHT
79743 );
79744
79745 var maxSpaceW = Math.max(0, width - minFinalWidth);
79746 var maxSpaceH = Math.max(0, height - minFinalHeight);
79747
79748 var pushMargin = fullLayout._pushmargin;
79749 var pushMarginIds = fullLayout._pushmarginIds;
79750
79751 if(margin.autoexpand !== false) {
79752 if(!o) {
79753 delete pushMargin[id];
79754 delete pushMarginIds[id];
79755 } else {
79756 var pad = o.pad;
79757 if(pad === undefined) {
79758 // if no explicit pad is given, use 12px unless there's a
79759 // specified margin that's smaller than that
79760 pad = Math.min(12, margin.l, margin.r, margin.t, margin.b);
79761 }
79762
79763 // if the item is too big, just give it enough automargin to
79764 // make sure you can still grab it and bring it back
79765 if(maxSpaceW) {
79766 var rW = (o.l + o.r) / maxSpaceW;
79767 if(rW > 1) {
79768 o.l /= rW;
79769 o.r /= rW;
79770 }
79771 }
79772 if(maxSpaceH) {
79773 var rH = (o.t + o.b) / maxSpaceH;
79774 if(rH > 1) {
79775 o.t /= rH;
79776 o.b /= rH;
79777 }
79778 }
79779
79780 var xl = o.xl !== undefined ? o.xl : o.x;
79781 var xr = o.xr !== undefined ? o.xr : o.x;
79782 var yt = o.yt !== undefined ? o.yt : o.y;
79783 var yb = o.yb !== undefined ? o.yb : o.y;
79784
79785 pushMargin[id] = {
79786 l: {val: xl, size: o.l + pad},
79787 r: {val: xr, size: o.r + pad},
79788 b: {val: yb, size: o.b + pad},
79789 t: {val: yt, size: o.t + pad}
79790 };
79791 pushMarginIds[id] = 1;
79792 }
79793
79794 if(!fullLayout._replotting) {
79795 return plots.doAutoMargin(gd);
79796 }
79797 }
79798};
79799
79800plots.doAutoMargin = function(gd) {
79801 var fullLayout = gd._fullLayout;
79802 var width = fullLayout.width;
79803 var height = fullLayout.height;
79804
79805 if(!fullLayout._size) fullLayout._size = {};
79806 initMargins(fullLayout);
79807
79808 var gs = fullLayout._size;
79809 var margin = fullLayout.margin;
79810 var oldMargins = Lib.extendFlat({}, gs);
79811
79812 // adjust margins for outside components
79813 // fullLayout.margin is the requested margin,
79814 // fullLayout._size has margins and plotsize after adjustment
79815 var ml = margin.l;
79816 var mr = margin.r;
79817 var mt = margin.t;
79818 var mb = margin.b;
79819 var pushMargin = fullLayout._pushmargin;
79820 var pushMarginIds = fullLayout._pushmarginIds;
79821
79822 if(fullLayout.margin.autoexpand !== false) {
79823 for(var k in pushMargin) {
79824 if(!pushMarginIds[k]) delete pushMargin[k];
79825 }
79826
79827 // fill in the requested margins
79828 pushMargin.base = {
79829 l: {val: 0, size: ml},
79830 r: {val: 1, size: mr},
79831 t: {val: 1, size: mt},
79832 b: {val: 0, size: mb}
79833 };
79834
79835 // now cycle through all the combinations of l and r
79836 // (and t and b) to find the required margins
79837
79838 for(var k1 in pushMargin) {
79839 var pushleft = pushMargin[k1].l || {};
79840 var pushbottom = pushMargin[k1].b || {};
79841 var fl = pushleft.val;
79842 var pl = pushleft.size;
79843 var fb = pushbottom.val;
79844 var pb = pushbottom.size;
79845
79846 for(var k2 in pushMargin) {
79847 if(isNumeric(pl) && pushMargin[k2].r) {
79848 var fr = pushMargin[k2].r.val;
79849 var pr = pushMargin[k2].r.size;
79850 if(fr > fl) {
79851 var newL = (pl * fr + (pr - width) * fl) / (fr - fl);
79852 var newR = (pr * (1 - fl) + (pl - width) * (1 - fr)) / (fr - fl);
79853 if(newL + newR > ml + mr) {
79854 ml = newL;
79855 mr = newR;
79856 }
79857 }
79858 }
79859
79860 if(isNumeric(pb) && pushMargin[k2].t) {
79861 var ft = pushMargin[k2].t.val;
79862 var pt = pushMargin[k2].t.size;
79863 if(ft > fb) {
79864 var newB = (pb * ft + (pt - height) * fb) / (ft - fb);
79865 var newT = (pt * (1 - fb) + (pb - height) * (1 - ft)) / (ft - fb);
79866 if(newB + newT > mb + mt) {
79867 mb = newB;
79868 mt = newT;
79869 }
79870 }
79871 }
79872 }
79873 }
79874 }
79875
79876 var minFinalWidth = Lib.constrain(
79877 width - margin.l - margin.r,
79878 MIN_SPECIFIED_WIDTH,
79879 MIN_REDUCED_WIDTH
79880 );
79881
79882 var minFinalHeight = Lib.constrain(
79883 height - margin.t - margin.b,
79884 MIN_SPECIFIED_HEIGHT,
79885 MIN_REDUCED_HEIGHT
79886 );
79887
79888 var maxSpaceW = Math.max(0, width - minFinalWidth);
79889 var maxSpaceH = Math.max(0, height - minFinalHeight);
79890
79891 if(maxSpaceW) {
79892 var rW = (ml + mr) / maxSpaceW;
79893 if(rW > 1) {
79894 ml /= rW;
79895 mr /= rW;
79896 }
79897 }
79898
79899 if(maxSpaceH) {
79900 var rH = (mb + mt) / maxSpaceH;
79901 if(rH > 1) {
79902 mb /= rH;
79903 mt /= rH;
79904 }
79905 }
79906
79907 gs.l = Math.round(ml);
79908 gs.r = Math.round(mr);
79909 gs.t = Math.round(mt);
79910 gs.b = Math.round(mb);
79911 gs.p = Math.round(margin.pad);
79912 gs.w = Math.round(width) - gs.l - gs.r;
79913 gs.h = Math.round(height) - gs.t - gs.b;
79914
79915 // if things changed and we're not already redrawing, trigger a redraw
79916 if(!fullLayout._replotting && plots.didMarginChange(oldMargins, gs)) {
79917 if('_redrawFromAutoMarginCount' in fullLayout) {
79918 fullLayout._redrawFromAutoMarginCount++;
79919 } else {
79920 fullLayout._redrawFromAutoMarginCount = 1;
79921 }
79922
79923 // Always allow at least one redraw and give each margin-push
79924 // call 3 loops to converge. Of course, for most cases this way too many,
79925 // but let's keep things on the safe side until we fix our
79926 // auto-margin pipeline problems:
79927 // https://github.com/plotly/plotly.js/issues/2704
79928 var maxNumberOfRedraws = 3 * (1 + Object.keys(pushMarginIds).length);
79929
79930 if(fullLayout._redrawFromAutoMarginCount < maxNumberOfRedraws) {
79931 return Registry.call('_doPlot', gd);
79932 } else {
79933 fullLayout._size = oldMargins;
79934 Lib.warn('Too many auto-margin redraws.');
79935 }
79936 }
79937
79938 refineTicks(gd);
79939};
79940
79941function refineTicks(gd) {
79942 var axList = axisIDs.list(gd, '', true);
79943
79944 [
79945 '_adjustTickLabelsOverflow',
79946 '_hideCounterAxisInsideTickLabels'
79947 ].forEach(function(k) {
79948 for(var i = 0; i < axList.length; i++) {
79949 var hideFn = axList[i][k];
79950 if(hideFn) hideFn();
79951 }
79952 });
79953}
79954
79955var marginKeys = ['l', 'r', 't', 'b', 'p', 'w', 'h'];
79956
79957plots.didMarginChange = function(margin0, margin1) {
79958 for(var i = 0; i < marginKeys.length; i++) {
79959 var k = marginKeys[i];
79960 var m0 = margin0[k];
79961 var m1 = margin1[k];
79962 // use 1px tolerance in case we old/new differ only
79963 // by rounding errors, which can lead to infinite loops
79964 if(!isNumeric(m0) || Math.abs(m1 - m0) > 1) {
79965 return true;
79966 }
79967 }
79968 return false;
79969};
79970
79971/**
79972 * JSONify the graph data and layout
79973 *
79974 * This function needs to recurse because some src can be inside
79975 * sub-objects.
79976 *
79977 * It also strips out functions and private (starts with _) elements.
79978 * Therefore, we can add temporary things to data and layout that don't
79979 * get saved.
79980 *
79981 * @param gd The graphDiv
79982 * @param {Boolean} dataonly If true, don't return layout.
79983 * @param {'keepref'|'keepdata'|'keepall'} [mode='keepref'] Filter what's kept
79984 * keepref: remove data for which there's a src present
79985 * eg if there's xsrc present (and xsrc is well-formed,
79986 * ie has : and some chars before it), strip out x
79987 * keepdata: remove all src tags, don't remove the data itself
79988 * keepall: keep data and src
79989 * @param {String} output If you specify 'object', the result will not be stringified
79990 * @param {Boolean} useDefaults If truthy, use _fullLayout and _fullData
79991 * @param {Boolean} includeConfig If truthy, include _context
79992 * @returns {Object|String}
79993 */
79994plots.graphJson = function(gd, dataonly, mode, output, useDefaults, includeConfig) {
79995 // if the defaults aren't supplied yet, we need to do that...
79996 if((useDefaults && dataonly && !gd._fullData) ||
79997 (useDefaults && !dataonly && !gd._fullLayout)) {
79998 plots.supplyDefaults(gd);
79999 }
80000
80001 var data = (useDefaults) ? gd._fullData : gd.data;
80002 var layout = (useDefaults) ? gd._fullLayout : gd.layout;
80003 var frames = (gd._transitionData || {})._frames;
80004
80005 function stripObj(d, keepFunction) {
80006 if(typeof d === 'function') {
80007 return keepFunction ? '_function_' : null;
80008 }
80009 if(Lib.isPlainObject(d)) {
80010 var o = {};
80011 var src;
80012 Object.keys(d).sort().forEach(function(v) {
80013 // remove private elements and functions
80014 // _ is for private, [ is a mistake ie [object Object]
80015 if(['_', '['].indexOf(v.charAt(0)) !== -1) return;
80016
80017 // if a function, add if necessary then move on
80018 if(typeof d[v] === 'function') {
80019 if(keepFunction) o[v] = '_function';
80020 return;
80021 }
80022
80023 // look for src/data matches and remove the appropriate one
80024 if(mode === 'keepdata') {
80025 // keepdata: remove all ...src tags
80026 if(v.substr(v.length - 3) === 'src') {
80027 return;
80028 }
80029 } else if(mode === 'keepstream') {
80030 // keep sourced data if it's being streamed.
80031 // similar to keepref, but if the 'stream' object exists
80032 // in a trace, we will keep the data array.
80033 src = d[v + 'src'];
80034 if(typeof src === 'string' && src.indexOf(':') > 0) {
80035 if(!Lib.isPlainObject(d.stream)) {
80036 return;
80037 }
80038 }
80039 } else if(mode !== 'keepall') {
80040 // keepref: remove sourced data but only
80041 // if the source tag is well-formed
80042 src = d[v + 'src'];
80043 if(typeof src === 'string' && src.indexOf(':') > 0) {
80044 return;
80045 }
80046 }
80047
80048 // OK, we're including this... recurse into it
80049 o[v] = stripObj(d[v], keepFunction);
80050 });
80051 return o;
80052 }
80053
80054 if(Array.isArray(d)) {
80055 return d.map(function(x) {return stripObj(x, keepFunction);});
80056 }
80057
80058 if(Lib.isTypedArray(d)) {
80059 return Lib.simpleMap(d, Lib.identity);
80060 }
80061
80062 // convert native dates to date strings...
80063 // mostly for external users exporting to plotly
80064 if(Lib.isJSDate(d)) return Lib.ms2DateTimeLocal(+d);
80065
80066 return d;
80067 }
80068
80069 var obj = {
80070 data: (data || []).map(function(v) {
80071 var d = stripObj(v);
80072 // fit has some little arrays in it that don't contain data,
80073 // just fit params and meta
80074 if(dataonly) { delete d.fit; }
80075 return d;
80076 })
80077 };
80078 if(!dataonly) {
80079 obj.layout = stripObj(layout);
80080 if(useDefaults) {
80081 var gs = layout._size;
80082 obj.layout.computed = {
80083 margin: {
80084 b: gs.b,
80085 l: gs.l,
80086 r: gs.r,
80087 t: gs.t
80088 }
80089 };
80090 }
80091 }
80092
80093 if(frames) obj.frames = stripObj(frames);
80094
80095 if(includeConfig) obj.config = stripObj(gd._context, true);
80096
80097 return (output === 'object') ? obj : JSON.stringify(obj);
80098};
80099
80100/**
80101 * Modify a keyframe using a list of operations:
80102 *
80103 * @param {array of objects} operations
80104 * Sequence of operations to be performed on the keyframes
80105 */
80106plots.modifyFrames = function(gd, operations) {
80107 var i, op, frame;
80108 var _frames = gd._transitionData._frames;
80109 var _frameHash = gd._transitionData._frameHash;
80110
80111 for(i = 0; i < operations.length; i++) {
80112 op = operations[i];
80113
80114 switch(op.type) {
80115 // No reason this couldn't exist, but is currently unused/untested:
80116 /* case 'rename':
80117 frame = _frames[op.index];
80118 delete _frameHash[frame.name];
80119 _frameHash[op.name] = frame;
80120 frame.name = op.name;
80121 break;*/
80122 case 'replace':
80123 frame = op.value;
80124 var oldName = (_frames[op.index] || {}).name;
80125 var newName = frame.name;
80126 _frames[op.index] = _frameHash[newName] = frame;
80127
80128 if(newName !== oldName) {
80129 // If name has changed in addition to replacement, then update
80130 // the lookup table:
80131 delete _frameHash[oldName];
80132 _frameHash[newName] = frame;
80133 }
80134
80135 break;
80136 case 'insert':
80137 frame = op.value;
80138 _frameHash[frame.name] = frame;
80139 _frames.splice(op.index, 0, frame);
80140 break;
80141 case 'delete':
80142 frame = _frames[op.index];
80143 delete _frameHash[frame.name];
80144 _frames.splice(op.index, 1);
80145 break;
80146 }
80147 }
80148
80149 return Promise.resolve();
80150};
80151
80152/*
80153 * Compute a keyframe. Merge a keyframe into its base frame(s) and
80154 * expand properties.
80155 *
80156 * @param {object} frameLookup
80157 * An object containing frames keyed by name (i.e. gd._transitionData._frameHash)
80158 * @param {string} frame
80159 * The name of the keyframe to be computed
80160 *
80161 * Returns: a new object with the merged content
80162 */
80163plots.computeFrame = function(gd, frameName) {
80164 var frameLookup = gd._transitionData._frameHash;
80165 var i, traceIndices, traceIndex, destIndex;
80166
80167 // Null or undefined will fail on .toString(). We'll allow numbers since we
80168 // make it clear frames must be given string names, but we'll allow numbers
80169 // here since they're otherwise fine for looking up frames as long as they're
80170 // properly cast to strings. We really just want to ensure here that this
80171 // 1) doesn't fail, and
80172 // 2) doens't give an incorrect answer (which String(frameName) would)
80173 if(!frameName) {
80174 throw new Error('computeFrame must be given a string frame name');
80175 }
80176
80177 var framePtr = frameLookup[frameName.toString()];
80178
80179 // Return false if the name is invalid:
80180 if(!framePtr) {
80181 return false;
80182 }
80183
80184 var frameStack = [framePtr];
80185 var frameNameStack = [framePtr.name];
80186
80187 // Follow frame pointers:
80188 while(framePtr.baseframe && (framePtr = frameLookup[framePtr.baseframe.toString()])) {
80189 // Avoid infinite loops:
80190 if(frameNameStack.indexOf(framePtr.name) !== -1) break;
80191
80192 frameStack.push(framePtr);
80193 frameNameStack.push(framePtr.name);
80194 }
80195
80196 // A new object for the merged result:
80197 var result = {};
80198
80199 // Merge, starting with the last and ending with the desired frame:
80200 while((framePtr = frameStack.pop())) {
80201 if(framePtr.layout) {
80202 result.layout = plots.extendLayout(result.layout, framePtr.layout);
80203 }
80204
80205 if(framePtr.data) {
80206 if(!result.data) {
80207 result.data = [];
80208 }
80209 traceIndices = framePtr.traces;
80210
80211 if(!traceIndices) {
80212 // If not defined, assume serial order starting at zero
80213 traceIndices = [];
80214 for(i = 0; i < framePtr.data.length; i++) {
80215 traceIndices[i] = i;
80216 }
80217 }
80218
80219 if(!result.traces) {
80220 result.traces = [];
80221 }
80222
80223 for(i = 0; i < framePtr.data.length; i++) {
80224 // Loop through this frames data, find out where it should go,
80225 // and merge it!
80226 traceIndex = traceIndices[i];
80227 if(traceIndex === undefined || traceIndex === null) {
80228 continue;
80229 }
80230
80231 destIndex = result.traces.indexOf(traceIndex);
80232 if(destIndex === -1) {
80233 destIndex = result.data.length;
80234 result.traces[destIndex] = traceIndex;
80235 }
80236
80237 result.data[destIndex] = plots.extendTrace(result.data[destIndex], framePtr.data[i]);
80238 }
80239 }
80240 }
80241
80242 return result;
80243};
80244
80245/*
80246 * Recompute the lookup table that maps frame name -> frame object. addFrames/
80247 * deleteFrames already manages this data one at a time, so the only time this
80248 * is necessary is if you poke around manually in `gd._transitionData._frames`
80249 * and create and haven't updated the lookup table.
80250 */
80251plots.recomputeFrameHash = function(gd) {
80252 var hash = gd._transitionData._frameHash = {};
80253 var frames = gd._transitionData._frames;
80254 for(var i = 0; i < frames.length; i++) {
80255 var frame = frames[i];
80256 if(frame && frame.name) {
80257 hash[frame.name] = frame;
80258 }
80259 }
80260};
80261
80262/**
80263 * Extend an object, treating container arrays very differently by extracting
80264 * their contents and merging them separately.
80265 *
80266 * This exists so that we can extendDeepNoArrays and avoid stepping into data
80267 * arrays without knowledge of the plot schema, but so that we may also manually
80268 * recurse into known container arrays, such as transforms.
80269 *
80270 * See extendTrace and extendLayout below for usage.
80271 */
80272plots.extendObjectWithContainers = function(dest, src, containerPaths) {
80273 var containerProp, containerVal, i, j, srcProp, destProp, srcContainer, destContainer;
80274 var copy = Lib.extendDeepNoArrays({}, src || {});
80275 var expandedObj = Lib.expandObjectPaths(copy);
80276 var containerObj = {};
80277
80278 // Step through and extract any container properties. Otherwise extendDeepNoArrays
80279 // will clobber any existing properties with an empty array and then supplyDefaults
80280 // will reset everything to defaults.
80281 if(containerPaths && containerPaths.length) {
80282 for(i = 0; i < containerPaths.length; i++) {
80283 containerProp = Lib.nestedProperty(expandedObj, containerPaths[i]);
80284 containerVal = containerProp.get();
80285
80286 if(containerVal === undefined) {
80287 Lib.nestedProperty(containerObj, containerPaths[i]).set(null);
80288 } else {
80289 containerProp.set(null);
80290 Lib.nestedProperty(containerObj, containerPaths[i]).set(containerVal);
80291 }
80292 }
80293 }
80294
80295 dest = Lib.extendDeepNoArrays(dest || {}, expandedObj);
80296
80297 if(containerPaths && containerPaths.length) {
80298 for(i = 0; i < containerPaths.length; i++) {
80299 srcProp = Lib.nestedProperty(containerObj, containerPaths[i]);
80300 srcContainer = srcProp.get();
80301
80302 if(!srcContainer) continue;
80303
80304 destProp = Lib.nestedProperty(dest, containerPaths[i]);
80305 destContainer = destProp.get();
80306
80307 if(!Array.isArray(destContainer)) {
80308 destContainer = [];
80309 destProp.set(destContainer);
80310 }
80311
80312 for(j = 0; j < srcContainer.length; j++) {
80313 var srcObj = srcContainer[j];
80314
80315 if(srcObj === null) destContainer[j] = null;
80316 else {
80317 destContainer[j] = plots.extendObjectWithContainers(destContainer[j], srcObj);
80318 }
80319 }
80320
80321 destProp.set(destContainer);
80322 }
80323 }
80324
80325 return dest;
80326};
80327
80328plots.dataArrayContainers = ['transforms', 'dimensions'];
80329plots.layoutArrayContainers = Registry.layoutArrayContainers;
80330
80331/*
80332 * Extend a trace definition. This method:
80333 *
80334 * 1. directly transfers any array references
80335 * 2. manually recurses into container arrays like transforms
80336 *
80337 * The result is the original object reference with the new contents merged in.
80338 */
80339plots.extendTrace = function(destTrace, srcTrace) {
80340 return plots.extendObjectWithContainers(destTrace, srcTrace, plots.dataArrayContainers);
80341};
80342
80343/*
80344 * Extend a layout definition. This method:
80345 *
80346 * 1. directly transfers any array references (not critically important for
80347 * layout since there aren't really data arrays)
80348 * 2. manually recurses into container arrays like annotations
80349 *
80350 * The result is the original object reference with the new contents merged in.
80351 */
80352plots.extendLayout = function(destLayout, srcLayout) {
80353 return plots.extendObjectWithContainers(destLayout, srcLayout, plots.layoutArrayContainers);
80354};
80355
80356/**
80357 * Transition to a set of new data and layout properties from Plotly.animate
80358 *
80359 * @param {DOM element} gd
80360 * @param {Object[]} data
80361 * an array of data objects following the normal Plotly data definition format
80362 * @param {Object} layout
80363 * a layout object, following normal Plotly layout format
80364 * @param {Number[]} traces
80365 * indices of the corresponding traces specified in `data`
80366 * @param {Object} frameOpts
80367 * options for the frame (i.e. whether to redraw post-transition)
80368 * @param {Object} transitionOpts
80369 * options for the transition
80370 */
80371plots.transition = function(gd, data, layout, traces, frameOpts, transitionOpts) {
80372 var opts = {redraw: frameOpts.redraw};
80373 var transitionedTraces = {};
80374 var axEdits = [];
80375
80376 opts.prepareFn = function() {
80377 var dataLength = Array.isArray(data) ? data.length : 0;
80378 var traceIndices = traces.slice(0, dataLength);
80379
80380 for(var i = 0; i < traceIndices.length; i++) {
80381 var traceIdx = traceIndices[i];
80382 var trace = gd._fullData[traceIdx];
80383 var _module = trace._module;
80384
80385 // There's nothing to do if this module is not defined:
80386 if(!_module) continue;
80387
80388 // Don't register the trace as transitioned if it doesn't know what to do.
80389 // If it *is* registered, it will receive a callback that it's responsible
80390 // for calling in order to register the transition as having completed.
80391 if(_module.animatable) {
80392 var n = _module.basePlotModule.name;
80393 if(!transitionedTraces[n]) transitionedTraces[n] = [];
80394 transitionedTraces[n].push(traceIdx);
80395 }
80396
80397 gd.data[traceIndices[i]] = plots.extendTrace(gd.data[traceIndices[i]], data[i]);
80398 }
80399
80400 // Follow the same procedure. Clone it so we don't mangle the input, then
80401 // expand any object paths so we can merge deep into gd.layout:
80402 var layoutUpdate = Lib.expandObjectPaths(Lib.extendDeepNoArrays({}, layout));
80403
80404 // Before merging though, we need to modify the incoming layout. We only
80405 // know how to *transition* layout ranges, so it's imperative that a new
80406 // range not be sent to the layout before the transition has started. So
80407 // we must remove the things we can transition:
80408 var axisAttrRe = /^[xy]axis[0-9]*$/;
80409 for(var attr in layoutUpdate) {
80410 if(!axisAttrRe.test(attr)) continue;
80411 delete layoutUpdate[attr].range;
80412 }
80413
80414 plots.extendLayout(gd.layout, layoutUpdate);
80415
80416 // Supply defaults after applying the incoming properties. Note that any attempt
80417 // to simplify this step and reduce the amount of work resulted in the reconstruction
80418 // of essentially the whole supplyDefaults step, so that it seems sensible to just use
80419 // supplyDefaults even though it's heavier than would otherwise be desired for
80420 // transitions:
80421
80422 // first delete calcdata so supplyDefaults knows a calc step is coming
80423 delete gd.calcdata;
80424
80425 plots.supplyDefaults(gd);
80426 plots.doCalcdata(gd);
80427
80428 var newLayout = Lib.expandObjectPaths(layout);
80429
80430 if(newLayout) {
80431 var subplots = gd._fullLayout._plots;
80432
80433 for(var k in subplots) {
80434 var plotinfo = subplots[k];
80435 var xa = plotinfo.xaxis;
80436 var ya = plotinfo.yaxis;
80437 var xr0 = xa.range.slice();
80438 var yr0 = ya.range.slice();
80439
80440 var xr1 = null;
80441 var yr1 = null;
80442 var editX = null;
80443 var editY = null;
80444
80445 if(Array.isArray(newLayout[xa._name + '.range'])) {
80446 xr1 = newLayout[xa._name + '.range'].slice();
80447 } else if(Array.isArray((newLayout[xa._name] || {}).range)) {
80448 xr1 = newLayout[xa._name].range.slice();
80449 }
80450 if(Array.isArray(newLayout[ya._name + '.range'])) {
80451 yr1 = newLayout[ya._name + '.range'].slice();
80452 } else if(Array.isArray((newLayout[ya._name] || {}).range)) {
80453 yr1 = newLayout[ya._name].range.slice();
80454 }
80455
80456 if(xr0 && xr1 &&
80457 (xa.r2l(xr0[0]) !== xa.r2l(xr1[0]) || xa.r2l(xr0[1]) !== xa.r2l(xr1[1]))
80458 ) {
80459 editX = {xr0: xr0, xr1: xr1};
80460 }
80461 if(yr0 && yr1 &&
80462 (ya.r2l(yr0[0]) !== ya.r2l(yr1[0]) || ya.r2l(yr0[1]) !== ya.r2l(yr1[1]))
80463 ) {
80464 editY = {yr0: yr0, yr1: yr1};
80465 }
80466
80467 if(editX || editY) {
80468 axEdits.push(Lib.extendFlat({plotinfo: plotinfo}, editX, editY));
80469 }
80470 }
80471 }
80472
80473 return Promise.resolve();
80474 };
80475
80476 opts.runFn = function(makeCallback) {
80477 var traceTransitionOpts;
80478 var basePlotModules = gd._fullLayout._basePlotModules;
80479 var hasAxisTransition = axEdits.length;
80480 var i;
80481
80482 if(layout) {
80483 for(i = 0; i < basePlotModules.length; i++) {
80484 if(basePlotModules[i].transitionAxes) {
80485 basePlotModules[i].transitionAxes(gd, axEdits, transitionOpts, makeCallback);
80486 }
80487 }
80488 }
80489
80490 // Here handle the exception that we refuse to animate scales and axes at the same
80491 // time. In other words, if there's an axis transition, then set the data transition
80492 // to instantaneous.
80493 if(hasAxisTransition) {
80494 traceTransitionOpts = Lib.extendFlat({}, transitionOpts);
80495 traceTransitionOpts.duration = 0;
80496 // This means do not transition cartesian traces,
80497 // this happens on layout-only (e.g. axis range) animations
80498 delete transitionedTraces.cartesian;
80499 } else {
80500 traceTransitionOpts = transitionOpts;
80501 }
80502
80503 // Note that we pass a callback to *create* the callback that must be invoked on completion.
80504 // This is since not all traces know about transitions, so it greatly simplifies matters if
80505 // the trace is responsible for creating a callback, if needed, and then executing it when
80506 // the time is right.
80507 for(var n in transitionedTraces) {
80508 var traceIndices = transitionedTraces[n];
80509 var _module = gd._fullData[traceIndices[0]]._module;
80510 _module.basePlotModule.plot(gd, traceIndices, traceTransitionOpts, makeCallback);
80511 }
80512 };
80513
80514 return _transition(gd, transitionOpts, opts);
80515};
80516
80517/**
80518 * Transition to a set of new data and layout properties from Plotly.react
80519 *
80520 * @param {DOM element} gd
80521 * @param {object} restyleFlags
80522 * - anim {'all'|'some'}
80523 * @param {object} relayoutFlags
80524 * - anim {'all'|'some'}
80525 * @param {object} oldFullLayout : old (pre Plotly.react) fullLayout
80526 */
80527plots.transitionFromReact = function(gd, restyleFlags, relayoutFlags, oldFullLayout) {
80528 var fullLayout = gd._fullLayout;
80529 var transitionOpts = fullLayout.transition;
80530 var opts = {};
80531 var axEdits = [];
80532
80533 opts.prepareFn = function() {
80534 var subplots = fullLayout._plots;
80535
80536 // no need to redraw at end of transition,
80537 // if all changes are animatable
80538 opts.redraw = false;
80539 if(restyleFlags.anim === 'some') opts.redraw = true;
80540 if(relayoutFlags.anim === 'some') opts.redraw = true;
80541
80542 for(var k in subplots) {
80543 var plotinfo = subplots[k];
80544 var xa = plotinfo.xaxis;
80545 var ya = plotinfo.yaxis;
80546 var xr0 = oldFullLayout[xa._name].range.slice();
80547 var yr0 = oldFullLayout[ya._name].range.slice();
80548 var xr1 = xa.range.slice();
80549 var yr1 = ya.range.slice();
80550
80551 xa.setScale();
80552 ya.setScale();
80553
80554 var editX = null;
80555 var editY = null;
80556
80557 if(xa.r2l(xr0[0]) !== xa.r2l(xr1[0]) || xa.r2l(xr0[1]) !== xa.r2l(xr1[1])) {
80558 editX = {xr0: xr0, xr1: xr1};
80559 }
80560 if(ya.r2l(yr0[0]) !== ya.r2l(yr1[0]) || ya.r2l(yr0[1]) !== ya.r2l(yr1[1])) {
80561 editY = {yr0: yr0, yr1: yr1};
80562 }
80563
80564 if(editX || editY) {
80565 axEdits.push(Lib.extendFlat({plotinfo: plotinfo}, editX, editY));
80566 }
80567 }
80568
80569 return Promise.resolve();
80570 };
80571
80572 opts.runFn = function(makeCallback) {
80573 var fullData = gd._fullData;
80574 var fullLayout = gd._fullLayout;
80575 var basePlotModules = fullLayout._basePlotModules;
80576
80577 var axisTransitionOpts;
80578 var traceTransitionOpts;
80579 var transitionedTraces;
80580
80581 var allTraceIndices = [];
80582 for(var i = 0; i < fullData.length; i++) {
80583 allTraceIndices.push(i);
80584 }
80585
80586 function transitionAxes() {
80587 if(!gd._fullLayout) return;
80588 for(var j = 0; j < basePlotModules.length; j++) {
80589 if(basePlotModules[j].transitionAxes) {
80590 basePlotModules[j].transitionAxes(gd, axEdits, axisTransitionOpts, makeCallback);
80591 }
80592 }
80593 }
80594
80595 function transitionTraces() {
80596 if(!gd._fullLayout) return;
80597 for(var j = 0; j < basePlotModules.length; j++) {
80598 basePlotModules[j].plot(gd, transitionedTraces, traceTransitionOpts, makeCallback);
80599 }
80600 }
80601
80602 if(axEdits.length && restyleFlags.anim) {
80603 if(transitionOpts.ordering === 'traces first') {
80604 axisTransitionOpts = Lib.extendFlat({}, transitionOpts, {duration: 0});
80605 transitionedTraces = allTraceIndices;
80606 traceTransitionOpts = transitionOpts;
80607 setTimeout(transitionAxes, transitionOpts.duration);
80608 transitionTraces();
80609 } else {
80610 axisTransitionOpts = transitionOpts;
80611 transitionedTraces = null;
80612 traceTransitionOpts = Lib.extendFlat({}, transitionOpts, {duration: 0});
80613 setTimeout(transitionTraces, axisTransitionOpts.duration);
80614 transitionAxes();
80615 }
80616 } else if(axEdits.length) {
80617 axisTransitionOpts = transitionOpts;
80618 transitionAxes();
80619 } else if(restyleFlags.anim) {
80620 transitionedTraces = allTraceIndices;
80621 traceTransitionOpts = transitionOpts;
80622 transitionTraces();
80623 }
80624 };
80625
80626 return _transition(gd, transitionOpts, opts);
80627};
80628
80629/**
80630 * trace/layout transition wrapper that works
80631 * for transitions initiated by Plotly.animate and Plotly.react.
80632 *
80633 * @param {DOM element} gd
80634 * @param {object} transitionOpts
80635 * @param {object} opts
80636 * - redraw {boolean}
80637 * - prepareFn {function} *should return a Promise*
80638 * - runFn {function} ran inside executeTransitions
80639 */
80640function _transition(gd, transitionOpts, opts) {
80641 var aborted = false;
80642
80643 function executeCallbacks(list) {
80644 var p = Promise.resolve();
80645 if(!list) return p;
80646 while(list.length) {
80647 p = p.then((list.shift()));
80648 }
80649 return p;
80650 }
80651
80652 function flushCallbacks(list) {
80653 if(!list) return;
80654 while(list.length) {
80655 list.shift();
80656 }
80657 }
80658
80659 function executeTransitions() {
80660 gd.emit('plotly_transitioning', []);
80661
80662 return new Promise(function(resolve) {
80663 // This flag is used to disabled things like autorange:
80664 gd._transitioning = true;
80665
80666 // When instantaneous updates are coming through quickly, it's too much to simply disable
80667 // all interaction, so store this flag so we can disambiguate whether mouse interactions
80668 // should be fully disabled or not:
80669 if(transitionOpts.duration > 0) {
80670 gd._transitioningWithDuration = true;
80671 }
80672
80673 // If another transition is triggered, this callback will be executed simply because it's
80674 // in the interruptCallbacks queue. If this transition completes, it will instead flush
80675 // that queue and forget about this callback.
80676 gd._transitionData._interruptCallbacks.push(function() {
80677 aborted = true;
80678 });
80679
80680 if(opts.redraw) {
80681 gd._transitionData._interruptCallbacks.push(function() {
80682 return Registry.call('redraw', gd);
80683 });
80684 }
80685
80686 // Emit this and make sure it happens last:
80687 gd._transitionData._interruptCallbacks.push(function() {
80688 gd.emit('plotly_transitioninterrupted', []);
80689 });
80690
80691 // Construct callbacks that are executed on transition end. This ensures the d3 transitions
80692 // are *complete* before anything else is done.
80693 var numCallbacks = 0;
80694 var numCompleted = 0;
80695 function makeCallback() {
80696 numCallbacks++;
80697 return function() {
80698 numCompleted++;
80699 // When all are complete, perform a redraw:
80700 if(!aborted && numCompleted === numCallbacks) {
80701 completeTransition(resolve);
80702 }
80703 };
80704 }
80705
80706 opts.runFn(makeCallback);
80707
80708 // If nothing else creates a callback, then this will trigger the completion in the next tick:
80709 setTimeout(makeCallback());
80710 });
80711 }
80712
80713 function completeTransition(callback) {
80714 // This a simple workaround for tests which purge the graph before animations
80715 // have completed. That's not a very common case, so this is the simplest
80716 // fix.
80717 if(!gd._transitionData) return;
80718
80719 flushCallbacks(gd._transitionData._interruptCallbacks);
80720
80721 return Promise.resolve().then(function() {
80722 if(opts.redraw) {
80723 return Registry.call('redraw', gd);
80724 }
80725 }).then(function() {
80726 // Set transitioning false again once the redraw has occurred. This is used, for example,
80727 // to prevent the trailing redraw from autoranging:
80728 gd._transitioning = false;
80729 gd._transitioningWithDuration = false;
80730
80731 gd.emit('plotly_transitioned', []);
80732 }).then(callback);
80733 }
80734
80735 function interruptPreviousTransitions() {
80736 // Fail-safe against purged plot:
80737 if(!gd._transitionData) return;
80738
80739 // If a transition is interrupted, set this to false. At the moment, the only thing that would
80740 // interrupt a transition is another transition, so that it will momentarily be set to true
80741 // again, but this determines whether autorange or dragbox work, so it's for the sake of
80742 // cleanliness:
80743 gd._transitioning = false;
80744
80745 return executeCallbacks(gd._transitionData._interruptCallbacks);
80746 }
80747
80748 var seq = [
80749 plots.previousPromises,
80750 interruptPreviousTransitions,
80751 opts.prepareFn,
80752 plots.rehover,
80753 executeTransitions
80754 ];
80755
80756 var transitionStarting = Lib.syncOrAsync(seq, gd);
80757
80758 if(!transitionStarting || !transitionStarting.then) {
80759 transitionStarting = Promise.resolve();
80760 }
80761
80762 return transitionStarting.then(function() { return gd; });
80763}
80764
80765plots.doCalcdata = function(gd, traces) {
80766 var axList = axisIDs.list(gd);
80767 var fullData = gd._fullData;
80768 var fullLayout = gd._fullLayout;
80769
80770 var trace, _module, i, j;
80771
80772 // XXX: Is this correct? Needs a closer look so that *some* traces can be recomputed without
80773 // *all* needing doCalcdata:
80774 var calcdata = new Array(fullData.length);
80775 var oldCalcdata = (gd.calcdata || []).slice();
80776 gd.calcdata = calcdata;
80777
80778 // extra helper variables
80779
80780 // how many box/violins plots do we have (in case they're grouped)
80781 fullLayout._numBoxes = 0;
80782 fullLayout._numViolins = 0;
80783
80784 // initialize violin per-scale-group stats container
80785 fullLayout._violinScaleGroupStats = {};
80786
80787 // for calculating avg luminosity of heatmaps
80788 gd._hmpixcount = 0;
80789 gd._hmlumcount = 0;
80790
80791 // for sharing colors across pies / sunbursts / treemap / icicle / funnelarea (and for legend)
80792 fullLayout._piecolormap = {};
80793 fullLayout._sunburstcolormap = {};
80794 fullLayout._treemapcolormap = {};
80795 fullLayout._iciclecolormap = {};
80796 fullLayout._funnelareacolormap = {};
80797
80798 // If traces were specified and this trace was not included,
80799 // then transfer it over from the old calcdata:
80800 for(i = 0; i < fullData.length; i++) {
80801 if(Array.isArray(traces) && traces.indexOf(i) === -1) {
80802 calcdata[i] = oldCalcdata[i];
80803 continue;
80804 }
80805 }
80806
80807 for(i = 0; i < fullData.length; i++) {
80808 trace = fullData[i];
80809
80810 trace._arrayAttrs = PlotSchema.findArrayAttributes(trace);
80811
80812 // keep track of trace extremes (for autorange) in here
80813 trace._extremes = {};
80814 }
80815
80816 // add polar axes to axis list
80817 var polarIds = fullLayout._subplots.polar || [];
80818 for(i = 0; i < polarIds.length; i++) {
80819 axList.push(
80820 fullLayout[polarIds[i]].radialaxis,
80821 fullLayout[polarIds[i]].angularaxis
80822 );
80823 }
80824
80825 // clear relinked cmin/cmax values in shared axes to start aggregation from scratch
80826 for(var k in fullLayout._colorAxes) {
80827 var cOpts = fullLayout[k];
80828 if(cOpts.cauto !== false) {
80829 delete cOpts.cmin;
80830 delete cOpts.cmax;
80831 }
80832 }
80833
80834 var hasCalcTransform = false;
80835
80836 function transformCalci(i) {
80837 trace = fullData[i];
80838 _module = trace._module;
80839
80840 if(trace.visible === true && trace.transforms) {
80841 // we need one round of trace module calc before
80842 // the calc transform to 'fill in' the categories list
80843 // used for example in the data-to-coordinate method
80844 if(_module && _module.calc) {
80845 var cdi = _module.calc(gd, trace);
80846
80847 // must clear scene 'batches', so that 2nd
80848 // _module.calc call starts from scratch
80849 if(cdi[0] && cdi[0].t && cdi[0].t._scene) {
80850 delete cdi[0].t._scene.dirty;
80851 }
80852 }
80853
80854 for(j = 0; j < trace.transforms.length; j++) {
80855 var transform = trace.transforms[j];
80856
80857 _module = transformsRegistry[transform.type];
80858 if(_module && _module.calcTransform) {
80859 trace._hasCalcTransform = true;
80860 hasCalcTransform = true;
80861 _module.calcTransform(gd, trace, transform);
80862 }
80863 }
80864 }
80865 }
80866
80867 function calci(i, isContainer) {
80868 trace = fullData[i];
80869 _module = trace._module;
80870
80871 if(!!_module.isContainer !== isContainer) return;
80872
80873 var cd = [];
80874
80875 if(trace.visible === true && trace._length !== 0) {
80876 // clear existing ref in case it got relinked
80877 delete trace._indexToPoints;
80878 // keep ref of index-to-points map object of the *last* enabled transform,
80879 // this index-to-points map object is required to determine the calcdata indices
80880 // that correspond to input indices (e.g. from 'selectedpoints')
80881 var transforms = trace.transforms || [];
80882 for(j = transforms.length - 1; j >= 0; j--) {
80883 if(transforms[j].enabled) {
80884 trace._indexToPoints = transforms[j]._indexToPoints;
80885 break;
80886 }
80887 }
80888
80889 if(_module && _module.calc) {
80890 cd = _module.calc(gd, trace);
80891 }
80892 }
80893
80894 // Make sure there is a first point.
80895 //
80896 // This ensures there is a calcdata item for every trace,
80897 // even if cartesian logic doesn't handle it (for things like legends).
80898 if(!Array.isArray(cd) || !cd[0]) {
80899 cd = [{x: BADNUM, y: BADNUM}];
80900 }
80901
80902 // add the trace-wide properties to the first point,
80903 // per point properties to every point
80904 // t is the holder for trace-wide properties
80905 if(!cd[0].t) cd[0].t = {};
80906 cd[0].trace = trace;
80907
80908 calcdata[i] = cd;
80909 }
80910
80911 setupAxisCategories(axList, fullData, fullLayout);
80912
80913 // 'transform' loop - must calc container traces first
80914 // so that if their dependent traces can get transform properly
80915 for(i = 0; i < fullData.length; i++) calci(i, true);
80916 for(i = 0; i < fullData.length; i++) transformCalci(i);
80917
80918 // clear stuff that should recomputed in 'regular' loop
80919 if(hasCalcTransform) setupAxisCategories(axList, fullData, fullLayout);
80920
80921 // 'regular' loop - make sure container traces (eg carpet) calc before
80922 // contained traces (eg contourcarpet)
80923 for(i = 0; i < fullData.length; i++) calci(i, true);
80924 for(i = 0; i < fullData.length; i++) calci(i, false);
80925
80926 doCrossTraceCalc(gd);
80927
80928 // Sort axis categories per value if specified
80929 var sorted = sortAxisCategoriesByValue(axList, gd);
80930 if(sorted.length) {
80931 // how many box/violins plots do we have (in case they're grouped)
80932 fullLayout._numBoxes = 0;
80933 fullLayout._numViolins = 0;
80934 // If a sort operation was performed, run calc() again
80935 for(i = 0; i < sorted.length; i++) calci(sorted[i], true);
80936 for(i = 0; i < sorted.length; i++) calci(sorted[i], false);
80937 doCrossTraceCalc(gd);
80938 }
80939
80940 Registry.getComponentMethod('fx', 'calc')(gd);
80941 Registry.getComponentMethod('errorbars', 'calc')(gd);
80942};
80943
80944var sortAxisCategoriesByValueRegex = /(total|sum|min|max|mean|median) (ascending|descending)/;
80945
80946function sortAxisCategoriesByValue(axList, gd) {
80947 var affectedTraces = [];
80948 var i, j, k, l, o;
80949
80950 function zMapCategory(type, ax, value) {
80951 var axLetter = ax._id.charAt(0);
80952 if(type === 'histogram2dcontour') {
80953 var counterAxLetter = ax._counterAxes[0];
80954 var counterAx = axisIDs.getFromId(gd, counterAxLetter);
80955
80956 var xCategorical = axLetter === 'x' || (counterAxLetter === 'x' && counterAx.type === 'category');
80957 var yCategorical = axLetter === 'y' || (counterAxLetter === 'y' && counterAx.type === 'category');
80958
80959 return function(o, l) {
80960 if(o === 0 || l === 0) return -1; // Skip first row and column
80961 if(xCategorical && o === value[l].length - 1) return -1;
80962 if(yCategorical && l === value.length - 1) return -1;
80963
80964 return (axLetter === 'y' ? l : o) - 1;
80965 };
80966 } else {
80967 return function(o, l) {
80968 return axLetter === 'y' ? l : o;
80969 };
80970 }
80971 }
80972
80973 var aggFn = {
80974 'min': function(values) {return Lib.aggNums(Math.min, null, values);},
80975 'max': function(values) {return Lib.aggNums(Math.max, null, values);},
80976 'sum': function(values) {return Lib.aggNums(function(a, b) { return a + b;}, null, values);},
80977 'total': function(values) {return Lib.aggNums(function(a, b) { return a + b;}, null, values);},
80978 'mean': function(values) {return Lib.mean(values);},
80979 'median': function(values) {return Lib.median(values);}
80980 };
80981
80982 for(i = 0; i < axList.length; i++) {
80983 var ax = axList[i];
80984 if(ax.type !== 'category') continue;
80985
80986 // Order by value
80987 var match = ax.categoryorder.match(sortAxisCategoriesByValueRegex);
80988 if(match) {
80989 var aggregator = match[1];
80990 var order = match[2];
80991
80992 var axLetter = ax._id.charAt(0);
80993 var isX = axLetter === 'x';
80994
80995 // Store values associated with each category
80996 var categoriesValue = [];
80997 for(j = 0; j < ax._categories.length; j++) {
80998 categoriesValue.push([ax._categories[j], []]);
80999 }
81000
81001 // Collect values across traces
81002 for(j = 0; j < ax._traceIndices.length; j++) {
81003 var traceIndex = ax._traceIndices[j];
81004 var fullTrace = gd._fullData[traceIndex];
81005
81006 // Skip over invisible traces
81007 if(fullTrace.visible !== true) continue;
81008
81009 var type = fullTrace.type;
81010 if(Registry.traceIs(fullTrace, 'histogram')) {
81011 delete fullTrace._xautoBinFinished;
81012 delete fullTrace._yautoBinFinished;
81013 }
81014 var isSplom = type === 'splom';
81015 var isScattergl = type === 'scattergl';
81016
81017 var cd = gd.calcdata[traceIndex];
81018 for(k = 0; k < cd.length; k++) {
81019 var cdi = cd[k];
81020 var catIndex, value;
81021
81022 if(isSplom) {
81023 // If `splom`, collect values across dimensions
81024 // Find which dimension the current axis is representing
81025 var currentDimensionIndex = fullTrace._axesDim[ax._id];
81026
81027 // Apply logic to associated x axis if it's defined
81028 if(!isX) {
81029 var associatedXAxisID = fullTrace._diag[currentDimensionIndex][0];
81030 if(associatedXAxisID) ax = gd._fullLayout[axisIDs.id2name(associatedXAxisID)];
81031 }
81032
81033 var categories = cdi.trace.dimensions[currentDimensionIndex].values;
81034 for(l = 0; l < categories.length; l++) {
81035 catIndex = ax._categoriesMap[categories[l]];
81036
81037 // Collect associated values at index `l` over all other dimensions
81038 for(o = 0; o < cdi.trace.dimensions.length; o++) {
81039 if(o === currentDimensionIndex) continue;
81040 var dimension = cdi.trace.dimensions[o];
81041 categoriesValue[catIndex][1].push(dimension.values[l]);
81042 }
81043 }
81044 } else if(isScattergl) {
81045 // If `scattergl`, collect all values stashed under cdi.t
81046 for(l = 0; l < cdi.t.x.length; l++) {
81047 if(isX) {
81048 catIndex = cdi.t.x[l];
81049 value = cdi.t.y[l];
81050 } else {
81051 catIndex = cdi.t.y[l];
81052 value = cdi.t.x[l];
81053 }
81054 categoriesValue[catIndex][1].push(value);
81055 }
81056 // must clear scene 'batches', so that 2nd
81057 // _module.calc call starts from scratch
81058 if(cdi.t && cdi.t._scene) {
81059 delete cdi.t._scene.dirty;
81060 }
81061 } else if(cdi.hasOwnProperty('z')) {
81062 // If 2dMap, collect values in `z`
81063 value = cdi.z;
81064 var mapping = zMapCategory(fullTrace.type, ax, value);
81065
81066 for(l = 0; l < value.length; l++) {
81067 for(o = 0; o < value[l].length; o++) {
81068 catIndex = mapping(o, l);
81069 if(catIndex + 1) categoriesValue[catIndex][1].push(value[l][o]);
81070 }
81071 }
81072 } else {
81073 // For all other 2d cartesian traces
81074 catIndex = cdi.p;
81075 if(catIndex === undefined) catIndex = cdi[axLetter];
81076
81077 value = cdi.s;
81078 if(value === undefined) value = cdi.v;
81079 if(value === undefined) value = isX ? cdi.y : cdi.x;
81080
81081 if(!Array.isArray(value)) {
81082 if(value === undefined) value = [];
81083 else value = [value];
81084 }
81085 for(l = 0; l < value.length; l++) {
81086 categoriesValue[catIndex][1].push(value[l]);
81087 }
81088 }
81089 }
81090 }
81091
81092 ax._categoriesValue = categoriesValue;
81093
81094 var categoriesAggregatedValue = [];
81095 for(j = 0; j < categoriesValue.length; j++) {
81096 categoriesAggregatedValue.push([
81097 categoriesValue[j][0],
81098 aggFn[aggregator](categoriesValue[j][1])
81099 ]);
81100 }
81101
81102 // Sort by aggregated value
81103 categoriesAggregatedValue.sort(function(a, b) {
81104 return a[1] - b[1];
81105 });
81106
81107 ax._categoriesAggregatedValue = categoriesAggregatedValue;
81108
81109 // Set new category order
81110 ax._initialCategories = categoriesAggregatedValue.map(function(c) {
81111 return c[0];
81112 });
81113
81114 // Reverse if descending
81115 if(order === 'descending') {
81116 ax._initialCategories.reverse();
81117 }
81118
81119 // Sort all matching axes
81120 affectedTraces = affectedTraces.concat(ax.sortByInitialCategories());
81121 }
81122 }
81123 return affectedTraces;
81124}
81125
81126function setupAxisCategories(axList, fullData, fullLayout) {
81127 var axLookup = {};
81128
81129 function setupOne(ax) {
81130 ax.clearCalc();
81131 if(ax.type === 'multicategory') {
81132 ax.setupMultiCategory(fullData);
81133 }
81134
81135 axLookup[ax._id] = 1;
81136 }
81137
81138 Lib.simpleMap(axList, setupOne);
81139
81140 // look into match groups for 'missing' axes
81141 var matchGroups = fullLayout._axisMatchGroups || [];
81142 for(var i = 0; i < matchGroups.length; i++) {
81143 for(var axId in matchGroups[i]) {
81144 if(!axLookup[axId]) {
81145 setupOne(fullLayout[axisIDs.id2name(axId)]);
81146 }
81147 }
81148 }
81149}
81150
81151function doCrossTraceCalc(gd) {
81152 var fullLayout = gd._fullLayout;
81153 var modules = fullLayout._visibleModules;
81154 var hash = {};
81155 var i, j, k;
81156
81157 // position and range calculations for traces that
81158 // depend on each other ie bars (stacked or grouped)
81159 // and boxes (grouped) push each other out of the way
81160
81161 for(j = 0; j < modules.length; j++) {
81162 var _module = modules[j];
81163 var fn = _module.crossTraceCalc;
81164 if(fn) {
81165 var spType = _module.basePlotModule.name;
81166 if(hash[spType]) {
81167 Lib.pushUnique(hash[spType], fn);
81168 } else {
81169 hash[spType] = [fn];
81170 }
81171 }
81172 }
81173
81174 for(k in hash) {
81175 var methods = hash[k];
81176 var subplots = fullLayout._subplots[k];
81177
81178 if(Array.isArray(subplots)) {
81179 for(i = 0; i < subplots.length; i++) {
81180 var sp = subplots[i];
81181 var spInfo = k === 'cartesian' ?
81182 fullLayout._plots[sp] :
81183 fullLayout[sp];
81184
81185 for(j = 0; j < methods.length; j++) {
81186 methods[j](gd, spInfo, sp);
81187 }
81188 }
81189 } else {
81190 for(j = 0; j < methods.length; j++) {
81191 methods[j](gd);
81192 }
81193 }
81194 }
81195}
81196
81197plots.rehover = function(gd) {
81198 if(gd._fullLayout._rehover) {
81199 gd._fullLayout._rehover();
81200 }
81201};
81202
81203plots.redrag = function(gd) {
81204 if(gd._fullLayout._redrag) {
81205 gd._fullLayout._redrag();
81206 }
81207};
81208
81209plots.generalUpdatePerTraceModule = function(gd, subplot, subplotCalcData, subplotLayout) {
81210 var traceHashOld = subplot.traceHash;
81211 var traceHash = {};
81212 var i;
81213
81214 // build up moduleName -> calcData hash
81215 for(i = 0; i < subplotCalcData.length; i++) {
81216 var calcTraces = subplotCalcData[i];
81217 var trace = calcTraces[0].trace;
81218
81219 // skip over visible === false traces
81220 // as they don't have `_module` ref
81221 if(trace.visible) {
81222 traceHash[trace.type] = traceHash[trace.type] || [];
81223 traceHash[trace.type].push(calcTraces);
81224 }
81225 }
81226
81227 // when a trace gets deleted, make sure that its module's
81228 // plot method is called so that it is properly
81229 // removed from the DOM.
81230 for(var moduleNameOld in traceHashOld) {
81231 if(!traceHash[moduleNameOld]) {
81232 var fakeCalcTrace = traceHashOld[moduleNameOld][0];
81233 var fakeTrace = fakeCalcTrace[0].trace;
81234
81235 fakeTrace.visible = false;
81236 traceHash[moduleNameOld] = [fakeCalcTrace];
81237 }
81238 }
81239
81240 // call module plot method
81241 for(var moduleName in traceHash) {
81242 var moduleCalcData = traceHash[moduleName];
81243 var _module = moduleCalcData[0][0].trace._module;
81244
81245 _module.plot(gd, subplot, Lib.filterVisible(moduleCalcData), subplotLayout);
81246 }
81247
81248 // update moduleName -> calcData hash
81249 subplot.traceHash = traceHash;
81250};
81251
81252plots.plotBasePlot = function(desiredType, gd, traces, transitionOpts, makeOnCompleteCallback) {
81253 var _module = Registry.getModule(desiredType);
81254 var cdmodule = getModuleCalcData(gd.calcdata, _module)[0];
81255 _module.plot(gd, cdmodule, transitionOpts, makeOnCompleteCallback);
81256};
81257
81258plots.cleanBasePlot = function(desiredType, newFullData, newFullLayout, oldFullData, oldFullLayout) {
81259 var had = (oldFullLayout._has && oldFullLayout._has(desiredType));
81260 var has = (newFullLayout._has && newFullLayout._has(desiredType));
81261
81262 if(had && !has) {
81263 oldFullLayout['_' + desiredType + 'layer'].selectAll('g.trace').remove();
81264 }
81265};
81266
81267},{"../components/color":157,"../constants/numerical":267,"../lib":287,"../plot_api/plot_schema":322,"../plot_api/plot_template":323,"../plots/get_data":365,"../registry":376,"./animation_attributes":328,"./attributes":330,"./cartesian/axis_ids":338,"./cartesian/handle_outline":345,"./command":361,"./font_attributes":363,"./frame_attributes":364,"./layout_attributes":367,"@plotly/d3":20,"d3-format":29,"d3-time-format":30,"fast-isnumeric":33}],370:[function(_dereq_,module,exports){
81268'use strict';
81269
81270var Lib = _dereq_('../lib');
81271var Template = _dereq_('../plot_api/plot_template');
81272var handleDomainDefaults = _dereq_('./domain').defaults;
81273
81274
81275/**
81276 * Find and supply defaults to all subplots of a given type
81277 * This handles subplots that are contained within one container - so
81278 * gl3d, geo, ternary... but not 2d axes which have separate x and y axes
81279 * finds subplots, coerces their `domain` attributes, then calls the
81280 * given handleDefaults function to fill in everything else.
81281 *
81282 * layoutIn: the complete user-supplied input layout
81283 * layoutOut: the complete finished layout
81284 * fullData: the finished data array, used only to find subplots
81285 * opts: {
81286 * type: subplot type string
81287 * attributes: subplot attributes object
81288 * partition: 'x' or 'y', which direction to divide domain space by default
81289 * (default 'x', ie side-by-side subplots)
81290 * TODO: this option is only here because 3D and geo made opposite
81291 * choices in this regard previously and I didn't want to change it.
81292 * Instead we should do:
81293 * - something consistent
81294 * - something more square (4 cuts 2x2, 5/6 cuts 2x3, etc.)
81295 * - something that includes all subplot types in one arrangement,
81296 * now that we can have them together!
81297 * handleDefaults: function of (subplotLayoutIn, subplotLayoutOut, coerce, opts)
81298 * this opts object is passed through to handleDefaults, so attach any
81299 * additional items needed by this function here as well
81300 * }
81301 */
81302module.exports = function handleSubplotDefaults(layoutIn, layoutOut, fullData, opts) {
81303 var subplotType = opts.type;
81304 var subplotAttributes = opts.attributes;
81305 var handleDefaults = opts.handleDefaults;
81306 var partition = opts.partition || 'x';
81307
81308 var ids = layoutOut._subplots[subplotType];
81309 var idsLength = ids.length;
81310
81311 var baseId = idsLength && ids[0].replace(/\d+$/, '');
81312
81313 var subplotLayoutIn, subplotLayoutOut;
81314
81315 function coerce(attr, dflt) {
81316 return Lib.coerce(subplotLayoutIn, subplotLayoutOut, subplotAttributes, attr, dflt);
81317 }
81318
81319 for(var i = 0; i < idsLength; i++) {
81320 var id = ids[i];
81321
81322 // ternary traces get a layout ternary for free!
81323 if(layoutIn[id]) subplotLayoutIn = layoutIn[id];
81324 else subplotLayoutIn = layoutIn[id] = {};
81325
81326 subplotLayoutOut = Template.newContainer(layoutOut, id, baseId);
81327
81328 // All subplot containers get a `uirevision` inheriting from the base.
81329 // Currently all subplots containers have some user interaction
81330 // attributes, but if we ever add one that doesn't, we would need an
81331 // option to skip this step.
81332 coerce('uirevision', layoutOut.uirevision);
81333
81334 var dfltDomains = {};
81335 dfltDomains[partition] = [i / idsLength, (i + 1) / idsLength];
81336 handleDomainDefaults(subplotLayoutOut, layoutOut, coerce, dfltDomains);
81337
81338 opts.id = id;
81339 handleDefaults(subplotLayoutIn, subplotLayoutOut, coerce, opts);
81340 }
81341};
81342
81343},{"../lib":287,"../plot_api/plot_template":323,"./domain":362}],371:[function(_dereq_,module,exports){
81344'use strict';
81345
81346var docs = _dereq_('../constants/docs');
81347var FORMAT_LINK = docs.FORMAT_LINK;
81348var DATE_FORMAT_LINK = docs.DATE_FORMAT_LINK;
81349
81350function templateFormatStringDescription(opts) {
81351 var supportOther = opts && opts.supportOther;
81352
81353 return [
81354 'Variables are inserted using %{variable},',
81355 'for example "y: %{y}"' + (
81356 supportOther ?
81357 ' as well as %{xother}, {%_xother}, {%_xother_}, {%xother_}. When showing info for several points, *xother* will be added to those with different x positions from the first point. An underscore before or after *(x|y)other* will add a space on that side, only when this field is shown.' :
81358 '.'
81359 ),
81360 'Numbers are formatted using d3-format\'s syntax %{variable:d3-format}, for example "Price: %{y:$.2f}".',
81361 FORMAT_LINK,
81362 'for details on the formatting syntax.',
81363 'Dates are formatted using d3-time-format\'s syntax %{variable|d3-time-format}, for example "Day: %{2019-01-01|%A}".',
81364 DATE_FORMAT_LINK,
81365 'for details on the date formatting syntax.'
81366 ].join(' ');
81367}
81368
81369function describeVariables(extra) {
81370 var descPart = extra.description ? ' ' + extra.description : '';
81371 var keys = extra.keys || [];
81372 if(keys.length > 0) {
81373 var quotedKeys = [];
81374 for(var i = 0; i < keys.length; i++) {
81375 quotedKeys[i] = '`' + keys[i] + '`';
81376 }
81377 descPart = descPart + 'Finally, the template string has access to ';
81378 if(keys.length === 1) {
81379 descPart = 'variable ' + quotedKeys[0];
81380 } else {
81381 descPart = 'variables ' + quotedKeys.slice(0, -1).join(', ') + ' and ' + quotedKeys.slice(-1) + '.';
81382 }
81383 }
81384 return descPart;
81385}
81386
81387exports.hovertemplateAttrs = function(opts, extra) {
81388 opts = opts || {};
81389 extra = extra || {};
81390
81391 var descPart = describeVariables(extra);
81392
81393 var hovertemplate = {
81394 valType: 'string',
81395 dflt: '',
81396 editType: opts.editType || 'none',
81397 };
81398
81399 if(opts.arrayOk !== false) {
81400 hovertemplate.arrayOk = true;
81401 }
81402
81403 return hovertemplate;
81404};
81405
81406exports.texttemplateAttrs = function(opts, extra) {
81407 opts = opts || {};
81408 extra = extra || {};
81409
81410 var descPart = describeVariables(extra);
81411
81412 var texttemplate = {
81413 valType: 'string',
81414 dflt: '',
81415 editType: opts.editType || 'calc',
81416 };
81417
81418 if(opts.arrayOk !== false) {
81419 texttemplate.arrayOk = true;
81420 }
81421 return texttemplate;
81422};
81423
81424},{"../constants/docs":264}],372:[function(_dereq_,module,exports){
81425'use strict';
81426
81427var Ternary = _dereq_('./ternary');
81428
81429var getSubplotCalcData = _dereq_('../../plots/get_data').getSubplotCalcData;
81430var counterRegex = _dereq_('../../lib').counterRegex;
81431var TERNARY = 'ternary';
81432
81433exports.name = TERNARY;
81434
81435var attr = exports.attr = 'subplot';
81436
81437exports.idRoot = TERNARY;
81438
81439exports.idRegex = exports.attrRegex = counterRegex(TERNARY);
81440
81441var attributes = exports.attributes = {};
81442attributes[attr] = {
81443 valType: 'subplotid',
81444 dflt: 'ternary',
81445 editType: 'calc',
81446};
81447
81448exports.layoutAttributes = _dereq_('./layout_attributes');
81449
81450exports.supplyLayoutDefaults = _dereq_('./layout_defaults');
81451
81452exports.plot = function plot(gd) {
81453 var fullLayout = gd._fullLayout;
81454 var calcData = gd.calcdata;
81455 var ternaryIds = fullLayout._subplots[TERNARY];
81456
81457 for(var i = 0; i < ternaryIds.length; i++) {
81458 var ternaryId = ternaryIds[i];
81459 var ternaryCalcData = getSubplotCalcData(calcData, TERNARY, ternaryId);
81460 var ternary = fullLayout[ternaryId]._subplot;
81461
81462 // If ternary is not instantiated, create one!
81463 if(!ternary) {
81464 ternary = new Ternary({
81465 id: ternaryId,
81466 graphDiv: gd,
81467 container: fullLayout._ternarylayer.node()
81468 },
81469 fullLayout
81470 );
81471
81472 fullLayout[ternaryId]._subplot = ternary;
81473 }
81474
81475 ternary.plot(ternaryCalcData, fullLayout, gd._promises);
81476 }
81477};
81478
81479exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) {
81480 var oldTernaryKeys = oldFullLayout._subplots[TERNARY] || [];
81481
81482 for(var i = 0; i < oldTernaryKeys.length; i++) {
81483 var oldTernaryKey = oldTernaryKeys[i];
81484 var oldTernary = oldFullLayout[oldTernaryKey]._subplot;
81485
81486 if(!newFullLayout[oldTernaryKey] && !!oldTernary) {
81487 oldTernary.plotContainer.remove();
81488 oldTernary.clipDef.remove();
81489 oldTernary.clipDefRelative.remove();
81490 oldTernary.layers['a-title'].remove();
81491 oldTernary.layers['b-title'].remove();
81492 oldTernary.layers['c-title'].remove();
81493 }
81494 }
81495};
81496
81497},{"../../lib":287,"../../plots/get_data":365,"./layout_attributes":373,"./layout_defaults":374,"./ternary":375}],373:[function(_dereq_,module,exports){
81498'use strict';
81499
81500var colorAttrs = _dereq_('../../components/color/attributes');
81501var domainAttrs = _dereq_('../domain').attributes;
81502var axesAttrs = _dereq_('../cartesian/layout_attributes');
81503
81504var overrideAll = _dereq_('../../plot_api/edit_types').overrideAll;
81505var extendFlat = _dereq_('../../lib/extend').extendFlat;
81506
81507var ternaryAxesAttrs = {
81508 title: {
81509 text: axesAttrs.title.text,
81510 font: axesAttrs.title.font
81511 // TODO does standoff here make sense?
81512 },
81513 color: axesAttrs.color,
81514 // ticks
81515 tickmode: axesAttrs.tickmode,
81516 nticks: extendFlat({}, axesAttrs.nticks, {dflt: 6, min: 1}),
81517 tick0: axesAttrs.tick0,
81518 dtick: axesAttrs.dtick,
81519 tickvals: axesAttrs.tickvals,
81520 ticktext: axesAttrs.ticktext,
81521 ticks: axesAttrs.ticks,
81522 ticklen: axesAttrs.ticklen,
81523 tickwidth: axesAttrs.tickwidth,
81524 tickcolor: axesAttrs.tickcolor,
81525 showticklabels: axesAttrs.showticklabels,
81526 showtickprefix: axesAttrs.showtickprefix,
81527 tickprefix: axesAttrs.tickprefix,
81528 showticksuffix: axesAttrs.showticksuffix,
81529 ticksuffix: axesAttrs.ticksuffix,
81530 showexponent: axesAttrs.showexponent,
81531 exponentformat: axesAttrs.exponentformat,
81532 minexponent: axesAttrs.minexponent,
81533 separatethousands: axesAttrs.separatethousands,
81534 tickfont: axesAttrs.tickfont,
81535 tickangle: axesAttrs.tickangle,
81536 tickformat: axesAttrs.tickformat,
81537 tickformatstops: axesAttrs.tickformatstops,
81538 hoverformat: axesAttrs.hoverformat,
81539 // lines and grids
81540 showline: extendFlat({}, axesAttrs.showline, {dflt: true}),
81541 linecolor: axesAttrs.linecolor,
81542 linewidth: axesAttrs.linewidth,
81543 showgrid: extendFlat({}, axesAttrs.showgrid, {dflt: true}),
81544 gridcolor: axesAttrs.gridcolor,
81545 gridwidth: axesAttrs.gridwidth,
81546 layer: axesAttrs.layer,
81547 // range
81548 min: {
81549 valType: 'number',
81550 dflt: 0,
81551 min: 0,
81552 },
81553 _deprecated: {
81554 title: axesAttrs._deprecated.title,
81555 titlefont: axesAttrs._deprecated.titlefont
81556 }
81557};
81558
81559var attrs = module.exports = overrideAll({
81560 domain: domainAttrs({name: 'ternary'}),
81561
81562 bgcolor: {
81563 valType: 'color',
81564 dflt: colorAttrs.background,
81565 },
81566 sum: {
81567 valType: 'number',
81568 dflt: 1,
81569 min: 0,
81570 },
81571 aaxis: ternaryAxesAttrs,
81572 baxis: ternaryAxesAttrs,
81573 caxis: ternaryAxesAttrs
81574}, 'plot', 'from-root');
81575
81576// set uirevisions outside of `overrideAll` so we can get `editType: none`
81577attrs.uirevision = {
81578 valType: 'any',
81579 editType: 'none',
81580};
81581
81582attrs.aaxis.uirevision = attrs.baxis.uirevision = attrs.caxis.uirevision = {
81583 valType: 'any',
81584 editType: 'none',
81585};
81586
81587},{"../../components/color/attributes":156,"../../lib/extend":281,"../../plot_api/edit_types":316,"../cartesian/layout_attributes":349,"../domain":362}],374:[function(_dereq_,module,exports){
81588'use strict';
81589
81590var Color = _dereq_('../../components/color');
81591var Template = _dereq_('../../plot_api/plot_template');
81592var Lib = _dereq_('../../lib');
81593
81594var handleSubplotDefaults = _dereq_('../subplot_defaults');
81595var handleTickLabelDefaults = _dereq_('../cartesian/tick_label_defaults');
81596var handleTickMarkDefaults = _dereq_('../cartesian/tick_mark_defaults');
81597var handleTickValueDefaults = _dereq_('../cartesian/tick_value_defaults');
81598var handleLineGridDefaults = _dereq_('../cartesian/line_grid_defaults');
81599var layoutAttributes = _dereq_('./layout_attributes');
81600
81601var axesNames = ['aaxis', 'baxis', 'caxis'];
81602
81603module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
81604 handleSubplotDefaults(layoutIn, layoutOut, fullData, {
81605 type: 'ternary',
81606 attributes: layoutAttributes,
81607 handleDefaults: handleTernaryDefaults,
81608 font: layoutOut.font,
81609 paper_bgcolor: layoutOut.paper_bgcolor
81610 });
81611};
81612
81613function handleTernaryDefaults(ternaryLayoutIn, ternaryLayoutOut, coerce, options) {
81614 var bgColor = coerce('bgcolor');
81615 var sum = coerce('sum');
81616 options.bgColor = Color.combine(bgColor, options.paper_bgcolor);
81617 var axName, containerIn, containerOut;
81618
81619 // TODO: allow most (if not all) axis attributes to be set
81620 // in the outer container and used as defaults in the individual axes?
81621
81622 for(var j = 0; j < axesNames.length; j++) {
81623 axName = axesNames[j];
81624 containerIn = ternaryLayoutIn[axName] || {};
81625 containerOut = Template.newContainer(ternaryLayoutOut, axName);
81626 containerOut._name = axName;
81627
81628 handleAxisDefaults(containerIn, containerOut, options, ternaryLayoutOut);
81629 }
81630
81631 // if the min values contradict each other, set them all to default (0)
81632 // and delete *all* the inputs so the user doesn't get confused later by
81633 // changing one and having them all change.
81634 var aaxis = ternaryLayoutOut.aaxis;
81635 var baxis = ternaryLayoutOut.baxis;
81636 var caxis = ternaryLayoutOut.caxis;
81637 if(aaxis.min + baxis.min + caxis.min >= sum) {
81638 aaxis.min = 0;
81639 baxis.min = 0;
81640 caxis.min = 0;
81641 if(ternaryLayoutIn.aaxis) delete ternaryLayoutIn.aaxis.min;
81642 if(ternaryLayoutIn.baxis) delete ternaryLayoutIn.baxis.min;
81643 if(ternaryLayoutIn.caxis) delete ternaryLayoutIn.caxis.min;
81644 }
81645}
81646
81647function handleAxisDefaults(containerIn, containerOut, options, ternaryLayoutOut) {
81648 var axAttrs = layoutAttributes[containerOut._name];
81649
81650 function coerce(attr, dflt) {
81651 return Lib.coerce(containerIn, containerOut, axAttrs, attr, dflt);
81652 }
81653
81654 coerce('uirevision', ternaryLayoutOut.uirevision);
81655
81656 containerOut.type = 'linear'; // no other types allowed for ternary
81657
81658 var dfltColor = coerce('color');
81659 // if axis.color was provided, use it for fonts too; otherwise,
81660 // inherit from global font color in case that was provided.
81661 var dfltFontColor = (dfltColor !== axAttrs.color.dflt) ? dfltColor : options.font.color;
81662
81663 var axName = containerOut._name;
81664 var letterUpper = axName.charAt(0).toUpperCase();
81665 var dfltTitle = 'Component ' + letterUpper;
81666
81667 var title = coerce('title.text', dfltTitle);
81668 containerOut._hovertitle = title === dfltTitle ? title : letterUpper;
81669
81670 Lib.coerceFont(coerce, 'title.font', {
81671 family: options.font.family,
81672 size: Lib.bigFont(options.font.size),
81673 color: dfltFontColor
81674 });
81675
81676 // range is just set by 'min' - max is determined by the other axes mins
81677 coerce('min');
81678
81679 handleTickValueDefaults(containerIn, containerOut, coerce, 'linear');
81680 handleTickLabelDefaults(containerIn, containerOut, coerce, 'linear', {});
81681 handleTickMarkDefaults(containerIn, containerOut, coerce,
81682 { outerTicks: true });
81683
81684 var showTickLabels = coerce('showticklabels');
81685 if(showTickLabels) {
81686 Lib.coerceFont(coerce, 'tickfont', {
81687 family: options.font.family,
81688 size: options.font.size,
81689 color: dfltFontColor
81690 });
81691 coerce('tickangle');
81692 coerce('tickformat');
81693 }
81694
81695 handleLineGridDefaults(containerIn, containerOut, coerce, {
81696 dfltColor: dfltColor,
81697 bgColor: options.bgColor,
81698 // default grid color is darker here (60%, vs cartesian default ~91%)
81699 // because the grid is not square so the eye needs heavier cues to follow
81700 blend: 60,
81701 showLine: true,
81702 showGrid: true,
81703 noZeroLine: true,
81704 attributes: axAttrs
81705 });
81706
81707 coerce('hoverformat');
81708 coerce('layer');
81709}
81710
81711},{"../../components/color":157,"../../lib":287,"../../plot_api/plot_template":323,"../cartesian/line_grid_defaults":351,"../cartesian/tick_label_defaults":356,"../cartesian/tick_mark_defaults":357,"../cartesian/tick_value_defaults":358,"../subplot_defaults":370,"./layout_attributes":373}],375:[function(_dereq_,module,exports){
81712'use strict';
81713
81714var d3 = _dereq_('@plotly/d3');
81715var tinycolor = _dereq_('tinycolor2');
81716
81717var Registry = _dereq_('../../registry');
81718var Lib = _dereq_('../../lib');
81719var strTranslate = Lib.strTranslate;
81720var _ = Lib._;
81721var Color = _dereq_('../../components/color');
81722var Drawing = _dereq_('../../components/drawing');
81723var setConvert = _dereq_('../cartesian/set_convert');
81724var extendFlat = _dereq_('../../lib/extend').extendFlat;
81725var Plots = _dereq_('../plots');
81726var Axes = _dereq_('../cartesian/axes');
81727var dragElement = _dereq_('../../components/dragelement');
81728var Fx = _dereq_('../../components/fx');
81729var dragHelpers = _dereq_('../../components/dragelement/helpers');
81730var freeMode = dragHelpers.freeMode;
81731var rectMode = dragHelpers.rectMode;
81732var Titles = _dereq_('../../components/titles');
81733var prepSelect = _dereq_('../cartesian/select').prepSelect;
81734var selectOnClick = _dereq_('../cartesian/select').selectOnClick;
81735var clearSelect = _dereq_('../cartesian/select').clearSelect;
81736var clearSelectionsCache = _dereq_('../cartesian/select').clearSelectionsCache;
81737var constants = _dereq_('../cartesian/constants');
81738
81739function Ternary(options, fullLayout) {
81740 this.id = options.id;
81741 this.graphDiv = options.graphDiv;
81742 this.init(fullLayout);
81743 this.makeFramework(fullLayout);
81744
81745 // unfortunately, we have to keep track of some axis tick settings
81746 // as ternary subplots do not implement the 'ticks' editType
81747 this.aTickLayout = null;
81748 this.bTickLayout = null;
81749 this.cTickLayout = null;
81750}
81751
81752module.exports = Ternary;
81753
81754var proto = Ternary.prototype;
81755
81756proto.init = function(fullLayout) {
81757 this.container = fullLayout._ternarylayer;
81758 this.defs = fullLayout._defs;
81759 this.layoutId = fullLayout._uid;
81760 this.traceHash = {};
81761 this.layers = {};
81762};
81763
81764proto.plot = function(ternaryCalcData, fullLayout) {
81765 var _this = this;
81766 var ternaryLayout = fullLayout[_this.id];
81767 var graphSize = fullLayout._size;
81768
81769 _this._hasClipOnAxisFalse = false;
81770 for(var i = 0; i < ternaryCalcData.length; i++) {
81771 var trace = ternaryCalcData[i][0].trace;
81772
81773 if(trace.cliponaxis === false) {
81774 _this._hasClipOnAxisFalse = true;
81775 break;
81776 }
81777 }
81778
81779 _this.updateLayers(ternaryLayout);
81780 _this.adjustLayout(ternaryLayout, graphSize);
81781 Plots.generalUpdatePerTraceModule(_this.graphDiv, _this, ternaryCalcData, ternaryLayout);
81782 _this.layers.plotbg.select('path').call(Color.fill, ternaryLayout.bgcolor);
81783};
81784
81785proto.makeFramework = function(fullLayout) {
81786 var _this = this;
81787 var gd = _this.graphDiv;
81788 var ternaryLayout = fullLayout[_this.id];
81789
81790 var clipId = _this.clipId = 'clip' + _this.layoutId + _this.id;
81791 var clipIdRelative = _this.clipIdRelative = 'clip-relative' + _this.layoutId + _this.id;
81792
81793 // clippath for this ternary subplot
81794 _this.clipDef = Lib.ensureSingleById(fullLayout._clips, 'clipPath', clipId, function(s) {
81795 s.append('path').attr('d', 'M0,0Z');
81796 });
81797
81798 // 'relative' clippath (i.e. no translation) for this ternary subplot
81799 _this.clipDefRelative = Lib.ensureSingleById(fullLayout._clips, 'clipPath', clipIdRelative, function(s) {
81800 s.append('path').attr('d', 'M0,0Z');
81801 });
81802
81803 // container for everything in this ternary subplot
81804 _this.plotContainer = Lib.ensureSingle(_this.container, 'g', _this.id);
81805 _this.updateLayers(ternaryLayout);
81806
81807 Drawing.setClipUrl(_this.layers.backplot, clipId, gd);
81808 Drawing.setClipUrl(_this.layers.grids, clipId, gd);
81809};
81810
81811proto.updateLayers = function(ternaryLayout) {
81812 var _this = this;
81813 var layers = _this.layers;
81814
81815 // inside that container, we have one container for the data, and
81816 // one each for the three axes around it.
81817
81818 var plotLayers = ['draglayer', 'plotbg', 'backplot', 'grids'];
81819
81820 if(ternaryLayout.aaxis.layer === 'below traces') {
81821 plotLayers.push('aaxis', 'aline');
81822 }
81823 if(ternaryLayout.baxis.layer === 'below traces') {
81824 plotLayers.push('baxis', 'bline');
81825 }
81826 if(ternaryLayout.caxis.layer === 'below traces') {
81827 plotLayers.push('caxis', 'cline');
81828 }
81829
81830 plotLayers.push('frontplot');
81831
81832 if(ternaryLayout.aaxis.layer === 'above traces') {
81833 plotLayers.push('aaxis', 'aline');
81834 }
81835 if(ternaryLayout.baxis.layer === 'above traces') {
81836 plotLayers.push('baxis', 'bline');
81837 }
81838 if(ternaryLayout.caxis.layer === 'above traces') {
81839 plotLayers.push('caxis', 'cline');
81840 }
81841
81842 var toplevel = _this.plotContainer.selectAll('g.toplevel')
81843 .data(plotLayers, String);
81844
81845 var grids = ['agrid', 'bgrid', 'cgrid'];
81846
81847 toplevel.enter().append('g')
81848 .attr('class', function(d) { return 'toplevel ' + d; })
81849 .each(function(d) {
81850 var s = d3.select(this);
81851 layers[d] = s;
81852
81853 // containers for different trace types.
81854 // NOTE - this is different from cartesian, where all traces
81855 // are in front of grids. Here I'm putting maps behind the grids
81856 // so the grids will always be visible if they're requested.
81857 // Perhaps we want that for cartesian too?
81858 if(d === 'frontplot') {
81859 s.append('g').classed('scatterlayer', true);
81860 } else if(d === 'backplot') {
81861 s.append('g').classed('maplayer', true);
81862 } else if(d === 'plotbg') {
81863 s.append('path').attr('d', 'M0,0Z');
81864 } else if(d === 'aline' || d === 'bline' || d === 'cline') {
81865 s.append('path');
81866 } else if(d === 'grids') {
81867 grids.forEach(function(d) {
81868 layers[d] = s.append('g').classed('grid ' + d, true);
81869 });
81870 }
81871 });
81872
81873 toplevel.order();
81874};
81875
81876var whRatio = Math.sqrt(4 / 3);
81877
81878proto.adjustLayout = function(ternaryLayout, graphSize) {
81879 var _this = this;
81880 var domain = ternaryLayout.domain;
81881 var xDomainCenter = (domain.x[0] + domain.x[1]) / 2;
81882 var yDomainCenter = (domain.y[0] + domain.y[1]) / 2;
81883 var xDomain = domain.x[1] - domain.x[0];
81884 var yDomain = domain.y[1] - domain.y[0];
81885 var wmax = xDomain * graphSize.w;
81886 var hmax = yDomain * graphSize.h;
81887 var sum = ternaryLayout.sum;
81888 var amin = ternaryLayout.aaxis.min;
81889 var bmin = ternaryLayout.baxis.min;
81890 var cmin = ternaryLayout.caxis.min;
81891
81892 var x0, y0, w, h, xDomainFinal, yDomainFinal;
81893
81894 if(wmax > whRatio * hmax) {
81895 h = hmax;
81896 w = h * whRatio;
81897 } else {
81898 w = wmax;
81899 h = w / whRatio;
81900 }
81901
81902 xDomainFinal = xDomain * w / wmax;
81903 yDomainFinal = yDomain * h / hmax;
81904
81905 x0 = graphSize.l + graphSize.w * xDomainCenter - w / 2;
81906 y0 = graphSize.t + graphSize.h * (1 - yDomainCenter) - h / 2;
81907
81908 _this.x0 = x0;
81909 _this.y0 = y0;
81910 _this.w = w;
81911 _this.h = h;
81912 _this.sum = sum;
81913
81914 // set up the x and y axis objects we'll use to lay out the points
81915 _this.xaxis = {
81916 type: 'linear',
81917 range: [amin + 2 * cmin - sum, sum - amin - 2 * bmin],
81918 domain: [
81919 xDomainCenter - xDomainFinal / 2,
81920 xDomainCenter + xDomainFinal / 2
81921 ],
81922 _id: 'x'
81923 };
81924 setConvert(_this.xaxis, _this.graphDiv._fullLayout);
81925 _this.xaxis.setScale();
81926 _this.xaxis.isPtWithinRange = function(d) {
81927 return (
81928 d.a >= _this.aaxis.range[0] &&
81929 d.a <= _this.aaxis.range[1] &&
81930 d.b >= _this.baxis.range[1] &&
81931 d.b <= _this.baxis.range[0] &&
81932 d.c >= _this.caxis.range[1] &&
81933 d.c <= _this.caxis.range[0]
81934 );
81935 };
81936
81937 _this.yaxis = {
81938 type: 'linear',
81939 range: [amin, sum - bmin - cmin],
81940 domain: [
81941 yDomainCenter - yDomainFinal / 2,
81942 yDomainCenter + yDomainFinal / 2
81943 ],
81944 _id: 'y'
81945 };
81946 setConvert(_this.yaxis, _this.graphDiv._fullLayout);
81947 _this.yaxis.setScale();
81948 _this.yaxis.isPtWithinRange = function() { return true; };
81949
81950 // set up the modified axes for tick drawing
81951 var yDomain0 = _this.yaxis.domain[0];
81952
81953 // aaxis goes up the left side. Set it up as a y axis, but with
81954 // fictitious angles and domain, but then rotate and translate
81955 // it into place at the end
81956 var aaxis = _this.aaxis = extendFlat({}, ternaryLayout.aaxis, {
81957 range: [amin, sum - bmin - cmin],
81958 side: 'left',
81959 // tickangle = 'auto' means 0 anyway for a y axis, need to coerce to 0 here
81960 // so we can shift by 30.
81961 tickangle: (+ternaryLayout.aaxis.tickangle || 0) - 30,
81962 domain: [yDomain0, yDomain0 + yDomainFinal * whRatio],
81963 anchor: 'free',
81964 position: 0,
81965 _id: 'y',
81966 _length: w
81967 });
81968 setConvert(aaxis, _this.graphDiv._fullLayout);
81969 aaxis.setScale();
81970
81971 // baxis goes across the bottom (backward). We can set it up as an x axis
81972 // without any enclosing transformation.
81973 var baxis = _this.baxis = extendFlat({}, ternaryLayout.baxis, {
81974 range: [sum - amin - cmin, bmin],
81975 side: 'bottom',
81976 domain: _this.xaxis.domain,
81977 anchor: 'free',
81978 position: 0,
81979 _id: 'x',
81980 _length: w
81981 });
81982 setConvert(baxis, _this.graphDiv._fullLayout);
81983 baxis.setScale();
81984
81985 // caxis goes down the right side. Set it up as a y axis, with
81986 // post-transformation similar to aaxis
81987 var caxis = _this.caxis = extendFlat({}, ternaryLayout.caxis, {
81988 range: [sum - amin - bmin, cmin],
81989 side: 'right',
81990 tickangle: (+ternaryLayout.caxis.tickangle || 0) + 30,
81991 domain: [yDomain0, yDomain0 + yDomainFinal * whRatio],
81992 anchor: 'free',
81993 position: 0,
81994 _id: 'y',
81995 _length: w
81996 });
81997 setConvert(caxis, _this.graphDiv._fullLayout);
81998 caxis.setScale();
81999
82000 var triangleClip = 'M' + x0 + ',' + (y0 + h) + 'h' + w + 'l-' + (w / 2) + ',-' + h + 'Z';
82001 _this.clipDef.select('path').attr('d', triangleClip);
82002 _this.layers.plotbg.select('path').attr('d', triangleClip);
82003
82004 var triangleClipRelative = 'M0,' + h + 'h' + w + 'l-' + (w / 2) + ',-' + h + 'Z';
82005 _this.clipDefRelative.select('path').attr('d', triangleClipRelative);
82006
82007 var plotTransform = strTranslate(x0, y0);
82008 _this.plotContainer.selectAll('.scatterlayer,.maplayer')
82009 .attr('transform', plotTransform);
82010
82011 _this.clipDefRelative.select('path').attr('transform', null);
82012
82013 // TODO: shift axes to accommodate linewidth*sin(30) tick mark angle
82014
82015 // TODO: there's probably an easier way to handle these translations/offsets now...
82016 var bTransform = strTranslate(x0 - baxis._offset, y0 + h);
82017
82018 _this.layers.baxis.attr('transform', bTransform);
82019 _this.layers.bgrid.attr('transform', bTransform);
82020
82021 var aTransform = strTranslate(x0 + w / 2, y0) +
82022 'rotate(30)' + strTranslate(0, -aaxis._offset);
82023 _this.layers.aaxis.attr('transform', aTransform);
82024 _this.layers.agrid.attr('transform', aTransform);
82025
82026 var cTransform = strTranslate(x0 + w / 2, y0) +
82027 'rotate(-30)' + strTranslate(0, -caxis._offset);
82028 _this.layers.caxis.attr('transform', cTransform);
82029 _this.layers.cgrid.attr('transform', cTransform);
82030
82031 _this.drawAxes(true);
82032
82033 _this.layers.aline.select('path')
82034 .attr('d', aaxis.showline ?
82035 'M' + x0 + ',' + (y0 + h) + 'l' + (w / 2) + ',-' + h : 'M0,0')
82036 .call(Color.stroke, aaxis.linecolor || '#000')
82037 .style('stroke-width', (aaxis.linewidth || 0) + 'px');
82038 _this.layers.bline.select('path')
82039 .attr('d', baxis.showline ?
82040 'M' + x0 + ',' + (y0 + h) + 'h' + w : 'M0,0')
82041 .call(Color.stroke, baxis.linecolor || '#000')
82042 .style('stroke-width', (baxis.linewidth || 0) + 'px');
82043 _this.layers.cline.select('path')
82044 .attr('d', caxis.showline ?
82045 'M' + (x0 + w / 2) + ',' + y0 + 'l' + (w / 2) + ',' + h : 'M0,0')
82046 .call(Color.stroke, caxis.linecolor || '#000')
82047 .style('stroke-width', (caxis.linewidth || 0) + 'px');
82048
82049 if(!_this.graphDiv._context.staticPlot) {
82050 _this.initInteractions();
82051 }
82052
82053 Drawing.setClipUrl(
82054 _this.layers.frontplot,
82055 _this._hasClipOnAxisFalse ? null : _this.clipId,
82056 _this.graphDiv
82057 );
82058};
82059
82060proto.drawAxes = function(doTitles) {
82061 var _this = this;
82062 var gd = _this.graphDiv;
82063 var titlesuffix = _this.id.substr(7) + 'title';
82064 var layers = _this.layers;
82065 var aaxis = _this.aaxis;
82066 var baxis = _this.baxis;
82067 var caxis = _this.caxis;
82068
82069 _this.drawAx(aaxis);
82070 _this.drawAx(baxis);
82071 _this.drawAx(caxis);
82072
82073 if(doTitles) {
82074 var apad = Math.max(aaxis.showticklabels ? aaxis.tickfont.size / 2 : 0,
82075 (caxis.showticklabels ? caxis.tickfont.size * 0.75 : 0) +
82076 (caxis.ticks === 'outside' ? caxis.ticklen * 0.87 : 0));
82077 var bpad = (baxis.showticklabels ? baxis.tickfont.size : 0) +
82078 (baxis.ticks === 'outside' ? baxis.ticklen : 0) + 3;
82079
82080 layers['a-title'] = Titles.draw(gd, 'a' + titlesuffix, {
82081 propContainer: aaxis,
82082 propName: _this.id + '.aaxis.title',
82083 placeholder: _(gd, 'Click to enter Component A title'),
82084 attributes: {
82085 x: _this.x0 + _this.w / 2,
82086 y: _this.y0 - aaxis.title.font.size / 3 - apad,
82087 'text-anchor': 'middle'
82088 }
82089 });
82090 layers['b-title'] = Titles.draw(gd, 'b' + titlesuffix, {
82091 propContainer: baxis,
82092 propName: _this.id + '.baxis.title',
82093 placeholder: _(gd, 'Click to enter Component B title'),
82094 attributes: {
82095 x: _this.x0 - bpad,
82096 y: _this.y0 + _this.h + baxis.title.font.size * 0.83 + bpad,
82097 'text-anchor': 'middle'
82098 }
82099 });
82100 layers['c-title'] = Titles.draw(gd, 'c' + titlesuffix, {
82101 propContainer: caxis,
82102 propName: _this.id + '.caxis.title',
82103 placeholder: _(gd, 'Click to enter Component C title'),
82104 attributes: {
82105 x: _this.x0 + _this.w + bpad,
82106 y: _this.y0 + _this.h + caxis.title.font.size * 0.83 + bpad,
82107 'text-anchor': 'middle'
82108 }
82109 });
82110 }
82111};
82112
82113proto.drawAx = function(ax) {
82114 var _this = this;
82115 var gd = _this.graphDiv;
82116 var axName = ax._name;
82117 var axLetter = axName.charAt(0);
82118 var axId = ax._id;
82119 var axLayer = _this.layers[axName];
82120 var counterAngle = 30;
82121
82122 var stashKey = axLetter + 'tickLayout';
82123 var newTickLayout = strTickLayout(ax);
82124 if(_this[stashKey] !== newTickLayout) {
82125 axLayer.selectAll('.' + axId + 'tick').remove();
82126 _this[stashKey] = newTickLayout;
82127 }
82128
82129 ax.setScale();
82130
82131 var vals = Axes.calcTicks(ax);
82132 var valsClipped = Axes.clipEnds(ax, vals);
82133 var transFn = Axes.makeTransTickFn(ax);
82134 var tickSign = Axes.getTickSigns(ax)[2];
82135
82136 var caRad = Lib.deg2rad(counterAngle);
82137 var pad = tickSign * (ax.linewidth || 1) / 2;
82138 var len = tickSign * ax.ticklen;
82139 var w = _this.w;
82140 var h = _this.h;
82141
82142 var tickPath = axLetter === 'b' ?
82143 'M0,' + pad + 'l' + (Math.sin(caRad) * len) + ',' + (Math.cos(caRad) * len) :
82144 'M' + pad + ',0l' + (Math.cos(caRad) * len) + ',' + (-Math.sin(caRad) * len);
82145
82146 var gridPath = {
82147 a: 'M0,0l' + h + ',-' + (w / 2),
82148 b: 'M0,0l-' + (w / 2) + ',-' + h,
82149 c: 'M0,0l-' + h + ',' + (w / 2)
82150 }[axLetter];
82151
82152 Axes.drawTicks(gd, ax, {
82153 vals: ax.ticks === 'inside' ? valsClipped : vals,
82154 layer: axLayer,
82155 path: tickPath,
82156 transFn: transFn,
82157 crisp: false
82158 });
82159
82160 Axes.drawGrid(gd, ax, {
82161 vals: valsClipped,
82162 layer: _this.layers[axLetter + 'grid'],
82163 path: gridPath,
82164 transFn: transFn,
82165 crisp: false
82166 });
82167
82168 Axes.drawLabels(gd, ax, {
82169 vals: vals,
82170 layer: axLayer,
82171 transFn: transFn,
82172 labelFns: Axes.makeLabelFns(ax, 0, counterAngle)
82173 });
82174};
82175
82176function strTickLayout(axLayout) {
82177 return axLayout.ticks + String(axLayout.ticklen) + String(axLayout.showticklabels);
82178}
82179
82180// hard coded paths for zoom corners
82181// uses the same sizing as cartesian, length is MINZOOM/2, width is 3px
82182var CLEN = constants.MINZOOM / 2 + 0.87;
82183var BLPATH = 'm-0.87,.5h' + CLEN + 'v3h-' + (CLEN + 5.2) +
82184 'l' + (CLEN / 2 + 2.6) + ',-' + (CLEN * 0.87 + 4.5) +
82185 'l2.6,1.5l-' + (CLEN / 2) + ',' + (CLEN * 0.87) + 'Z';
82186var BRPATH = 'm0.87,.5h-' + CLEN + 'v3h' + (CLEN + 5.2) +
82187 'l-' + (CLEN / 2 + 2.6) + ',-' + (CLEN * 0.87 + 4.5) +
82188 'l-2.6,1.5l' + (CLEN / 2) + ',' + (CLEN * 0.87) + 'Z';
82189var TOPPATH = 'm0,1l' + (CLEN / 2) + ',' + (CLEN * 0.87) +
82190 'l2.6,-1.5l-' + (CLEN / 2 + 2.6) + ',-' + (CLEN * 0.87 + 4.5) +
82191 'l-' + (CLEN / 2 + 2.6) + ',' + (CLEN * 0.87 + 4.5) +
82192 'l2.6,1.5l' + (CLEN / 2) + ',-' + (CLEN * 0.87) + 'Z';
82193var STARTMARKER = 'm0.5,0.5h5v-2h-5v-5h-2v5h-5v2h5v5h2Z';
82194
82195// I guess this could be shared with cartesian... but for now it's separate.
82196var SHOWZOOMOUTTIP = true;
82197
82198proto.clearSelect = function() {
82199 clearSelectionsCache(this.dragOptions);
82200 clearSelect(this.dragOptions.gd);
82201};
82202
82203proto.initInteractions = function() {
82204 var _this = this;
82205 var dragger = _this.layers.plotbg.select('path').node();
82206 var gd = _this.graphDiv;
82207 var zoomLayer = gd._fullLayout._zoomlayer;
82208 var scaleX;
82209 var scaleY;
82210
82211 // use plotbg for the main interactions
82212 this.dragOptions = {
82213 element: dragger,
82214 gd: gd,
82215 plotinfo: {
82216 id: _this.id,
82217 domain: gd._fullLayout[_this.id].domain,
82218 xaxis: _this.xaxis,
82219 yaxis: _this.yaxis
82220 },
82221 subplot: _this.id,
82222 prepFn: function(e, startX, startY) {
82223 // these aren't available yet when initInteractions
82224 // is called
82225 _this.dragOptions.xaxes = [_this.xaxis];
82226 _this.dragOptions.yaxes = [_this.yaxis];
82227
82228 scaleX = gd._fullLayout._invScaleX;
82229 scaleY = gd._fullLayout._invScaleY;
82230
82231 var dragModeNow = _this.dragOptions.dragmode = gd._fullLayout.dragmode;
82232
82233 if(freeMode(dragModeNow)) _this.dragOptions.minDrag = 1;
82234 else _this.dragOptions.minDrag = undefined;
82235
82236 if(dragModeNow === 'zoom') {
82237 _this.dragOptions.moveFn = zoomMove;
82238 _this.dragOptions.clickFn = clickZoomPan;
82239 _this.dragOptions.doneFn = zoomDone;
82240 zoomPrep(e, startX, startY);
82241 } else if(dragModeNow === 'pan') {
82242 _this.dragOptions.moveFn = plotDrag;
82243 _this.dragOptions.clickFn = clickZoomPan;
82244 _this.dragOptions.doneFn = dragDone;
82245 panPrep();
82246 _this.clearSelect(gd);
82247 } else if(rectMode(dragModeNow) || freeMode(dragModeNow)) {
82248 prepSelect(e, startX, startY, _this.dragOptions, dragModeNow);
82249 }
82250 }
82251 };
82252
82253 var x0, y0, mins0, span0, mins, lum, path0, dimmed, zb, corners;
82254
82255 function makeUpdate(_mins) {
82256 var attrs = {};
82257 attrs[_this.id + '.aaxis.min'] = _mins.a;
82258 attrs[_this.id + '.baxis.min'] = _mins.b;
82259 attrs[_this.id + '.caxis.min'] = _mins.c;
82260 return attrs;
82261 }
82262
82263 function clickZoomPan(numClicks, evt) {
82264 var clickMode = gd._fullLayout.clickmode;
82265
82266 removeZoombox(gd);
82267
82268 if(numClicks === 2) {
82269 gd.emit('plotly_doubleclick', null);
82270 Registry.call('_guiRelayout', gd, makeUpdate({a: 0, b: 0, c: 0}));
82271 }
82272
82273 if(clickMode.indexOf('select') > -1 && numClicks === 1) {
82274 selectOnClick(evt, gd, [_this.xaxis], [_this.yaxis], _this.id, _this.dragOptions);
82275 }
82276
82277 if(clickMode.indexOf('event') > -1) {
82278 Fx.click(gd, evt, _this.id);
82279 }
82280 }
82281
82282 function zoomPrep(e, startX, startY) {
82283 var dragBBox = dragger.getBoundingClientRect();
82284 x0 = startX - dragBBox.left;
82285 y0 = startY - dragBBox.top;
82286
82287 gd._fullLayout._calcInverseTransform(gd);
82288 var inverse = gd._fullLayout._invTransform;
82289 var transformedCoords = Lib.apply3DTransform(inverse)(x0, y0);
82290 x0 = transformedCoords[0];
82291 y0 = transformedCoords[1];
82292
82293 mins0 = {
82294 a: _this.aaxis.range[0],
82295 b: _this.baxis.range[1],
82296 c: _this.caxis.range[1]
82297 };
82298 mins = mins0;
82299 span0 = _this.aaxis.range[1] - mins0.a;
82300 lum = tinycolor(_this.graphDiv._fullLayout[_this.id].bgcolor).getLuminance();
82301 path0 = 'M0,' + _this.h + 'L' + (_this.w / 2) + ', 0L' + _this.w + ',' + _this.h + 'Z';
82302 dimmed = false;
82303
82304 zb = zoomLayer.append('path')
82305 .attr('class', 'zoombox')
82306 .attr('transform', strTranslate(_this.x0, _this.y0))
82307 .style({
82308 'fill': lum > 0.2 ? 'rgba(0,0,0,0)' : 'rgba(255,255,255,0)',
82309 'stroke-width': 0
82310 })
82311 .attr('d', path0);
82312
82313 corners = zoomLayer.append('path')
82314 .attr('class', 'zoombox-corners')
82315 .attr('transform', strTranslate(_this.x0, _this.y0))
82316 .style({
82317 fill: Color.background,
82318 stroke: Color.defaultLine,
82319 'stroke-width': 1,
82320 opacity: 0
82321 })
82322 .attr('d', 'M0,0Z');
82323
82324 _this.clearSelect(gd);
82325 }
82326
82327 function getAFrac(x, y) { return 1 - (y / _this.h); }
82328 function getBFrac(x, y) { return 1 - ((x + (_this.h - y) / Math.sqrt(3)) / _this.w); }
82329 function getCFrac(x, y) { return ((x - (_this.h - y) / Math.sqrt(3)) / _this.w); }
82330
82331 function zoomMove(dx0, dy0) {
82332 var x1 = x0 + dx0 * scaleX;
82333 var y1 = y0 + dy0 * scaleY;
82334 var afrac = Math.max(0, Math.min(1, getAFrac(x0, y0), getAFrac(x1, y1)));
82335 var bfrac = Math.max(0, Math.min(1, getBFrac(x0, y0), getBFrac(x1, y1)));
82336 var cfrac = Math.max(0, Math.min(1, getCFrac(x0, y0), getCFrac(x1, y1)));
82337 var xLeft = ((afrac / 2) + cfrac) * _this.w;
82338 var xRight = (1 - (afrac / 2) - bfrac) * _this.w;
82339 var xCenter = (xLeft + xRight) / 2;
82340 var xSpan = xRight - xLeft;
82341 var yBottom = (1 - afrac) * _this.h;
82342 var yTop = yBottom - xSpan / whRatio;
82343
82344 if(xSpan < constants.MINZOOM) {
82345 mins = mins0;
82346 zb.attr('d', path0);
82347 corners.attr('d', 'M0,0Z');
82348 } else {
82349 mins = {
82350 a: mins0.a + afrac * span0,
82351 b: mins0.b + bfrac * span0,
82352 c: mins0.c + cfrac * span0
82353 };
82354 zb.attr('d', path0 + 'M' + xLeft + ',' + yBottom +
82355 'H' + xRight + 'L' + xCenter + ',' + yTop +
82356 'L' + xLeft + ',' + yBottom + 'Z');
82357 corners.attr('d', 'M' + x0 + ',' + y0 + STARTMARKER +
82358 'M' + xLeft + ',' + yBottom + BLPATH +
82359 'M' + xRight + ',' + yBottom + BRPATH +
82360 'M' + xCenter + ',' + yTop + TOPPATH);
82361 }
82362
82363 if(!dimmed) {
82364 zb.transition()
82365 .style('fill', lum > 0.2 ? 'rgba(0,0,0,0.4)' :
82366 'rgba(255,255,255,0.3)')
82367 .duration(200);
82368 corners.transition()
82369 .style('opacity', 1)
82370 .duration(200);
82371 dimmed = true;
82372 }
82373
82374 gd.emit('plotly_relayouting', makeUpdate(mins));
82375 }
82376
82377 function zoomDone() {
82378 removeZoombox(gd);
82379
82380 if(mins === mins0) return;
82381
82382 Registry.call('_guiRelayout', gd, makeUpdate(mins));
82383
82384 if(SHOWZOOMOUTTIP && gd.data && gd._context.showTips) {
82385 Lib.notifier(_(gd, 'Double-click to zoom back out'), 'long');
82386 SHOWZOOMOUTTIP = false;
82387 }
82388 }
82389
82390 function panPrep() {
82391 mins0 = {
82392 a: _this.aaxis.range[0],
82393 b: _this.baxis.range[1],
82394 c: _this.caxis.range[1]
82395 };
82396 mins = mins0;
82397 }
82398
82399 function plotDrag(dx, dy) {
82400 var dxScaled = dx / _this.xaxis._m;
82401 var dyScaled = dy / _this.yaxis._m;
82402 mins = {
82403 a: mins0.a - dyScaled,
82404 b: mins0.b + (dxScaled + dyScaled) / 2,
82405 c: mins0.c - (dxScaled - dyScaled) / 2
82406 };
82407 var minsorted = [mins.a, mins.b, mins.c].sort(Lib.sorterAsc);
82408 var minindices = {
82409 a: minsorted.indexOf(mins.a),
82410 b: minsorted.indexOf(mins.b),
82411 c: minsorted.indexOf(mins.c)
82412 };
82413 if(minsorted[0] < 0) {
82414 if(minsorted[1] + minsorted[0] / 2 < 0) {
82415 minsorted[2] += minsorted[0] + minsorted[1];
82416 minsorted[0] = minsorted[1] = 0;
82417 } else {
82418 minsorted[2] += minsorted[0] / 2;
82419 minsorted[1] += minsorted[0] / 2;
82420 minsorted[0] = 0;
82421 }
82422 mins = {
82423 a: minsorted[minindices.a],
82424 b: minsorted[minindices.b],
82425 c: minsorted[minindices.c]
82426 };
82427 dy = (mins0.a - mins.a) * _this.yaxis._m;
82428 dx = (mins0.c - mins.c - mins0.b + mins.b) * _this.xaxis._m;
82429 }
82430
82431 // move the data (translate, don't redraw)
82432 var plotTransform = strTranslate(_this.x0 + dx, _this.y0 + dy);
82433 _this.plotContainer.selectAll('.scatterlayer,.maplayer')
82434 .attr('transform', plotTransform);
82435
82436 var plotTransform2 = strTranslate(-dx, -dy);
82437 _this.clipDefRelative.select('path').attr('transform', plotTransform2);
82438
82439 // move the ticks
82440 _this.aaxis.range = [mins.a, _this.sum - mins.b - mins.c];
82441 _this.baxis.range = [_this.sum - mins.a - mins.c, mins.b];
82442 _this.caxis.range = [_this.sum - mins.a - mins.b, mins.c];
82443
82444 _this.drawAxes(false);
82445
82446 if(_this._hasClipOnAxisFalse) {
82447 _this.plotContainer
82448 .select('.scatterlayer').selectAll('.trace')
82449 .call(Drawing.hideOutsideRangePoints, _this);
82450 }
82451
82452 gd.emit('plotly_relayouting', makeUpdate(mins));
82453 }
82454
82455 function dragDone() {
82456 Registry.call('_guiRelayout', gd, makeUpdate(mins));
82457 }
82458
82459 // finally, set up hover and click
82460 // these event handlers must already be set before dragElement.init
82461 // so it can stash them and override them.
82462 dragger.onmousemove = function(evt) {
82463 Fx.hover(gd, evt, _this.id);
82464 gd._fullLayout._lasthover = dragger;
82465 gd._fullLayout._hoversubplot = _this.id;
82466 };
82467
82468 dragger.onmouseout = function(evt) {
82469 if(gd._dragging) return;
82470
82471 dragElement.unhover(gd, evt);
82472 };
82473
82474 dragElement.init(this.dragOptions);
82475};
82476
82477function removeZoombox(gd) {
82478 d3.select(gd)
82479 .selectAll('.zoombox,.js-zoombox-backdrop,.js-zoombox-menu,.zoombox-corners')
82480 .remove();
82481}
82482
82483},{"../../components/color":157,"../../components/dragelement":176,"../../components/dragelement/helpers":175,"../../components/drawing":179,"../../components/fx":197,"../../components/titles":255,"../../lib":287,"../../lib/extend":281,"../../registry":376,"../cartesian/axes":334,"../cartesian/constants":341,"../cartesian/select":354,"../cartesian/set_convert":355,"../plots":369,"@plotly/d3":20,"tinycolor2":121}],376:[function(_dereq_,module,exports){
82484'use strict';
82485
82486var Loggers = _dereq_('./lib/loggers');
82487var noop = _dereq_('./lib/noop');
82488var pushUnique = _dereq_('./lib/push_unique');
82489var isPlainObject = _dereq_('./lib/is_plain_object');
82490var addStyleRule = _dereq_('./lib/dom').addStyleRule;
82491var ExtendModule = _dereq_('./lib/extend');
82492
82493var basePlotAttributes = _dereq_('./plots/attributes');
82494var baseLayoutAttributes = _dereq_('./plots/layout_attributes');
82495
82496var extendFlat = ExtendModule.extendFlat;
82497var extendDeepAll = ExtendModule.extendDeepAll;
82498
82499exports.modules = {};
82500exports.allCategories = {};
82501exports.allTypes = [];
82502exports.subplotsRegistry = {};
82503exports.transformsRegistry = {};
82504exports.componentsRegistry = {};
82505exports.layoutArrayContainers = [];
82506exports.layoutArrayRegexes = [];
82507exports.traceLayoutAttributes = {};
82508exports.localeRegistry = {};
82509exports.apiMethodRegistry = {};
82510exports.collectableSubplotTypes = null;
82511
82512/**
82513 * Top-level register routine, exported as Plotly.register
82514 *
82515 * @param {object array or array of objects} _modules :
82516 * module object or list of module object to register.
82517 *
82518 * A valid `moduleType: 'trace'` module has fields:
82519 * - name {string} : the trace type
82520 * - categories {array} : categories associated with this trace type,
82521 * tested with Register.traceIs()
82522 * - meta {object} : meta info (mostly for plot-schema)
82523 *
82524 * A valid `moduleType: 'locale'` module has fields:
82525 * - name {string} : the locale name. Should be a 2-digit language string ('en', 'de')
82526 * optionally with a country/region code ('en-GB', 'de-CH'). If a country
82527 * code is used but the base language locale has not yet been supplied,
82528 * we will use this locale for the base as well.
82529 * - dictionary {object} : the dictionary mapping input strings to localized strings
82530 * generally the keys should be the literal input strings, but
82531 * if default translations are provided you can use any string as a key.
82532 * - format {object} : a `d3.locale` format specifier for this locale
82533 * any omitted keys we'll fall back on en-US.
82534 *
82535 * A valid `moduleType: 'transform'` module has fields:
82536 * - name {string} : transform name
82537 * - transform {function} : default-level transform function
82538 * - calcTransform {function} : calc-level transform function
82539 * - attributes {object} : transform attributes declarations
82540 * - supplyDefaults {function} : attributes default-supply function
82541 *
82542 * A valid `moduleType: 'component'` module has fields:
82543 * - name {string} : the component name, used it with Register.getComponentMethod()
82544 * to employ component method.
82545 *
82546 * A valid `moduleType: 'apiMethod'` module has fields:
82547 * - name {string} : the api method name.
82548 * - fn {function} : the api method called with Register.call();
82549 *
82550 */
82551exports.register = function register(_modules) {
82552 exports.collectableSubplotTypes = null;
82553
82554 if(!_modules) {
82555 throw new Error('No argument passed to Plotly.register.');
82556 } else if(_modules && !Array.isArray(_modules)) {
82557 _modules = [_modules];
82558 }
82559
82560 for(var i = 0; i < _modules.length; i++) {
82561 var newModule = _modules[i];
82562
82563 if(!newModule) {
82564 throw new Error('Invalid module was attempted to be registered!');
82565 }
82566
82567 switch(newModule.moduleType) {
82568 case 'trace':
82569 registerTraceModule(newModule);
82570 break;
82571 case 'transform':
82572 registerTransformModule(newModule);
82573 break;
82574 case 'component':
82575 registerComponentModule(newModule);
82576 break;
82577 case 'locale':
82578 registerLocale(newModule);
82579 break;
82580 case 'apiMethod':
82581 var name = newModule.name;
82582 exports.apiMethodRegistry[name] = newModule.fn;
82583 break;
82584 default:
82585 throw new Error('Invalid module was attempted to be registered!');
82586 }
82587 }
82588};
82589
82590/**
82591 * Get registered module using trace object or trace type
82592 *
82593 * @param {object||string} trace
82594 * trace object with prop 'type' or trace type as a string
82595 * @return {object}
82596 * module object corresponding to trace type
82597 */
82598exports.getModule = function(trace) {
82599 var _module = exports.modules[getTraceType(trace)];
82600 if(!_module) return false;
82601 return _module._module;
82602};
82603
82604/**
82605 * Determine if this trace type is in a given category
82606 *
82607 * @param {object||string} traceType
82608 * a trace (object) or trace type (string)
82609 * @param {string} category
82610 * category in question
82611 * @return {boolean}
82612 */
82613exports.traceIs = function(traceType, category) {
82614 traceType = getTraceType(traceType);
82615
82616 // old Chart Studio Cloud workspace hack, nothing to see here
82617 if(traceType === 'various') return false;
82618
82619 var _module = exports.modules[traceType];
82620
82621 if(!_module) {
82622 if(traceType) {
82623 Loggers.log('Unrecognized trace type ' + traceType + '.');
82624 }
82625
82626 _module = exports.modules[basePlotAttributes.type.dflt];
82627 }
82628
82629 return !!_module.categories[category];
82630};
82631
82632/**
82633 * Determine if this trace has a transform of the given type and return
82634 * array of matching indices.
82635 *
82636 * @param {object} data
82637 * a trace object (member of data or fullData)
82638 * @param {string} type
82639 * type of trace to test
82640 * @return {array}
82641 * array of matching indices. If none found, returns []
82642 */
82643exports.getTransformIndices = function(data, type) {
82644 var indices = [];
82645 var transforms = data.transforms || [];
82646 for(var i = 0; i < transforms.length; i++) {
82647 if(transforms[i].type === type) {
82648 indices.push(i);
82649 }
82650 }
82651 return indices;
82652};
82653
82654/**
82655 * Determine if this trace has a transform of the given type
82656 *
82657 * @param {object} data
82658 * a trace object (member of data or fullData)
82659 * @param {string} type
82660 * type of trace to test
82661 * @return {boolean}
82662 */
82663exports.hasTransform = function(data, type) {
82664 var transforms = data.transforms || [];
82665 for(var i = 0; i < transforms.length; i++) {
82666 if(transforms[i].type === type) {
82667 return true;
82668 }
82669 }
82670 return false;
82671};
82672
82673/**
82674 * Retrieve component module method. Falls back on noop if either the
82675 * module or the method is missing, so the result can always be safely called
82676 *
82677 * @param {string} name
82678 * name of component (as declared in component module)
82679 * @param {string} method
82680 * name of component module method
82681 * @return {function}
82682 */
82683exports.getComponentMethod = function(name, method) {
82684 var _module = exports.componentsRegistry[name];
82685
82686 if(!_module) return noop;
82687 return _module[method] || noop;
82688};
82689
82690/**
82691 * Call registered api method.
82692 *
82693 * @param {string} name : api method name
82694 * @param {...array} args : arguments passed to api method
82695 * @return {any} : returns api method output
82696 */
82697exports.call = function() {
82698 var name = arguments[0];
82699 var args = [].slice.call(arguments, 1);
82700 return exports.apiMethodRegistry[name].apply(null, args);
82701};
82702
82703function registerTraceModule(_module) {
82704 var thisType = _module.name;
82705 var categoriesIn = _module.categories;
82706 var meta = _module.meta;
82707
82708 if(exports.modules[thisType]) {
82709 Loggers.log('Type ' + thisType + ' already registered');
82710 return;
82711 }
82712
82713 if(!exports.subplotsRegistry[_module.basePlotModule.name]) {
82714 registerSubplot(_module.basePlotModule);
82715 }
82716
82717 var categoryObj = {};
82718 for(var i = 0; i < categoriesIn.length; i++) {
82719 categoryObj[categoriesIn[i]] = true;
82720 exports.allCategories[categoriesIn[i]] = true;
82721 }
82722
82723 exports.modules[thisType] = {
82724 _module: _module,
82725 categories: categoryObj
82726 };
82727
82728 if(meta && Object.keys(meta).length) {
82729 exports.modules[thisType].meta = meta;
82730 }
82731
82732 exports.allTypes.push(thisType);
82733
82734 for(var componentName in exports.componentsRegistry) {
82735 mergeComponentAttrsToTrace(componentName, thisType);
82736 }
82737
82738 /*
82739 * Collect all trace layout attributes in one place for easier lookup later
82740 * but don't merge them into the base schema as it would confuse the docs
82741 * (at least after https://github.com/plotly/documentation/issues/202 gets done!)
82742 */
82743 if(_module.layoutAttributes) {
82744 extendFlat(exports.traceLayoutAttributes, _module.layoutAttributes);
82745 }
82746
82747 var basePlotModule = _module.basePlotModule;
82748 var bpmName = basePlotModule.name;
82749
82750 // add mapbox-gl CSS here to avoid console warning on instantiation
82751 if(bpmName === 'mapbox') {
82752 var styleRules = basePlotModule.constants.styleRules;
82753 for(var k in styleRules) {
82754 addStyleRule('.js-plotly-plot .plotly .mapboxgl-' + k, styleRules[k]);
82755 }
82756 }
82757
82758 // if `plotly-geo-assets.js` is not included,
82759 // add `PlotlyGeoAssets` global to stash references to all fetched
82760 // topojson / geojson data
82761 if((bpmName === 'geo' || bpmName === 'mapbox') &&
82762 (typeof window !== undefined && window.PlotlyGeoAssets === undefined)
82763 ) {
82764 window.PlotlyGeoAssets = {topojson: {}};
82765 }
82766}
82767
82768function registerSubplot(_module) {
82769 var plotType = _module.name;
82770
82771 if(exports.subplotsRegistry[plotType]) {
82772 Loggers.log('Plot type ' + plotType + ' already registered.');
82773 return;
82774 }
82775
82776 // relayout array handling will look for component module methods with this
82777 // name and won't find them because this is a subplot module... but that
82778 // should be fine, it will just fall back on redrawing the plot.
82779 findArrayRegexps(_module);
82780
82781 // not sure what's best for the 'cartesian' type at this point
82782 exports.subplotsRegistry[plotType] = _module;
82783
82784 for(var componentName in exports.componentsRegistry) {
82785 mergeComponentAttrsToSubplot(componentName, _module.name);
82786 }
82787}
82788
82789function registerComponentModule(_module) {
82790 if(typeof _module.name !== 'string') {
82791 throw new Error('Component module *name* must be a string.');
82792 }
82793
82794 var name = _module.name;
82795 exports.componentsRegistry[name] = _module;
82796
82797 if(_module.layoutAttributes) {
82798 if(_module.layoutAttributes._isLinkedToArray) {
82799 pushUnique(exports.layoutArrayContainers, name);
82800 }
82801 findArrayRegexps(_module);
82802 }
82803
82804 for(var traceType in exports.modules) {
82805 mergeComponentAttrsToTrace(name, traceType);
82806 }
82807
82808 for(var subplotName in exports.subplotsRegistry) {
82809 mergeComponentAttrsToSubplot(name, subplotName);
82810 }
82811
82812 for(var transformType in exports.transformsRegistry) {
82813 mergeComponentAttrsToTransform(name, transformType);
82814 }
82815
82816 if(_module.schema && _module.schema.layout) {
82817 extendDeepAll(baseLayoutAttributes, _module.schema.layout);
82818 }
82819}
82820
82821function registerTransformModule(_module) {
82822 if(typeof _module.name !== 'string') {
82823 throw new Error('Transform module *name* must be a string.');
82824 }
82825
82826 var prefix = 'Transform module ' + _module.name;
82827 var hasTransform = typeof _module.transform === 'function';
82828 var hasCalcTransform = typeof _module.calcTransform === 'function';
82829
82830 if(!hasTransform && !hasCalcTransform) {
82831 throw new Error(prefix + ' is missing a *transform* or *calcTransform* method.');
82832 }
82833 if(hasTransform && hasCalcTransform) {
82834 Loggers.log([
82835 prefix + ' has both a *transform* and *calcTransform* methods.',
82836 'Please note that all *transform* methods are executed',
82837 'before all *calcTransform* methods.'
82838 ].join(' '));
82839 }
82840 if(!isPlainObject(_module.attributes)) {
82841 Loggers.log(prefix + ' registered without an *attributes* object.');
82842 }
82843 if(typeof _module.supplyDefaults !== 'function') {
82844 Loggers.log(prefix + ' registered without a *supplyDefaults* method.');
82845 }
82846
82847 exports.transformsRegistry[_module.name] = _module;
82848
82849 for(var componentName in exports.componentsRegistry) {
82850 mergeComponentAttrsToTransform(componentName, _module.name);
82851 }
82852}
82853
82854function registerLocale(_module) {
82855 var locale = _module.name;
82856 var baseLocale = locale.split('-')[0];
82857
82858 var newDict = _module.dictionary;
82859 var newFormat = _module.format;
82860 var hasDict = newDict && Object.keys(newDict).length;
82861 var hasFormat = newFormat && Object.keys(newFormat).length;
82862
82863 var locales = exports.localeRegistry;
82864
82865 var localeObj = locales[locale];
82866 if(!localeObj) locales[locale] = localeObj = {};
82867
82868 // Should we use this dict for the base locale?
82869 // In case we're overwriting a previous dict for this locale, check
82870 // whether the base matches the full locale dict now. If we're not
82871 // overwriting, locales[locale] is undefined so this just checks if
82872 // baseLocale already had a dict or not.
82873 // Same logic for dateFormats
82874 if(baseLocale !== locale) {
82875 var baseLocaleObj = locales[baseLocale];
82876 if(!baseLocaleObj) locales[baseLocale] = baseLocaleObj = {};
82877
82878 if(hasDict && baseLocaleObj.dictionary === localeObj.dictionary) {
82879 baseLocaleObj.dictionary = newDict;
82880 }
82881 if(hasFormat && baseLocaleObj.format === localeObj.format) {
82882 baseLocaleObj.format = newFormat;
82883 }
82884 }
82885
82886 if(hasDict) localeObj.dictionary = newDict;
82887 if(hasFormat) localeObj.format = newFormat;
82888}
82889
82890function findArrayRegexps(_module) {
82891 if(_module.layoutAttributes) {
82892 var arrayAttrRegexps = _module.layoutAttributes._arrayAttrRegexps;
82893 if(arrayAttrRegexps) {
82894 for(var i = 0; i < arrayAttrRegexps.length; i++) {
82895 pushUnique(exports.layoutArrayRegexes, arrayAttrRegexps[i]);
82896 }
82897 }
82898 }
82899}
82900
82901function mergeComponentAttrsToTrace(componentName, traceType) {
82902 var componentSchema = exports.componentsRegistry[componentName].schema;
82903 if(!componentSchema || !componentSchema.traces) return;
82904
82905 var traceAttrs = componentSchema.traces[traceType];
82906 if(traceAttrs) {
82907 extendDeepAll(exports.modules[traceType]._module.attributes, traceAttrs);
82908 }
82909}
82910
82911function mergeComponentAttrsToTransform(componentName, transformType) {
82912 var componentSchema = exports.componentsRegistry[componentName].schema;
82913 if(!componentSchema || !componentSchema.transforms) return;
82914
82915 var transformAttrs = componentSchema.transforms[transformType];
82916 if(transformAttrs) {
82917 extendDeepAll(exports.transformsRegistry[transformType].attributes, transformAttrs);
82918 }
82919}
82920
82921function mergeComponentAttrsToSubplot(componentName, subplotName) {
82922 var componentSchema = exports.componentsRegistry[componentName].schema;
82923 if(!componentSchema || !componentSchema.subplots) return;
82924
82925 var subplotModule = exports.subplotsRegistry[subplotName];
82926 var subplotAttrs = subplotModule.layoutAttributes;
82927 var subplotAttr = subplotModule.attr === 'subplot' ? subplotModule.name : subplotModule.attr;
82928 if(Array.isArray(subplotAttr)) subplotAttr = subplotAttr[0];
82929
82930 var componentLayoutAttrs = componentSchema.subplots[subplotAttr];
82931 if(subplotAttrs && componentLayoutAttrs) {
82932 extendDeepAll(subplotAttrs, componentLayoutAttrs);
82933 }
82934}
82935
82936function getTraceType(traceType) {
82937 if(typeof traceType === 'object') traceType = traceType.type;
82938 return traceType;
82939}
82940
82941},{"./lib/dom":279,"./lib/extend":281,"./lib/is_plain_object":288,"./lib/loggers":291,"./lib/noop":296,"./lib/push_unique":301,"./plots/attributes":330,"./plots/layout_attributes":367}],377:[function(_dereq_,module,exports){
82942'use strict';
82943
82944var Registry = _dereq_('../registry');
82945var Lib = _dereq_('../lib');
82946
82947var extendFlat = Lib.extendFlat;
82948var extendDeep = Lib.extendDeep;
82949
82950// Put default plotTile layouts here
82951function cloneLayoutOverride(tileClass) {
82952 var override;
82953
82954 switch(tileClass) {
82955 case 'themes__thumb':
82956 override = {
82957 autosize: true,
82958 width: 150,
82959 height: 150,
82960 title: {text: ''},
82961 showlegend: false,
82962 margin: {l: 5, r: 5, t: 5, b: 5, pad: 0},
82963 annotations: []
82964 };
82965 break;
82966
82967 case 'thumbnail':
82968 override = {
82969 title: {text: ''},
82970 hidesources: true,
82971 showlegend: false,
82972 borderwidth: 0,
82973 bordercolor: '',
82974 margin: {l: 1, r: 1, t: 1, b: 1, pad: 0},
82975 annotations: []
82976 };
82977 break;
82978
82979 default:
82980 override = {};
82981 }
82982
82983
82984 return override;
82985}
82986
82987function keyIsAxis(keyName) {
82988 var types = ['xaxis', 'yaxis', 'zaxis'];
82989 return (types.indexOf(keyName.slice(0, 5)) > -1);
82990}
82991
82992
82993module.exports = function clonePlot(graphObj, options) {
82994 var i;
82995 var oldData = graphObj.data;
82996 var oldLayout = graphObj.layout;
82997 var newData = extendDeep([], oldData);
82998 var newLayout = extendDeep({}, oldLayout, cloneLayoutOverride(options.tileClass));
82999 var context = graphObj._context || {};
83000
83001 if(options.width) newLayout.width = options.width;
83002 if(options.height) newLayout.height = options.height;
83003
83004 if(options.tileClass === 'thumbnail' || options.tileClass === 'themes__thumb') {
83005 // kill annotations
83006 newLayout.annotations = [];
83007 var keys = Object.keys(newLayout);
83008
83009 for(i = 0; i < keys.length; i++) {
83010 if(keyIsAxis(keys[i])) {
83011 newLayout[keys[i]].title = {text: ''};
83012 }
83013 }
83014
83015 // kill colorbar and pie labels
83016 for(i = 0; i < newData.length; i++) {
83017 var trace = newData[i];
83018 trace.showscale = false;
83019 if(trace.marker) trace.marker.showscale = false;
83020 if(Registry.traceIs(trace, 'pie-like')) trace.textposition = 'none';
83021 }
83022 }
83023
83024 if(Array.isArray(options.annotations)) {
83025 for(i = 0; i < options.annotations.length; i++) {
83026 newLayout.annotations.push(options.annotations[i]);
83027 }
83028 }
83029
83030 // TODO: does this scene modification really belong here?
83031 // If we still need it, can it move into the gl3d module?
83032 var sceneIds = Object.keys(newLayout).filter(function(key) {
83033 return key.match(/^scene\d*$/);
83034 });
83035 if(sceneIds.length) {
83036 var axesImageOverride = {};
83037 if(options.tileClass === 'thumbnail') {
83038 axesImageOverride = {
83039 title: {text: ''},
83040 showaxeslabels: false,
83041 showticklabels: false,
83042 linetickenable: false
83043 };
83044 }
83045 for(i = 0; i < sceneIds.length; i++) {
83046 var scene = newLayout[sceneIds[i]];
83047
83048 if(!scene.xaxis) {
83049 scene.xaxis = {};
83050 }
83051
83052 if(!scene.yaxis) {
83053 scene.yaxis = {};
83054 }
83055
83056 if(!scene.zaxis) {
83057 scene.zaxis = {};
83058 }
83059
83060 extendFlat(scene.xaxis, axesImageOverride);
83061 extendFlat(scene.yaxis, axesImageOverride);
83062 extendFlat(scene.zaxis, axesImageOverride);
83063
83064 // TODO what does this do?
83065 scene._scene = null;
83066 }
83067 }
83068
83069 var gd = document.createElement('div');
83070 if(options.tileClass) gd.className = options.tileClass;
83071
83072 var plotTile = {
83073 gd: gd,
83074 td: gd, // for external (image server) compatibility
83075 layout: newLayout,
83076 data: newData,
83077 config: {
83078 staticPlot: (options.staticPlot === undefined) ?
83079 true :
83080 options.staticPlot,
83081 plotGlPixelRatio: (options.plotGlPixelRatio === undefined) ?
83082 2 :
83083 options.plotGlPixelRatio,
83084 displaylogo: options.displaylogo || false,
83085 showLink: options.showLink || false,
83086 showTips: options.showTips || false,
83087 mapboxAccessToken: context.mapboxAccessToken
83088 }
83089 };
83090
83091 if(options.setBackground !== 'transparent') {
83092 plotTile.config.setBackground = options.setBackground || 'opaque';
83093 }
83094
83095 // attaching the default Layout the gd, so you can grab it later
83096 plotTile.gd.defaultLayout = cloneLayoutOverride(options.tileClass);
83097
83098 return plotTile;
83099};
83100
83101},{"../lib":287,"../registry":376}],378:[function(_dereq_,module,exports){
83102'use strict';
83103
83104var Lib = _dereq_('../lib');
83105
83106var toImage = _dereq_('../plot_api/to_image');
83107
83108var fileSaver = _dereq_('./filesaver');
83109var helpers = _dereq_('./helpers');
83110
83111/**
83112 * Plotly.downloadImage
83113 *
83114 * @param {object | string | HTML div} gd
83115 * can either be a data/layout/config object
83116 * or an existing graph <div>
83117 * or an id to an existing graph <div>
83118 * @param {object} opts (see Plotly.toImage in ../plot_api/to_image)
83119 * @return {promise}
83120 */
83121function downloadImage(gd, opts) {
83122 var _gd;
83123 if(!Lib.isPlainObject(gd)) _gd = Lib.getGraphDiv(gd);
83124
83125 opts = opts || {};
83126 opts.format = opts.format || 'png';
83127 opts.width = opts.width || null;
83128 opts.height = opts.height || null;
83129 opts.imageDataOnly = true;
83130
83131 return new Promise(function(resolve, reject) {
83132 if(_gd && _gd._snapshotInProgress) {
83133 reject(new Error('Snapshotting already in progress.'));
83134 }
83135
83136 // see comments within svgtoimg for additional
83137 // discussion of problems with IE
83138 // can now draw to canvas, but CORS tainted canvas
83139 // does not allow toDataURL
83140 // svg format will work though
83141 if(Lib.isIE() && opts.format !== 'svg') {
83142 reject(new Error(helpers.MSG_IE_BAD_FORMAT));
83143 }
83144
83145 if(_gd) _gd._snapshotInProgress = true;
83146 var promise = toImage(gd, opts);
83147
83148 var filename = opts.filename || gd.fn || 'newplot';
83149 filename += '.' + opts.format.replace('-', '.');
83150
83151 promise.then(function(result) {
83152 if(_gd) _gd._snapshotInProgress = false;
83153 return fileSaver(result, filename, opts.format);
83154 }).then(function(name) {
83155 resolve(name);
83156 }).catch(function(err) {
83157 if(_gd) _gd._snapshotInProgress = false;
83158 reject(err);
83159 });
83160 });
83161}
83162
83163module.exports = downloadImage;
83164
83165},{"../lib":287,"../plot_api/to_image":326,"./filesaver":379,"./helpers":380}],379:[function(_dereq_,module,exports){
83166'use strict';
83167
83168var Lib = _dereq_('../lib');
83169var helpers = _dereq_('./helpers');
83170
83171/*
83172* substantial portions of this code from FileSaver.js
83173* https://github.com/eligrey/FileSaver.js
83174* License: https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md
83175* FileSaver.js
83176* A saveAs() FileSaver implementation.
83177* 1.1.20160328
83178*
83179* By Eli Grey, http://eligrey.com
83180* License: MIT
83181* See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md
83182*/
83183function fileSaver(url, name, format) {
83184 var saveLink = document.createElement('a');
83185 var canUseSaveLink = 'download' in saveLink;
83186
83187 var promise = new Promise(function(resolve, reject) {
83188 var blob;
83189 var objectUrl;
83190
83191 // IE 10+ (native saveAs)
83192 if(Lib.isIE()) {
83193 // At this point we are only dealing with a decoded SVG as
83194 // a data URL (since IE only supports SVG)
83195 blob = helpers.createBlob(url, 'svg');
83196 window.navigator.msSaveBlob(blob, name);
83197 blob = null;
83198 return resolve(name);
83199 }
83200
83201 if(canUseSaveLink) {
83202 blob = helpers.createBlob(url, format);
83203 objectUrl = helpers.createObjectURL(blob);
83204
83205 saveLink.href = objectUrl;
83206 saveLink.download = name;
83207 document.body.appendChild(saveLink);
83208 saveLink.click();
83209
83210 document.body.removeChild(saveLink);
83211 helpers.revokeObjectURL(objectUrl);
83212 blob = null;
83213
83214 return resolve(name);
83215 }
83216
83217 // Older versions of Safari did not allow downloading of blob urls
83218 if(Lib.isSafari()) {
83219 var prefix = format === 'svg' ? ',' : ';base64,';
83220 helpers.octetStream(prefix + encodeURIComponent(url));
83221 return resolve(name);
83222 }
83223
83224 reject(new Error('download error'));
83225 });
83226
83227 return promise;
83228}
83229
83230
83231module.exports = fileSaver;
83232
83233},{"../lib":287,"./helpers":380}],380:[function(_dereq_,module,exports){
83234'use strict';
83235
83236var Registry = _dereq_('../registry');
83237
83238exports.getDelay = function(fullLayout) {
83239 if(!fullLayout._has) return 0;
83240
83241 return (
83242 fullLayout._has('gl3d') ||
83243 fullLayout._has('gl2d') ||
83244 fullLayout._has('mapbox')
83245 ) ? 500 : 0;
83246};
83247
83248exports.getRedrawFunc = function(gd) {
83249 return function() {
83250 Registry.getComponentMethod('colorbar', 'draw')(gd);
83251 };
83252};
83253
83254exports.encodeSVG = function(svg) {
83255 return 'data:image/svg+xml,' + encodeURIComponent(svg);
83256};
83257
83258exports.encodeJSON = function(json) {
83259 return 'data:application/json,' + encodeURIComponent(json);
83260};
83261
83262var DOM_URL = window.URL || window.webkitURL;
83263
83264exports.createObjectURL = function(blob) {
83265 return DOM_URL.createObjectURL(blob);
83266};
83267
83268exports.revokeObjectURL = function(url) {
83269 return DOM_URL.revokeObjectURL(url);
83270};
83271
83272exports.createBlob = function(url, format) {
83273 if(format === 'svg') {
83274 return new window.Blob([url], {type: 'image/svg+xml;charset=utf-8'});
83275 } else if(format === 'full-json') {
83276 return new window.Blob([url], {type: 'application/json;charset=utf-8'});
83277 } else {
83278 var binary = fixBinary(window.atob(url));
83279 return new window.Blob([binary], {type: 'image/' + format});
83280 }
83281};
83282
83283exports.octetStream = function(s) {
83284 document.location.href = 'data:application/octet-stream' + s;
83285};
83286
83287// Taken from https://bl.ocks.org/nolanlawson/0eac306e4dac2114c752
83288function fixBinary(b) {
83289 var len = b.length;
83290 var buf = new ArrayBuffer(len);
83291 var arr = new Uint8Array(buf);
83292 for(var i = 0; i < len; i++) {
83293 arr[i] = b.charCodeAt(i);
83294 }
83295 return buf;
83296}
83297
83298exports.IMAGE_URL_PREFIX = /^data:image\/\w+;base64,/;
83299
83300exports.MSG_IE_BAD_FORMAT = 'Sorry IE does not support downloading from canvas. Try {format:\'svg\'} instead.';
83301
83302},{"../registry":376}],381:[function(_dereq_,module,exports){
83303'use strict';
83304
83305var helpers = _dereq_('./helpers');
83306
83307var Snapshot = {
83308 getDelay: helpers.getDelay,
83309 getRedrawFunc: helpers.getRedrawFunc,
83310 clone: _dereq_('./cloneplot'),
83311 toSVG: _dereq_('./tosvg'),
83312 svgToImg: _dereq_('./svgtoimg'),
83313 toImage: _dereq_('./toimage'),
83314 downloadImage: _dereq_('./download')
83315};
83316
83317module.exports = Snapshot;
83318
83319},{"./cloneplot":377,"./download":378,"./helpers":380,"./svgtoimg":382,"./toimage":383,"./tosvg":384}],382:[function(_dereq_,module,exports){
83320'use strict';
83321
83322var Lib = _dereq_('../lib');
83323var EventEmitter = _dereq_('events').EventEmitter;
83324
83325var helpers = _dereq_('./helpers');
83326
83327function svgToImg(opts) {
83328 var ev = opts.emitter || new EventEmitter();
83329
83330 var promise = new Promise(function(resolve, reject) {
83331 var Image = window.Image;
83332 var svg = opts.svg;
83333 var format = opts.format || 'png';
83334
83335 // IE only support svg
83336 if(Lib.isIE() && format !== 'svg') {
83337 var ieSvgError = new Error(helpers.MSG_IE_BAD_FORMAT);
83338 reject(ieSvgError);
83339 // eventually remove the ev
83340 // in favor of promises
83341 if(!opts.promise) {
83342 return ev.emit('error', ieSvgError);
83343 } else {
83344 return promise;
83345 }
83346 }
83347
83348 var canvas = opts.canvas;
83349 var scale = opts.scale || 1;
83350 var w0 = opts.width || 300;
83351 var h0 = opts.height || 150;
83352 var w1 = scale * w0;
83353 var h1 = scale * h0;
83354
83355 var ctx = canvas.getContext('2d');
83356 var img = new Image();
83357 var svgBlob, url;
83358
83359 if(format === 'svg' || Lib.isSafari()) {
83360 url = helpers.encodeSVG(svg);
83361 } else {
83362 svgBlob = helpers.createBlob(svg, 'svg');
83363 url = helpers.createObjectURL(svgBlob);
83364 }
83365
83366 canvas.width = w1;
83367 canvas.height = h1;
83368
83369 img.onload = function() {
83370 var imgData;
83371
83372 svgBlob = null;
83373 helpers.revokeObjectURL(url);
83374
83375 // don't need to draw to canvas if svg
83376 // save some time and also avoid failure on IE
83377 if(format !== 'svg') {
83378 ctx.drawImage(img, 0, 0, w1, h1);
83379 }
83380
83381 switch(format) {
83382 case 'jpeg':
83383 imgData = canvas.toDataURL('image/jpeg');
83384 break;
83385 case 'png':
83386 imgData = canvas.toDataURL('image/png');
83387 break;
83388 case 'webp':
83389 imgData = canvas.toDataURL('image/webp');
83390 break;
83391 case 'svg':
83392 imgData = url;
83393 break;
83394 default:
83395 var errorMsg = 'Image format is not jpeg, png, svg or webp.';
83396 reject(new Error(errorMsg));
83397 // eventually remove the ev
83398 // in favor of promises
83399 if(!opts.promise) {
83400 return ev.emit('error', errorMsg);
83401 }
83402 }
83403 resolve(imgData);
83404 // eventually remove the ev
83405 // in favor of promises
83406 if(!opts.promise) {
83407 ev.emit('success', imgData);
83408 }
83409 };
83410
83411 img.onerror = function(err) {
83412 svgBlob = null;
83413 helpers.revokeObjectURL(url);
83414
83415 reject(err);
83416 // eventually remove the ev
83417 // in favor of promises
83418 if(!opts.promise) {
83419 return ev.emit('error', err);
83420 }
83421 };
83422
83423 img.src = url;
83424 });
83425
83426 // temporary for backward compatibility
83427 // move to only Promise in 2.0.0
83428 // and eliminate the EventEmitter
83429 if(opts.promise) {
83430 return promise;
83431 }
83432
83433 return ev;
83434}
83435
83436module.exports = svgToImg;
83437
83438},{"../lib":287,"./helpers":380,"events":27}],383:[function(_dereq_,module,exports){
83439'use strict';
83440
83441var EventEmitter = _dereq_('events').EventEmitter;
83442
83443var Registry = _dereq_('../registry');
83444var Lib = _dereq_('../lib');
83445
83446var helpers = _dereq_('./helpers');
83447var clonePlot = _dereq_('./cloneplot');
83448var toSVG = _dereq_('./tosvg');
83449var svgToImg = _dereq_('./svgtoimg');
83450
83451/**
83452 * @param {object} gd figure Object
83453 * @param {object} opts option object
83454 * @param opts.format 'jpeg' | 'png' | 'webp' | 'svg'
83455 */
83456function toImage(gd, opts) {
83457 // first clone the GD so we can operate in a clean environment
83458 var ev = new EventEmitter();
83459
83460 var clone = clonePlot(gd, {format: 'png'});
83461 var clonedGd = clone.gd;
83462
83463 // put the cloned div somewhere off screen before attaching to DOM
83464 clonedGd.style.position = 'absolute';
83465 clonedGd.style.left = '-5000px';
83466 document.body.appendChild(clonedGd);
83467
83468 function wait() {
83469 var delay = helpers.getDelay(clonedGd._fullLayout);
83470
83471 setTimeout(function() {
83472 var svg = toSVG(clonedGd);
83473
83474 var canvas = document.createElement('canvas');
83475 canvas.id = Lib.randstr();
83476
83477 ev = svgToImg({
83478 format: opts.format,
83479 width: clonedGd._fullLayout.width,
83480 height: clonedGd._fullLayout.height,
83481 canvas: canvas,
83482 emitter: ev,
83483 svg: svg
83484 });
83485
83486 ev.clean = function() {
83487 if(clonedGd) document.body.removeChild(clonedGd);
83488 };
83489 }, delay);
83490 }
83491
83492 var redrawFunc = helpers.getRedrawFunc(clonedGd);
83493
83494 Registry.call('_doPlot', clonedGd, clone.data, clone.layout, clone.config)
83495 .then(redrawFunc)
83496 .then(wait)
83497 .catch(function(err) {
83498 ev.emit('error', err);
83499 });
83500
83501
83502 return ev;
83503}
83504
83505module.exports = toImage;
83506
83507},{"../lib":287,"../registry":376,"./cloneplot":377,"./helpers":380,"./svgtoimg":382,"./tosvg":384,"events":27}],384:[function(_dereq_,module,exports){
83508'use strict';
83509
83510var d3 = _dereq_('@plotly/d3');
83511
83512var Lib = _dereq_('../lib');
83513var Drawing = _dereq_('../components/drawing');
83514var Color = _dereq_('../components/color');
83515
83516var xmlnsNamespaces = _dereq_('../constants/xmlns_namespaces');
83517var DOUBLEQUOTE_REGEX = /"/g;
83518var DUMMY_SUB = 'TOBESTRIPPED';
83519var DUMMY_REGEX = new RegExp('("' + DUMMY_SUB + ')|(' + DUMMY_SUB + '")', 'g');
83520
83521function htmlEntityDecode(s) {
83522 var hiddenDiv = d3.select('body').append('div').style({display: 'none'}).html('');
83523 var replaced = s.replace(/(&[^;]*;)/gi, function(d) {
83524 if(d === '&lt;') { return '&#60;'; } // special handling for brackets
83525 if(d === '&rt;') { return '&#62;'; }
83526 if(d.indexOf('<') !== -1 || d.indexOf('>') !== -1) { return ''; }
83527 return hiddenDiv.html(d).text(); // everything else, let the browser decode it to unicode
83528 });
83529 hiddenDiv.remove();
83530 return replaced;
83531}
83532
83533function xmlEntityEncode(str) {
83534 return str.replace(/&(?!\w+;|\#[0-9]+;| \#x[0-9A-F]+;)/g, '&amp;');
83535}
83536
83537module.exports = function toSVG(gd, format, scale) {
83538 var fullLayout = gd._fullLayout;
83539 var svg = fullLayout._paper;
83540 var toppaper = fullLayout._toppaper;
83541 var width = fullLayout.width;
83542 var height = fullLayout.height;
83543 var i, k;
83544
83545 // make background color a rect in the svg, then revert after scraping
83546 // all other alterations have been dealt with by properly preparing the svg
83547 // in the first place... like setting cursors with css classes so we don't
83548 // have to remove them, and providing the right namespaces in the svg to
83549 // begin with
83550 svg.insert('rect', ':first-child')
83551 .call(Drawing.setRect, 0, 0, width, height)
83552 .call(Color.fill, fullLayout.paper_bgcolor);
83553
83554 // subplot-specific to-SVG methods
83555 // which notably add the contents of the gl-container
83556 // into the main svg node
83557 var basePlotModules = fullLayout._basePlotModules || [];
83558 for(i = 0; i < basePlotModules.length; i++) {
83559 var _module = basePlotModules[i];
83560
83561 if(_module.toSVG) _module.toSVG(gd);
83562 }
83563
83564 // add top items above them assumes everything in toppaper is either
83565 // a group or a defs, and if it's empty (like hoverlayer) we can ignore it.
83566 if(toppaper) {
83567 var nodes = toppaper.node().childNodes;
83568
83569 // make copy of nodes as childNodes prop gets mutated in loop below
83570 var topGroups = Array.prototype.slice.call(nodes);
83571
83572 for(i = 0; i < topGroups.length; i++) {
83573 var topGroup = topGroups[i];
83574
83575 if(topGroup.childNodes.length) svg.node().appendChild(topGroup);
83576 }
83577 }
83578
83579 // remove draglayer for Adobe Illustrator compatibility
83580 if(fullLayout._draggers) {
83581 fullLayout._draggers.remove();
83582 }
83583
83584 // in case the svg element had an explicit background color, remove this
83585 // we want the rect to get the color so it's the right size; svg bg will
83586 // fill whatever container it's displayed in regardless of plot size.
83587 svg.node().style.background = '';
83588
83589 svg.selectAll('text')
83590 .attr({'data-unformatted': null, 'data-math': null})
83591 .each(function() {
83592 var txt = d3.select(this);
83593
83594 // hidden text is pre-formatting mathjax, the browser ignores it
83595 // but in a static plot it's useless and it can confuse batik
83596 // we've tried to standardize on display:none but make sure we still
83597 // catch visibility:hidden if it ever arises
83598 if(this.style.visibility === 'hidden' || this.style.display === 'none') {
83599 txt.remove();
83600 return;
83601 } else {
83602 // clear other visibility/display values to default
83603 // to not potentially confuse non-browser SVG implementations
83604 txt.style({visibility: null, display: null});
83605 }
83606
83607 // Font family styles break things because of quotation marks,
83608 // so we must remove them *after* the SVG DOM has been serialized
83609 // to a string (browsers convert singles back)
83610 var ff = this.style.fontFamily;
83611 if(ff && ff.indexOf('"') !== -1) {
83612 txt.style('font-family', ff.replace(DOUBLEQUOTE_REGEX, DUMMY_SUB));
83613 }
83614 });
83615
83616 var queryParts = [];
83617 if(fullLayout._gradientUrlQueryParts) {
83618 for(k in fullLayout._gradientUrlQueryParts) queryParts.push(k);
83619 }
83620
83621 if(fullLayout._patternUrlQueryParts) {
83622 for(k in fullLayout._patternUrlQueryParts) queryParts.push(k);
83623 }
83624
83625 if(queryParts.length) {
83626 svg.selectAll(queryParts.join(',')).each(function() {
83627 var pt = d3.select(this);
83628
83629 // similar to font family styles above,
83630 // we must remove " after the SVG DOM has been serialized
83631 var fill = this.style.fill;
83632 if(fill && fill.indexOf('url(') !== -1) {
83633 pt.style('fill', fill.replace(DOUBLEQUOTE_REGEX, DUMMY_SUB));
83634 }
83635
83636 var stroke = this.style.stroke;
83637 if(stroke && stroke.indexOf('url(') !== -1) {
83638 pt.style('stroke', stroke.replace(DOUBLEQUOTE_REGEX, DUMMY_SUB));
83639 }
83640 });
83641 }
83642
83643 if(format === 'pdf' || format === 'eps') {
83644 // these formats make the extra line MathJax adds around symbols look super thick in some cases
83645 // it looks better if this is removed entirely.
83646 svg.selectAll('#MathJax_SVG_glyphs path')
83647 .attr('stroke-width', 0);
83648 }
83649
83650 // fix for IE namespacing quirk?
83651 // http://stackoverflow.com/questions/19610089/unwanted-namespaces-on-svg-markup-when-using-xmlserializer-in-javascript-with-ie
83652 svg.node().setAttributeNS(xmlnsNamespaces.xmlns, 'xmlns', xmlnsNamespaces.svg);
83653 svg.node().setAttributeNS(xmlnsNamespaces.xmlns, 'xmlns:xlink', xmlnsNamespaces.xlink);
83654
83655 if(format === 'svg' && scale) {
83656 svg.attr('width', scale * width);
83657 svg.attr('height', scale * height);
83658 svg.attr('viewBox', '0 0 ' + width + ' ' + height);
83659 }
83660
83661 var s = new window.XMLSerializer().serializeToString(svg.node());
83662 s = htmlEntityDecode(s);
83663 s = xmlEntityEncode(s);
83664
83665 // Fix quotations around font strings and gradient URLs
83666 s = s.replace(DUMMY_REGEX, '\'');
83667
83668 // Do we need this process now that IE9 and IE10 are not supported?
83669
83670 // IE is very strict, so we will need to clean
83671 // svg with the following regex
83672 // yes this is messy, but do not know a better way
83673 // Even with this IE will not work due to tainted canvas
83674 // see https://github.com/kangax/fabric.js/issues/1957
83675 // http://stackoverflow.com/questions/18112047/canvas-todataurl-working-in-all-browsers-except-ie10
83676 // Leave here just in case the CORS/tainted IE issue gets resolved
83677 if(Lib.isIE()) {
83678 // replace double quote with single quote
83679 s = s.replace(/"/gi, '\'');
83680 // url in svg are single quoted
83681 // since we changed double to single
83682 // we'll need to change these to double-quoted
83683 s = s.replace(/(\('#)([^']*)('\))/gi, '(\"#$2\")');
83684 // font names with spaces will be escaped single-quoted
83685 // we'll need to change these to double-quoted
83686 s = s.replace(/(\\')/gi, '\"');
83687 }
83688
83689 return s;
83690};
83691
83692},{"../components/color":157,"../components/drawing":179,"../constants/xmlns_namespaces":268,"../lib":287,"@plotly/d3":20}],385:[function(_dereq_,module,exports){
83693'use strict';
83694
83695var Lib = _dereq_('../../lib');
83696
83697// arrayOk attributes, merge them into calcdata array
83698module.exports = function arraysToCalcdata(cd, trace) {
83699 for(var i = 0; i < cd.length; i++) cd[i].i = i;
83700
83701 Lib.mergeArray(trace.text, cd, 'tx');
83702 Lib.mergeArray(trace.hovertext, cd, 'htx');
83703
83704 var marker = trace.marker;
83705 if(marker) {
83706 Lib.mergeArray(marker.opacity, cd, 'mo', true);
83707 Lib.mergeArray(marker.color, cd, 'mc');
83708
83709 var markerLine = marker.line;
83710 if(markerLine) {
83711 Lib.mergeArray(markerLine.color, cd, 'mlc');
83712 Lib.mergeArrayCastPositive(markerLine.width, cd, 'mlw');
83713 }
83714 }
83715};
83716
83717},{"../../lib":287}],386:[function(_dereq_,module,exports){
83718'use strict';
83719
83720var scatterAttrs = _dereq_('../scatter/attributes');
83721var axisHoverFormat = _dereq_('../../plots/cartesian/axis_format_attributes').axisHoverFormat;
83722var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs;
83723var texttemplateAttrs = _dereq_('../../plots/template_attributes').texttemplateAttrs;
83724var colorScaleAttrs = _dereq_('../../components/colorscale/attributes');
83725var fontAttrs = _dereq_('../../plots/font_attributes');
83726var constants = _dereq_('./constants');
83727var pattern = _dereq_('../../components/drawing/attributes').pattern;
83728
83729var extendFlat = _dereq_('../../lib/extend').extendFlat;
83730
83731var textFontAttrs = fontAttrs({
83732 editType: 'calc',
83733 arrayOk: true,
83734 colorEditType: 'style',
83735});
83736
83737var scatterMarkerAttrs = scatterAttrs.marker;
83738var scatterMarkerLineAttrs = scatterMarkerAttrs.line;
83739
83740var markerLineWidth = extendFlat({},
83741 scatterMarkerLineAttrs.width, { dflt: 0 });
83742
83743var markerLine = extendFlat({
83744 width: markerLineWidth,
83745 editType: 'calc'
83746}, colorScaleAttrs('marker.line'));
83747
83748var marker = extendFlat({
83749 line: markerLine,
83750 editType: 'calc'
83751}, colorScaleAttrs('marker'), {
83752 opacity: {
83753 valType: 'number',
83754 arrayOk: true,
83755 dflt: 1,
83756 min: 0,
83757 max: 1,
83758 editType: 'style',
83759 },
83760 pattern: pattern
83761});
83762
83763module.exports = {
83764 x: scatterAttrs.x,
83765 x0: scatterAttrs.x0,
83766 dx: scatterAttrs.dx,
83767 y: scatterAttrs.y,
83768 y0: scatterAttrs.y0,
83769 dy: scatterAttrs.dy,
83770
83771 xperiod: scatterAttrs.xperiod,
83772 yperiod: scatterAttrs.yperiod,
83773 xperiod0: scatterAttrs.xperiod0,
83774 yperiod0: scatterAttrs.yperiod0,
83775 xperiodalignment: scatterAttrs.xperiodalignment,
83776 yperiodalignment: scatterAttrs.yperiodalignment,
83777 xhoverformat: axisHoverFormat('x'),
83778 yhoverformat: axisHoverFormat('y'),
83779
83780 text: scatterAttrs.text,
83781 texttemplate: texttemplateAttrs({editType: 'plot'}, {
83782 keys: constants.eventDataKeys
83783 }),
83784 hovertext: scatterAttrs.hovertext,
83785 hovertemplate: hovertemplateAttrs({}, {
83786 keys: constants.eventDataKeys
83787 }),
83788
83789 textposition: {
83790 valType: 'enumerated',
83791 values: ['inside', 'outside', 'auto', 'none'],
83792 dflt: 'auto',
83793 arrayOk: true,
83794 editType: 'calc',
83795 },
83796
83797 insidetextanchor: {
83798 valType: 'enumerated',
83799 values: ['end', 'middle', 'start'],
83800 dflt: 'end',
83801 editType: 'plot',
83802 },
83803
83804 textangle: {
83805 valType: 'angle',
83806 dflt: 'auto',
83807 editType: 'plot',
83808 },
83809
83810 textfont: extendFlat({}, textFontAttrs, {
83811 }),
83812
83813 insidetextfont: extendFlat({}, textFontAttrs, {
83814 }),
83815
83816 outsidetextfont: extendFlat({}, textFontAttrs, {
83817 }),
83818
83819 constraintext: {
83820 valType: 'enumerated',
83821 values: ['inside', 'outside', 'both', 'none'],
83822 dflt: 'both',
83823 editType: 'calc',
83824 },
83825
83826 cliponaxis: extendFlat({}, scatterAttrs.cliponaxis, {
83827 }),
83828
83829 orientation: {
83830 valType: 'enumerated',
83831 values: ['v', 'h'],
83832 editType: 'calc+clearAxisTypes',
83833 },
83834
83835 base: {
83836 valType: 'any',
83837 dflt: null,
83838 arrayOk: true,
83839 editType: 'calc',
83840 },
83841
83842 offset: {
83843 valType: 'number',
83844 dflt: null,
83845 arrayOk: true,
83846 editType: 'calc',
83847 },
83848
83849 width: {
83850 valType: 'number',
83851 dflt: null,
83852 min: 0,
83853 arrayOk: true,
83854 editType: 'calc',
83855 },
83856
83857 marker: marker,
83858
83859 offsetgroup: {
83860 valType: 'string',
83861 dflt: '',
83862 editType: 'calc',
83863 },
83864 alignmentgroup: {
83865 valType: 'string',
83866 dflt: '',
83867 editType: 'calc',
83868 },
83869
83870 selected: {
83871 marker: {
83872 opacity: scatterAttrs.selected.marker.opacity,
83873 color: scatterAttrs.selected.marker.color,
83874 editType: 'style'
83875 },
83876 textfont: scatterAttrs.selected.textfont,
83877 editType: 'style'
83878 },
83879 unselected: {
83880 marker: {
83881 opacity: scatterAttrs.unselected.marker.opacity,
83882 color: scatterAttrs.unselected.marker.color,
83883 editType: 'style'
83884 },
83885 textfont: scatterAttrs.unselected.textfont,
83886 editType: 'style'
83887 },
83888
83889 _deprecated: {
83890 bardir: {
83891 valType: 'enumerated',
83892 editType: 'calc',
83893 values: ['v', 'h'],
83894 }
83895 }
83896};
83897
83898},{"../../components/colorscale/attributes":164,"../../components/drawing/attributes":178,"../../lib/extend":281,"../../plots/cartesian/axis_format_attributes":337,"../../plots/font_attributes":363,"../../plots/template_attributes":371,"../scatter/attributes":497,"./constants":388}],387:[function(_dereq_,module,exports){
83899'use strict';
83900
83901var Axes = _dereq_('../../plots/cartesian/axes');
83902var alignPeriod = _dereq_('../../plots/cartesian/align_period');
83903var hasColorscale = _dereq_('../../components/colorscale/helpers').hasColorscale;
83904var colorscaleCalc = _dereq_('../../components/colorscale/calc');
83905var arraysToCalcdata = _dereq_('./arrays_to_calcdata');
83906var calcSelection = _dereq_('../scatter/calc_selection');
83907
83908module.exports = function calc(gd, trace) {
83909 var xa = Axes.getFromId(gd, trace.xaxis || 'x');
83910 var ya = Axes.getFromId(gd, trace.yaxis || 'y');
83911 var size, pos, origPos, pObj, hasPeriod, pLetter;
83912
83913 var sizeOpts = {
83914 msUTC: !!(trace.base || trace.base === 0)
83915 };
83916
83917 if(trace.orientation === 'h') {
83918 size = xa.makeCalcdata(trace, 'x', sizeOpts);
83919 origPos = ya.makeCalcdata(trace, 'y');
83920 pObj = alignPeriod(trace, ya, 'y', origPos);
83921 hasPeriod = !!trace.yperiodalignment;
83922 pLetter = 'y';
83923 } else {
83924 size = ya.makeCalcdata(trace, 'y', sizeOpts);
83925 origPos = xa.makeCalcdata(trace, 'x');
83926 pObj = alignPeriod(trace, xa, 'x', origPos);
83927 hasPeriod = !!trace.xperiodalignment;
83928 pLetter = 'x';
83929 }
83930 pos = pObj.vals;
83931
83932 // create the "calculated data" to plot
83933 var serieslen = Math.min(pos.length, size.length);
83934 var cd = new Array(serieslen);
83935
83936 // set position and size
83937 for(var i = 0; i < serieslen; i++) {
83938 cd[i] = { p: pos[i], s: size[i] };
83939
83940 if(hasPeriod) {
83941 cd[i].orig_p = origPos[i]; // used by hover
83942 cd[i][pLetter + 'End'] = pObj.ends[i];
83943 cd[i][pLetter + 'Start'] = pObj.starts[i];
83944 }
83945
83946 if(trace.ids) {
83947 cd[i].id = String(trace.ids[i]);
83948 }
83949 }
83950
83951 // auto-z and autocolorscale if applicable
83952 if(hasColorscale(trace, 'marker')) {
83953 colorscaleCalc(gd, trace, {
83954 vals: trace.marker.color,
83955 containerStr: 'marker',
83956 cLetter: 'c'
83957 });
83958 }
83959 if(hasColorscale(trace, 'marker.line')) {
83960 colorscaleCalc(gd, trace, {
83961 vals: trace.marker.line.color,
83962 containerStr: 'marker.line',
83963 cLetter: 'c'
83964 });
83965 }
83966
83967 arraysToCalcdata(cd, trace);
83968 calcSelection(cd, trace);
83969
83970 return cd;
83971};
83972
83973},{"../../components/colorscale/calc":165,"../../components/colorscale/helpers":168,"../../plots/cartesian/align_period":331,"../../plots/cartesian/axes":334,"../scatter/calc_selection":499,"./arrays_to_calcdata":385}],388:[function(_dereq_,module,exports){
83974'use strict';
83975
83976module.exports = {
83977 // padding in pixels around text
83978 TEXTPAD: 3,
83979 // 'value' and 'label' are not really necessary for bar traces,
83980 // but they were made available to `texttemplate` (maybe by accident)
83981 // via tokens `%{value}` and `%{label}` starting in 1.50.0,
83982 // so let's include them in the event data also.
83983 eventDataKeys: ['value', 'label']
83984};
83985
83986},{}],389:[function(_dereq_,module,exports){
83987'use strict';
83988
83989var isNumeric = _dereq_('fast-isnumeric');
83990var isArrayOrTypedArray = _dereq_('../../lib').isArrayOrTypedArray;
83991var BADNUM = _dereq_('../../constants/numerical').BADNUM;
83992
83993var Registry = _dereq_('../../registry');
83994var Axes = _dereq_('../../plots/cartesian/axes');
83995var getAxisGroup = _dereq_('../../plots/cartesian/constraints').getAxisGroup;
83996var Sieve = _dereq_('./sieve.js');
83997
83998/*
83999 * Bar chart stacking/grouping positioning and autoscaling calculations
84000 * for each direction separately calculate the ranges and positions
84001 * note that this handles histograms too
84002 * now doing this one subplot at a time
84003 */
84004
84005function crossTraceCalc(gd, plotinfo) {
84006 var xa = plotinfo.xaxis;
84007 var ya = plotinfo.yaxis;
84008
84009 var fullLayout = gd._fullLayout;
84010 var fullTraces = gd._fullData;
84011 var calcTraces = gd.calcdata;
84012 var calcTracesHorz = [];
84013 var calcTracesVert = [];
84014
84015 for(var i = 0; i < fullTraces.length; i++) {
84016 var fullTrace = fullTraces[i];
84017 if(
84018 fullTrace.visible === true &&
84019 Registry.traceIs(fullTrace, 'bar') &&
84020 fullTrace.xaxis === xa._id &&
84021 fullTrace.yaxis === ya._id
84022 ) {
84023 if(fullTrace.orientation === 'h') {
84024 calcTracesHorz.push(calcTraces[i]);
84025 } else {
84026 calcTracesVert.push(calcTraces[i]);
84027 }
84028
84029 if(fullTrace._computePh) {
84030 var cd = gd.calcdata[i];
84031 for(var j = 0; j < cd.length; j++) {
84032 if(typeof cd[j].ph0 === 'function') cd[j].ph0 = cd[j].ph0();
84033 if(typeof cd[j].ph1 === 'function') cd[j].ph1 = cd[j].ph1();
84034 }
84035 }
84036 }
84037 }
84038
84039 var opts = {
84040 xCat: xa.type === 'category' || xa.type === 'multicategory',
84041 yCat: ya.type === 'category' || ya.type === 'multicategory',
84042
84043 mode: fullLayout.barmode,
84044 norm: fullLayout.barnorm,
84045 gap: fullLayout.bargap,
84046 groupgap: fullLayout.bargroupgap
84047 };
84048
84049 setGroupPositions(gd, xa, ya, calcTracesVert, opts);
84050 setGroupPositions(gd, ya, xa, calcTracesHorz, opts);
84051}
84052
84053function setGroupPositions(gd, pa, sa, calcTraces, opts) {
84054 if(!calcTraces.length) return;
84055
84056 var excluded;
84057 var included;
84058 var i, calcTrace, fullTrace;
84059
84060 initBase(sa, calcTraces);
84061
84062 switch(opts.mode) {
84063 case 'overlay':
84064 setGroupPositionsInOverlayMode(pa, sa, calcTraces, opts);
84065 break;
84066
84067 case 'group':
84068 // exclude from the group those traces for which the user set an offset
84069 excluded = [];
84070 included = [];
84071 for(i = 0; i < calcTraces.length; i++) {
84072 calcTrace = calcTraces[i];
84073 fullTrace = calcTrace[0].trace;
84074
84075 if(fullTrace.offset === undefined) included.push(calcTrace);
84076 else excluded.push(calcTrace);
84077 }
84078
84079 if(included.length) {
84080 setGroupPositionsInGroupMode(gd, pa, sa, included, opts);
84081 }
84082 if(excluded.length) {
84083 setGroupPositionsInOverlayMode(pa, sa, excluded, opts);
84084 }
84085 break;
84086
84087 case 'stack':
84088 case 'relative':
84089 // exclude from the stack those traces for which the user set a base
84090 excluded = [];
84091 included = [];
84092 for(i = 0; i < calcTraces.length; i++) {
84093 calcTrace = calcTraces[i];
84094 fullTrace = calcTrace[0].trace;
84095
84096 if(fullTrace.base === undefined) included.push(calcTrace);
84097 else excluded.push(calcTrace);
84098 }
84099
84100 if(included.length) {
84101 setGroupPositionsInStackOrRelativeMode(gd, pa, sa, included, opts);
84102 }
84103 if(excluded.length) {
84104 setGroupPositionsInOverlayMode(pa, sa, excluded, opts);
84105 }
84106 break;
84107 }
84108
84109 collectExtents(calcTraces, pa);
84110}
84111
84112function initBase(sa, calcTraces) {
84113 var i, j;
84114
84115 for(i = 0; i < calcTraces.length; i++) {
84116 var cd = calcTraces[i];
84117 var trace = cd[0].trace;
84118 var base = (trace.type === 'funnel') ? trace._base : trace.base;
84119 var b;
84120
84121 // not sure if it really makes sense to have dates for bar size data...
84122 // ideally if we want to make gantt charts or something we'd treat
84123 // the actual size (trace.x or y) as time delta but base as absolute
84124 // time. But included here for completeness.
84125 var scalendar = trace.orientation === 'h' ? trace.xcalendar : trace.ycalendar;
84126
84127 // 'base' on categorical axes makes no sense
84128 var d2c = sa.type === 'category' || sa.type === 'multicategory' ?
84129 function() { return null; } :
84130 sa.d2c;
84131
84132 if(isArrayOrTypedArray(base)) {
84133 for(j = 0; j < Math.min(base.length, cd.length); j++) {
84134 b = d2c(base[j], 0, scalendar);
84135 if(isNumeric(b)) {
84136 cd[j].b = +b;
84137 cd[j].hasB = 1;
84138 } else cd[j].b = 0;
84139 }
84140 for(; j < cd.length; j++) {
84141 cd[j].b = 0;
84142 }
84143 } else {
84144 b = d2c(base, 0, scalendar);
84145 var hasBase = isNumeric(b);
84146 b = hasBase ? b : 0;
84147 for(j = 0; j < cd.length; j++) {
84148 cd[j].b = b;
84149 if(hasBase) cd[j].hasB = 1;
84150 }
84151 }
84152 }
84153}
84154
84155function setGroupPositionsInOverlayMode(pa, sa, calcTraces, opts) {
84156 // update position axis and set bar offsets and widths
84157 for(var i = 0; i < calcTraces.length; i++) {
84158 var calcTrace = calcTraces[i];
84159
84160 var sieve = new Sieve([calcTrace], {
84161 posAxis: pa,
84162 sepNegVal: false,
84163 overlapNoMerge: !opts.norm
84164 });
84165
84166 // set bar offsets and widths, and update position axis
84167 setOffsetAndWidth(pa, sieve, opts);
84168
84169 // set bar bases and sizes, and update size axis
84170 //
84171 // (note that `setGroupPositionsInOverlayMode` handles the case barnorm
84172 // is defined, because this function is also invoked for traces that
84173 // can't be grouped or stacked)
84174 if(opts.norm) {
84175 sieveBars(sieve);
84176 normalizeBars(sa, sieve, opts);
84177 } else {
84178 setBaseAndTop(sa, sieve);
84179 }
84180 }
84181}
84182
84183function setGroupPositionsInGroupMode(gd, pa, sa, calcTraces, opts) {
84184 var sieve = new Sieve(calcTraces, {
84185 posAxis: pa,
84186 sepNegVal: false,
84187 overlapNoMerge: !opts.norm
84188 });
84189
84190 // set bar offsets and widths, and update position axis
84191 setOffsetAndWidthInGroupMode(gd, pa, sieve, opts);
84192
84193 // relative-stack bars within the same trace that would otherwise
84194 // be hidden
84195 unhideBarsWithinTrace(sieve, pa);
84196
84197 // set bar bases and sizes, and update size axis
84198 if(opts.norm) {
84199 sieveBars(sieve);
84200 normalizeBars(sa, sieve, opts);
84201 } else {
84202 setBaseAndTop(sa, sieve);
84203 }
84204}
84205
84206function setGroupPositionsInStackOrRelativeMode(gd, pa, sa, calcTraces, opts) {
84207 var sieve = new Sieve(calcTraces, {
84208 posAxis: pa,
84209 sepNegVal: opts.mode === 'relative',
84210 overlapNoMerge: !(opts.norm || opts.mode === 'stack' || opts.mode === 'relative')
84211 });
84212
84213 // set bar offsets and widths, and update position axis
84214 setOffsetAndWidth(pa, sieve, opts);
84215
84216 // set bar bases and sizes, and update size axis
84217 stackBars(sa, sieve, opts);
84218
84219 // flag the outmost bar (for text display purposes)
84220 for(var i = 0; i < calcTraces.length; i++) {
84221 var calcTrace = calcTraces[i];
84222
84223 for(var j = 0; j < calcTrace.length; j++) {
84224 var bar = calcTrace[j];
84225
84226 if(bar.s !== BADNUM) {
84227 var isOutmostBar = ((bar.b + bar.s) === sieve.get(bar.p, bar.s));
84228 if(isOutmostBar) bar._outmost = true;
84229 }
84230 }
84231 }
84232
84233 // Note that marking the outmost bars has to be done
84234 // before `normalizeBars` changes `bar.b` and `bar.s`.
84235 if(opts.norm) normalizeBars(sa, sieve, opts);
84236}
84237
84238function setOffsetAndWidth(pa, sieve, opts) {
84239 var minDiff = sieve.minDiff;
84240 var calcTraces = sieve.traces;
84241
84242 // set bar offsets and widths
84243 var barGroupWidth = minDiff * (1 - opts.gap);
84244 var barWidthPlusGap = barGroupWidth;
84245 var barWidth = barWidthPlusGap * (1 - (opts.groupgap || 0));
84246
84247 // computer bar group center and bar offset
84248 var offsetFromCenter = -barWidth / 2;
84249
84250 for(var i = 0; i < calcTraces.length; i++) {
84251 var calcTrace = calcTraces[i];
84252 var t = calcTrace[0].t;
84253
84254 // store bar width and offset for this trace
84255 t.barwidth = barWidth;
84256 t.poffset = offsetFromCenter;
84257 t.bargroupwidth = barGroupWidth;
84258 t.bardelta = minDiff;
84259 }
84260
84261 // stack bars that only differ by rounding
84262 sieve.binWidth = calcTraces[0][0].t.barwidth / 100;
84263
84264 // if defined, apply trace offset and width
84265 applyAttributes(sieve);
84266
84267 // store the bar center in each calcdata item
84268 setBarCenterAndWidth(pa, sieve);
84269
84270 // update position axes
84271 updatePositionAxis(pa, sieve);
84272}
84273
84274function setOffsetAndWidthInGroupMode(gd, pa, sieve, opts) {
84275 var fullLayout = gd._fullLayout;
84276 var positions = sieve.positions;
84277 var distinctPositions = sieve.distinctPositions;
84278 var minDiff = sieve.minDiff;
84279 var calcTraces = sieve.traces;
84280 var nTraces = calcTraces.length;
84281
84282 // if there aren't any overlapping positions,
84283 // let them have full width even if mode is group
84284 var overlap = (positions.length !== distinctPositions.length);
84285 var barGroupWidth = minDiff * (1 - opts.gap);
84286
84287 var groupId = getAxisGroup(fullLayout, pa._id) + calcTraces[0][0].trace.orientation;
84288 var alignmentGroups = fullLayout._alignmentOpts[groupId] || {};
84289
84290 for(var i = 0; i < nTraces; i++) {
84291 var calcTrace = calcTraces[i];
84292 var trace = calcTrace[0].trace;
84293
84294 var alignmentGroupOpts = alignmentGroups[trace.alignmentgroup] || {};
84295 var nOffsetGroups = Object.keys(alignmentGroupOpts.offsetGroups || {}).length;
84296
84297 var barWidthPlusGap;
84298 if(nOffsetGroups) {
84299 barWidthPlusGap = barGroupWidth / nOffsetGroups;
84300 } else {
84301 barWidthPlusGap = overlap ? barGroupWidth / nTraces : barGroupWidth;
84302 }
84303
84304 var barWidth = barWidthPlusGap * (1 - (opts.groupgap || 0));
84305
84306 var offsetFromCenter;
84307 if(nOffsetGroups) {
84308 offsetFromCenter = ((2 * trace._offsetIndex + 1 - nOffsetGroups) * barWidthPlusGap - barWidth) / 2;
84309 } else {
84310 offsetFromCenter = overlap ?
84311 ((2 * i + 1 - nTraces) * barWidthPlusGap - barWidth) / 2 :
84312 -barWidth / 2;
84313 }
84314
84315 var t = calcTrace[0].t;
84316 t.barwidth = barWidth;
84317 t.poffset = offsetFromCenter;
84318 t.bargroupwidth = barGroupWidth;
84319 t.bardelta = minDiff;
84320 }
84321
84322 // stack bars that only differ by rounding
84323 sieve.binWidth = calcTraces[0][0].t.barwidth / 100;
84324
84325 // if defined, apply trace width
84326 applyAttributes(sieve);
84327
84328 // store the bar center in each calcdata item
84329 setBarCenterAndWidth(pa, sieve);
84330
84331 // update position axes
84332 updatePositionAxis(pa, sieve, overlap);
84333}
84334
84335function applyAttributes(sieve) {
84336 var calcTraces = sieve.traces;
84337 var i, j;
84338
84339 for(i = 0; i < calcTraces.length; i++) {
84340 var calcTrace = calcTraces[i];
84341 var calcTrace0 = calcTrace[0];
84342 var fullTrace = calcTrace0.trace;
84343 var t = calcTrace0.t;
84344 var offset = fullTrace._offset || fullTrace.offset;
84345 var initialPoffset = t.poffset;
84346 var newPoffset;
84347
84348 if(isArrayOrTypedArray(offset)) {
84349 // if offset is an array, then clone it into t.poffset.
84350 newPoffset = Array.prototype.slice.call(offset, 0, calcTrace.length);
84351
84352 // guard against non-numeric items
84353 for(j = 0; j < newPoffset.length; j++) {
84354 if(!isNumeric(newPoffset[j])) {
84355 newPoffset[j] = initialPoffset;
84356 }
84357 }
84358
84359 // if the length of the array is too short,
84360 // then extend it with the initial value of t.poffset
84361 for(j = newPoffset.length; j < calcTrace.length; j++) {
84362 newPoffset.push(initialPoffset);
84363 }
84364
84365 t.poffset = newPoffset;
84366 } else if(offset !== undefined) {
84367 t.poffset = offset;
84368 }
84369
84370 var width = fullTrace._width || fullTrace.width;
84371 var initialBarwidth = t.barwidth;
84372
84373 if(isArrayOrTypedArray(width)) {
84374 // if width is an array, then clone it into t.barwidth.
84375 var newBarwidth = Array.prototype.slice.call(width, 0, calcTrace.length);
84376
84377 // guard against non-numeric items
84378 for(j = 0; j < newBarwidth.length; j++) {
84379 if(!isNumeric(newBarwidth[j])) newBarwidth[j] = initialBarwidth;
84380 }
84381
84382 // if the length of the array is too short,
84383 // then extend it with the initial value of t.barwidth
84384 for(j = newBarwidth.length; j < calcTrace.length; j++) {
84385 newBarwidth.push(initialBarwidth);
84386 }
84387
84388 t.barwidth = newBarwidth;
84389
84390 // if user didn't set offset,
84391 // then correct t.poffset to ensure bars remain centered
84392 if(offset === undefined) {
84393 newPoffset = [];
84394 for(j = 0; j < calcTrace.length; j++) {
84395 newPoffset.push(
84396 initialPoffset + (initialBarwidth - newBarwidth[j]) / 2
84397 );
84398 }
84399 t.poffset = newPoffset;
84400 }
84401 } else if(width !== undefined) {
84402 t.barwidth = width;
84403
84404 // if user didn't set offset,
84405 // then correct t.poffset to ensure bars remain centered
84406 if(offset === undefined) {
84407 t.poffset = initialPoffset + (initialBarwidth - width) / 2;
84408 }
84409 }
84410 }
84411}
84412
84413function setBarCenterAndWidth(pa, sieve) {
84414 var calcTraces = sieve.traces;
84415 var pLetter = getAxisLetter(pa);
84416
84417 for(var i = 0; i < calcTraces.length; i++) {
84418 var calcTrace = calcTraces[i];
84419 var t = calcTrace[0].t;
84420 var poffset = t.poffset;
84421 var poffsetIsArray = Array.isArray(poffset);
84422 var barwidth = t.barwidth;
84423 var barwidthIsArray = Array.isArray(barwidth);
84424
84425 for(var j = 0; j < calcTrace.length; j++) {
84426 var calcBar = calcTrace[j];
84427
84428 // store the actual bar width and position, for use by hover
84429 var width = calcBar.w = barwidthIsArray ? barwidth[j] : barwidth;
84430 calcBar[pLetter] = calcBar.p + (poffsetIsArray ? poffset[j] : poffset) + width / 2;
84431 }
84432 }
84433}
84434
84435function updatePositionAxis(pa, sieve, allowMinDtick) {
84436 var calcTraces = sieve.traces;
84437 var minDiff = sieve.minDiff;
84438 var vpad = minDiff / 2;
84439
84440 Axes.minDtick(pa, sieve.minDiff, sieve.distinctPositions[0], allowMinDtick);
84441
84442 for(var i = 0; i < calcTraces.length; i++) {
84443 var calcTrace = calcTraces[i];
84444 var calcTrace0 = calcTrace[0];
84445 var fullTrace = calcTrace0.trace;
84446 var pts = [];
84447 var bar, l, r, j;
84448
84449 for(j = 0; j < calcTrace.length; j++) {
84450 bar = calcTrace[j];
84451 l = bar.p - vpad;
84452 r = bar.p + vpad;
84453 pts.push(l, r);
84454 }
84455
84456 if(fullTrace.width || fullTrace.offset) {
84457 var t = calcTrace0.t;
84458 var poffset = t.poffset;
84459 var barwidth = t.barwidth;
84460 var poffsetIsArray = Array.isArray(poffset);
84461 var barwidthIsArray = Array.isArray(barwidth);
84462
84463 for(j = 0; j < calcTrace.length; j++) {
84464 bar = calcTrace[j];
84465 var calcBarOffset = poffsetIsArray ? poffset[j] : poffset;
84466 var calcBarWidth = barwidthIsArray ? barwidth[j] : barwidth;
84467 l = bar.p + calcBarOffset;
84468 r = l + calcBarWidth;
84469 pts.push(l, r);
84470 }
84471 }
84472
84473 fullTrace._extremes[pa._id] = Axes.findExtremes(pa, pts, {padded: false});
84474 }
84475}
84476
84477// store these bar bases and tops in calcdata
84478// and make sure the size axis includes zero,
84479// along with the bases and tops of each bar.
84480function setBaseAndTop(sa, sieve) {
84481 var calcTraces = sieve.traces;
84482 var sLetter = getAxisLetter(sa);
84483
84484 for(var i = 0; i < calcTraces.length; i++) {
84485 var calcTrace = calcTraces[i];
84486 var fullTrace = calcTrace[0].trace;
84487 var pts = [];
84488 var tozero = false;
84489
84490 for(var j = 0; j < calcTrace.length; j++) {
84491 var bar = calcTrace[j];
84492 var base = bar.b;
84493 var top = base + bar.s;
84494
84495 bar[sLetter] = top;
84496 pts.push(top);
84497 if(bar.hasB) pts.push(base);
84498
84499 if(!bar.hasB || !bar.b) {
84500 tozero = true;
84501 }
84502 }
84503
84504 fullTrace._extremes[sa._id] = Axes.findExtremes(sa, pts, {
84505 tozero: tozero,
84506 padded: true
84507 });
84508 }
84509}
84510
84511function stackBars(sa, sieve, opts) {
84512 var sLetter = getAxisLetter(sa);
84513 var calcTraces = sieve.traces;
84514 var calcTrace;
84515 var fullTrace;
84516 var isFunnel;
84517 var i, j;
84518 var bar;
84519
84520 for(i = 0; i < calcTraces.length; i++) {
84521 calcTrace = calcTraces[i];
84522 fullTrace = calcTrace[0].trace;
84523
84524 if(fullTrace.type === 'funnel') {
84525 for(j = 0; j < calcTrace.length; j++) {
84526 bar = calcTrace[j];
84527
84528 if(bar.s !== BADNUM) {
84529 // create base of funnels
84530 sieve.put(bar.p, -0.5 * bar.s);
84531 }
84532 }
84533 }
84534 }
84535
84536 for(i = 0; i < calcTraces.length; i++) {
84537 calcTrace = calcTraces[i];
84538 fullTrace = calcTrace[0].trace;
84539
84540 isFunnel = (fullTrace.type === 'funnel');
84541
84542 var pts = [];
84543
84544 for(j = 0; j < calcTrace.length; j++) {
84545 bar = calcTrace[j];
84546
84547 if(bar.s !== BADNUM) {
84548 // stack current bar and get previous sum
84549 var value;
84550 if(isFunnel) {
84551 value = bar.s;
84552 } else {
84553 value = bar.s + bar.b;
84554 }
84555
84556 var base = sieve.put(bar.p, value);
84557
84558 var top = base + value;
84559
84560 // store the bar base and top in each calcdata item
84561 bar.b = base;
84562 bar[sLetter] = top;
84563
84564 if(!opts.norm) {
84565 pts.push(top);
84566 if(bar.hasB) {
84567 pts.push(base);
84568 }
84569 }
84570 }
84571 }
84572
84573 // if barnorm is set, let normalizeBars update the axis range
84574 if(!opts.norm) {
84575 fullTrace._extremes[sa._id] = Axes.findExtremes(sa, pts, {
84576 // N.B. we don't stack base with 'base',
84577 // so set tozero:true always!
84578 tozero: true,
84579 padded: true
84580 });
84581 }
84582 }
84583}
84584
84585function sieveBars(sieve) {
84586 var calcTraces = sieve.traces;
84587
84588 for(var i = 0; i < calcTraces.length; i++) {
84589 var calcTrace = calcTraces[i];
84590
84591 for(var j = 0; j < calcTrace.length; j++) {
84592 var bar = calcTrace[j];
84593
84594 if(bar.s !== BADNUM) {
84595 sieve.put(bar.p, bar.b + bar.s);
84596 }
84597 }
84598 }
84599}
84600
84601function unhideBarsWithinTrace(sieve, pa) {
84602 var calcTraces = sieve.traces;
84603
84604 for(var i = 0; i < calcTraces.length; i++) {
84605 var calcTrace = calcTraces[i];
84606 var fullTrace = calcTrace[0].trace;
84607
84608 if(fullTrace.base === undefined) {
84609 var inTraceSieve = new Sieve([calcTrace], {
84610 posAxis: pa,
84611 sepNegVal: true,
84612 overlapNoMerge: true
84613 });
84614
84615 for(var j = 0; j < calcTrace.length; j++) {
84616 var bar = calcTrace[j];
84617
84618 if(bar.p !== BADNUM) {
84619 // stack current bar and get previous sum
84620 var base = inTraceSieve.put(bar.p, bar.b + bar.s);
84621
84622 // if previous sum if non-zero, this means:
84623 // multiple bars have same starting point are potentially hidden,
84624 // shift them vertically so that all bars are visible by default
84625 if(base) bar.b = base;
84626 }
84627 }
84628 }
84629 }
84630}
84631
84632// Note:
84633//
84634// normalizeBars requires that either sieveBars or stackBars has been
84635// previously invoked.
84636function normalizeBars(sa, sieve, opts) {
84637 var calcTraces = sieve.traces;
84638 var sLetter = getAxisLetter(sa);
84639 var sTop = opts.norm === 'fraction' ? 1 : 100;
84640 var sTiny = sTop / 1e9; // in case of rounding error in sum
84641 var sMin = sa.l2c(sa.c2l(0));
84642 var sMax = opts.mode === 'stack' ? sTop : sMin;
84643
84644 function needsPadding(v) {
84645 return (
84646 isNumeric(sa.c2l(v)) &&
84647 ((v < sMin - sTiny) || (v > sMax + sTiny) || !isNumeric(sMin))
84648 );
84649 }
84650
84651 for(var i = 0; i < calcTraces.length; i++) {
84652 var calcTrace = calcTraces[i];
84653 var fullTrace = calcTrace[0].trace;
84654 var pts = [];
84655 var tozero = false;
84656 var padded = false;
84657
84658 for(var j = 0; j < calcTrace.length; j++) {
84659 var bar = calcTrace[j];
84660
84661 if(bar.s !== BADNUM) {
84662 var scale = Math.abs(sTop / sieve.get(bar.p, bar.s));
84663 bar.b *= scale;
84664 bar.s *= scale;
84665
84666 var base = bar.b;
84667 var top = base + bar.s;
84668
84669 bar[sLetter] = top;
84670 pts.push(top);
84671 padded = padded || needsPadding(top);
84672
84673 if(bar.hasB) {
84674 pts.push(base);
84675 padded = padded || needsPadding(base);
84676 }
84677
84678 if(!bar.hasB || !bar.b) {
84679 tozero = true;
84680 }
84681 }
84682 }
84683
84684 fullTrace._extremes[sa._id] = Axes.findExtremes(sa, pts, {
84685 tozero: tozero,
84686 padded: padded
84687 });
84688 }
84689}
84690
84691// find the full position span of bars at each position
84692// for use by hover, to ensure labels move in if bars are
84693// narrower than the space they're in.
84694// run once per trace group (subplot & direction) and
84695// the same mapping is attached to all calcdata traces
84696function collectExtents(calcTraces, pa) {
84697 var pLetter = getAxisLetter(pa);
84698 var extents = {};
84699 var i, j, cd;
84700
84701 var pMin = Infinity;
84702 var pMax = -Infinity;
84703
84704 for(i = 0; i < calcTraces.length; i++) {
84705 cd = calcTraces[i];
84706 for(j = 0; j < cd.length; j++) {
84707 var p = cd[j].p;
84708 if(isNumeric(p)) {
84709 pMin = Math.min(pMin, p);
84710 pMax = Math.max(pMax, p);
84711 }
84712 }
84713 }
84714
84715 // this is just for positioning of hover labels, and nobody will care if
84716 // the label is 1px too far out; so round positions to 1/10K in case
84717 // position values don't exactly match from trace to trace
84718 var roundFactor = 10000 / (pMax - pMin);
84719 var round = extents.round = function(p) {
84720 return String(Math.round(roundFactor * (p - pMin)));
84721 };
84722
84723 for(i = 0; i < calcTraces.length; i++) {
84724 cd = calcTraces[i];
84725 cd[0].t.extents = extents;
84726
84727 var poffset = cd[0].t.poffset;
84728 var poffsetIsArray = Array.isArray(poffset);
84729
84730 for(j = 0; j < cd.length; j++) {
84731 var di = cd[j];
84732 var p0 = di[pLetter] - di.w / 2;
84733
84734 if(isNumeric(p0)) {
84735 var p1 = di[pLetter] + di.w / 2;
84736 var pVal = round(di.p);
84737 if(extents[pVal]) {
84738 extents[pVal] = [Math.min(p0, extents[pVal][0]), Math.max(p1, extents[pVal][1])];
84739 } else {
84740 extents[pVal] = [p0, p1];
84741 }
84742 }
84743
84744 di.p0 = di.p + (poffsetIsArray ? poffset[j] : poffset);
84745 di.p1 = di.p0 + di.w;
84746 di.s0 = di.b;
84747 di.s1 = di.s0 + di.s;
84748 }
84749 }
84750}
84751
84752function getAxisLetter(ax) {
84753 return ax._id.charAt(0);
84754}
84755
84756module.exports = {
84757 crossTraceCalc: crossTraceCalc,
84758 setGroupPositions: setGroupPositions
84759};
84760
84761},{"../../constants/numerical":267,"../../lib":287,"../../plots/cartesian/axes":334,"../../plots/cartesian/constraints":342,"../../registry":376,"./sieve.js":399,"fast-isnumeric":33}],390:[function(_dereq_,module,exports){
84762'use strict';
84763
84764var Lib = _dereq_('../../lib');
84765var Color = _dereq_('../../components/color');
84766var Registry = _dereq_('../../registry');
84767
84768var handleXYDefaults = _dereq_('../scatter/xy_defaults');
84769var handlePeriodDefaults = _dereq_('../scatter/period_defaults');
84770var handleStyleDefaults = _dereq_('./style_defaults');
84771var getAxisGroup = _dereq_('../../plots/cartesian/constraints').getAxisGroup;
84772var attributes = _dereq_('./attributes');
84773
84774var coerceFont = Lib.coerceFont;
84775
84776function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
84777 function coerce(attr, dflt) {
84778 return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
84779 }
84780
84781 var len = handleXYDefaults(traceIn, traceOut, layout, coerce);
84782 if(!len) {
84783 traceOut.visible = false;
84784 return;
84785 }
84786
84787 handlePeriodDefaults(traceIn, traceOut, layout, coerce);
84788 coerce('xhoverformat');
84789 coerce('yhoverformat');
84790
84791 coerce('orientation', (traceOut.x && !traceOut.y) ? 'h' : 'v');
84792 coerce('base');
84793 coerce('offset');
84794 coerce('width');
84795
84796 coerce('text');
84797 coerce('hovertext');
84798 coerce('hovertemplate');
84799
84800 var textposition = coerce('textposition');
84801 handleText(traceIn, traceOut, layout, coerce, textposition, {
84802 moduleHasSelected: true,
84803 moduleHasUnselected: true,
84804 moduleHasConstrain: true,
84805 moduleHasCliponaxis: true,
84806 moduleHasTextangle: true,
84807 moduleHasInsideanchor: true
84808 });
84809
84810 handleStyleDefaults(traceIn, traceOut, coerce, defaultColor, layout);
84811
84812 var lineColor = (traceOut.marker.line || {}).color;
84813
84814 // override defaultColor for error bars with defaultLine
84815 var errorBarsSupplyDefaults = Registry.getComponentMethod('errorbars', 'supplyDefaults');
84816 errorBarsSupplyDefaults(traceIn, traceOut, lineColor || Color.defaultLine, {axis: 'y'});
84817 errorBarsSupplyDefaults(traceIn, traceOut, lineColor || Color.defaultLine, {axis: 'x', inherit: 'y'});
84818
84819 Lib.coerceSelectionMarkerOpacity(traceOut, coerce);
84820}
84821
84822function handleGroupingDefaults(traceIn, traceOut, fullLayout, coerce) {
84823 var orientation = traceOut.orientation;
84824 // N.B. grouping is done across all trace types that support it
84825 var posAxId = traceOut[{v: 'x', h: 'y'}[orientation] + 'axis'];
84826 var groupId = getAxisGroup(fullLayout, posAxId) + orientation;
84827
84828 var alignmentOpts = fullLayout._alignmentOpts || {};
84829 var alignmentgroup = coerce('alignmentgroup');
84830
84831 var alignmentGroups = alignmentOpts[groupId];
84832 if(!alignmentGroups) alignmentGroups = alignmentOpts[groupId] = {};
84833
84834 var alignmentGroupOpts = alignmentGroups[alignmentgroup];
84835
84836 if(alignmentGroupOpts) {
84837 alignmentGroupOpts.traces.push(traceOut);
84838 } else {
84839 alignmentGroupOpts = alignmentGroups[alignmentgroup] = {
84840 traces: [traceOut],
84841 alignmentIndex: Object.keys(alignmentGroups).length,
84842 offsetGroups: {}
84843 };
84844 }
84845
84846 var offsetgroup = coerce('offsetgroup');
84847 var offsetGroups = alignmentGroupOpts.offsetGroups;
84848 var offsetGroupOpts = offsetGroups[offsetgroup];
84849
84850 if(offsetgroup) {
84851 if(!offsetGroupOpts) {
84852 offsetGroupOpts = offsetGroups[offsetgroup] = {
84853 offsetIndex: Object.keys(offsetGroups).length
84854 };
84855 }
84856
84857 traceOut._offsetIndex = offsetGroupOpts.offsetIndex;
84858 }
84859}
84860
84861function crossTraceDefaults(fullData, fullLayout) {
84862 var traceIn, traceOut;
84863
84864 function coerce(attr) {
84865 return Lib.coerce(traceOut._input, traceOut, attributes, attr);
84866 }
84867
84868 if(fullLayout.barmode === 'group') {
84869 for(var i = 0; i < fullData.length; i++) {
84870 traceOut = fullData[i];
84871
84872 if(traceOut.type === 'bar') {
84873 traceIn = traceOut._input;
84874 handleGroupingDefaults(traceIn, traceOut, fullLayout, coerce);
84875 }
84876 }
84877 }
84878}
84879
84880function handleText(traceIn, traceOut, layout, coerce, textposition, opts) {
84881 opts = opts || {};
84882 var moduleHasSelected = !(opts.moduleHasSelected === false);
84883 var moduleHasUnselected = !(opts.moduleHasUnselected === false);
84884 var moduleHasConstrain = !(opts.moduleHasConstrain === false);
84885 var moduleHasCliponaxis = !(opts.moduleHasCliponaxis === false);
84886 var moduleHasTextangle = !(opts.moduleHasTextangle === false);
84887 var moduleHasInsideanchor = !(opts.moduleHasInsideanchor === false);
84888 var hasPathbar = !!opts.hasPathbar;
84889
84890 var hasBoth = Array.isArray(textposition) || textposition === 'auto';
84891 var hasInside = hasBoth || textposition === 'inside';
84892 var hasOutside = hasBoth || textposition === 'outside';
84893
84894 if(hasInside || hasOutside) {
84895 var dfltFont = coerceFont(coerce, 'textfont', layout.font);
84896
84897 // Note that coercing `insidetextfont` is always needed –
84898 // even if `textposition` is `outside` for each trace – since
84899 // an outside label can become an inside one, for example because
84900 // of a bar being stacked on top of it.
84901 var insideTextFontDefault = Lib.extendFlat({}, dfltFont);
84902 var isTraceTextfontColorSet = traceIn.textfont && traceIn.textfont.color;
84903 var isColorInheritedFromLayoutFont = !isTraceTextfontColorSet;
84904 if(isColorInheritedFromLayoutFont) {
84905 delete insideTextFontDefault.color;
84906 }
84907 coerceFont(coerce, 'insidetextfont', insideTextFontDefault);
84908
84909 if(hasPathbar) {
84910 var pathbarTextFontDefault = Lib.extendFlat({}, dfltFont);
84911 if(isColorInheritedFromLayoutFont) {
84912 delete pathbarTextFontDefault.color;
84913 }
84914 coerceFont(coerce, 'pathbar.textfont', pathbarTextFontDefault);
84915 }
84916
84917 if(hasOutside) coerceFont(coerce, 'outsidetextfont', dfltFont);
84918
84919 if(moduleHasSelected) coerce('selected.textfont.color');
84920 if(moduleHasUnselected) coerce('unselected.textfont.color');
84921 if(moduleHasConstrain) coerce('constraintext');
84922 if(moduleHasCliponaxis) coerce('cliponaxis');
84923 if(moduleHasTextangle) coerce('textangle');
84924
84925 coerce('texttemplate');
84926 }
84927
84928 if(hasInside) {
84929 if(moduleHasInsideanchor) coerce('insidetextanchor');
84930 }
84931}
84932
84933module.exports = {
84934 supplyDefaults: supplyDefaults,
84935 crossTraceDefaults: crossTraceDefaults,
84936 handleGroupingDefaults: handleGroupingDefaults,
84937 handleText: handleText
84938};
84939
84940},{"../../components/color":157,"../../lib":287,"../../plots/cartesian/constraints":342,"../../registry":376,"../scatter/period_defaults":517,"../scatter/xy_defaults":524,"./attributes":386,"./style_defaults":401}],391:[function(_dereq_,module,exports){
84941'use strict';
84942
84943module.exports = function eventData(out, pt, trace) {
84944 // standard cartesian event data
84945 out.x = 'xVal' in pt ? pt.xVal : pt.x;
84946 out.y = 'yVal' in pt ? pt.yVal : pt.y;
84947 if(pt.xa) out.xaxis = pt.xa;
84948 if(pt.ya) out.yaxis = pt.ya;
84949
84950 if(trace.orientation === 'h') {
84951 out.label = out.y;
84952 out.value = out.x;
84953 } else {
84954 out.label = out.x;
84955 out.value = out.y;
84956 }
84957
84958 return out;
84959};
84960
84961},{}],392:[function(_dereq_,module,exports){
84962'use strict';
84963
84964var isNumeric = _dereq_('fast-isnumeric');
84965var tinycolor = _dereq_('tinycolor2');
84966var isArrayOrTypedArray = _dereq_('../../lib').isArrayOrTypedArray;
84967
84968exports.coerceString = function(attributeDefinition, value, defaultValue) {
84969 if(typeof value === 'string') {
84970 if(value || !attributeDefinition.noBlank) return value;
84971 } else if(typeof value === 'number' || value === true) {
84972 if(!attributeDefinition.strict) return String(value);
84973 }
84974
84975 return (defaultValue !== undefined) ?
84976 defaultValue :
84977 attributeDefinition.dflt;
84978};
84979
84980exports.coerceNumber = function(attributeDefinition, value, defaultValue) {
84981 if(isNumeric(value)) {
84982 value = +value;
84983
84984 var min = attributeDefinition.min;
84985 var max = attributeDefinition.max;
84986 var isOutOfBounds = (min !== undefined && value < min) ||
84987 (max !== undefined && value > max);
84988
84989 if(!isOutOfBounds) return value;
84990 }
84991
84992 return (defaultValue !== undefined) ?
84993 defaultValue :
84994 attributeDefinition.dflt;
84995};
84996
84997exports.coerceColor = function(attributeDefinition, value, defaultValue) {
84998 if(tinycolor(value).isValid()) return value;
84999
85000 return (defaultValue !== undefined) ?
85001 defaultValue :
85002 attributeDefinition.dflt;
85003};
85004
85005exports.coerceEnumerated = function(attributeDefinition, value, defaultValue) {
85006 if(attributeDefinition.coerceNumber) value = +value;
85007
85008 if(attributeDefinition.values.indexOf(value) !== -1) return value;
85009
85010 return (defaultValue !== undefined) ?
85011 defaultValue :
85012 attributeDefinition.dflt;
85013};
85014
85015exports.getValue = function(arrayOrScalar, index) {
85016 var value;
85017 if(!Array.isArray(arrayOrScalar)) value = arrayOrScalar;
85018 else if(index < arrayOrScalar.length) value = arrayOrScalar[index];
85019 return value;
85020};
85021
85022exports.getLineWidth = function(trace, di) {
85023 var w =
85024 (0 < di.mlw) ? di.mlw :
85025 !isArrayOrTypedArray(trace.marker.line.width) ? trace.marker.line.width :
85026 0;
85027
85028 return w;
85029};
85030
85031},{"../../lib":287,"fast-isnumeric":33,"tinycolor2":121}],393:[function(_dereq_,module,exports){
85032'use strict';
85033
85034var Fx = _dereq_('../../components/fx');
85035var Registry = _dereq_('../../registry');
85036var Color = _dereq_('../../components/color');
85037
85038var fillText = _dereq_('../../lib').fillText;
85039var getLineWidth = _dereq_('./helpers').getLineWidth;
85040var hoverLabelText = _dereq_('../../plots/cartesian/axes').hoverLabelText;
85041var BADNUM = _dereq_('../../constants/numerical').BADNUM;
85042
85043function hoverPoints(pointData, xval, yval, hovermode, opts) {
85044 var barPointData = hoverOnBars(pointData, xval, yval, hovermode, opts);
85045
85046 if(barPointData) {
85047 var cd = barPointData.cd;
85048 var trace = cd[0].trace;
85049 var di = cd[barPointData.index];
85050
85051 barPointData.color = getTraceColor(trace, di);
85052 Registry.getComponentMethod('errorbars', 'hoverInfo')(di, trace, barPointData);
85053
85054 return [barPointData];
85055 }
85056}
85057
85058function hoverOnBars(pointData, xval, yval, hovermode, opts) {
85059 var cd = pointData.cd;
85060 var trace = cd[0].trace;
85061 var t = cd[0].t;
85062 var isClosest = (hovermode === 'closest');
85063 var isWaterfall = (trace.type === 'waterfall');
85064 var maxHoverDistance = pointData.maxHoverDistance;
85065 var maxSpikeDistance = pointData.maxSpikeDistance;
85066
85067 var posVal, sizeVal, posLetter, sizeLetter, dx, dy, pRangeCalc;
85068
85069 if(trace.orientation === 'h') {
85070 posVal = yval;
85071 sizeVal = xval;
85072 posLetter = 'y';
85073 sizeLetter = 'x';
85074 dx = sizeFn;
85075 dy = positionFn;
85076 } else {
85077 posVal = xval;
85078 sizeVal = yval;
85079 posLetter = 'x';
85080 sizeLetter = 'y';
85081 dy = sizeFn;
85082 dx = positionFn;
85083 }
85084
85085 var period = trace[posLetter + 'period'];
85086 var isClosestOrPeriod = isClosest || period;
85087
85088 function thisBarMinPos(di) { return thisBarExtPos(di, -1); }
85089 function thisBarMaxPos(di) { return thisBarExtPos(di, 1); }
85090
85091 function thisBarExtPos(di, sgn) {
85092 var w = di.w;
85093
85094 return di[posLetter] + sgn * w / 2;
85095 }
85096
85097 function periodLength(di) {
85098 return di[posLetter + 'End'] - di[posLetter + 'Start'];
85099 }
85100
85101 var minPos = isClosest ?
85102 thisBarMinPos : period ?
85103 function(di) {
85104 return di.p - periodLength(di) / 2;
85105 } :
85106 function(di) {
85107 /*
85108 * In compare mode, accept a bar if you're on it *or* its group.
85109 * Nearly always it's the group that matters, but in case the bar
85110 * was explicitly set wider than its group we'd better accept the
85111 * whole bar.
85112 *
85113 * use `bardelta` instead of `bargroupwidth` so we accept hover
85114 * in the gap. That way hover doesn't flash on and off as you
85115 * mouse over the plot in compare modes.
85116 * In 'closest' mode though the flashing seems inevitable,
85117 * without far more complex logic
85118 */
85119 return Math.min(thisBarMinPos(di), di.p - t.bardelta / 2);
85120 };
85121
85122 var maxPos = isClosest ?
85123 thisBarMaxPos : period ?
85124 function(di) {
85125 return di.p + periodLength(di) / 2;
85126 } :
85127 function(di) {
85128 return Math.max(thisBarMaxPos(di), di.p + t.bardelta / 2);
85129 };
85130
85131 function inbox(_minPos, _maxPos, maxDistance) {
85132 if(opts.finiteRange) maxDistance = 0;
85133
85134 // add a little to the pseudo-distance for wider bars, so that like scatter,
85135 // if you are over two overlapping bars, the narrower one wins.
85136 return Fx.inbox(_minPos - posVal, _maxPos - posVal,
85137 maxDistance + Math.min(1, Math.abs(_maxPos - _minPos) / pRangeCalc) - 1);
85138 }
85139
85140 function positionFn(di) {
85141 return inbox(minPos(di), maxPos(di), maxHoverDistance);
85142 }
85143
85144 function thisBarPositionFn(di) {
85145 return inbox(thisBarMinPos(di), thisBarMaxPos(di), maxSpikeDistance);
85146 }
85147
85148 function getSize(di) {
85149 var s = di[sizeLetter];
85150
85151 if(isWaterfall) {
85152 var rawS = Math.abs(di.rawS) || 0;
85153 if(sizeVal > 0) {
85154 s += rawS;
85155 } else if(sizeVal < 0) {
85156 s -= rawS;
85157 }
85158 }
85159
85160 return s;
85161 }
85162
85163 function sizeFn(di) {
85164 var v = sizeVal;
85165 var b = di.b;
85166 var s = getSize(di);
85167
85168 // add a gradient so hovering near the end of a
85169 // bar makes it a little closer match
85170 return Fx.inbox(b - v, s - v, maxHoverDistance + (s - v) / (s - b) - 1);
85171 }
85172
85173 function thisBarSizeFn(di) {
85174 var v = sizeVal;
85175 var b = di.b;
85176 var s = getSize(di);
85177
85178 // add a gradient so hovering near the end of a
85179 // bar makes it a little closer match
85180 return Fx.inbox(b - v, s - v, maxSpikeDistance + (s - v) / (s - b) - 1);
85181 }
85182
85183 var pa = pointData[posLetter + 'a'];
85184 var sa = pointData[sizeLetter + 'a'];
85185
85186 pRangeCalc = Math.abs(pa.r2c(pa.range[1]) - pa.r2c(pa.range[0]));
85187
85188 function dxy(di) { return (dx(di) + dy(di)) / 2; }
85189 var distfn = Fx.getDistanceFunction(hovermode, dx, dy, dxy);
85190 Fx.getClosest(cd, distfn, pointData);
85191
85192 // skip the rest (for this trace) if we didn't find a close point
85193 if(pointData.index === false) return;
85194
85195 // skip points inside axis rangebreaks
85196 if(cd[pointData.index].p === BADNUM) return;
85197
85198 // if we get here and we're not in 'closest' mode, push min/max pos back
85199 // onto the group - even though that means occasionally the mouse will be
85200 // over the hover label.
85201 if(!isClosestOrPeriod) {
85202 minPos = function(di) {
85203 return Math.min(thisBarMinPos(di), di.p - t.bargroupwidth / 2);
85204 };
85205 maxPos = function(di) {
85206 return Math.max(thisBarMaxPos(di), di.p + t.bargroupwidth / 2);
85207 };
85208 }
85209
85210 // the closest data point
85211 var index = pointData.index;
85212 var di = cd[index];
85213
85214 var size = (trace.base) ? di.b + di.s : di.s;
85215 pointData[sizeLetter + '0'] = pointData[sizeLetter + '1'] = sa.c2p(di[sizeLetter], true);
85216 pointData[sizeLetter + 'LabelVal'] = size;
85217
85218 var extent = t.extents[t.extents.round(di.p)];
85219 pointData[posLetter + '0'] = pa.c2p(isClosest ? minPos(di) : extent[0], true);
85220 pointData[posLetter + '1'] = pa.c2p(isClosest ? maxPos(di) : extent[1], true);
85221
85222 var hasPeriod = di.orig_p !== undefined;
85223 pointData[posLetter + 'LabelVal'] = hasPeriod ? di.orig_p : di.p;
85224
85225 pointData.labelLabel = hoverLabelText(pa, pointData[posLetter + 'LabelVal'], trace[posLetter + 'hoverformat']);
85226 pointData.valueLabel = hoverLabelText(sa, pointData[sizeLetter + 'LabelVal'], trace[sizeLetter + 'hoverformat']);
85227 pointData.baseLabel = hoverLabelText(sa, di.b, trace[sizeLetter + 'hoverformat']);
85228
85229 // spikelines always want "closest" distance regardless of hovermode
85230 pointData.spikeDistance = (thisBarSizeFn(di) + thisBarPositionFn(di)) / 2;
85231 // they also want to point to the data value, regardless of where the label goes
85232 // in case of bars shifted within groups
85233 pointData[posLetter + 'Spike'] = pa.c2p(di.p, true);
85234
85235 fillText(di, trace, pointData);
85236 pointData.hovertemplate = trace.hovertemplate;
85237
85238 return pointData;
85239}
85240
85241function getTraceColor(trace, di) {
85242 var mc = di.mcc || trace.marker.color;
85243 var mlc = di.mlcc || trace.marker.line.color;
85244 var mlw = getLineWidth(trace, di);
85245
85246 if(Color.opacity(mc)) return mc;
85247 else if(Color.opacity(mlc) && mlw) return mlc;
85248}
85249
85250module.exports = {
85251 hoverPoints: hoverPoints,
85252 hoverOnBars: hoverOnBars,
85253 getTraceColor: getTraceColor
85254};
85255
85256},{"../../components/color":157,"../../components/fx":197,"../../constants/numerical":267,"../../lib":287,"../../plots/cartesian/axes":334,"../../registry":376,"./helpers":392}],394:[function(_dereq_,module,exports){
85257'use strict';
85258
85259module.exports = {
85260 attributes: _dereq_('./attributes'),
85261 layoutAttributes: _dereq_('./layout_attributes'),
85262 supplyDefaults: _dereq_('./defaults').supplyDefaults,
85263 crossTraceDefaults: _dereq_('./defaults').crossTraceDefaults,
85264 supplyLayoutDefaults: _dereq_('./layout_defaults'),
85265 calc: _dereq_('./calc'),
85266 crossTraceCalc: _dereq_('./cross_trace_calc').crossTraceCalc,
85267 colorbar: _dereq_('../scatter/marker_colorbar'),
85268 arraysToCalcdata: _dereq_('./arrays_to_calcdata'),
85269 plot: _dereq_('./plot').plot,
85270 style: _dereq_('./style').style,
85271 styleOnSelect: _dereq_('./style').styleOnSelect,
85272 hoverPoints: _dereq_('./hover').hoverPoints,
85273 eventData: _dereq_('./event_data'),
85274 selectPoints: _dereq_('./select'),
85275
85276 moduleType: 'trace',
85277 name: 'bar',
85278 basePlotModule: _dereq_('../../plots/cartesian'),
85279 categories: ['bar-like', 'cartesian', 'svg', 'bar', 'oriented', 'errorBarsOK', 'showLegend', 'zoomScale'],
85280 animatable: true,
85281 meta: {
85282 }
85283};
85284
85285},{"../../plots/cartesian":348,"../scatter/marker_colorbar":515,"./arrays_to_calcdata":385,"./attributes":386,"./calc":387,"./cross_trace_calc":389,"./defaults":390,"./event_data":391,"./hover":393,"./layout_attributes":395,"./layout_defaults":396,"./plot":397,"./select":398,"./style":400}],395:[function(_dereq_,module,exports){
85286'use strict';
85287
85288
85289module.exports = {
85290 barmode: {
85291 valType: 'enumerated',
85292 values: ['stack', 'group', 'overlay', 'relative'],
85293 dflt: 'group',
85294 editType: 'calc',
85295 },
85296 barnorm: {
85297 valType: 'enumerated',
85298 values: ['', 'fraction', 'percent'],
85299 dflt: '',
85300 editType: 'calc',
85301 },
85302 bargap: {
85303 valType: 'number',
85304 min: 0,
85305 max: 1,
85306 editType: 'calc',
85307 },
85308 bargroupgap: {
85309 valType: 'number',
85310 min: 0,
85311 max: 1,
85312 dflt: 0,
85313 editType: 'calc',
85314 }
85315};
85316
85317},{}],396:[function(_dereq_,module,exports){
85318'use strict';
85319
85320var Registry = _dereq_('../../registry');
85321var Axes = _dereq_('../../plots/cartesian/axes');
85322var Lib = _dereq_('../../lib');
85323
85324var layoutAttributes = _dereq_('./layout_attributes');
85325
85326module.exports = function(layoutIn, layoutOut, fullData) {
85327 function coerce(attr, dflt) {
85328 return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt);
85329 }
85330
85331 var hasBars = false;
85332 var shouldBeGapless = false;
85333 var gappedAnyway = false;
85334 var usedSubplots = {};
85335
85336 var mode = coerce('barmode');
85337
85338 for(var i = 0; i < fullData.length; i++) {
85339 var trace = fullData[i];
85340 if(Registry.traceIs(trace, 'bar') && trace.visible) hasBars = true;
85341 else continue;
85342
85343 // if we have at least 2 grouped bar traces on the same subplot,
85344 // we should default to a gap anyway, even if the data is histograms
85345 if(mode === 'group') {
85346 var subploti = trace.xaxis + trace.yaxis;
85347 if(usedSubplots[subploti]) gappedAnyway = true;
85348 usedSubplots[subploti] = true;
85349 }
85350
85351 if(trace.visible && trace.type === 'histogram') {
85352 var pa = Axes.getFromId({_fullLayout: layoutOut},
85353 trace[trace.orientation === 'v' ? 'xaxis' : 'yaxis']);
85354 if(pa.type !== 'category') shouldBeGapless = true;
85355 }
85356 }
85357
85358 if(!hasBars) {
85359 delete layoutOut.barmode;
85360 return;
85361 }
85362
85363 if(mode !== 'overlay') coerce('barnorm');
85364
85365 coerce('bargap', (shouldBeGapless && !gappedAnyway) ? 0 : 0.2);
85366 coerce('bargroupgap');
85367};
85368
85369},{"../../lib":287,"../../plots/cartesian/axes":334,"../../registry":376,"./layout_attributes":395}],397:[function(_dereq_,module,exports){
85370'use strict';
85371
85372var d3 = _dereq_('@plotly/d3');
85373var isNumeric = _dereq_('fast-isnumeric');
85374
85375var Lib = _dereq_('../../lib');
85376var svgTextUtils = _dereq_('../../lib/svg_text_utils');
85377
85378var Color = _dereq_('../../components/color');
85379var Drawing = _dereq_('../../components/drawing');
85380var Registry = _dereq_('../../registry');
85381var tickText = _dereq_('../../plots/cartesian/axes').tickText;
85382
85383var uniformText = _dereq_('./uniform_text');
85384var recordMinTextSize = uniformText.recordMinTextSize;
85385var clearMinTextSize = uniformText.clearMinTextSize;
85386
85387var style = _dereq_('./style');
85388var helpers = _dereq_('./helpers');
85389var constants = _dereq_('./constants');
85390var attributes = _dereq_('./attributes');
85391
85392var attributeText = attributes.text;
85393var attributeTextPosition = attributes.textposition;
85394
85395var appendArrayPointValue = _dereq_('../../components/fx/helpers').appendArrayPointValue;
85396
85397var TEXTPAD = constants.TEXTPAD;
85398
85399function keyFunc(d) {return d.id;}
85400function getKeyFunc(trace) {
85401 if(trace.ids) {
85402 return keyFunc;
85403 }
85404}
85405
85406function dirSign(a, b) {
85407 return (a < b) ? 1 : -1;
85408}
85409
85410function getXY(di, xa, ya, isHorizontal) {
85411 var s = [];
85412 var p = [];
85413
85414 var sAxis = isHorizontal ? xa : ya;
85415 var pAxis = isHorizontal ? ya : xa;
85416
85417 s[0] = sAxis.c2p(di.s0, true);
85418 p[0] = pAxis.c2p(di.p0, true);
85419
85420 s[1] = sAxis.c2p(di.s1, true);
85421 p[1] = pAxis.c2p(di.p1, true);
85422
85423 return isHorizontal ? [s, p] : [p, s];
85424}
85425
85426function transition(selection, fullLayout, opts, makeOnCompleteCallback) {
85427 if(!fullLayout.uniformtext.mode && hasTransition(opts)) {
85428 var onComplete;
85429 if(makeOnCompleteCallback) {
85430 onComplete = makeOnCompleteCallback();
85431 }
85432 return selection
85433 .transition()
85434 .duration(opts.duration)
85435 .ease(opts.easing)
85436 .each('end', function() { onComplete && onComplete(); })
85437 .each('interrupt', function() { onComplete && onComplete(); });
85438 } else {
85439 return selection;
85440 }
85441}
85442
85443function hasTransition(transitionOpts) {
85444 return transitionOpts && transitionOpts.duration > 0;
85445}
85446
85447function plot(gd, plotinfo, cdModule, traceLayer, opts, makeOnCompleteCallback) {
85448 var xa = plotinfo.xaxis;
85449 var ya = plotinfo.yaxis;
85450 var fullLayout = gd._fullLayout;
85451
85452 if(!opts) {
85453 opts = {
85454 mode: fullLayout.barmode,
85455 norm: fullLayout.barmode,
85456 gap: fullLayout.bargap,
85457 groupgap: fullLayout.bargroupgap
85458 };
85459
85460 // don't clear bar when this is called from waterfall or funnel
85461 clearMinTextSize('bar', fullLayout);
85462 }
85463
85464 var bartraces = Lib.makeTraceGroups(traceLayer, cdModule, 'trace bars').each(function(cd) {
85465 var plotGroup = d3.select(this);
85466 var trace = cd[0].trace;
85467 var isWaterfall = (trace.type === 'waterfall');
85468 var isFunnel = (trace.type === 'funnel');
85469 var isBar = (trace.type === 'bar');
85470 var shouldDisplayZeros = (isBar || isFunnel);
85471
85472 var adjustPixel = 0;
85473 if(isWaterfall && trace.connector.visible && trace.connector.mode === 'between') {
85474 adjustPixel = trace.connector.line.width / 2;
85475 }
85476
85477 var isHorizontal = (trace.orientation === 'h');
85478 var withTransition = hasTransition(opts);
85479
85480 var pointGroup = Lib.ensureSingle(plotGroup, 'g', 'points');
85481
85482 var keyFunc = getKeyFunc(trace);
85483 var bars = pointGroup.selectAll('g.point').data(Lib.identity, keyFunc);
85484
85485 bars.enter().append('g')
85486 .classed('point', true);
85487
85488 bars.exit().remove();
85489
85490 bars.each(function(di, i) {
85491 var bar = d3.select(this);
85492
85493 // now display the bar
85494 // clipped xf/yf (2nd arg true): non-positive
85495 // log values go off-screen by plotwidth
85496 // so you see them continue if you drag the plot
85497 var xy = getXY(di, xa, ya, isHorizontal);
85498
85499 var x0 = xy[0][0];
85500 var x1 = xy[0][1];
85501 var y0 = xy[1][0];
85502 var y1 = xy[1][1];
85503
85504 // empty bars
85505 var isBlank = (isHorizontal ? x1 - x0 : y1 - y0) === 0;
85506
85507 // display zeros if line.width > 0
85508 if(isBlank && shouldDisplayZeros && helpers.getLineWidth(trace, di)) {
85509 isBlank = false;
85510 }
85511
85512 // skip nulls
85513 if(!isBlank) {
85514 isBlank = (
85515 !isNumeric(x0) ||
85516 !isNumeric(x1) ||
85517 !isNumeric(y0) ||
85518 !isNumeric(y1)
85519 );
85520 }
85521
85522 // record isBlank
85523 di.isBlank = isBlank;
85524
85525 // for blank bars, ensure start and end positions are equal - important for smooth transitions
85526 if(isBlank) {
85527 if(isHorizontal) {
85528 x1 = x0;
85529 } else {
85530 y1 = y0;
85531 }
85532 }
85533
85534 // in waterfall mode `between` we need to adjust bar end points to match the connector width
85535 if(adjustPixel && !isBlank) {
85536 if(isHorizontal) {
85537 x0 -= dirSign(x0, x1) * adjustPixel;
85538 x1 += dirSign(x0, x1) * adjustPixel;
85539 } else {
85540 y0 -= dirSign(y0, y1) * adjustPixel;
85541 y1 += dirSign(y0, y1) * adjustPixel;
85542 }
85543 }
85544
85545 var lw;
85546 var mc;
85547
85548 if(trace.type === 'waterfall') {
85549 if(!isBlank) {
85550 var cont = trace[di.dir].marker;
85551 lw = cont.line.width;
85552 mc = cont.color;
85553 }
85554 } else {
85555 lw = helpers.getLineWidth(trace, di);
85556 mc = di.mc || trace.marker.color;
85557 }
85558
85559 function roundWithLine(v) {
85560 var offset = d3.round((lw / 2) % 1, 2);
85561
85562 // if there are explicit gaps, don't round,
85563 // it can make the gaps look crappy
85564 return (opts.gap === 0 && opts.groupgap === 0) ?
85565 d3.round(Math.round(v) - offset, 2) : v;
85566 }
85567
85568 function expandToVisible(v, vc, hideZeroSpan) {
85569 if(hideZeroSpan && v === vc) {
85570 // should not expand zero span bars
85571 // when start and end positions are identical
85572 // i.e. for vertical when y0 === y1
85573 // and for horizontal when x0 === x1
85574 return v;
85575 }
85576
85577 // if it's not in danger of disappearing entirely,
85578 // round more precisely
85579 return Math.abs(v - vc) >= 2 ? roundWithLine(v) :
85580 // but if it's very thin, expand it so it's
85581 // necessarily visible, even if it might overlap
85582 // its neighbor
85583 (v > vc ? Math.ceil(v) : Math.floor(v));
85584 }
85585
85586 if(!gd._context.staticPlot) {
85587 // if bars are not fully opaque or they have a line
85588 // around them, round to integer pixels, mainly for
85589 // safari so we prevent overlaps from its expansive
85590 // pixelation. if the bars ARE fully opaque and have
85591 // no line, expand to a full pixel to make sure we
85592 // can see them
85593
85594 var op = Color.opacity(mc);
85595 var fixpx = (op < 1 || lw > 0.01) ? roundWithLine : expandToVisible;
85596
85597 x0 = fixpx(x0, x1, isHorizontal);
85598 x1 = fixpx(x1, x0, isHorizontal);
85599 y0 = fixpx(y0, y1, !isHorizontal);
85600 y1 = fixpx(y1, y0, !isHorizontal);
85601 }
85602
85603 var sel = transition(Lib.ensureSingle(bar, 'path'), fullLayout, opts, makeOnCompleteCallback);
85604 sel
85605 .style('vector-effect', 'non-scaling-stroke')
85606 .attr('d', (isNaN((x1 - x0) * (y1 - y0)) || (isBlank && gd._context.staticPlot)) ? 'M0,0Z' : 'M' + x0 + ',' + y0 + 'V' + y1 + 'H' + x1 + 'V' + y0 + 'Z')
85607 .call(Drawing.setClipUrl, plotinfo.layerClipId, gd);
85608
85609 if(!fullLayout.uniformtext.mode && withTransition) {
85610 var styleFns = Drawing.makePointStyleFns(trace);
85611 Drawing.singlePointStyle(di, sel, trace, styleFns, gd);
85612 }
85613
85614 appendBarText(gd, plotinfo, bar, cd, i, x0, x1, y0, y1, opts, makeOnCompleteCallback);
85615
85616 if(plotinfo.layerClipId) {
85617 Drawing.hideOutsideRangePoint(di, bar.select('text'), xa, ya, trace.xcalendar, trace.ycalendar);
85618 }
85619 });
85620
85621 // lastly, clip points groups of `cliponaxis !== false` traces
85622 // on `plotinfo._hasClipOnAxisFalse === true` subplots
85623 var hasClipOnAxisFalse = trace.cliponaxis === false;
85624 Drawing.setClipUrl(plotGroup, hasClipOnAxisFalse ? null : plotinfo.layerClipId, gd);
85625 });
85626
85627 // error bars are on the top
85628 Registry.getComponentMethod('errorbars', 'plot')(gd, bartraces, plotinfo, opts);
85629}
85630
85631function appendBarText(gd, plotinfo, bar, cd, i, x0, x1, y0, y1, opts, makeOnCompleteCallback) {
85632 var xa = plotinfo.xaxis;
85633 var ya = plotinfo.yaxis;
85634
85635 var fullLayout = gd._fullLayout;
85636 var textPosition;
85637
85638 function appendTextNode(bar, text, font) {
85639 var textSelection = Lib.ensureSingle(bar, 'text')
85640 .text(text)
85641 .attr({
85642 'class': 'bartext bartext-' + textPosition,
85643 'text-anchor': 'middle',
85644 // prohibit tex interpretation until we can handle
85645 // tex and regular text together
85646 'data-notex': 1
85647 })
85648 .call(Drawing.font, font)
85649 .call(svgTextUtils.convertToTspans, gd);
85650
85651 return textSelection;
85652 }
85653
85654 // get trace attributes
85655 var trace = cd[0].trace;
85656 var isHorizontal = (trace.orientation === 'h');
85657
85658 var text = getText(fullLayout, cd, i, xa, ya);
85659 textPosition = getTextPosition(trace, i);
85660
85661 // compute text position
85662 var inStackOrRelativeMode =
85663 opts.mode === 'stack' ||
85664 opts.mode === 'relative';
85665
85666 var calcBar = cd[i];
85667 var isOutmostBar = !inStackOrRelativeMode || calcBar._outmost;
85668
85669 if(!text ||
85670 textPosition === 'none' ||
85671 ((calcBar.isBlank || x0 === x1 || y0 === y1) && (
85672 textPosition === 'auto' ||
85673 textPosition === 'inside'))) {
85674 bar.select('text').remove();
85675 return;
85676 }
85677
85678 var layoutFont = fullLayout.font;
85679 var barColor = style.getBarColor(cd[i], trace);
85680 var insideTextFont = style.getInsideTextFont(trace, i, layoutFont, barColor);
85681 var outsideTextFont = style.getOutsideTextFont(trace, i, layoutFont);
85682
85683 // Special case: don't use the c2p(v, true) value on log size axes,
85684 // so that we can get correctly inside text scaling
85685 var di = bar.datum();
85686 if(isHorizontal) {
85687 if(xa.type === 'log' && di.s0 <= 0) {
85688 if(xa.range[0] < xa.range[1]) {
85689 x0 = 0;
85690 } else {
85691 x0 = xa._length;
85692 }
85693 }
85694 } else {
85695 if(ya.type === 'log' && di.s0 <= 0) {
85696 if(ya.range[0] < ya.range[1]) {
85697 y0 = ya._length;
85698 } else {
85699 y0 = 0;
85700 }
85701 }
85702 }
85703
85704 // padding excluded
85705 var barWidth = Math.abs(x1 - x0) - 2 * TEXTPAD;
85706 var barHeight = Math.abs(y1 - y0) - 2 * TEXTPAD;
85707
85708 var textSelection;
85709 var textBB;
85710 var textWidth;
85711 var textHeight;
85712 var font;
85713
85714 if(textPosition === 'outside') {
85715 if(!isOutmostBar && !calcBar.hasB) textPosition = 'inside';
85716 }
85717
85718 if(textPosition === 'auto') {
85719 if(isOutmostBar) {
85720 // draw text using insideTextFont and check if it fits inside bar
85721 textPosition = 'inside';
85722
85723 font = Lib.ensureUniformFontSize(gd, insideTextFont);
85724
85725 textSelection = appendTextNode(bar, text, font);
85726
85727 textBB = Drawing.bBox(textSelection.node()),
85728 textWidth = textBB.width,
85729 textHeight = textBB.height;
85730
85731 var textHasSize = (textWidth > 0 && textHeight > 0);
85732 var fitsInside = (textWidth <= barWidth && textHeight <= barHeight);
85733 var fitsInsideIfRotated = (textWidth <= barHeight && textHeight <= barWidth);
85734 var fitsInsideIfShrunk = (isHorizontal) ?
85735 (barWidth >= textWidth * (barHeight / textHeight)) :
85736 (barHeight >= textHeight * (barWidth / textWidth));
85737
85738 if(textHasSize && (
85739 fitsInside ||
85740 fitsInsideIfRotated ||
85741 fitsInsideIfShrunk)
85742 ) {
85743 textPosition = 'inside';
85744 } else {
85745 textPosition = 'outside';
85746 textSelection.remove();
85747 textSelection = null;
85748 }
85749 } else {
85750 textPosition = 'inside';
85751 }
85752 }
85753
85754 if(!textSelection) {
85755 font = Lib.ensureUniformFontSize(gd, (textPosition === 'outside') ? outsideTextFont : insideTextFont);
85756
85757 textSelection = appendTextNode(bar, text, font);
85758
85759 var currentTransform = textSelection.attr('transform');
85760 textSelection.attr('transform', '');
85761 textBB = Drawing.bBox(textSelection.node()),
85762 textWidth = textBB.width,
85763 textHeight = textBB.height;
85764 textSelection.attr('transform', currentTransform);
85765
85766 if(textWidth <= 0 || textHeight <= 0) {
85767 textSelection.remove();
85768 return;
85769 }
85770 }
85771
85772 var angle = trace.textangle;
85773
85774 // compute text transform
85775 var transform, constrained;
85776 if(textPosition === 'outside') {
85777 constrained =
85778 trace.constraintext === 'both' ||
85779 trace.constraintext === 'outside';
85780
85781 transform = toMoveOutsideBar(x0, x1, y0, y1, textBB, {
85782 isHorizontal: isHorizontal,
85783 constrained: constrained,
85784 angle: angle
85785 });
85786 } else {
85787 constrained =
85788 trace.constraintext === 'both' ||
85789 trace.constraintext === 'inside';
85790
85791 transform = toMoveInsideBar(x0, x1, y0, y1, textBB, {
85792 isHorizontal: isHorizontal,
85793 constrained: constrained,
85794 angle: angle,
85795 anchor: trace.insidetextanchor
85796 });
85797 }
85798
85799 transform.fontSize = font.size;
85800 recordMinTextSize(trace.type, transform, fullLayout);
85801 calcBar.transform = transform;
85802
85803 transition(textSelection, fullLayout, opts, makeOnCompleteCallback)
85804 .attr('transform', Lib.getTextTransform(transform));
85805}
85806
85807function getRotateFromAngle(angle) {
85808 return (angle === 'auto') ? 0 : angle;
85809}
85810
85811function getRotatedTextSize(textBB, rotate) {
85812 var a = Math.PI / 180 * rotate;
85813 var absSin = Math.abs(Math.sin(a));
85814 var absCos = Math.abs(Math.cos(a));
85815
85816 return {
85817 x: textBB.width * absCos + textBB.height * absSin,
85818 y: textBB.width * absSin + textBB.height * absCos
85819 };
85820}
85821
85822function toMoveInsideBar(x0, x1, y0, y1, textBB, opts) {
85823 var isHorizontal = !!opts.isHorizontal;
85824 var constrained = !!opts.constrained;
85825 var angle = opts.angle || 0;
85826 var anchor = opts.anchor || 'end';
85827 var isEnd = anchor === 'end';
85828 var isStart = anchor === 'start';
85829 var leftToRight = opts.leftToRight || 0; // left: -1, center: 0, right: 1
85830 var toRight = (leftToRight + 1) / 2;
85831 var toLeft = 1 - toRight;
85832
85833 var textWidth = textBB.width;
85834 var textHeight = textBB.height;
85835 var lx = Math.abs(x1 - x0);
85836 var ly = Math.abs(y1 - y0);
85837
85838 // compute remaining space
85839 var textpad = (
85840 lx > (2 * TEXTPAD) &&
85841 ly > (2 * TEXTPAD)
85842 ) ? TEXTPAD : 0;
85843
85844 lx -= 2 * textpad;
85845 ly -= 2 * textpad;
85846
85847 var rotate = getRotateFromAngle(angle);
85848 if((angle === 'auto') &&
85849 !(textWidth <= lx && textHeight <= ly) &&
85850 (textWidth > lx || textHeight > ly) && (
85851 !(textWidth > ly || textHeight > lx) ||
85852 ((textWidth < textHeight) !== (lx < ly))
85853 )) {
85854 rotate += 90;
85855 }
85856
85857 var t = getRotatedTextSize(textBB, rotate);
85858
85859 var scale = 1;
85860 if(constrained) {
85861 scale = Math.min(
85862 1,
85863 lx / t.x,
85864 ly / t.y
85865 );
85866 }
85867
85868 // compute text and target positions
85869 var textX = (
85870 textBB.left * toLeft +
85871 textBB.right * toRight
85872 );
85873 var textY = (textBB.top + textBB.bottom) / 2;
85874 var targetX = (
85875 (x0 + TEXTPAD) * toLeft +
85876 (x1 - TEXTPAD) * toRight
85877 );
85878 var targetY = (y0 + y1) / 2;
85879 var anchorX = 0;
85880 var anchorY = 0;
85881 if(isStart || isEnd) {
85882 var extrapad = (isHorizontal ? t.x : t.y) / 2;
85883 var dir = isHorizontal ? dirSign(x0, x1) : dirSign(y0, y1);
85884
85885 if(isHorizontal) {
85886 if(isStart) {
85887 targetX = x0 + dir * textpad;
85888 anchorX = -dir * extrapad;
85889 } else {
85890 targetX = x1 - dir * textpad;
85891 anchorX = dir * extrapad;
85892 }
85893 } else {
85894 if(isStart) {
85895 targetY = y0 + dir * textpad;
85896 anchorY = -dir * extrapad;
85897 } else {
85898 targetY = y1 - dir * textpad;
85899 anchorY = dir * extrapad;
85900 }
85901 }
85902 }
85903
85904 return {
85905 textX: textX,
85906 textY: textY,
85907 targetX: targetX,
85908 targetY: targetY,
85909 anchorX: anchorX,
85910 anchorY: anchorY,
85911 scale: scale,
85912 rotate: rotate
85913 };
85914}
85915
85916function toMoveOutsideBar(x0, x1, y0, y1, textBB, opts) {
85917 var isHorizontal = !!opts.isHorizontal;
85918 var constrained = !!opts.constrained;
85919 var angle = opts.angle || 0;
85920
85921 var textWidth = textBB.width;
85922 var textHeight = textBB.height;
85923 var lx = Math.abs(x1 - x0);
85924 var ly = Math.abs(y1 - y0);
85925
85926 var textpad;
85927 // Keep the padding so the text doesn't sit right against
85928 // the bars, but don't factor it into barWidth
85929 if(isHorizontal) {
85930 textpad = (ly > 2 * TEXTPAD) ? TEXTPAD : 0;
85931 } else {
85932 textpad = (lx > 2 * TEXTPAD) ? TEXTPAD : 0;
85933 }
85934
85935 // compute rotate and scale
85936 var scale = 1;
85937 if(constrained) {
85938 scale = (isHorizontal) ?
85939 Math.min(1, ly / textHeight) :
85940 Math.min(1, lx / textWidth);
85941 }
85942
85943 var rotate = getRotateFromAngle(angle);
85944 var t = getRotatedTextSize(textBB, rotate);
85945
85946 // compute text and target positions
85947 var extrapad = (isHorizontal ? t.x : t.y) / 2;
85948 var textX = (textBB.left + textBB.right) / 2;
85949 var textY = (textBB.top + textBB.bottom) / 2;
85950 var targetX = (x0 + x1) / 2;
85951 var targetY = (y0 + y1) / 2;
85952 var anchorX = 0;
85953 var anchorY = 0;
85954
85955 var dir = isHorizontal ? dirSign(x1, x0) : dirSign(y0, y1);
85956 if(isHorizontal) {
85957 targetX = x1 - dir * textpad;
85958 anchorX = dir * extrapad;
85959 } else {
85960 targetY = y1 + dir * textpad;
85961 anchorY = -dir * extrapad;
85962 }
85963
85964 return {
85965 textX: textX,
85966 textY: textY,
85967 targetX: targetX,
85968 targetY: targetY,
85969 anchorX: anchorX,
85970 anchorY: anchorY,
85971 scale: scale,
85972 rotate: rotate
85973 };
85974}
85975
85976function getText(fullLayout, cd, index, xa, ya) {
85977 var trace = cd[0].trace;
85978 var texttemplate = trace.texttemplate;
85979
85980 var value;
85981 if(texttemplate) {
85982 value = calcTexttemplate(fullLayout, cd, index, xa, ya);
85983 } else if(trace.textinfo) {
85984 value = calcTextinfo(cd, index, xa, ya);
85985 } else {
85986 value = helpers.getValue(trace.text, index);
85987 }
85988
85989 return helpers.coerceString(attributeText, value);
85990}
85991
85992function getTextPosition(trace, index) {
85993 var value = helpers.getValue(trace.textposition, index);
85994 return helpers.coerceEnumerated(attributeTextPosition, value);
85995}
85996
85997function calcTexttemplate(fullLayout, cd, index, xa, ya) {
85998 var trace = cd[0].trace;
85999 var texttemplate = Lib.castOption(trace, index, 'texttemplate');
86000 if(!texttemplate) return '';
86001 var isWaterfall = (trace.type === 'waterfall');
86002 var isFunnel = (trace.type === 'funnel');
86003
86004 var pLetter, pAxis;
86005 var vLetter, vAxis;
86006 if(trace.orientation === 'h') {
86007 pLetter = 'y';
86008 pAxis = ya;
86009 vLetter = 'x';
86010 vAxis = xa;
86011 } else {
86012 pLetter = 'x';
86013 pAxis = xa;
86014 vLetter = 'y';
86015 vAxis = ya;
86016 }
86017
86018 function formatLabel(u) {
86019 return tickText(pAxis, pAxis.c2l(u), true).text;
86020 }
86021
86022 function formatNumber(v) {
86023 return tickText(vAxis, vAxis.c2l(v), true).text;
86024 }
86025
86026 var cdi = cd[index];
86027 var obj = {};
86028
86029 obj.label = cdi.p;
86030 obj.labelLabel = obj[pLetter + 'Label'] = formatLabel(cdi.p);
86031
86032 var tx = Lib.castOption(trace, cdi.i, 'text');
86033 if(tx === 0 || tx) obj.text = tx;
86034
86035 obj.value = cdi.s;
86036 obj.valueLabel = obj[vLetter + 'Label'] = formatNumber(cdi.s);
86037
86038 var pt = {};
86039 appendArrayPointValue(pt, trace, cdi.i);
86040
86041 if(isWaterfall) {
86042 obj.delta = +cdi.rawS || cdi.s;
86043 obj.deltaLabel = formatNumber(obj.delta);
86044 obj.final = cdi.v;
86045 obj.finalLabel = formatNumber(obj.final);
86046 obj.initial = obj.final - obj.delta;
86047 obj.initialLabel = formatNumber(obj.initial);
86048 }
86049
86050 if(isFunnel) {
86051 obj.value = cdi.s;
86052 obj.valueLabel = formatNumber(obj.value);
86053
86054 obj.percentInitial = cdi.begR;
86055 obj.percentInitialLabel = Lib.formatPercent(cdi.begR);
86056 obj.percentPrevious = cdi.difR;
86057 obj.percentPreviousLabel = Lib.formatPercent(cdi.difR);
86058 obj.percentTotal = cdi.sumR;
86059 obj.percenTotalLabel = Lib.formatPercent(cdi.sumR);
86060 }
86061
86062 var customdata = Lib.castOption(trace, cdi.i, 'customdata');
86063 if(customdata) obj.customdata = customdata;
86064 return Lib.texttemplateString(texttemplate, obj, fullLayout._d3locale, pt, obj, trace._meta || {});
86065}
86066
86067function calcTextinfo(cd, index, xa, ya) {
86068 var trace = cd[0].trace;
86069 var isHorizontal = (trace.orientation === 'h');
86070 var isWaterfall = (trace.type === 'waterfall');
86071 var isFunnel = (trace.type === 'funnel');
86072
86073 function formatLabel(u) {
86074 var pAxis = isHorizontal ? ya : xa;
86075 return tickText(pAxis, u, true).text;
86076 }
86077
86078 function formatNumber(v) {
86079 var sAxis = isHorizontal ? xa : ya;
86080 return tickText(sAxis, +v, true).text;
86081 }
86082
86083 var textinfo = trace.textinfo;
86084 var cdi = cd[index];
86085
86086 var parts = textinfo.split('+');
86087 var text = [];
86088 var tx;
86089
86090 var hasFlag = function(flag) { return parts.indexOf(flag) !== -1; };
86091
86092 if(hasFlag('label')) {
86093 text.push(formatLabel(cd[index].p));
86094 }
86095
86096 if(hasFlag('text')) {
86097 tx = Lib.castOption(trace, cdi.i, 'text');
86098 if(tx === 0 || tx) text.push(tx);
86099 }
86100
86101 if(isWaterfall) {
86102 var delta = +cdi.rawS || cdi.s;
86103 var final = cdi.v;
86104 var initial = final - delta;
86105
86106 if(hasFlag('initial')) text.push(formatNumber(initial));
86107 if(hasFlag('delta')) text.push(formatNumber(delta));
86108 if(hasFlag('final')) text.push(formatNumber(final));
86109 }
86110
86111 if(isFunnel) {
86112 if(hasFlag('value')) text.push(formatNumber(cdi.s));
86113
86114 var nPercent = 0;
86115 if(hasFlag('percent initial')) nPercent++;
86116 if(hasFlag('percent previous')) nPercent++;
86117 if(hasFlag('percent total')) nPercent++;
86118
86119 var hasMultiplePercents = nPercent > 1;
86120
86121 if(hasFlag('percent initial')) {
86122 tx = Lib.formatPercent(cdi.begR);
86123 if(hasMultiplePercents) tx += ' of initial';
86124 text.push(tx);
86125 }
86126 if(hasFlag('percent previous')) {
86127 tx = Lib.formatPercent(cdi.difR);
86128 if(hasMultiplePercents) tx += ' of previous';
86129 text.push(tx);
86130 }
86131 if(hasFlag('percent total')) {
86132 tx = Lib.formatPercent(cdi.sumR);
86133 if(hasMultiplePercents) tx += ' of total';
86134 text.push(tx);
86135 }
86136 }
86137
86138 return text.join('<br>');
86139}
86140
86141module.exports = {
86142 plot: plot,
86143 toMoveInsideBar: toMoveInsideBar
86144};
86145
86146},{"../../components/color":157,"../../components/drawing":179,"../../components/fx/helpers":193,"../../lib":287,"../../lib/svg_text_utils":310,"../../plots/cartesian/axes":334,"../../registry":376,"./attributes":386,"./constants":388,"./helpers":392,"./style":400,"./uniform_text":402,"@plotly/d3":20,"fast-isnumeric":33}],398:[function(_dereq_,module,exports){
86147'use strict';
86148
86149module.exports = function selectPoints(searchInfo, selectionTester) {
86150 var cd = searchInfo.cd;
86151 var xa = searchInfo.xaxis;
86152 var ya = searchInfo.yaxis;
86153 var trace = cd[0].trace;
86154 var isFunnel = (trace.type === 'funnel');
86155 var isHorizontal = (trace.orientation === 'h');
86156 var selection = [];
86157 var i;
86158
86159 if(selectionTester === false) {
86160 // clear selection
86161 for(i = 0; i < cd.length; i++) {
86162 cd[i].selected = 0;
86163 }
86164 } else {
86165 for(i = 0; i < cd.length; i++) {
86166 var di = cd[i];
86167 var ct = 'ct' in di ? di.ct : getCentroid(di, xa, ya, isHorizontal, isFunnel);
86168
86169 if(selectionTester.contains(ct, false, i, searchInfo)) {
86170 selection.push({
86171 pointNumber: i,
86172 x: xa.c2d(di.x),
86173 y: ya.c2d(di.y)
86174 });
86175 di.selected = 1;
86176 } else {
86177 di.selected = 0;
86178 }
86179 }
86180 }
86181
86182 return selection;
86183};
86184
86185function getCentroid(d, xa, ya, isHorizontal, isFunnel) {
86186 var x0 = xa.c2p(isHorizontal ? d.s0 : d.p0, true);
86187 var x1 = xa.c2p(isHorizontal ? d.s1 : d.p1, true);
86188 var y0 = ya.c2p(isHorizontal ? d.p0 : d.s0, true);
86189 var y1 = ya.c2p(isHorizontal ? d.p1 : d.s1, true);
86190
86191 if(isFunnel) {
86192 return [(x0 + x1) / 2, (y0 + y1) / 2];
86193 } else {
86194 if(isHorizontal) {
86195 return [x1, (y0 + y1) / 2];
86196 } else {
86197 return [(x0 + x1) / 2, y1];
86198 }
86199 }
86200}
86201
86202},{}],399:[function(_dereq_,module,exports){
86203'use strict';
86204
86205module.exports = Sieve;
86206
86207var distinctVals = _dereq_('../../lib').distinctVals;
86208var BADNUM = _dereq_('../../constants/numerical').BADNUM;
86209
86210/**
86211 * Helper class to sieve data from traces into bins
86212 *
86213 * @class
86214 *
86215 * @param {Array} traces
86216* Array of calculated traces
86217 * @param {object} opts
86218 * - @param {boolean} [sepNegVal]
86219 * If true, then split data at the same position into a bar
86220 * for positive values and another for negative values
86221 * - @param {boolean} [overlapNoMerge]
86222 * If true, then don't merge overlapping bars into a single bar
86223 */
86224function Sieve(traces, opts) {
86225 this.traces = traces;
86226 this.sepNegVal = opts.sepNegVal;
86227 this.overlapNoMerge = opts.overlapNoMerge;
86228
86229 // for single-bin histograms - see histogram/calc
86230 var width1 = Infinity;
86231
86232 var positions = [];
86233 for(var i = 0; i < traces.length; i++) {
86234 var trace = traces[i];
86235 for(var j = 0; j < trace.length; j++) {
86236 var bar = trace[j];
86237 if(bar.p !== BADNUM) positions.push(bar.p);
86238 }
86239 if(trace[0] && trace[0].width1) {
86240 width1 = Math.min(trace[0].width1, width1);
86241 }
86242 }
86243 this.positions = positions;
86244
86245 var dv = distinctVals(positions);
86246
86247 this.distinctPositions = dv.vals;
86248 if(dv.vals.length === 1 && width1 !== Infinity) this.minDiff = width1;
86249 else this.minDiff = Math.min(dv.minDiff, width1);
86250
86251 var type = (opts.posAxis || {}).type;
86252 if(type === 'category' || type === 'multicategory') {
86253 this.minDiff = 1;
86254 }
86255
86256 this.binWidth = this.minDiff;
86257
86258 this.bins = {};
86259}
86260
86261/**
86262 * Sieve datum
86263 *
86264 * @method
86265 * @param {number} position
86266 * @param {number} value
86267 * @returns {number} Previous bin value
86268 */
86269Sieve.prototype.put = function put(position, value) {
86270 var label = this.getLabel(position, value);
86271 var oldValue = this.bins[label] || 0;
86272
86273 this.bins[label] = oldValue + value;
86274
86275 return oldValue;
86276};
86277
86278/**
86279 * Get current bin value for a given datum
86280 *
86281 * @method
86282 * @param {number} position Position of datum
86283 * @param {number} [value] Value of datum
86284 * (required if this.sepNegVal is true)
86285 * @returns {number} Current bin value
86286 */
86287Sieve.prototype.get = function get(position, value) {
86288 var label = this.getLabel(position, value);
86289 return this.bins[label] || 0;
86290};
86291
86292/**
86293 * Get bin label for a given datum
86294 *
86295 * @method
86296 * @param {number} position Position of datum
86297 * @param {number} [value] Value of datum
86298 * (required if this.sepNegVal is true)
86299 * @returns {string} Bin label
86300 * (prefixed with a 'v' if value is negative and this.sepNegVal is
86301 * true; otherwise prefixed with '^')
86302 */
86303Sieve.prototype.getLabel = function getLabel(position, value) {
86304 var prefix = (value < 0 && this.sepNegVal) ? 'v' : '^';
86305 var label = (this.overlapNoMerge) ?
86306 position :
86307 Math.round(position / this.binWidth);
86308 return prefix + label;
86309};
86310
86311},{"../../constants/numerical":267,"../../lib":287}],400:[function(_dereq_,module,exports){
86312'use strict';
86313
86314var d3 = _dereq_('@plotly/d3');
86315var Color = _dereq_('../../components/color');
86316var Drawing = _dereq_('../../components/drawing');
86317var Lib = _dereq_('../../lib');
86318var Registry = _dereq_('../../registry');
86319
86320var resizeText = _dereq_('./uniform_text').resizeText;
86321var attributes = _dereq_('./attributes');
86322var attributeTextFont = attributes.textfont;
86323var attributeInsideTextFont = attributes.insidetextfont;
86324var attributeOutsideTextFont = attributes.outsidetextfont;
86325var helpers = _dereq_('./helpers');
86326
86327function style(gd) {
86328 var s = d3.select(gd).selectAll('g.barlayer').selectAll('g.trace');
86329 resizeText(gd, s, 'bar');
86330
86331 var barcount = s.size();
86332 var fullLayout = gd._fullLayout;
86333
86334 // trace styling
86335 s.style('opacity', function(d) { return d[0].trace.opacity; })
86336
86337 // for gapless (either stacked or neighboring grouped) bars use
86338 // crispEdges to turn off antialiasing so an artificial gap
86339 // isn't introduced.
86340 .each(function(d) {
86341 if((fullLayout.barmode === 'stack' && barcount > 1) ||
86342 (fullLayout.bargap === 0 &&
86343 fullLayout.bargroupgap === 0 &&
86344 !d[0].trace.marker.line.width)) {
86345 d3.select(this).attr('shape-rendering', 'crispEdges');
86346 }
86347 });
86348
86349 s.selectAll('g.points').each(function(d) {
86350 var sel = d3.select(this);
86351 var trace = d[0].trace;
86352 stylePoints(sel, trace, gd);
86353 });
86354
86355 Registry.getComponentMethod('errorbars', 'style')(s);
86356}
86357
86358function stylePoints(sel, trace, gd) {
86359 Drawing.pointStyle(sel.selectAll('path'), trace, gd);
86360 styleTextPoints(sel, trace, gd);
86361}
86362
86363function styleTextPoints(sel, trace, gd) {
86364 sel.selectAll('text').each(function(d) {
86365 var tx = d3.select(this);
86366 var font = Lib.ensureUniformFontSize(gd, determineFont(tx, d, trace, gd));
86367
86368 Drawing.font(tx, font);
86369 });
86370}
86371
86372function styleOnSelect(gd, cd, sel) {
86373 var trace = cd[0].trace;
86374
86375 if(trace.selectedpoints) {
86376 stylePointsInSelectionMode(sel, trace, gd);
86377 } else {
86378 stylePoints(sel, trace, gd);
86379 Registry.getComponentMethod('errorbars', 'style')(sel);
86380 }
86381}
86382
86383function stylePointsInSelectionMode(s, trace, gd) {
86384 Drawing.selectedPointStyle(s.selectAll('path'), trace);
86385 styleTextInSelectionMode(s.selectAll('text'), trace, gd);
86386}
86387
86388function styleTextInSelectionMode(txs, trace, gd) {
86389 txs.each(function(d) {
86390 var tx = d3.select(this);
86391 var font;
86392
86393 if(d.selected) {
86394 font = Lib.ensureUniformFontSize(gd, determineFont(tx, d, trace, gd));
86395
86396 var selectedFontColor = trace.selected.textfont && trace.selected.textfont.color;
86397 if(selectedFontColor) {
86398 font.color = selectedFontColor;
86399 }
86400
86401 Drawing.font(tx, font);
86402 } else {
86403 Drawing.selectedTextStyle(tx, trace);
86404 }
86405 });
86406}
86407
86408function determineFont(tx, d, trace, gd) {
86409 var layoutFont = gd._fullLayout.font;
86410 var textFont = trace.textfont;
86411
86412 if(tx.classed('bartext-inside')) {
86413 var barColor = getBarColor(d, trace);
86414 textFont = getInsideTextFont(trace, d.i, layoutFont, barColor);
86415 } else if(tx.classed('bartext-outside')) {
86416 textFont = getOutsideTextFont(trace, d.i, layoutFont);
86417 }
86418
86419 return textFont;
86420}
86421
86422function getTextFont(trace, index, defaultValue) {
86423 return getFontValue(
86424 attributeTextFont, trace.textfont, index, defaultValue);
86425}
86426
86427function getInsideTextFont(trace, index, layoutFont, barColor) {
86428 var defaultFont = getTextFont(trace, index, layoutFont);
86429
86430 var wouldFallBackToLayoutFont =
86431 (trace._input.textfont === undefined || trace._input.textfont.color === undefined) ||
86432 (Array.isArray(trace.textfont.color) && trace.textfont.color[index] === undefined);
86433 if(wouldFallBackToLayoutFont) {
86434 defaultFont = {
86435 color: Color.contrast(barColor),
86436 family: defaultFont.family,
86437 size: defaultFont.size
86438 };
86439 }
86440
86441 return getFontValue(
86442 attributeInsideTextFont, trace.insidetextfont, index, defaultFont);
86443}
86444
86445function getOutsideTextFont(trace, index, layoutFont) {
86446 var defaultFont = getTextFont(trace, index, layoutFont);
86447 return getFontValue(
86448 attributeOutsideTextFont, trace.outsidetextfont, index, defaultFont);
86449}
86450
86451function getFontValue(attributeDefinition, attributeValue, index, defaultValue) {
86452 attributeValue = attributeValue || {};
86453
86454 var familyValue = helpers.getValue(attributeValue.family, index);
86455 var sizeValue = helpers.getValue(attributeValue.size, index);
86456 var colorValue = helpers.getValue(attributeValue.color, index);
86457
86458 return {
86459 family: helpers.coerceString(
86460 attributeDefinition.family, familyValue, defaultValue.family),
86461 size: helpers.coerceNumber(
86462 attributeDefinition.size, sizeValue, defaultValue.size),
86463 color: helpers.coerceColor(
86464 attributeDefinition.color, colorValue, defaultValue.color)
86465 };
86466}
86467
86468function getBarColor(cd, trace) {
86469 if(trace.type === 'waterfall') {
86470 return trace[cd.dir].marker.color;
86471 }
86472 return cd.mcc || cd.mc || trace.marker.color;
86473}
86474
86475module.exports = {
86476 style: style,
86477 styleTextPoints: styleTextPoints,
86478 styleOnSelect: styleOnSelect,
86479 getInsideTextFont: getInsideTextFont,
86480 getOutsideTextFont: getOutsideTextFont,
86481 getBarColor: getBarColor,
86482 resizeText: resizeText
86483};
86484
86485},{"../../components/color":157,"../../components/drawing":179,"../../lib":287,"../../registry":376,"./attributes":386,"./helpers":392,"./uniform_text":402,"@plotly/d3":20}],401:[function(_dereq_,module,exports){
86486'use strict';
86487
86488var Color = _dereq_('../../components/color');
86489var hasColorscale = _dereq_('../../components/colorscale/helpers').hasColorscale;
86490var colorscaleDefaults = _dereq_('../../components/colorscale/defaults');
86491var coercePattern = _dereq_('../../lib').coercePattern;
86492
86493module.exports = function handleStyleDefaults(traceIn, traceOut, coerce, defaultColor, layout) {
86494 var markerColor = coerce('marker.color', defaultColor);
86495 var hasMarkerColorscale = hasColorscale(traceIn, 'marker');
86496 if(hasMarkerColorscale) {
86497 colorscaleDefaults(
86498 traceIn, traceOut, layout, coerce, {prefix: 'marker.', cLetter: 'c'}
86499 );
86500 }
86501
86502 coerce('marker.line.color', Color.defaultLine);
86503
86504 if(hasColorscale(traceIn, 'marker.line')) {
86505 colorscaleDefaults(
86506 traceIn, traceOut, layout, coerce, {prefix: 'marker.line.', cLetter: 'c'}
86507 );
86508 }
86509
86510 coerce('marker.line.width');
86511 coerce('marker.opacity');
86512 coercePattern(coerce, 'marker.pattern', markerColor, hasMarkerColorscale);
86513 coerce('selected.marker.color');
86514 coerce('unselected.marker.color');
86515};
86516
86517},{"../../components/color":157,"../../components/colorscale/defaults":167,"../../components/colorscale/helpers":168,"../../lib":287}],402:[function(_dereq_,module,exports){
86518'use strict';
86519
86520var d3 = _dereq_('@plotly/d3');
86521var Lib = _dereq_('../../lib');
86522
86523function resizeText(gd, gTrace, traceType) {
86524 var fullLayout = gd._fullLayout;
86525 var minSize = fullLayout['_' + traceType + 'Text_minsize'];
86526 if(minSize) {
86527 var shouldHide = fullLayout.uniformtext.mode === 'hide';
86528
86529 var selector;
86530 switch(traceType) {
86531 case 'funnelarea' :
86532 case 'pie' :
86533 case 'sunburst' :
86534 selector = 'g.slice';
86535 break;
86536 case 'treemap' :
86537 case 'icicle' :
86538 selector = 'g.slice, g.pathbar';
86539 break;
86540 default :
86541 selector = 'g.points > g.point';
86542 }
86543
86544 gTrace.selectAll(selector).each(function(d) {
86545 var transform = d.transform;
86546 if(transform) {
86547 transform.scale = (shouldHide && transform.hide) ? 0 : minSize / transform.fontSize;
86548
86549 var el = d3.select(this).select('text');
86550 el.attr('transform', Lib.getTextTransform(transform));
86551 }
86552 });
86553 }
86554}
86555
86556function recordMinTextSize(
86557 traceType, // in
86558 transform, // inout
86559 fullLayout // inout
86560) {
86561 if(fullLayout.uniformtext.mode) {
86562 var minKey = getMinKey(traceType);
86563 var minSize = fullLayout.uniformtext.minsize;
86564 var size = transform.scale * transform.fontSize;
86565
86566 transform.hide = size < minSize;
86567
86568 fullLayout[minKey] = fullLayout[minKey] || Infinity;
86569 if(!transform.hide) {
86570 fullLayout[minKey] = Math.min(
86571 fullLayout[minKey],
86572 Math.max(size, minSize)
86573 );
86574 }
86575 }
86576}
86577
86578function clearMinTextSize(
86579 traceType, // in
86580 fullLayout // inout
86581) {
86582 var minKey = getMinKey(traceType);
86583 fullLayout[minKey] = undefined;
86584}
86585
86586function getMinKey(traceType) {
86587 return '_' + traceType + 'Text_minsize';
86588}
86589
86590module.exports = {
86591 recordMinTextSize: recordMinTextSize,
86592 clearMinTextSize: clearMinTextSize,
86593 resizeText: resizeText
86594};
86595
86596},{"../../lib":287,"@plotly/d3":20}],403:[function(_dereq_,module,exports){
86597'use strict';
86598
86599var scatterAttrs = _dereq_('../scatter/attributes');
86600var barAttrs = _dereq_('../bar/attributes');
86601var colorAttrs = _dereq_('../../components/color/attributes');
86602var axisHoverFormat = _dereq_('../../plots/cartesian/axis_format_attributes').axisHoverFormat;
86603var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs;
86604var extendFlat = _dereq_('../../lib/extend').extendFlat;
86605
86606var scatterMarkerAttrs = scatterAttrs.marker;
86607var scatterMarkerLineAttrs = scatterMarkerAttrs.line;
86608
86609module.exports = {
86610 y: {
86611 valType: 'data_array',
86612 editType: 'calc+clearAxisTypes',
86613 },
86614 x: {
86615 valType: 'data_array',
86616 editType: 'calc+clearAxisTypes',
86617 },
86618 x0: {
86619 valType: 'any',
86620 editType: 'calc+clearAxisTypes',
86621 },
86622 y0: {
86623 valType: 'any',
86624 editType: 'calc+clearAxisTypes',
86625 },
86626
86627 dx: {
86628 valType: 'number',
86629 editType: 'calc',
86630 },
86631 dy: {
86632 valType: 'number',
86633 editType: 'calc',
86634 },
86635
86636 xperiod: scatterAttrs.xperiod,
86637 yperiod: scatterAttrs.yperiod,
86638 xperiod0: scatterAttrs.xperiod0,
86639 yperiod0: scatterAttrs.yperiod0,
86640 xperiodalignment: scatterAttrs.xperiodalignment,
86641 yperiodalignment: scatterAttrs.yperiodalignment,
86642 xhoverformat: axisHoverFormat('x'),
86643 yhoverformat: axisHoverFormat('y'),
86644
86645 name: {
86646 valType: 'string',
86647 editType: 'calc+clearAxisTypes',
86648 },
86649
86650 q1: {
86651 valType: 'data_array',
86652 editType: 'calc+clearAxisTypes',
86653 },
86654 median: {
86655 valType: 'data_array',
86656 editType: 'calc+clearAxisTypes',
86657 },
86658 q3: {
86659 valType: 'data_array',
86660 editType: 'calc+clearAxisTypes',
86661 },
86662 lowerfence: {
86663 valType: 'data_array',
86664 editType: 'calc',
86665 },
86666 upperfence: {
86667 valType: 'data_array',
86668 editType: 'calc',
86669 },
86670
86671 notched: {
86672 valType: 'boolean',
86673 editType: 'calc',
86674 },
86675 notchwidth: {
86676 valType: 'number',
86677 min: 0,
86678 max: 0.5,
86679 dflt: 0.25,
86680 editType: 'calc',
86681 },
86682 notchspan: {
86683 valType: 'data_array',
86684 editType: 'calc',
86685 },
86686
86687 // TODO
86688 // maybe add
86689 // - loweroutlierbound / upperoutlierbound
86690 // - lowersuspectedoutlierbound / uppersuspectedoutlierbound
86691
86692 boxpoints: {
86693 valType: 'enumerated',
86694 values: ['all', 'outliers', 'suspectedoutliers', false],
86695 editType: 'calc',
86696 },
86697 jitter: {
86698 valType: 'number',
86699 min: 0,
86700 max: 1,
86701 editType: 'calc',
86702 },
86703 pointpos: {
86704 valType: 'number',
86705 min: -2,
86706 max: 2,
86707 editType: 'calc',
86708 },
86709
86710 boxmean: {
86711 valType: 'enumerated',
86712 values: [true, 'sd', false],
86713 editType: 'calc',
86714 },
86715 mean: {
86716 valType: 'data_array',
86717 editType: 'calc',
86718 },
86719 sd: {
86720 valType: 'data_array',
86721 editType: 'calc',
86722 },
86723
86724 orientation: {
86725 valType: 'enumerated',
86726 values: ['v', 'h'],
86727 editType: 'calc+clearAxisTypes',
86728 },
86729
86730 quartilemethod: {
86731 valType: 'enumerated',
86732 values: ['linear', 'exclusive', 'inclusive'],
86733 dflt: 'linear',
86734 editType: 'calc',
86735 },
86736
86737 width: {
86738 valType: 'number',
86739 min: 0,
86740 dflt: 0,
86741 editType: 'calc',
86742 },
86743
86744 marker: {
86745 outliercolor: {
86746 valType: 'color',
86747 dflt: 'rgba(0, 0, 0, 0)',
86748 editType: 'style',
86749 },
86750 symbol: extendFlat({}, scatterMarkerAttrs.symbol,
86751 {arrayOk: false, editType: 'plot'}),
86752 opacity: extendFlat({}, scatterMarkerAttrs.opacity,
86753 {arrayOk: false, dflt: 1, editType: 'style'}),
86754 size: extendFlat({}, scatterMarkerAttrs.size,
86755 {arrayOk: false, editType: 'calc'}),
86756 color: extendFlat({}, scatterMarkerAttrs.color,
86757 {arrayOk: false, editType: 'style'}),
86758 line: {
86759 color: extendFlat({}, scatterMarkerLineAttrs.color,
86760 {arrayOk: false, dflt: colorAttrs.defaultLine, editType: 'style'}
86761 ),
86762 width: extendFlat({}, scatterMarkerLineAttrs.width,
86763 {arrayOk: false, dflt: 0, editType: 'style'}
86764 ),
86765 outliercolor: {
86766 valType: 'color',
86767 editType: 'style',
86768 },
86769 outlierwidth: {
86770 valType: 'number',
86771 min: 0,
86772 dflt: 1,
86773 editType: 'style',
86774 },
86775 editType: 'style'
86776 },
86777 editType: 'plot'
86778 },
86779
86780 line: {
86781 color: {
86782 valType: 'color',
86783 editType: 'style',
86784 },
86785 width: {
86786 valType: 'number',
86787 min: 0,
86788 dflt: 2,
86789 editType: 'style',
86790 },
86791 editType: 'plot'
86792 },
86793
86794 fillcolor: scatterAttrs.fillcolor,
86795
86796 whiskerwidth: {
86797 valType: 'number',
86798 min: 0,
86799 max: 1,
86800 dflt: 0.5,
86801 editType: 'calc',
86802 },
86803
86804 offsetgroup: barAttrs.offsetgroup,
86805 alignmentgroup: barAttrs.alignmentgroup,
86806
86807 selected: {
86808 marker: scatterAttrs.selected.marker,
86809 editType: 'style'
86810 },
86811 unselected: {
86812 marker: scatterAttrs.unselected.marker,
86813 editType: 'style'
86814 },
86815
86816 text: extendFlat({}, scatterAttrs.text, {
86817 }),
86818 hovertext: extendFlat({}, scatterAttrs.hovertext, {
86819 }),
86820 hovertemplate: hovertemplateAttrs({
86821 }),
86822
86823 hoveron: {
86824 valType: 'flaglist',
86825 flags: ['boxes', 'points'],
86826 dflt: 'boxes+points',
86827 editType: 'style',
86828 }
86829};
86830
86831},{"../../components/color/attributes":156,"../../lib/extend":281,"../../plots/cartesian/axis_format_attributes":337,"../../plots/template_attributes":371,"../bar/attributes":386,"../scatter/attributes":497}],404:[function(_dereq_,module,exports){
86832'use strict';
86833
86834var isNumeric = _dereq_('fast-isnumeric');
86835
86836var Axes = _dereq_('../../plots/cartesian/axes');
86837var alignPeriod = _dereq_('../../plots/cartesian/align_period');
86838var Lib = _dereq_('../../lib');
86839
86840var BADNUM = _dereq_('../../constants/numerical').BADNUM;
86841var _ = Lib._;
86842
86843module.exports = function calc(gd, trace) {
86844 var fullLayout = gd._fullLayout;
86845 var xa = Axes.getFromId(gd, trace.xaxis || 'x');
86846 var ya = Axes.getFromId(gd, trace.yaxis || 'y');
86847 var cd = [];
86848
86849 // N.B. violin reuses same Box.calc
86850 var numKey = trace.type === 'violin' ? '_numViolins' : '_numBoxes';
86851
86852 var i, j;
86853 var valAxis, valLetter;
86854 var posAxis, posLetter;
86855
86856 var hasPeriod;
86857 if(trace.orientation === 'h') {
86858 valAxis = xa;
86859 valLetter = 'x';
86860 posAxis = ya;
86861 posLetter = 'y';
86862 hasPeriod = !!trace.yperiodalignment;
86863 } else {
86864 valAxis = ya;
86865 valLetter = 'y';
86866 posAxis = xa;
86867 posLetter = 'x';
86868 hasPeriod = !!trace.xperiodalignment;
86869 }
86870
86871 var allPosArrays = getPosArrays(trace, posLetter, posAxis, fullLayout[numKey]);
86872 var posArray = allPosArrays[0];
86873 var origPos = allPosArrays[1];
86874 var dv = Lib.distinctVals(posArray, posAxis);
86875 var posDistinct = dv.vals;
86876 var dPos = dv.minDiff / 2;
86877
86878 // item in trace calcdata
86879 var cdi;
86880 // array of {v: v, i, i} sample pts
86881 var pts;
86882 // values of the `pts` array of objects
86883 var boxVals;
86884 // length of sample
86885 var N;
86886 // single sample point
86887 var pt;
86888 // single sample value
86889 var v;
86890
86891 // filter function for outlier pts
86892 // outlier definition based on http://www.physics.csbsju.edu/stats/box2.html
86893 var ptFilterFn = (trace.boxpoints || trace.points) === 'all' ?
86894 Lib.identity :
86895 function(pt) { return (pt.v < cdi.lf || pt.v > cdi.uf); };
86896
86897 if(trace._hasPreCompStats) {
86898 var valArrayRaw = trace[valLetter];
86899 var d2c = function(k) { return valAxis.d2c((trace[k] || [])[i]); };
86900 var minVal = Infinity;
86901 var maxVal = -Infinity;
86902
86903 for(i = 0; i < trace._length; i++) {
86904 var posi = posArray[i];
86905 if(!isNumeric(posi)) continue;
86906
86907 cdi = {};
86908 cdi.pos = cdi[posLetter] = posi;
86909 if(hasPeriod && origPos) {
86910 cdi.orig_p = origPos[i]; // used by hover
86911 }
86912
86913 cdi.q1 = d2c('q1');
86914 cdi.med = d2c('median');
86915 cdi.q3 = d2c('q3');
86916
86917 pts = [];
86918 if(valArrayRaw && Lib.isArrayOrTypedArray(valArrayRaw[i])) {
86919 for(j = 0; j < valArrayRaw[i].length; j++) {
86920 v = valAxis.d2c(valArrayRaw[i][j]);
86921 if(v !== BADNUM) {
86922 pt = {v: v, i: [i, j]};
86923 arraysToCalcdata(pt, trace, [i, j]);
86924 pts.push(pt);
86925 }
86926 }
86927 }
86928 cdi.pts = pts.sort(sortByVal);
86929 boxVals = cdi[valLetter] = pts.map(extractVal);
86930 N = boxVals.length;
86931
86932 if(cdi.med !== BADNUM && cdi.q1 !== BADNUM && cdi.q3 !== BADNUM &&
86933 cdi.med >= cdi.q1 && cdi.q3 >= cdi.med
86934 ) {
86935 var lf = d2c('lowerfence');
86936 cdi.lf = (lf !== BADNUM && lf <= cdi.q1) ?
86937 lf :
86938 computeLowerFence(cdi, boxVals, N);
86939
86940 var uf = d2c('upperfence');
86941 cdi.uf = (uf !== BADNUM && uf >= cdi.q3) ?
86942 uf :
86943 computeUpperFence(cdi, boxVals, N);
86944
86945 var mean = d2c('mean');
86946 cdi.mean = (mean !== BADNUM) ?
86947 mean :
86948 (N ? Lib.mean(boxVals, N) : (cdi.q1 + cdi.q3) / 2);
86949
86950 var sd = d2c('sd');
86951 cdi.sd = (mean !== BADNUM && sd >= 0) ?
86952 sd :
86953 (N ? Lib.stdev(boxVals, N, cdi.mean) : (cdi.q3 - cdi.q1));
86954
86955 cdi.lo = computeLowerOutlierBound(cdi);
86956 cdi.uo = computeUpperOutlierBound(cdi);
86957
86958 var ns = d2c('notchspan');
86959 ns = (ns !== BADNUM && ns > 0) ? ns : computeNotchSpan(cdi, N);
86960 cdi.ln = cdi.med - ns;
86961 cdi.un = cdi.med + ns;
86962
86963 var imin = cdi.lf;
86964 var imax = cdi.uf;
86965 if(trace.boxpoints && boxVals.length) {
86966 imin = Math.min(imin, boxVals[0]);
86967 imax = Math.max(imax, boxVals[N - 1]);
86968 }
86969 if(trace.notched) {
86970 imin = Math.min(imin, cdi.ln);
86971 imax = Math.max(imax, cdi.un);
86972 }
86973 cdi.min = imin;
86974 cdi.max = imax;
86975 } else {
86976 Lib.warn([
86977 'Invalid input - make sure that q1 <= median <= q3',
86978 'q1 = ' + cdi.q1,
86979 'median = ' + cdi.med,
86980 'q3 = ' + cdi.q3
86981 ].join('\n'));
86982
86983 var v0;
86984 if(cdi.med !== BADNUM) {
86985 v0 = cdi.med;
86986 } else if(cdi.q1 !== BADNUM) {
86987 if(cdi.q3 !== BADNUM) v0 = (cdi.q1 + cdi.q3) / 2;
86988 else v0 = cdi.q1;
86989 } else if(cdi.q3 !== BADNUM) {
86990 v0 = cdi.q3;
86991 } else {
86992 v0 = 0;
86993 }
86994
86995 // draw box as line segment
86996 cdi.med = v0;
86997 cdi.q1 = cdi.q3 = v0;
86998 cdi.lf = cdi.uf = v0;
86999 cdi.mean = cdi.sd = v0;
87000 cdi.ln = cdi.un = v0;
87001 cdi.min = cdi.max = v0;
87002 }
87003
87004 minVal = Math.min(minVal, cdi.min);
87005 maxVal = Math.max(maxVal, cdi.max);
87006
87007 cdi.pts2 = pts.filter(ptFilterFn);
87008
87009 cd.push(cdi);
87010 }
87011
87012 trace._extremes[valAxis._id] = Axes.findExtremes(valAxis,
87013 [minVal, maxVal],
87014 {padded: true}
87015 );
87016 } else {
87017 var valArray = valAxis.makeCalcdata(trace, valLetter);
87018 var posBins = makeBins(posDistinct, dPos);
87019 var pLen = posDistinct.length;
87020 var ptsPerBin = initNestedArray(pLen);
87021
87022 // bin pts info per position bins
87023 for(i = 0; i < trace._length; i++) {
87024 v = valArray[i];
87025 if(!isNumeric(v)) continue;
87026
87027 var n = Lib.findBin(posArray[i], posBins);
87028 if(n >= 0 && n < pLen) {
87029 pt = {v: v, i: i};
87030 arraysToCalcdata(pt, trace, i);
87031 ptsPerBin[n].push(pt);
87032 }
87033 }
87034
87035 var minLowerNotch = Infinity;
87036 var maxUpperNotch = -Infinity;
87037
87038 var quartilemethod = trace.quartilemethod;
87039 var usesExclusive = quartilemethod === 'exclusive';
87040 var usesInclusive = quartilemethod === 'inclusive';
87041
87042 // build calcdata trace items, one item per distinct position
87043 for(i = 0; i < pLen; i++) {
87044 if(ptsPerBin[i].length > 0) {
87045 cdi = {};
87046 cdi.pos = cdi[posLetter] = posDistinct[i];
87047
87048 pts = cdi.pts = ptsPerBin[i].sort(sortByVal);
87049 boxVals = cdi[valLetter] = pts.map(extractVal);
87050 N = boxVals.length;
87051
87052 cdi.min = boxVals[0];
87053 cdi.max = boxVals[N - 1];
87054 cdi.mean = Lib.mean(boxVals, N);
87055 cdi.sd = Lib.stdev(boxVals, N, cdi.mean);
87056 cdi.med = Lib.interp(boxVals, 0.5);
87057
87058 if((N % 2) && (usesExclusive || usesInclusive)) {
87059 var lower;
87060 var upper;
87061
87062 if(usesExclusive) {
87063 // do NOT include the median in either half
87064 lower = boxVals.slice(0, N / 2);
87065 upper = boxVals.slice(N / 2 + 1);
87066 } else if(usesInclusive) {
87067 // include the median in either half
87068 lower = boxVals.slice(0, N / 2 + 1);
87069 upper = boxVals.slice(N / 2);
87070 }
87071
87072 cdi.q1 = Lib.interp(lower, 0.5);
87073 cdi.q3 = Lib.interp(upper, 0.5);
87074 } else {
87075 cdi.q1 = Lib.interp(boxVals, 0.25);
87076 cdi.q3 = Lib.interp(boxVals, 0.75);
87077 }
87078
87079 // lower and upper fences
87080 cdi.lf = computeLowerFence(cdi, boxVals, N);
87081 cdi.uf = computeUpperFence(cdi, boxVals, N);
87082
87083 // lower and upper outliers bounds
87084 cdi.lo = computeLowerOutlierBound(cdi);
87085 cdi.uo = computeUpperOutlierBound(cdi);
87086
87087 // lower and upper notches
87088 var mci = computeNotchSpan(cdi, N);
87089 cdi.ln = cdi.med - mci;
87090 cdi.un = cdi.med + mci;
87091 minLowerNotch = Math.min(minLowerNotch, cdi.ln);
87092 maxUpperNotch = Math.max(maxUpperNotch, cdi.un);
87093
87094 cdi.pts2 = pts.filter(ptFilterFn);
87095
87096 cd.push(cdi);
87097 }
87098 }
87099
87100 trace._extremes[valAxis._id] = Axes.findExtremes(valAxis,
87101 trace.notched ? valArray.concat([minLowerNotch, maxUpperNotch]) : valArray,
87102 {padded: true}
87103 );
87104 }
87105
87106 calcSelection(cd, trace);
87107
87108 if(cd.length > 0) {
87109 cd[0].t = {
87110 num: fullLayout[numKey],
87111 dPos: dPos,
87112 posLetter: posLetter,
87113 valLetter: valLetter,
87114 labels: {
87115 med: _(gd, 'median:'),
87116 min: _(gd, 'min:'),
87117 q1: _(gd, 'q1:'),
87118 q3: _(gd, 'q3:'),
87119 max: _(gd, 'max:'),
87120 mean: trace.boxmean === 'sd' ? _(gd, 'mean ± σ:') : _(gd, 'mean:'),
87121 lf: _(gd, 'lower fence:'),
87122 uf: _(gd, 'upper fence:')
87123 }
87124 };
87125
87126 fullLayout[numKey]++;
87127 return cd;
87128 } else {
87129 return [{t: {empty: true}}];
87130 }
87131};
87132
87133// In vertical (horizontal) box plots:
87134// if no x (y) data, use x0 (y0), or name
87135// so if you want one box
87136// per trace, set x0 (y0) to the x (y) value or category for this trace
87137// (or set x (y) to a constant array matching y (x))
87138function getPosArrays(trace, posLetter, posAxis, num) {
87139 var hasPosArray = posLetter in trace;
87140 var hasPos0 = posLetter + '0' in trace;
87141 var hasPosStep = 'd' + posLetter in trace;
87142
87143 if(hasPosArray || (hasPos0 && hasPosStep)) {
87144 var origPos = posAxis.makeCalcdata(trace, posLetter);
87145 var pos = alignPeriod(trace, posAxis, posLetter, origPos).vals;
87146 return [pos, origPos];
87147 }
87148
87149 var pos0;
87150 if(hasPos0) {
87151 pos0 = trace[posLetter + '0'];
87152 } else if('name' in trace && (
87153 posAxis.type === 'category' || (
87154 isNumeric(trace.name) &&
87155 ['linear', 'log'].indexOf(posAxis.type) !== -1
87156 ) || (
87157 Lib.isDateTime(trace.name) &&
87158 posAxis.type === 'date'
87159 )
87160 )) {
87161 pos0 = trace.name;
87162 } else {
87163 pos0 = num;
87164 }
87165
87166 var pos0c = posAxis.type === 'multicategory' ?
87167 posAxis.r2c_just_indices(pos0) :
87168 posAxis.d2c(pos0, 0, trace[posLetter + 'calendar']);
87169
87170 var len = trace._length;
87171 var out = new Array(len);
87172 for(var i = 0; i < len; i++) out[i] = pos0c;
87173
87174 return [out];
87175}
87176
87177function makeBins(x, dx) {
87178 var len = x.length;
87179 var bins = new Array(len + 1);
87180
87181 for(var i = 0; i < len; i++) {
87182 bins[i] = x[i] - dx;
87183 }
87184 bins[len] = x[len - 1] + dx;
87185
87186 return bins;
87187}
87188
87189function initNestedArray(len) {
87190 var arr = new Array(len);
87191 for(var i = 0; i < len; i++) {
87192 arr[i] = [];
87193 }
87194 return arr;
87195}
87196
87197var TRACE_TO_CALC = {
87198 text: 'tx',
87199 hovertext: 'htx'
87200};
87201
87202function arraysToCalcdata(pt, trace, ptNumber) {
87203 for(var k in TRACE_TO_CALC) {
87204 if(Lib.isArrayOrTypedArray(trace[k])) {
87205 if(Array.isArray(ptNumber)) {
87206 if(Lib.isArrayOrTypedArray(trace[k][ptNumber[0]])) {
87207 pt[TRACE_TO_CALC[k]] = trace[k][ptNumber[0]][ptNumber[1]];
87208 }
87209 } else {
87210 pt[TRACE_TO_CALC[k]] = trace[k][ptNumber];
87211 }
87212 }
87213 }
87214}
87215
87216function calcSelection(cd, trace) {
87217 if(Lib.isArrayOrTypedArray(trace.selectedpoints)) {
87218 for(var i = 0; i < cd.length; i++) {
87219 var pts = cd[i].pts || [];
87220 var ptNumber2cdIndex = {};
87221
87222 for(var j = 0; j < pts.length; j++) {
87223 ptNumber2cdIndex[pts[j].i] = j;
87224 }
87225
87226 Lib.tagSelected(pts, trace, ptNumber2cdIndex);
87227 }
87228 }
87229}
87230
87231function sortByVal(a, b) { return a.v - b.v; }
87232
87233function extractVal(o) { return o.v; }
87234
87235// last point below 1.5 * IQR
87236function computeLowerFence(cdi, boxVals, N) {
87237 if(N === 0) return cdi.q1;
87238 return Math.min(
87239 cdi.q1,
87240 boxVals[Math.min(
87241 Lib.findBin(2.5 * cdi.q1 - 1.5 * cdi.q3, boxVals, true) + 1,
87242 N - 1
87243 )]
87244 );
87245}
87246
87247// last point above 1.5 * IQR
87248function computeUpperFence(cdi, boxVals, N) {
87249 if(N === 0) return cdi.q3;
87250 return Math.max(
87251 cdi.q3,
87252 boxVals[Math.max(
87253 Lib.findBin(2.5 * cdi.q3 - 1.5 * cdi.q1, boxVals),
87254 0
87255 )]
87256 );
87257}
87258
87259// 3 IQR below (don't clip to max/min,
87260// this is only for discriminating suspected & far outliers)
87261function computeLowerOutlierBound(cdi) {
87262 return 4 * cdi.q1 - 3 * cdi.q3;
87263}
87264
87265// 3 IQR above (don't clip to max/min,
87266// this is only for discriminating suspected & far outliers)
87267function computeUpperOutlierBound(cdi) {
87268 return 4 * cdi.q3 - 3 * cdi.q1;
87269}
87270
87271// 95% confidence intervals for median
87272function computeNotchSpan(cdi, N) {
87273 if(N === 0) return 0;
87274 return 1.57 * (cdi.q3 - cdi.q1) / Math.sqrt(N);
87275}
87276
87277},{"../../constants/numerical":267,"../../lib":287,"../../plots/cartesian/align_period":331,"../../plots/cartesian/axes":334,"fast-isnumeric":33}],405:[function(_dereq_,module,exports){
87278'use strict';
87279
87280var Axes = _dereq_('../../plots/cartesian/axes');
87281var Lib = _dereq_('../../lib');
87282var getAxisGroup = _dereq_('../../plots/cartesian/constraints').getAxisGroup;
87283
87284var orientations = ['v', 'h'];
87285
87286function crossTraceCalc(gd, plotinfo) {
87287 var calcdata = gd.calcdata;
87288 var xa = plotinfo.xaxis;
87289 var ya = plotinfo.yaxis;
87290
87291 for(var i = 0; i < orientations.length; i++) {
87292 var orientation = orientations[i];
87293 var posAxis = orientation === 'h' ? ya : xa;
87294 var boxList = [];
87295
87296 // make list of boxes / candlesticks
87297 // For backward compatibility, candlesticks are treated as if they *are* box traces here
87298 for(var j = 0; j < calcdata.length; j++) {
87299 var cd = calcdata[j];
87300 var t = cd[0].t;
87301 var trace = cd[0].trace;
87302
87303 if(trace.visible === true &&
87304 (trace.type === 'box' || trace.type === 'candlestick') &&
87305 !t.empty &&
87306 (trace.orientation || 'v') === orientation &&
87307 trace.xaxis === xa._id &&
87308 trace.yaxis === ya._id
87309 ) {
87310 boxList.push(j);
87311 }
87312 }
87313
87314 setPositionOffset('box', gd, boxList, posAxis);
87315 }
87316}
87317
87318function setPositionOffset(traceType, gd, boxList, posAxis) {
87319 var calcdata = gd.calcdata;
87320 var fullLayout = gd._fullLayout;
87321 var axId = posAxis._id;
87322 var axLetter = axId.charAt(0);
87323
87324 var i, j, calcTrace;
87325 var pointList = [];
87326 var shownPts = 0;
87327
87328 // make list of box points
87329 for(i = 0; i < boxList.length; i++) {
87330 calcTrace = calcdata[boxList[i]];
87331 for(j = 0; j < calcTrace.length; j++) {
87332 pointList.push(posAxis.c2l(calcTrace[j].pos, true));
87333 shownPts += (calcTrace[j].pts2 || []).length;
87334 }
87335 }
87336
87337 if(!pointList.length) return;
87338
87339 // box plots - update dPos based on multiple traces
87340 var boxdv = Lib.distinctVals(pointList);
87341 if(posAxis.type === 'category' || posAxis.type === 'multicategory') {
87342 boxdv.minDiff = 1;
87343 }
87344
87345 var dPos0 = boxdv.minDiff / 2;
87346
87347 // check for forced minimum dtick
87348 Axes.minDtick(posAxis, boxdv.minDiff, boxdv.vals[0], true);
87349
87350 var numKey = traceType === 'violin' ? '_numViolins' : '_numBoxes';
87351 var numTotal = fullLayout[numKey];
87352 var group = fullLayout[traceType + 'mode'] === 'group' && numTotal > 1;
87353 var groupFraction = 1 - fullLayout[traceType + 'gap'];
87354 var groupGapFraction = 1 - fullLayout[traceType + 'groupgap'];
87355
87356 for(i = 0; i < boxList.length; i++) {
87357 calcTrace = calcdata[boxList[i]];
87358
87359 var trace = calcTrace[0].trace;
87360 var t = calcTrace[0].t;
87361 var width = trace.width;
87362 var side = trace.side;
87363
87364 // position coordinate delta
87365 var dPos;
87366 // box half width;
87367 var bdPos;
87368 // box center offset
87369 var bPos;
87370 // half-width within which to accept hover for this box/violin
87371 // always split the distance to the closest box/violin
87372 var wHover;
87373
87374 if(width) {
87375 dPos = bdPos = wHover = width / 2;
87376 bPos = 0;
87377 } else {
87378 dPos = dPos0;
87379
87380 if(group) {
87381 var groupId = getAxisGroup(fullLayout, posAxis._id) + trace.orientation;
87382 var alignmentGroups = fullLayout._alignmentOpts[groupId] || {};
87383 var alignmentGroupOpts = alignmentGroups[trace.alignmentgroup] || {};
87384 var nOffsetGroups = Object.keys(alignmentGroupOpts.offsetGroups || {}).length;
87385 var num = nOffsetGroups || numTotal;
87386 var shift = nOffsetGroups ? trace._offsetIndex : t.num;
87387
87388 bdPos = dPos * groupFraction * groupGapFraction / num;
87389 bPos = 2 * dPos * (-0.5 + (shift + 0.5) / num) * groupFraction;
87390 wHover = dPos * groupFraction / num;
87391 } else {
87392 bdPos = dPos * groupFraction * groupGapFraction;
87393 bPos = 0;
87394 wHover = dPos;
87395 }
87396 }
87397 t.dPos = dPos;
87398 t.bPos = bPos;
87399 t.bdPos = bdPos;
87400 t.wHover = wHover;
87401
87402 // box/violin-only value-space push value
87403 var pushplus;
87404 var pushminus;
87405 // edge of box/violin
87406 var edge = bPos + bdPos;
87407 var edgeplus;
87408 var edgeminus;
87409 // value-space padding
87410 var vpadplus;
87411 var vpadminus;
87412 // pixel-space padding
87413 var ppadplus;
87414 var ppadminus;
87415 // do we add 5% of both sides (more logic for points beyond box/violin below)
87416 var padded = Boolean(width);
87417 // does this trace show points?
87418 var hasPts = (trace.boxpoints || trace.points) && (shownPts > 0);
87419
87420 if(side === 'positive') {
87421 pushplus = dPos * (width ? 1 : 0.5);
87422 edgeplus = edge;
87423 pushminus = edgeplus = bPos;
87424 } else if(side === 'negative') {
87425 pushplus = edgeplus = bPos;
87426 pushminus = dPos * (width ? 1 : 0.5);
87427 edgeminus = edge;
87428 } else {
87429 pushplus = pushminus = dPos;
87430 edgeplus = edgeminus = edge;
87431 }
87432
87433 if(hasPts) {
87434 var pointpos = trace.pointpos;
87435 var jitter = trace.jitter;
87436 var ms = trace.marker.size / 2;
87437
87438 var pp = 0;
87439 if((pointpos + jitter) >= 0) {
87440 pp = edge * (pointpos + jitter);
87441 if(pp > pushplus) {
87442 // (++) beyond plus-value, use pp
87443 padded = true;
87444 ppadplus = ms;
87445 vpadplus = pp;
87446 } else if(pp > edgeplus) {
87447 // (+), use push-value (it's bigger), but add px-pad
87448 ppadplus = ms;
87449 vpadplus = pushplus;
87450 }
87451 }
87452 if(pp <= pushplus) {
87453 // (->) fallback to push value
87454 vpadplus = pushplus;
87455 }
87456
87457 var pm = 0;
87458 if((pointpos - jitter) <= 0) {
87459 pm = -edge * (pointpos - jitter);
87460 if(pm > pushminus) {
87461 // (--) beyond plus-value, use pp
87462 padded = true;
87463 ppadminus = ms;
87464 vpadminus = pm;
87465 } else if(pm > edgeminus) {
87466 // (-), use push-value (it's bigger), but add px-pad
87467 ppadminus = ms;
87468 vpadminus = pushminus;
87469 }
87470 }
87471 if(pm <= pushminus) {
87472 // (<-) fallback to push value
87473 vpadminus = pushminus;
87474 }
87475 } else {
87476 vpadplus = pushplus;
87477 vpadminus = pushminus;
87478 }
87479
87480 var pos = new Array(calcTrace.length);
87481 for(j = 0; j < calcTrace.length; j++) {
87482 pos[j] = calcTrace[j].pos;
87483 }
87484
87485 trace._extremes[axId] = Axes.findExtremes(posAxis, pos, {
87486 padded: padded,
87487 vpadminus: vpadminus,
87488 vpadplus: vpadplus,
87489 vpadLinearized: true,
87490 // N.B. SVG px-space positive/negative
87491 ppadminus: {x: ppadminus, y: ppadplus}[axLetter],
87492 ppadplus: {x: ppadplus, y: ppadminus}[axLetter],
87493 });
87494 }
87495}
87496
87497module.exports = {
87498 crossTraceCalc: crossTraceCalc,
87499 setPositionOffset: setPositionOffset
87500};
87501
87502},{"../../lib":287,"../../plots/cartesian/axes":334,"../../plots/cartesian/constraints":342}],406:[function(_dereq_,module,exports){
87503'use strict';
87504
87505var Lib = _dereq_('../../lib');
87506var Registry = _dereq_('../../registry');
87507var Color = _dereq_('../../components/color');
87508var handlePeriodDefaults = _dereq_('../scatter/period_defaults');
87509var handleGroupingDefaults = _dereq_('../bar/defaults').handleGroupingDefaults;
87510var autoType = _dereq_('../../plots/cartesian/axis_autotype');
87511var attributes = _dereq_('./attributes');
87512
87513function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
87514 function coerce(attr, dflt) {
87515 return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
87516 }
87517
87518 handleSampleDefaults(traceIn, traceOut, coerce, layout);
87519 if(traceOut.visible === false) return;
87520
87521 handlePeriodDefaults(traceIn, traceOut, layout, coerce);
87522 coerce('xhoverformat');
87523 coerce('yhoverformat');
87524
87525 var hasPreCompStats = traceOut._hasPreCompStats;
87526
87527 if(hasPreCompStats) {
87528 coerce('lowerfence');
87529 coerce('upperfence');
87530 }
87531
87532 coerce('line.color', (traceIn.marker || {}).color || defaultColor);
87533 coerce('line.width');
87534 coerce('fillcolor', Color.addOpacity(traceOut.line.color, 0.5));
87535
87536 var boxmeanDflt = false;
87537 if(hasPreCompStats) {
87538 var mean = coerce('mean');
87539 var sd = coerce('sd');
87540 if(mean && mean.length) {
87541 boxmeanDflt = true;
87542 if(sd && sd.length) boxmeanDflt = 'sd';
87543 }
87544 }
87545 coerce('boxmean', boxmeanDflt);
87546
87547 coerce('whiskerwidth');
87548 coerce('width');
87549 coerce('quartilemethod');
87550
87551 var notchedDflt = false;
87552 if(hasPreCompStats) {
87553 var notchspan = coerce('notchspan');
87554 if(notchspan && notchspan.length) {
87555 notchedDflt = true;
87556 }
87557 } else if(Lib.validate(traceIn.notchwidth, attributes.notchwidth)) {
87558 notchedDflt = true;
87559 }
87560 var notched = coerce('notched', notchedDflt);
87561 if(notched) coerce('notchwidth');
87562
87563 handlePointsDefaults(traceIn, traceOut, coerce, {prefix: 'box'});
87564}
87565
87566function handleSampleDefaults(traceIn, traceOut, coerce, layout) {
87567 function getDims(arr) {
87568 var dims = 0;
87569 if(arr && arr.length) {
87570 dims += 1;
87571 if(Lib.isArrayOrTypedArray(arr[0]) && arr[0].length) {
87572 dims += 1;
87573 }
87574 }
87575 return dims;
87576 }
87577
87578 function valid(astr) {
87579 return Lib.validate(traceIn[astr], attributes[astr]);
87580 }
87581
87582 var y = coerce('y');
87583 var x = coerce('x');
87584
87585 var sLen;
87586 if(traceOut.type === 'box') {
87587 var q1 = coerce('q1');
87588 var median = coerce('median');
87589 var q3 = coerce('q3');
87590
87591 traceOut._hasPreCompStats = (
87592 q1 && q1.length &&
87593 median && median.length &&
87594 q3 && q3.length
87595 );
87596 sLen = Math.min(
87597 Lib.minRowLength(q1),
87598 Lib.minRowLength(median),
87599 Lib.minRowLength(q3)
87600 );
87601 }
87602
87603 var yDims = getDims(y);
87604 var xDims = getDims(x);
87605 var yLen = yDims && Lib.minRowLength(y);
87606 var xLen = xDims && Lib.minRowLength(x);
87607
87608 var calendar = layout.calendar;
87609 var opts = {
87610 autotypenumbers: layout.autotypenumbers
87611 };
87612
87613 var defaultOrientation, len;
87614 if(traceOut._hasPreCompStats) {
87615 switch(String(xDims) + String(yDims)) {
87616 // no x / no y
87617 case '00':
87618 var setInX = valid('x0') || valid('dx');
87619 var setInY = valid('y0') || valid('dy');
87620
87621 if(setInY && !setInX) {
87622 defaultOrientation = 'h';
87623 } else {
87624 defaultOrientation = 'v';
87625 }
87626
87627 len = sLen;
87628 break;
87629 // just x
87630 case '10':
87631 defaultOrientation = 'v';
87632 len = Math.min(sLen, xLen);
87633 break;
87634 case '20':
87635 defaultOrientation = 'h';
87636 len = Math.min(sLen, x.length);
87637 break;
87638 // just y
87639 case '01':
87640 defaultOrientation = 'h';
87641 len = Math.min(sLen, yLen);
87642 break;
87643 case '02':
87644 defaultOrientation = 'v';
87645 len = Math.min(sLen, y.length);
87646 break;
87647 // both
87648 case '12':
87649 defaultOrientation = 'v';
87650 len = Math.min(sLen, xLen, y.length);
87651 break;
87652 case '21':
87653 defaultOrientation = 'h';
87654 len = Math.min(sLen, x.length, yLen);
87655 break;
87656 case '11':
87657 // this one is ill-defined
87658 len = 0;
87659 break;
87660 case '22':
87661 var hasCategories = false;
87662 var i;
87663 for(i = 0; i < x.length; i++) {
87664 if(autoType(x[i], calendar, opts) === 'category') {
87665 hasCategories = true;
87666 break;
87667 }
87668 }
87669
87670 if(hasCategories) {
87671 defaultOrientation = 'v';
87672 len = Math.min(sLen, xLen, y.length);
87673 } else {
87674 for(i = 0; i < y.length; i++) {
87675 if(autoType(y[i], calendar, opts) === 'category') {
87676 hasCategories = true;
87677 break;
87678 }
87679 }
87680
87681 if(hasCategories) {
87682 defaultOrientation = 'h';
87683 len = Math.min(sLen, x.length, yLen);
87684 } else {
87685 defaultOrientation = 'v';
87686 len = Math.min(sLen, xLen, y.length);
87687 }
87688 }
87689 break;
87690 }
87691 } else if(yDims > 0) {
87692 defaultOrientation = 'v';
87693 if(xDims > 0) {
87694 len = Math.min(xLen, yLen);
87695 } else {
87696 len = Math.min(yLen);
87697 }
87698 } else if(xDims > 0) {
87699 defaultOrientation = 'h';
87700 len = Math.min(xLen);
87701 } else {
87702 len = 0;
87703 }
87704
87705 if(!len) {
87706 traceOut.visible = false;
87707 return;
87708 }
87709 traceOut._length = len;
87710
87711 var orientation = coerce('orientation', defaultOrientation);
87712
87713 // these are just used for positioning, they never define the sample
87714 if(traceOut._hasPreCompStats) {
87715 if(orientation === 'v' && xDims === 0) {
87716 coerce('x0', 0);
87717 coerce('dx', 1);
87718 } else if(orientation === 'h' && yDims === 0) {
87719 coerce('y0', 0);
87720 coerce('dy', 1);
87721 }
87722 } else {
87723 if(orientation === 'v' && xDims === 0) {
87724 coerce('x0');
87725 } else if(orientation === 'h' && yDims === 0) {
87726 coerce('y0');
87727 }
87728 }
87729
87730 var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults');
87731 handleCalendarDefaults(traceIn, traceOut, ['x', 'y'], layout);
87732}
87733
87734function handlePointsDefaults(traceIn, traceOut, coerce, opts) {
87735 var prefix = opts.prefix;
87736
87737 var outlierColorDflt = Lib.coerce2(traceIn, traceOut, attributes, 'marker.outliercolor');
87738 var lineoutliercolor = coerce('marker.line.outliercolor');
87739
87740 var modeDflt = 'outliers';
87741 if(traceOut._hasPreCompStats) {
87742 modeDflt = 'all';
87743 } else if(outlierColorDflt || lineoutliercolor) {
87744 modeDflt = 'suspectedoutliers';
87745 }
87746
87747 var mode = coerce(prefix + 'points', modeDflt);
87748
87749 if(mode) {
87750 coerce('jitter', mode === 'all' ? 0.3 : 0);
87751 coerce('pointpos', mode === 'all' ? -1.5 : 0);
87752
87753 coerce('marker.symbol');
87754 coerce('marker.opacity');
87755 coerce('marker.size');
87756 coerce('marker.color', traceOut.line.color);
87757 coerce('marker.line.color');
87758 coerce('marker.line.width');
87759
87760 if(mode === 'suspectedoutliers') {
87761 coerce('marker.line.outliercolor', traceOut.marker.color);
87762 coerce('marker.line.outlierwidth');
87763 }
87764
87765 coerce('selected.marker.color');
87766 coerce('unselected.marker.color');
87767 coerce('selected.marker.size');
87768 coerce('unselected.marker.size');
87769
87770 coerce('text');
87771 coerce('hovertext');
87772 } else {
87773 delete traceOut.marker;
87774 }
87775
87776 var hoveron = coerce('hoveron');
87777 if(hoveron === 'all' || hoveron.indexOf('points') !== -1) {
87778 coerce('hovertemplate');
87779 }
87780
87781 Lib.coerceSelectionMarkerOpacity(traceOut, coerce);
87782}
87783
87784function crossTraceDefaults(fullData, fullLayout) {
87785 var traceIn, traceOut;
87786
87787 function coerce(attr) {
87788 return Lib.coerce(traceOut._input, traceOut, attributes, attr);
87789 }
87790
87791 for(var i = 0; i < fullData.length; i++) {
87792 traceOut = fullData[i];
87793 var traceType = traceOut.type;
87794
87795 if(traceType === 'box' || traceType === 'violin') {
87796 traceIn = traceOut._input;
87797 if(fullLayout[traceType + 'mode'] === 'group') {
87798 handleGroupingDefaults(traceIn, traceOut, fullLayout, coerce);
87799 }
87800 }
87801 }
87802}
87803
87804module.exports = {
87805 supplyDefaults: supplyDefaults,
87806 crossTraceDefaults: crossTraceDefaults,
87807
87808 handleSampleDefaults: handleSampleDefaults,
87809 handlePointsDefaults: handlePointsDefaults
87810};
87811
87812},{"../../components/color":157,"../../lib":287,"../../plots/cartesian/axis_autotype":335,"../../registry":376,"../bar/defaults":390,"../scatter/period_defaults":517,"./attributes":403}],407:[function(_dereq_,module,exports){
87813'use strict';
87814
87815module.exports = function eventData(out, pt) {
87816 // Note: hoverOnBox property is needed for click-to-select
87817 // to ignore when a box was clicked. This is the reason box
87818 // implements this custom eventData function.
87819 if(pt.hoverOnBox) out.hoverOnBox = pt.hoverOnBox;
87820
87821 if('xVal' in pt) out.x = pt.xVal;
87822 if('yVal' in pt) out.y = pt.yVal;
87823 if(pt.xa) out.xaxis = pt.xa;
87824 if(pt.ya) out.yaxis = pt.ya;
87825
87826 return out;
87827};
87828
87829},{}],408:[function(_dereq_,module,exports){
87830'use strict';
87831
87832var Axes = _dereq_('../../plots/cartesian/axes');
87833var Lib = _dereq_('../../lib');
87834var Fx = _dereq_('../../components/fx');
87835var Color = _dereq_('../../components/color');
87836var fillText = Lib.fillText;
87837
87838function hoverPoints(pointData, xval, yval, hovermode) {
87839 var cd = pointData.cd;
87840 var trace = cd[0].trace;
87841 var hoveron = trace.hoveron;
87842 var closeBoxData = [];
87843 var closePtData;
87844
87845 if(hoveron.indexOf('boxes') !== -1) {
87846 closeBoxData = closeBoxData.concat(hoverOnBoxes(pointData, xval, yval, hovermode));
87847 }
87848
87849 if(hoveron.indexOf('points') !== -1) {
87850 closePtData = hoverOnPoints(pointData, xval, yval);
87851 }
87852
87853 // If there's a point in range and hoveron has points, show the best single point only.
87854 // If hoveron has boxes and there's no point in range (or hoveron doesn't have points), show the box stats.
87855 if(hovermode === 'closest') {
87856 if(closePtData) return [closePtData];
87857 return closeBoxData;
87858 }
87859
87860 // Otherwise in compare mode, allow a point AND the box stats to be labeled
87861 // If there are multiple boxes in range (ie boxmode = 'overlay') we'll see stats for all of them.
87862 if(closePtData) {
87863 closeBoxData.push(closePtData);
87864 return closeBoxData;
87865 }
87866 return closeBoxData;
87867}
87868
87869function hoverOnBoxes(pointData, xval, yval, hovermode) {
87870 var cd = pointData.cd;
87871 var xa = pointData.xa;
87872 var ya = pointData.ya;
87873 var trace = cd[0].trace;
87874 var t = cd[0].t;
87875 var isViolin = trace.type === 'violin';
87876 var closeBoxData = [];
87877
87878 var pLetter, vLetter, pAxis, vAxis, vVal, pVal, dx, dy, dPos,
87879 hoverPseudoDistance, spikePseudoDistance;
87880
87881 var boxDelta = t.bdPos;
87882 var boxDeltaPos, boxDeltaNeg;
87883 var posAcceptance = t.wHover;
87884 var shiftPos = function(di) { return pAxis.c2l(di.pos) + t.bPos - pAxis.c2l(pVal); };
87885
87886 if(isViolin && trace.side !== 'both') {
87887 if(trace.side === 'positive') {
87888 dPos = function(di) {
87889 var pos = shiftPos(di);
87890 return Fx.inbox(pos, pos + posAcceptance, hoverPseudoDistance);
87891 };
87892 boxDeltaPos = boxDelta;
87893 boxDeltaNeg = 0;
87894 }
87895 if(trace.side === 'negative') {
87896 dPos = function(di) {
87897 var pos = shiftPos(di);
87898 return Fx.inbox(pos - posAcceptance, pos, hoverPseudoDistance);
87899 };
87900 boxDeltaPos = 0;
87901 boxDeltaNeg = boxDelta;
87902 }
87903 } else {
87904 dPos = function(di) {
87905 var pos = shiftPos(di);
87906 return Fx.inbox(pos - posAcceptance, pos + posAcceptance, hoverPseudoDistance);
87907 };
87908 boxDeltaPos = boxDeltaNeg = boxDelta;
87909 }
87910
87911 var dVal;
87912
87913 if(isViolin) {
87914 dVal = function(di) {
87915 return Fx.inbox(di.span[0] - vVal, di.span[1] - vVal, hoverPseudoDistance);
87916 };
87917 } else {
87918 dVal = function(di) {
87919 return Fx.inbox(di.min - vVal, di.max - vVal, hoverPseudoDistance);
87920 };
87921 }
87922
87923 if(trace.orientation === 'h') {
87924 vVal = xval;
87925 pVal = yval;
87926 dx = dVal;
87927 dy = dPos;
87928 pLetter = 'y';
87929 pAxis = ya;
87930 vLetter = 'x';
87931 vAxis = xa;
87932 } else {
87933 vVal = yval;
87934 pVal = xval;
87935 dx = dPos;
87936 dy = dVal;
87937 pLetter = 'x';
87938 pAxis = xa;
87939 vLetter = 'y';
87940 vAxis = ya;
87941 }
87942
87943 // if two boxes are overlaying, let the narrowest one win
87944 var pseudoDistance = Math.min(1, boxDelta / Math.abs(pAxis.r2c(pAxis.range[1]) - pAxis.r2c(pAxis.range[0])));
87945 hoverPseudoDistance = pointData.maxHoverDistance - pseudoDistance;
87946 spikePseudoDistance = pointData.maxSpikeDistance - pseudoDistance;
87947
87948 function dxy(di) { return (dx(di) + dy(di)) / 2; }
87949 var distfn = Fx.getDistanceFunction(hovermode, dx, dy, dxy);
87950 Fx.getClosest(cd, distfn, pointData);
87951
87952 // skip the rest (for this trace) if we didn't find a close point
87953 // and create the item(s) in closedata for this point
87954 if(pointData.index === false) return [];
87955
87956 var di = cd[pointData.index];
87957 var lc = trace.line.color;
87958 var mc = (trace.marker || {}).color;
87959
87960 if(Color.opacity(lc) && trace.line.width) pointData.color = lc;
87961 else if(Color.opacity(mc) && trace.boxpoints) pointData.color = mc;
87962 else pointData.color = trace.fillcolor;
87963
87964 pointData[pLetter + '0'] = pAxis.c2p(di.pos + t.bPos - boxDeltaNeg, true);
87965 pointData[pLetter + '1'] = pAxis.c2p(di.pos + t.bPos + boxDeltaPos, true);
87966
87967 pointData[pLetter + 'LabelVal'] = di.orig_p !== undefined ? di.orig_p : di.pos;
87968
87969 var spikePosAttr = pLetter + 'Spike';
87970 pointData.spikeDistance = dxy(di) * spikePseudoDistance / hoverPseudoDistance;
87971 pointData[spikePosAttr] = pAxis.c2p(di.pos, true);
87972
87973 // box plots: each "point" gets many labels
87974 var usedVals = {};
87975 var attrs = ['med', 'q1', 'q3', 'min', 'max'];
87976
87977 if(trace.boxmean || (trace.meanline || {}).visible) {
87978 attrs.push('mean');
87979 }
87980 if(trace.boxpoints || trace.points) {
87981 attrs.push('lf', 'uf');
87982 }
87983
87984 for(var i = 0; i < attrs.length; i++) {
87985 var attr = attrs[i];
87986
87987 if(!(attr in di) || (di[attr] in usedVals)) continue;
87988 usedVals[di[attr]] = true;
87989
87990 // copy out to a new object for each value to label
87991 var val = di[attr];
87992 var valPx = vAxis.c2p(val, true);
87993 var pointData2 = Lib.extendFlat({}, pointData);
87994
87995 pointData2.attr = attr;
87996 pointData2[vLetter + '0'] = pointData2[vLetter + '1'] = valPx;
87997 pointData2[vLetter + 'LabelVal'] = val;
87998 pointData2[vLetter + 'Label'] = (t.labels ? t.labels[attr] + ' ' : '') + Axes.hoverLabelText(vAxis, val, trace[vLetter + 'hoverformat']);
87999
88000 // Note: introduced to be able to distinguish a
88001 // clicked point from a box during click-to-select
88002 pointData2.hoverOnBox = true;
88003
88004 if(attr === 'mean' && ('sd' in di) && trace.boxmean === 'sd') {
88005 pointData2[vLetter + 'err'] = di.sd;
88006 }
88007
88008 // only keep name and spikes on the first item (median)
88009 pointData.name = '';
88010 pointData.spikeDistance = undefined;
88011 pointData[spikePosAttr] = undefined;
88012
88013 // no hovertemplate support yet
88014 pointData2.hovertemplate = false;
88015
88016 closeBoxData.push(pointData2);
88017 }
88018
88019 return closeBoxData;
88020}
88021
88022function hoverOnPoints(pointData, xval, yval) {
88023 var cd = pointData.cd;
88024 var xa = pointData.xa;
88025 var ya = pointData.ya;
88026 var trace = cd[0].trace;
88027 var xPx = xa.c2p(xval);
88028 var yPx = ya.c2p(yval);
88029 var closePtData;
88030
88031 var dx = function(di) {
88032 var rad = Math.max(3, di.mrc || 0);
88033 return Math.max(Math.abs(xa.c2p(di.x) - xPx) - rad, 1 - 3 / rad);
88034 };
88035 var dy = function(di) {
88036 var rad = Math.max(3, di.mrc || 0);
88037 return Math.max(Math.abs(ya.c2p(di.y) - yPx) - rad, 1 - 3 / rad);
88038 };
88039 var distfn = Fx.quadrature(dx, dy);
88040
88041 // show one point per trace
88042 var ijClosest = false;
88043 var di, pt;
88044
88045 for(var i = 0; i < cd.length; i++) {
88046 di = cd[i];
88047
88048 for(var j = 0; j < (di.pts || []).length; j++) {
88049 pt = di.pts[j];
88050
88051 var newDistance = distfn(pt);
88052 if(newDistance <= pointData.distance) {
88053 pointData.distance = newDistance;
88054 ijClosest = [i, j];
88055 }
88056 }
88057 }
88058
88059 if(!ijClosest) return false;
88060
88061 di = cd[ijClosest[0]];
88062 pt = di.pts[ijClosest[1]];
88063
88064 var xc = xa.c2p(pt.x, true);
88065 var yc = ya.c2p(pt.y, true);
88066 var rad = pt.mrc || 1;
88067
88068 closePtData = Lib.extendFlat({}, pointData, {
88069 // corresponds to index in x/y input data array
88070 index: pt.i,
88071 color: (trace.marker || {}).color,
88072 name: trace.name,
88073 x0: xc - rad,
88074 x1: xc + rad,
88075 y0: yc - rad,
88076 y1: yc + rad,
88077 spikeDistance: pointData.distance,
88078 hovertemplate: trace.hovertemplate
88079 });
88080
88081 var origPos = di.orig_p;
88082 var pos = origPos !== undefined ? origPos : di.pos;
88083 var pa;
88084 if(trace.orientation === 'h') {
88085 pa = ya;
88086 closePtData.xLabelVal = pt.x;
88087 closePtData.yLabelVal = pos;
88088 } else {
88089 pa = xa;
88090 closePtData.xLabelVal = pos;
88091 closePtData.yLabelVal = pt.y;
88092 }
88093
88094 var pLetter = pa._id.charAt(0);
88095 closePtData[pLetter + 'Spike'] = pa.c2p(di.pos, true);
88096
88097 fillText(pt, trace, closePtData);
88098
88099 return closePtData;
88100}
88101
88102module.exports = {
88103 hoverPoints: hoverPoints,
88104 hoverOnBoxes: hoverOnBoxes,
88105 hoverOnPoints: hoverOnPoints
88106};
88107
88108},{"../../components/color":157,"../../components/fx":197,"../../lib":287,"../../plots/cartesian/axes":334}],409:[function(_dereq_,module,exports){
88109'use strict';
88110
88111module.exports = {
88112 attributes: _dereq_('./attributes'),
88113 layoutAttributes: _dereq_('./layout_attributes'),
88114 supplyDefaults: _dereq_('./defaults').supplyDefaults,
88115 crossTraceDefaults: _dereq_('./defaults').crossTraceDefaults,
88116 supplyLayoutDefaults: _dereq_('./layout_defaults').supplyLayoutDefaults,
88117 calc: _dereq_('./calc'),
88118 crossTraceCalc: _dereq_('./cross_trace_calc').crossTraceCalc,
88119 plot: _dereq_('./plot').plot,
88120 style: _dereq_('./style').style,
88121 styleOnSelect: _dereq_('./style').styleOnSelect,
88122 hoverPoints: _dereq_('./hover').hoverPoints,
88123 eventData: _dereq_('./event_data'),
88124 selectPoints: _dereq_('./select'),
88125
88126 moduleType: 'trace',
88127 name: 'box',
88128 basePlotModule: _dereq_('../../plots/cartesian'),
88129 categories: ['cartesian', 'svg', 'symbols', 'oriented', 'box-violin', 'showLegend', 'boxLayout', 'zoomScale'],
88130 meta: {
88131 }
88132};
88133
88134},{"../../plots/cartesian":348,"./attributes":403,"./calc":404,"./cross_trace_calc":405,"./defaults":406,"./event_data":407,"./hover":408,"./layout_attributes":410,"./layout_defaults":411,"./plot":412,"./select":413,"./style":414}],410:[function(_dereq_,module,exports){
88135'use strict';
88136
88137
88138module.exports = {
88139 boxmode: {
88140 valType: 'enumerated',
88141 values: ['group', 'overlay'],
88142 dflt: 'overlay',
88143 editType: 'calc',
88144 },
88145 boxgap: {
88146 valType: 'number',
88147 min: 0,
88148 max: 1,
88149 dflt: 0.3,
88150 editType: 'calc',
88151 },
88152 boxgroupgap: {
88153 valType: 'number',
88154 min: 0,
88155 max: 1,
88156 dflt: 0.3,
88157 editType: 'calc',
88158 }
88159};
88160
88161},{}],411:[function(_dereq_,module,exports){
88162'use strict';
88163
88164var Registry = _dereq_('../../registry');
88165var Lib = _dereq_('../../lib');
88166var layoutAttributes = _dereq_('./layout_attributes');
88167
88168function _supply(layoutIn, layoutOut, fullData, coerce, traceType) {
88169 var category = traceType + 'Layout';
88170 var hasTraceType = false;
88171
88172 for(var i = 0; i < fullData.length; i++) {
88173 var trace = fullData[i];
88174
88175 if(Registry.traceIs(trace, category)) {
88176 hasTraceType = true;
88177 break;
88178 }
88179 }
88180 if(!hasTraceType) return;
88181
88182 coerce(traceType + 'mode');
88183 coerce(traceType + 'gap');
88184 coerce(traceType + 'groupgap');
88185}
88186
88187function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
88188 function coerce(attr, dflt) {
88189 return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt);
88190 }
88191 _supply(layoutIn, layoutOut, fullData, coerce, 'box');
88192}
88193
88194module.exports = {
88195 supplyLayoutDefaults: supplyLayoutDefaults,
88196 _supply: _supply
88197};
88198
88199},{"../../lib":287,"../../registry":376,"./layout_attributes":410}],412:[function(_dereq_,module,exports){
88200'use strict';
88201
88202var d3 = _dereq_('@plotly/d3');
88203
88204var Lib = _dereq_('../../lib');
88205var Drawing = _dereq_('../../components/drawing');
88206
88207// constants for dynamic jitter (ie less jitter for sparser points)
88208var JITTERCOUNT = 5; // points either side of this to include
88209var JITTERSPREAD = 0.01; // fraction of IQR to count as "dense"
88210
88211function plot(gd, plotinfo, cdbox, boxLayer) {
88212 var xa = plotinfo.xaxis;
88213 var ya = plotinfo.yaxis;
88214
88215 Lib.makeTraceGroups(boxLayer, cdbox, 'trace boxes').each(function(cd) {
88216 var plotGroup = d3.select(this);
88217 var cd0 = cd[0];
88218 var t = cd0.t;
88219 var trace = cd0.trace;
88220
88221 // whisker width
88222 t.wdPos = t.bdPos * trace.whiskerwidth;
88223
88224 if(trace.visible !== true || t.empty) {
88225 plotGroup.remove();
88226 return;
88227 }
88228
88229 var posAxis, valAxis;
88230
88231 if(trace.orientation === 'h') {
88232 posAxis = ya;
88233 valAxis = xa;
88234 } else {
88235 posAxis = xa;
88236 valAxis = ya;
88237 }
88238
88239 plotBoxAndWhiskers(plotGroup, {pos: posAxis, val: valAxis}, trace, t);
88240 plotPoints(plotGroup, {x: xa, y: ya}, trace, t);
88241 plotBoxMean(plotGroup, {pos: posAxis, val: valAxis}, trace, t);
88242 });
88243}
88244
88245function plotBoxAndWhiskers(sel, axes, trace, t) {
88246 var isHorizontal = trace.orientation === 'h';
88247 var valAxis = axes.val;
88248 var posAxis = axes.pos;
88249 var posHasRangeBreaks = !!posAxis.rangebreaks;
88250
88251 var bPos = t.bPos;
88252 var wdPos = t.wdPos || 0;
88253 var bPosPxOffset = t.bPosPxOffset || 0;
88254 var whiskerWidth = trace.whiskerwidth || 0;
88255 var notched = trace.notched || false;
88256 var nw = notched ? 1 - 2 * trace.notchwidth : 1;
88257
88258 // to support for one-sided box
88259 var bdPos0;
88260 var bdPos1;
88261 if(Array.isArray(t.bdPos)) {
88262 bdPos0 = t.bdPos[0];
88263 bdPos1 = t.bdPos[1];
88264 } else {
88265 bdPos0 = t.bdPos;
88266 bdPos1 = t.bdPos;
88267 }
88268
88269 var paths = sel.selectAll('path.box').data((
88270 trace.type !== 'violin' ||
88271 trace.box.visible
88272 ) ? Lib.identity : []);
88273
88274 paths.enter().append('path')
88275 .style('vector-effect', 'non-scaling-stroke')
88276 .attr('class', 'box');
88277
88278 paths.exit().remove();
88279
88280 paths.each(function(d) {
88281 if(d.empty) return 'M0,0Z';
88282
88283 var lcenter = posAxis.c2l(d.pos + bPos, true);
88284
88285 var pos0 = posAxis.l2p(lcenter - bdPos0) + bPosPxOffset;
88286 var pos1 = posAxis.l2p(lcenter + bdPos1) + bPosPxOffset;
88287 var posc = posHasRangeBreaks ? (pos0 + pos1) / 2 : posAxis.l2p(lcenter) + bPosPxOffset;
88288
88289 var r = trace.whiskerwidth;
88290 var posw0 = posHasRangeBreaks ? pos0 * r + (1 - r) * posc : posAxis.l2p(lcenter - wdPos) + bPosPxOffset;
88291 var posw1 = posHasRangeBreaks ? pos1 * r + (1 - r) * posc : posAxis.l2p(lcenter + wdPos) + bPosPxOffset;
88292
88293 var posm0 = posAxis.l2p(lcenter - bdPos0 * nw) + bPosPxOffset;
88294 var posm1 = posAxis.l2p(lcenter + bdPos1 * nw) + bPosPxOffset;
88295 var q1 = valAxis.c2p(d.q1, true);
88296 var q3 = valAxis.c2p(d.q3, true);
88297 // make sure median isn't identical to either of the
88298 // quartiles, so we can see it
88299 var m = Lib.constrain(
88300 valAxis.c2p(d.med, true),
88301 Math.min(q1, q3) + 1, Math.max(q1, q3) - 1
88302 );
88303
88304 // for compatibility with box, violin, and candlestick
88305 // perhaps we should put this into cd0.t instead so it's more explicit,
88306 // but what we have now is:
88307 // - box always has d.lf, but boxpoints can be anything
88308 // - violin has d.lf and should always use it (boxpoints is undefined)
88309 // - candlestick has only min/max
88310 var useExtremes = (d.lf === undefined) || (trace.boxpoints === false);
88311 var lf = valAxis.c2p(useExtremes ? d.min : d.lf, true);
88312 var uf = valAxis.c2p(useExtremes ? d.max : d.uf, true);
88313 var ln = valAxis.c2p(d.ln, true);
88314 var un = valAxis.c2p(d.un, true);
88315
88316 if(isHorizontal) {
88317 d3.select(this).attr('d',
88318 'M' + m + ',' + posm0 + 'V' + posm1 + // median line
88319 'M' + q1 + ',' + pos0 + 'V' + pos1 + // left edge
88320 (notched ?
88321 'H' + ln + 'L' + m + ',' + posm1 + 'L' + un + ',' + pos1 :
88322 ''
88323 ) + // top notched edge
88324 'H' + q3 + // end of the top edge
88325 'V' + pos0 + // right edge
88326 (notched ? 'H' + un + 'L' + m + ',' + posm0 + 'L' + ln + ',' + pos0 : '') + // bottom notched edge
88327 'Z' + // end of the box
88328 'M' + q1 + ',' + posc + 'H' + lf + 'M' + q3 + ',' + posc + 'H' + uf + // whiskers
88329 (whiskerWidth === 0 ?
88330 '' : // whisker caps
88331 'M' + lf + ',' + posw0 + 'V' + posw1 + 'M' + uf + ',' + posw0 + 'V' + posw1
88332 )
88333 );
88334 } else {
88335 d3.select(this).attr('d',
88336 'M' + posm0 + ',' + m + 'H' + posm1 + // median line
88337 'M' + pos0 + ',' + q1 + 'H' + pos1 + // top of the box
88338 (notched ?
88339 'V' + ln + 'L' + posm1 + ',' + m + 'L' + pos1 + ',' + un :
88340 ''
88341 ) + // notched right edge
88342 'V' + q3 + // end of the right edge
88343 'H' + pos0 + // bottom of the box
88344 (notched ?
88345 'V' + un + 'L' + posm0 + ',' + m + 'L' + pos0 + ',' + ln :
88346 ''
88347 ) + // notched left edge
88348 'Z' + // end of the box
88349 'M' + posc + ',' + q1 + 'V' + lf + 'M' + posc + ',' + q3 + 'V' + uf + // whiskers
88350 (whiskerWidth === 0 ?
88351 '' : // whisker caps
88352 'M' + posw0 + ',' + lf + 'H' + posw1 + 'M' + posw0 + ',' + uf + 'H' + posw1
88353 )
88354 );
88355 }
88356 });
88357}
88358
88359function plotPoints(sel, axes, trace, t) {
88360 var xa = axes.x;
88361 var ya = axes.y;
88362 var bdPos = t.bdPos;
88363 var bPos = t.bPos;
88364
88365 // to support violin points
88366 var mode = trace.boxpoints || trace.points;
88367
88368 // repeatable pseudo-random number generator
88369 Lib.seedPseudoRandom();
88370
88371 // since box plot points get an extra level of nesting, each
88372 // box needs the trace styling info
88373 var fn = function(d) {
88374 d.forEach(function(v) {
88375 v.t = t;
88376 v.trace = trace;
88377 });
88378 return d;
88379 };
88380
88381 var gPoints = sel.selectAll('g.points')
88382 .data(mode ? fn : []);
88383
88384 gPoints.enter().append('g')
88385 .attr('class', 'points');
88386
88387 gPoints.exit().remove();
88388
88389 var paths = gPoints.selectAll('path')
88390 .data(function(d) {
88391 var i;
88392 var pts = d.pts2;
88393
88394 // normally use IQR, but if this is 0 or too small, use max-min
88395 var typicalSpread = Math.max((d.max - d.min) / 10, d.q3 - d.q1);
88396 var minSpread = typicalSpread * 1e-9;
88397 var spreadLimit = typicalSpread * JITTERSPREAD;
88398 var jitterFactors = [];
88399 var maxJitterFactor = 0;
88400 var newJitter;
88401
88402 // dynamic jitter
88403 if(trace.jitter) {
88404 if(typicalSpread === 0) {
88405 // edge case of no spread at all: fall back to max jitter
88406 maxJitterFactor = 1;
88407 jitterFactors = new Array(pts.length);
88408 for(i = 0; i < pts.length; i++) {
88409 jitterFactors[i] = 1;
88410 }
88411 } else {
88412 for(i = 0; i < pts.length; i++) {
88413 var i0 = Math.max(0, i - JITTERCOUNT);
88414 var pmin = pts[i0].v;
88415 var i1 = Math.min(pts.length - 1, i + JITTERCOUNT);
88416 var pmax = pts[i1].v;
88417
88418 if(mode !== 'all') {
88419 if(pts[i].v < d.lf) pmax = Math.min(pmax, d.lf);
88420 else pmin = Math.max(pmin, d.uf);
88421 }
88422
88423 var jitterFactor = Math.sqrt(spreadLimit * (i1 - i0) / (pmax - pmin + minSpread)) || 0;
88424 jitterFactor = Lib.constrain(Math.abs(jitterFactor), 0, 1);
88425
88426 jitterFactors.push(jitterFactor);
88427 maxJitterFactor = Math.max(jitterFactor, maxJitterFactor);
88428 }
88429 }
88430 newJitter = trace.jitter * 2 / (maxJitterFactor || 1);
88431 }
88432
88433 // fills in 'x' and 'y' in calcdata 'pts' item
88434 for(i = 0; i < pts.length; i++) {
88435 var pt = pts[i];
88436 var v = pt.v;
88437
88438 var jitterOffset = trace.jitter ?
88439 (newJitter * jitterFactors[i] * (Lib.pseudoRandom() - 0.5)) :
88440 0;
88441
88442 var posPx = d.pos + bPos + bdPos * (trace.pointpos + jitterOffset);
88443
88444 if(trace.orientation === 'h') {
88445 pt.y = posPx;
88446 pt.x = v;
88447 } else {
88448 pt.x = posPx;
88449 pt.y = v;
88450 }
88451
88452 // tag suspected outliers
88453 if(mode === 'suspectedoutliers' && v < d.uo && v > d.lo) {
88454 pt.so = true;
88455 }
88456 }
88457
88458 return pts;
88459 });
88460
88461 paths.enter().append('path')
88462 .classed('point', true);
88463
88464 paths.exit().remove();
88465
88466 paths.call(Drawing.translatePoints, xa, ya);
88467}
88468
88469function plotBoxMean(sel, axes, trace, t) {
88470 var valAxis = axes.val;
88471 var posAxis = axes.pos;
88472 var posHasRangeBreaks = !!posAxis.rangebreaks;
88473
88474 var bPos = t.bPos;
88475 var bPosPxOffset = t.bPosPxOffset || 0;
88476
88477 // to support violin mean lines
88478 var mode = trace.boxmean || (trace.meanline || {}).visible;
88479
88480 // to support for one-sided box
88481 var bdPos0;
88482 var bdPos1;
88483 if(Array.isArray(t.bdPos)) {
88484 bdPos0 = t.bdPos[0];
88485 bdPos1 = t.bdPos[1];
88486 } else {
88487 bdPos0 = t.bdPos;
88488 bdPos1 = t.bdPos;
88489 }
88490
88491 var paths = sel.selectAll('path.mean').data((
88492 (trace.type === 'box' && trace.boxmean) ||
88493 (trace.type === 'violin' && trace.box.visible && trace.meanline.visible)
88494 ) ? Lib.identity : []);
88495
88496 paths.enter().append('path')
88497 .attr('class', 'mean')
88498 .style({
88499 fill: 'none',
88500 'vector-effect': 'non-scaling-stroke'
88501 });
88502
88503 paths.exit().remove();
88504
88505 paths.each(function(d) {
88506 var lcenter = posAxis.c2l(d.pos + bPos, true);
88507
88508 var pos0 = posAxis.l2p(lcenter - bdPos0) + bPosPxOffset;
88509 var pos1 = posAxis.l2p(lcenter + bdPos1) + bPosPxOffset;
88510 var posc = posHasRangeBreaks ? (pos0 + pos1) / 2 : posAxis.l2p(lcenter) + bPosPxOffset;
88511
88512 var m = valAxis.c2p(d.mean, true);
88513 var sl = valAxis.c2p(d.mean - d.sd, true);
88514 var sh = valAxis.c2p(d.mean + d.sd, true);
88515
88516 if(trace.orientation === 'h') {
88517 d3.select(this).attr('d',
88518 'M' + m + ',' + pos0 + 'V' + pos1 +
88519 (mode === 'sd' ?
88520 'm0,0L' + sl + ',' + posc + 'L' + m + ',' + pos0 + 'L' + sh + ',' + posc + 'Z' :
88521 '')
88522 );
88523 } else {
88524 d3.select(this).attr('d',
88525 'M' + pos0 + ',' + m + 'H' + pos1 +
88526 (mode === 'sd' ?
88527 'm0,0L' + posc + ',' + sl + 'L' + pos0 + ',' + m + 'L' + posc + ',' + sh + 'Z' :
88528 '')
88529 );
88530 }
88531 });
88532}
88533
88534module.exports = {
88535 plot: plot,
88536 plotBoxAndWhiskers: plotBoxAndWhiskers,
88537 plotPoints: plotPoints,
88538 plotBoxMean: plotBoxMean
88539};
88540
88541},{"../../components/drawing":179,"../../lib":287,"@plotly/d3":20}],413:[function(_dereq_,module,exports){
88542'use strict';
88543
88544module.exports = function selectPoints(searchInfo, selectionTester) {
88545 var cd = searchInfo.cd;
88546 var xa = searchInfo.xaxis;
88547 var ya = searchInfo.yaxis;
88548 var selection = [];
88549 var i, j;
88550
88551 if(selectionTester === false) {
88552 for(i = 0; i < cd.length; i++) {
88553 for(j = 0; j < (cd[i].pts || []).length; j++) {
88554 // clear selection
88555 cd[i].pts[j].selected = 0;
88556 }
88557 }
88558 } else {
88559 for(i = 0; i < cd.length; i++) {
88560 for(j = 0; j < (cd[i].pts || []).length; j++) {
88561 var pt = cd[i].pts[j];
88562 var x = xa.c2p(pt.x);
88563 var y = ya.c2p(pt.y);
88564
88565 if(selectionTester.contains([x, y], null, pt.i, searchInfo)) {
88566 selection.push({
88567 pointNumber: pt.i,
88568 x: xa.c2d(pt.x),
88569 y: ya.c2d(pt.y)
88570 });
88571 pt.selected = 1;
88572 } else {
88573 pt.selected = 0;
88574 }
88575 }
88576 }
88577 }
88578
88579 return selection;
88580};
88581
88582},{}],414:[function(_dereq_,module,exports){
88583'use strict';
88584
88585var d3 = _dereq_('@plotly/d3');
88586var Color = _dereq_('../../components/color');
88587var Drawing = _dereq_('../../components/drawing');
88588
88589function style(gd, cd, sel) {
88590 var s = sel ? sel : d3.select(gd).selectAll('g.trace.boxes');
88591
88592 s.style('opacity', function(d) { return d[0].trace.opacity; });
88593
88594 s.each(function(d) {
88595 var el = d3.select(this);
88596 var trace = d[0].trace;
88597 var lineWidth = trace.line.width;
88598
88599 function styleBox(boxSel, lineWidth, lineColor, fillColor) {
88600 boxSel.style('stroke-width', lineWidth + 'px')
88601 .call(Color.stroke, lineColor)
88602 .call(Color.fill, fillColor);
88603 }
88604
88605 var allBoxes = el.selectAll('path.box');
88606
88607 if(trace.type === 'candlestick') {
88608 allBoxes.each(function(boxData) {
88609 if(boxData.empty) return;
88610
88611 var thisBox = d3.select(this);
88612 var container = trace[boxData.dir]; // dir = 'increasing' or 'decreasing'
88613 styleBox(thisBox, container.line.width, container.line.color, container.fillcolor);
88614 // TODO: custom selection style for candlesticks
88615 thisBox.style('opacity', trace.selectedpoints && !boxData.selected ? 0.3 : 1);
88616 });
88617 } else {
88618 styleBox(allBoxes, lineWidth, trace.line.color, trace.fillcolor);
88619 el.selectAll('path.mean')
88620 .style({
88621 'stroke-width': lineWidth,
88622 'stroke-dasharray': (2 * lineWidth) + 'px,' + lineWidth + 'px'
88623 })
88624 .call(Color.stroke, trace.line.color);
88625
88626 var pts = el.selectAll('path.point');
88627 Drawing.pointStyle(pts, trace, gd);
88628 }
88629 });
88630}
88631
88632function styleOnSelect(gd, cd, sel) {
88633 var trace = cd[0].trace;
88634 var pts = sel.selectAll('path.point');
88635
88636 if(trace.selectedpoints) {
88637 Drawing.selectedPointStyle(pts, trace);
88638 } else {
88639 Drawing.pointStyle(pts, trace, gd);
88640 }
88641}
88642
88643module.exports = {
88644 style: style,
88645 styleOnSelect: styleOnSelect
88646};
88647
88648},{"../../components/color":157,"../../components/drawing":179,"@plotly/d3":20}],415:[function(_dereq_,module,exports){
88649'use strict';
88650
88651var heatmapAttrs = _dereq_('../heatmap/attributes');
88652var scatterAttrs = _dereq_('../scatter/attributes');
88653var axisFormat = _dereq_('../../plots/cartesian/axis_format_attributes');
88654var axisHoverFormat = axisFormat.axisHoverFormat;
88655var descriptionOnlyNumbers = axisFormat.descriptionOnlyNumbers;
88656var colorScaleAttrs = _dereq_('../../components/colorscale/attributes');
88657var dash = _dereq_('../../components/drawing/attributes').dash;
88658var fontAttrs = _dereq_('../../plots/font_attributes');
88659var extendFlat = _dereq_('../../lib/extend').extendFlat;
88660
88661var filterOps = _dereq_('../../constants/filter_ops');
88662var COMPARISON_OPS2 = filterOps.COMPARISON_OPS2;
88663var INTERVAL_OPS = filterOps.INTERVAL_OPS;
88664
88665
88666var scatterLineAttrs = scatterAttrs.line;
88667
88668module.exports = extendFlat({
88669 z: heatmapAttrs.z,
88670 x: heatmapAttrs.x,
88671 x0: heatmapAttrs.x0,
88672 dx: heatmapAttrs.dx,
88673 y: heatmapAttrs.y,
88674 y0: heatmapAttrs.y0,
88675 dy: heatmapAttrs.dy,
88676
88677 xperiod: heatmapAttrs.xperiod,
88678 yperiod: heatmapAttrs.yperiod,
88679 xperiod0: scatterAttrs.xperiod0,
88680 yperiod0: scatterAttrs.yperiod0,
88681 xperiodalignment: heatmapAttrs.xperiodalignment,
88682 yperiodalignment: heatmapAttrs.yperiodalignment,
88683
88684 text: heatmapAttrs.text,
88685 hovertext: heatmapAttrs.hovertext,
88686 transpose: heatmapAttrs.transpose,
88687 xtype: heatmapAttrs.xtype,
88688 ytype: heatmapAttrs.ytype,
88689 xhoverformat: axisHoverFormat('x'),
88690 yhoverformat: axisHoverFormat('y'),
88691 zhoverformat: axisHoverFormat('z', 1),
88692 hovertemplate: heatmapAttrs.hovertemplate,
88693 hoverongaps: heatmapAttrs.hoverongaps,
88694 connectgaps: extendFlat({}, heatmapAttrs.connectgaps, {
88695 }),
88696
88697 fillcolor: {
88698 valType: 'color',
88699 editType: 'calc',
88700 },
88701
88702 autocontour: {
88703 valType: 'boolean',
88704 dflt: true,
88705 editType: 'calc',
88706 impliedEdits: {
88707 'contours.start': undefined,
88708 'contours.end': undefined,
88709 'contours.size': undefined
88710 },
88711 },
88712 ncontours: {
88713 valType: 'integer',
88714 dflt: 15,
88715 min: 1,
88716 editType: 'calc',
88717 },
88718
88719 contours: {
88720 type: {
88721 valType: 'enumerated',
88722 values: ['levels', 'constraint'],
88723 dflt: 'levels',
88724 editType: 'calc',
88725 },
88726 start: {
88727 valType: 'number',
88728 dflt: null,
88729 editType: 'plot',
88730 impliedEdits: {'^autocontour': false},
88731 },
88732 end: {
88733 valType: 'number',
88734 dflt: null,
88735 editType: 'plot',
88736 impliedEdits: {'^autocontour': false},
88737 },
88738 size: {
88739 valType: 'number',
88740 dflt: null,
88741 min: 0,
88742 editType: 'plot',
88743 impliedEdits: {'^autocontour': false},
88744 },
88745 coloring: {
88746 valType: 'enumerated',
88747 values: ['fill', 'heatmap', 'lines', 'none'],
88748 dflt: 'fill',
88749 editType: 'calc',
88750 },
88751 showlines: {
88752 valType: 'boolean',
88753 dflt: true,
88754 editType: 'plot',
88755 },
88756 showlabels: {
88757 valType: 'boolean',
88758 dflt: false,
88759 editType: 'plot',
88760 },
88761 labelfont: fontAttrs({
88762 editType: 'plot',
88763 colorEditType: 'style',
88764 }),
88765 labelformat: {
88766 valType: 'string',
88767 dflt: '',
88768 editType: 'plot',
88769 description: descriptionOnlyNumbers('contour label')
88770 },
88771 operation: {
88772 valType: 'enumerated',
88773 values: [].concat(COMPARISON_OPS2).concat(INTERVAL_OPS),
88774 dflt: '=',
88775 editType: 'calc',
88776 },
88777 value: {
88778 valType: 'any',
88779 dflt: 0,
88780 editType: 'calc',
88781 },
88782 editType: 'calc',
88783 impliedEdits: {'autocontour': false}
88784 },
88785
88786 line: {
88787 color: extendFlat({}, scatterLineAttrs.color, {
88788 editType: 'style+colorbars',
88789 }),
88790 width: {
88791 valType: 'number',
88792 min: 0,
88793 editType: 'style+colorbars',
88794 },
88795 dash: dash,
88796 smoothing: extendFlat({}, scatterLineAttrs.smoothing, {
88797 }),
88798 editType: 'plot'
88799 }
88800},
88801 colorScaleAttrs('', {
88802 cLetter: 'z',
88803 autoColorDflt: false,
88804 editTypeOverride: 'calc'
88805 })
88806);
88807
88808},{"../../components/colorscale/attributes":164,"../../components/drawing/attributes":178,"../../constants/filter_ops":265,"../../lib/extend":281,"../../plots/cartesian/axis_format_attributes":337,"../../plots/font_attributes":363,"../heatmap/attributes":437,"../scatter/attributes":497}],416:[function(_dereq_,module,exports){
88809'use strict';
88810
88811var Colorscale = _dereq_('../../components/colorscale');
88812
88813var heatmapCalc = _dereq_('../heatmap/calc');
88814var setContours = _dereq_('./set_contours');
88815var endPlus = _dereq_('./end_plus');
88816
88817// most is the same as heatmap calc, then adjust it
88818// though a few things inside heatmap calc still look for
88819// contour maps, because the makeBoundArray calls are too entangled
88820module.exports = function calc(gd, trace) {
88821 var cd = heatmapCalc(gd, trace);
88822
88823 var zOut = cd[0].z;
88824 setContours(trace, zOut);
88825
88826 var contours = trace.contours;
88827 var cOpts = Colorscale.extractOpts(trace);
88828 var cVals;
88829
88830 if(contours.coloring === 'heatmap' && cOpts.auto && trace.autocontour === false) {
88831 var start = contours.start;
88832 var end = endPlus(contours);
88833 var cs = contours.size || 1;
88834 var nc = Math.floor((end - start) / cs) + 1;
88835
88836 if(!isFinite(cs)) {
88837 cs = 1;
88838 nc = 1;
88839 }
88840
88841 var min0 = start - cs / 2;
88842 var max0 = min0 + nc * cs;
88843 cVals = [min0, max0];
88844 } else {
88845 cVals = zOut;
88846 }
88847
88848 Colorscale.calc(gd, trace, {vals: cVals, cLetter: 'z'});
88849
88850 return cd;
88851};
88852
88853},{"../../components/colorscale":169,"../heatmap/calc":438,"./end_plus":426,"./set_contours":434}],417:[function(_dereq_,module,exports){
88854'use strict';
88855
88856module.exports = function(pathinfo, contours) {
88857 var pi0 = pathinfo[0];
88858 var z = pi0.z;
88859 var i;
88860
88861 switch(contours.type) {
88862 case 'levels':
88863 // Why (just) use z[0][0] and z[0][1]?
88864 //
88865 // N.B. using boundaryMin instead of edgeVal2 here makes the
88866 // `contour_scatter` mock fail
88867 var edgeVal2 = Math.min(z[0][0], z[0][1]);
88868
88869 for(i = 0; i < pathinfo.length; i++) {
88870 var pi = pathinfo[i];
88871 pi.prefixBoundary = !pi.edgepaths.length &&
88872 (edgeVal2 > pi.level || pi.starts.length && edgeVal2 === pi.level);
88873 }
88874 break;
88875 case 'constraint':
88876 // after convertToConstraints, pathinfo has length=0
88877 pi0.prefixBoundary = false;
88878
88879 // joinAllPaths does enough already when edgepaths are present
88880 if(pi0.edgepaths.length) return;
88881
88882 var na = pi0.x.length;
88883 var nb = pi0.y.length;
88884 var boundaryMax = -Infinity;
88885 var boundaryMin = Infinity;
88886
88887 for(i = 0; i < nb; i++) {
88888 boundaryMin = Math.min(boundaryMin, z[i][0]);
88889 boundaryMin = Math.min(boundaryMin, z[i][na - 1]);
88890 boundaryMax = Math.max(boundaryMax, z[i][0]);
88891 boundaryMax = Math.max(boundaryMax, z[i][na - 1]);
88892 }
88893 for(i = 1; i < na - 1; i++) {
88894 boundaryMin = Math.min(boundaryMin, z[0][i]);
88895 boundaryMin = Math.min(boundaryMin, z[nb - 1][i]);
88896 boundaryMax = Math.max(boundaryMax, z[0][i]);
88897 boundaryMax = Math.max(boundaryMax, z[nb - 1][i]);
88898 }
88899
88900 var contoursValue = contours.value;
88901 var v1, v2;
88902
88903 switch(contours._operation) {
88904 case '>':
88905 if(contoursValue > boundaryMax) {
88906 pi0.prefixBoundary = true;
88907 }
88908 break;
88909 case '<':
88910 if(contoursValue < boundaryMin ||
88911 (pi0.starts.length && contoursValue === boundaryMin)) {
88912 pi0.prefixBoundary = true;
88913 }
88914 break;
88915 case '[]':
88916 v1 = Math.min(contoursValue[0], contoursValue[1]);
88917 v2 = Math.max(contoursValue[0], contoursValue[1]);
88918 if(v2 < boundaryMin || v1 > boundaryMax ||
88919 (pi0.starts.length && v2 === boundaryMin)) {
88920 pi0.prefixBoundary = true;
88921 }
88922 break;
88923 case '][':
88924 v1 = Math.min(contoursValue[0], contoursValue[1]);
88925 v2 = Math.max(contoursValue[0], contoursValue[1]);
88926 if(v1 < boundaryMin && v2 > boundaryMax) {
88927 pi0.prefixBoundary = true;
88928 }
88929 break;
88930 }
88931 break;
88932 }
88933};
88934
88935},{}],418:[function(_dereq_,module,exports){
88936'use strict';
88937
88938var Colorscale = _dereq_('../../components/colorscale');
88939var makeColorMap = _dereq_('./make_color_map');
88940var endPlus = _dereq_('./end_plus');
88941
88942function calc(gd, trace, opts) {
88943 var contours = trace.contours;
88944 var line = trace.line;
88945 var cs = contours.size || 1;
88946 var coloring = contours.coloring;
88947 var colorMap = makeColorMap(trace, {isColorbar: true});
88948
88949 if(coloring === 'heatmap') {
88950 var cOpts = Colorscale.extractOpts(trace);
88951 opts._fillgradient = cOpts.reversescale ?
88952 Colorscale.flipScale(cOpts.colorscale) :
88953 cOpts.colorscale;
88954 opts._zrange = [cOpts.min, cOpts.max];
88955 } else if(coloring === 'fill') {
88956 opts._fillcolor = colorMap;
88957 }
88958
88959 opts._line = {
88960 color: coloring === 'lines' ? colorMap : line.color,
88961 width: contours.showlines !== false ? line.width : 0,
88962 dash: line.dash
88963 };
88964
88965 opts._levels = {
88966 start: contours.start,
88967 end: endPlus(contours),
88968 size: cs
88969 };
88970}
88971
88972module.exports = {
88973 min: 'zmin',
88974 max: 'zmax',
88975 calc: calc
88976};
88977
88978},{"../../components/colorscale":169,"./end_plus":426,"./make_color_map":431}],419:[function(_dereq_,module,exports){
88979'use strict';
88980module.exports = {
88981 // some constants to help with marching squares algorithm
88982 // where does the path start for each index?
88983 BOTTOMSTART: [1, 9, 13, 104, 713],
88984 TOPSTART: [4, 6, 7, 104, 713],
88985 LEFTSTART: [8, 12, 14, 208, 1114],
88986 RIGHTSTART: [2, 3, 11, 208, 1114],
88987
88988 // which way [dx,dy] do we leave a given index?
88989 // saddles are already disambiguated
88990 NEWDELTA: [
88991 null, [-1, 0], [0, -1], [-1, 0],
88992 [1, 0], null, [0, -1], [-1, 0],
88993 [0, 1], [0, 1], null, [0, 1],
88994 [1, 0], [1, 0], [0, -1]
88995 ],
88996
88997 // for each saddle, the first index here is used
88998 // for dx||dy<0, the second for dx||dy>0
88999 CHOOSESADDLE: {
89000 104: [4, 1],
89001 208: [2, 8],
89002 713: [7, 13],
89003 1114: [11, 14]
89004 },
89005
89006 // after one index has been used for a saddle, which do we
89007 // substitute to be used up later?
89008 SADDLEREMAINDER: {1: 4, 2: 8, 4: 1, 7: 13, 8: 2, 11: 14, 13: 7, 14: 11},
89009
89010 // length of a contour, as a multiple of the plot area diagonal, per label
89011 LABELDISTANCE: 2,
89012
89013 // number of contour levels after which we start increasing the number of
89014 // labels we draw. Many contours means they will generally be close
89015 // together, so it will be harder to follow a long way to find a label
89016 LABELINCREASE: 10,
89017
89018 // minimum length of a contour line, as a multiple of the label length,
89019 // at which we draw *any* labels
89020 LABELMIN: 3,
89021
89022 // max number of labels to draw on a single contour path, no matter how long
89023 LABELMAX: 10,
89024
89025 // constants for the label position cost function
89026 LABELOPTIMIZER: {
89027 // weight given to edge proximity
89028 EDGECOST: 1,
89029 // weight given to the angle off horizontal
89030 ANGLECOST: 1,
89031 // weight given to distance from already-placed labels
89032 NEIGHBORCOST: 5,
89033 // cost multiplier for labels on the same level
89034 SAMELEVELFACTOR: 10,
89035 // minimum distance (as a multiple of the label length)
89036 // for labels on the same level
89037 SAMELEVELDISTANCE: 5,
89038 // maximum cost before we won't even place the label
89039 MAXCOST: 100,
89040 // number of evenly spaced points to look at in the first
89041 // iteration of the search
89042 INITIALSEARCHPOINTS: 10,
89043 // number of binary search iterations after the initial wide search
89044 ITERATIONS: 5
89045 }
89046};
89047
89048},{}],420:[function(_dereq_,module,exports){
89049'use strict';
89050var isNumeric = _dereq_('fast-isnumeric');
89051
89052var handleLabelDefaults = _dereq_('./label_defaults');
89053
89054var Color = _dereq_('../../components/color');
89055var addOpacity = Color.addOpacity;
89056var opacity = Color.opacity;
89057
89058var filterOps = _dereq_('../../constants/filter_ops');
89059var CONSTRAINT_REDUCTION = filterOps.CONSTRAINT_REDUCTION;
89060var COMPARISON_OPS2 = filterOps.COMPARISON_OPS2;
89061
89062module.exports = function handleConstraintDefaults(traceIn, traceOut, coerce, layout, defaultColor, opts) {
89063 var contours = traceOut.contours;
89064 var showLines, lineColor, fillColor;
89065
89066 var operation = coerce('contours.operation');
89067 contours._operation = CONSTRAINT_REDUCTION[operation];
89068
89069 handleConstraintValueDefaults(coerce, contours);
89070
89071 if(operation === '=') {
89072 showLines = contours.showlines = true;
89073 } else {
89074 showLines = coerce('contours.showlines');
89075 fillColor = coerce('fillcolor', addOpacity(
89076 (traceIn.line || {}).color || defaultColor, 0.5
89077 ));
89078 }
89079
89080 if(showLines) {
89081 var lineDfltColor = fillColor && opacity(fillColor) ?
89082 addOpacity(traceOut.fillcolor, 1) :
89083 defaultColor;
89084 lineColor = coerce('line.color', lineDfltColor);
89085 coerce('line.width', 2);
89086 coerce('line.dash');
89087 }
89088
89089 coerce('line.smoothing');
89090
89091 handleLabelDefaults(coerce, layout, lineColor, opts);
89092};
89093
89094function handleConstraintValueDefaults(coerce, contours) {
89095 var zvalue;
89096
89097 if(COMPARISON_OPS2.indexOf(contours.operation) === -1) {
89098 // Requires an array of two numbers:
89099 coerce('contours.value', [0, 1]);
89100
89101 if(!Array.isArray(contours.value)) {
89102 if(isNumeric(contours.value)) {
89103 zvalue = parseFloat(contours.value);
89104 contours.value = [zvalue, zvalue + 1];
89105 }
89106 } else if(contours.value.length > 2) {
89107 contours.value = contours.value.slice(2);
89108 } else if(contours.length === 0) {
89109 contours.value = [0, 1];
89110 } else if(contours.length < 2) {
89111 zvalue = parseFloat(contours.value[0]);
89112 contours.value = [zvalue, zvalue + 1];
89113 } else {
89114 contours.value = [
89115 parseFloat(contours.value[0]),
89116 parseFloat(contours.value[1])
89117 ];
89118 }
89119 } else {
89120 // Requires a single scalar:
89121 coerce('contours.value', 0);
89122
89123 if(!isNumeric(contours.value)) {
89124 if(Array.isArray(contours.value)) {
89125 contours.value = parseFloat(contours.value[0]);
89126 } else {
89127 contours.value = 0;
89128 }
89129 }
89130 }
89131}
89132
89133},{"../../components/color":157,"../../constants/filter_ops":265,"./label_defaults":430,"fast-isnumeric":33}],421:[function(_dereq_,module,exports){
89134'use strict';
89135
89136var filterOps = _dereq_('../../constants/filter_ops');
89137var isNumeric = _dereq_('fast-isnumeric');
89138
89139// This syntax conforms to the existing filter transform syntax, but we don't care
89140// about open vs. closed intervals for simply drawing contours constraints:
89141module.exports = {
89142 '[]': makeRangeSettings('[]'),
89143 '][': makeRangeSettings(']['),
89144 '>': makeInequalitySettings('>'),
89145 '<': makeInequalitySettings('<'),
89146 '=': makeInequalitySettings('=')
89147};
89148
89149// This does not in any way shape or form support calendars. It's adapted from
89150// transforms/filter.js.
89151function coerceValue(operation, value) {
89152 var hasArrayValue = Array.isArray(value);
89153
89154 var coercedValue;
89155
89156 function coerce(value) {
89157 return isNumeric(value) ? (+value) : null;
89158 }
89159
89160 if(filterOps.COMPARISON_OPS2.indexOf(operation) !== -1) {
89161 coercedValue = hasArrayValue ? coerce(value[0]) : coerce(value);
89162 } else if(filterOps.INTERVAL_OPS.indexOf(operation) !== -1) {
89163 coercedValue = hasArrayValue ?
89164 [coerce(value[0]), coerce(value[1])] :
89165 [coerce(value), coerce(value)];
89166 } else if(filterOps.SET_OPS.indexOf(operation) !== -1) {
89167 coercedValue = hasArrayValue ? value.map(coerce) : [coerce(value)];
89168 }
89169
89170 return coercedValue;
89171}
89172
89173// Returns a parabola scaled so that the min/max is either +/- 1 and zero at the two values
89174// provided. The data is mapped by this function when constructing intervals so that it's
89175// very easy to construct contours as normal.
89176function makeRangeSettings(operation) {
89177 return function(value) {
89178 value = coerceValue(operation, value);
89179
89180 // Ensure proper ordering:
89181 var min = Math.min(value[0], value[1]);
89182 var max = Math.max(value[0], value[1]);
89183
89184 return {
89185 start: min,
89186 end: max,
89187 size: max - min
89188 };
89189 };
89190}
89191
89192function makeInequalitySettings(operation) {
89193 return function(value) {
89194 value = coerceValue(operation, value);
89195
89196 return {
89197 start: value,
89198 end: Infinity,
89199 size: Infinity
89200 };
89201 };
89202}
89203
89204},{"../../constants/filter_ops":265,"fast-isnumeric":33}],422:[function(_dereq_,module,exports){
89205'use strict';
89206
89207module.exports = function handleContourDefaults(traceIn, traceOut, coerce, coerce2) {
89208 var contourStart = coerce2('contours.start');
89209 var contourEnd = coerce2('contours.end');
89210 var missingEnd = (contourStart === false) || (contourEnd === false);
89211
89212 // normally we only need size if autocontour is off. But contour.calc
89213 // pushes its calculated contour size back to the input trace, so for
89214 // things like restyle that can call supplyDefaults without calc
89215 // after the initial draw, we can just reuse the previous calculation
89216 var contourSize = coerce('contours.size');
89217 var autoContour;
89218
89219 if(missingEnd) autoContour = traceOut.autocontour = true;
89220 else autoContour = coerce('autocontour', false);
89221
89222 if(autoContour || !contourSize) coerce('ncontours');
89223};
89224
89225},{}],423:[function(_dereq_,module,exports){
89226'use strict';
89227
89228var Lib = _dereq_('../../lib');
89229
89230// The contour extraction is great, except it totally fails for constraints because we
89231// need weird range loops and flipped contours instead of the usual format. This function
89232// does some weird manipulation of the extracted pathinfo data such that it magically
89233// draws contours correctly *as* constraints.
89234//
89235// ** I do not know which "weird range loops" the comment above is referring to.
89236module.exports = function(pathinfo, operation) {
89237 var i, pi0, pi1;
89238
89239 var op0 = function(arr) { return arr.reverse(); };
89240 var op1 = function(arr) { return arr; };
89241
89242 switch(operation) {
89243 case '=':
89244 case '<':
89245 return pathinfo;
89246 case '>':
89247 if(pathinfo.length !== 1) {
89248 Lib.warn('Contour data invalid for the specified inequality operation.');
89249 }
89250
89251 // In this case there should be exactly one contour levels in pathinfo.
89252 // We flip all of the data. This will draw the contour as closed.
89253 pi0 = pathinfo[0];
89254
89255 for(i = 0; i < pi0.edgepaths.length; i++) {
89256 pi0.edgepaths[i] = op0(pi0.edgepaths[i]);
89257 }
89258 for(i = 0; i < pi0.paths.length; i++) {
89259 pi0.paths[i] = op0(pi0.paths[i]);
89260 }
89261 for(i = 0; i < pi0.starts.length; i++) {
89262 pi0.starts[i] = op0(pi0.starts[i]);
89263 }
89264
89265 return pathinfo;
89266 case '][':
89267 var tmp = op0;
89268 op0 = op1;
89269 op1 = tmp;
89270 // It's a nice rule, except this definitely *is* what's intended here.
89271 /* eslint-disable: no-fallthrough */
89272 case '[]':
89273 /* eslint-enable: no-fallthrough */
89274 if(pathinfo.length !== 2) {
89275 Lib.warn('Contour data invalid for the specified inequality range operation.');
89276 }
89277
89278 // In this case there should be exactly two contour levels in pathinfo.
89279 // - We concatenate the info into one pathinfo.
89280 // - We must also flip all of the data in the `[]` case.
89281 // This will draw the contours as closed.
89282 pi0 = copyPathinfo(pathinfo[0]);
89283 pi1 = copyPathinfo(pathinfo[1]);
89284
89285 for(i = 0; i < pi0.edgepaths.length; i++) {
89286 pi0.edgepaths[i] = op0(pi0.edgepaths[i]);
89287 }
89288 for(i = 0; i < pi0.paths.length; i++) {
89289 pi0.paths[i] = op0(pi0.paths[i]);
89290 }
89291 for(i = 0; i < pi0.starts.length; i++) {
89292 pi0.starts[i] = op0(pi0.starts[i]);
89293 }
89294
89295 while(pi1.edgepaths.length) {
89296 pi0.edgepaths.push(op1(pi1.edgepaths.shift()));
89297 }
89298 while(pi1.paths.length) {
89299 pi0.paths.push(op1(pi1.paths.shift()));
89300 }
89301 while(pi1.starts.length) {
89302 pi0.starts.push(op1(pi1.starts.shift()));
89303 }
89304
89305 return [pi0];
89306 }
89307};
89308
89309function copyPathinfo(pi) {
89310 return Lib.extendFlat({}, pi, {
89311 edgepaths: Lib.extendDeep([], pi.edgepaths),
89312 paths: Lib.extendDeep([], pi.paths),
89313 starts: Lib.extendDeep([], pi.starts)
89314 });
89315}
89316
89317},{"../../lib":287}],424:[function(_dereq_,module,exports){
89318'use strict';
89319
89320var Lib = _dereq_('../../lib');
89321
89322var handleXYZDefaults = _dereq_('../heatmap/xyz_defaults');
89323var handlePeriodDefaults = _dereq_('../scatter/period_defaults');
89324var handleConstraintDefaults = _dereq_('./constraint_defaults');
89325var handleContoursDefaults = _dereq_('./contours_defaults');
89326var handleStyleDefaults = _dereq_('./style_defaults');
89327var attributes = _dereq_('./attributes');
89328
89329
89330module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
89331 function coerce(attr, dflt) {
89332 return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
89333 }
89334
89335 function coerce2(attr) {
89336 return Lib.coerce2(traceIn, traceOut, attributes, attr);
89337 }
89338
89339 var len = handleXYZDefaults(traceIn, traceOut, coerce, layout);
89340 if(!len) {
89341 traceOut.visible = false;
89342 return;
89343 }
89344
89345 handlePeriodDefaults(traceIn, traceOut, layout, coerce);
89346 coerce('xhoverformat');
89347 coerce('yhoverformat');
89348
89349 coerce('text');
89350 coerce('hovertext');
89351 coerce('hovertemplate');
89352 coerce('hoverongaps');
89353
89354 var isConstraint = (coerce('contours.type') === 'constraint');
89355 coerce('connectgaps', Lib.isArray1D(traceOut.z));
89356
89357 if(isConstraint) {
89358 handleConstraintDefaults(traceIn, traceOut, coerce, layout, defaultColor);
89359 } else {
89360 handleContoursDefaults(traceIn, traceOut, coerce, coerce2);
89361 handleStyleDefaults(traceIn, traceOut, coerce, layout);
89362 }
89363};
89364
89365},{"../../lib":287,"../heatmap/xyz_defaults":451,"../scatter/period_defaults":517,"./attributes":415,"./constraint_defaults":420,"./contours_defaults":422,"./style_defaults":436}],425:[function(_dereq_,module,exports){
89366'use strict';
89367
89368var Lib = _dereq_('../../lib');
89369var constraintMapping = _dereq_('./constraint_mapping');
89370var endPlus = _dereq_('./end_plus');
89371
89372module.exports = function emptyPathinfo(contours, plotinfo, cd0) {
89373 var contoursFinal = (contours.type === 'constraint') ?
89374 constraintMapping[contours._operation](contours.value) :
89375 contours;
89376
89377 var cs = contoursFinal.size;
89378 var pathinfo = [];
89379 var end = endPlus(contoursFinal);
89380
89381 var carpet = cd0.trace._carpetTrace;
89382
89383 var basePathinfo = carpet ? {
89384 // store axes so we can convert to px
89385 xaxis: carpet.aaxis,
89386 yaxis: carpet.baxis,
89387 // full data arrays to use for interpolation
89388 x: cd0.a,
89389 y: cd0.b
89390 } : {
89391 xaxis: plotinfo.xaxis,
89392 yaxis: plotinfo.yaxis,
89393 x: cd0.x,
89394 y: cd0.y
89395 };
89396
89397 for(var ci = contoursFinal.start; ci < end; ci += cs) {
89398 pathinfo.push(Lib.extendFlat({
89399 level: ci,
89400 // all the cells with nontrivial marching index
89401 crossings: {},
89402 // starting points on the edges of the lattice for each contour
89403 starts: [],
89404 // all unclosed paths (may have less items than starts,
89405 // if a path is closed by rounding)
89406 edgepaths: [],
89407 // all closed paths
89408 paths: [],
89409 z: cd0.z,
89410 smoothing: cd0.trace.line.smoothing
89411 }, basePathinfo));
89412
89413 if(pathinfo.length > 1000) {
89414 Lib.warn('Too many contours, clipping at 1000', contours);
89415 break;
89416 }
89417 }
89418 return pathinfo;
89419};
89420
89421},{"../../lib":287,"./constraint_mapping":421,"./end_plus":426}],426:[function(_dereq_,module,exports){
89422'use strict';
89423
89424/*
89425 * tiny helper to move the end of the contours a little to prevent
89426 * losing the last contour to rounding errors
89427 */
89428module.exports = function endPlus(contours) {
89429 return contours.end + contours.size / 1e6;
89430};
89431
89432},{}],427:[function(_dereq_,module,exports){
89433'use strict';
89434
89435var Lib = _dereq_('../../lib');
89436var constants = _dereq_('./constants');
89437
89438module.exports = function findAllPaths(pathinfo, xtol, ytol) {
89439 var cnt,
89440 startLoc,
89441 i,
89442 pi,
89443 j;
89444
89445 // Default just passes these values through as they were before:
89446 xtol = xtol || 0.01;
89447 ytol = ytol || 0.01;
89448
89449 for(i = 0; i < pathinfo.length; i++) {
89450 pi = pathinfo[i];
89451
89452 for(j = 0; j < pi.starts.length; j++) {
89453 startLoc = pi.starts[j];
89454 makePath(pi, startLoc, 'edge', xtol, ytol);
89455 }
89456
89457 cnt = 0;
89458 while(Object.keys(pi.crossings).length && cnt < 10000) {
89459 cnt++;
89460 startLoc = Object.keys(pi.crossings)[0].split(',').map(Number);
89461 makePath(pi, startLoc, undefined, xtol, ytol);
89462 }
89463 if(cnt === 10000) Lib.log('Infinite loop in contour?');
89464 }
89465};
89466
89467function equalPts(pt1, pt2, xtol, ytol) {
89468 return Math.abs(pt1[0] - pt2[0]) < xtol &&
89469 Math.abs(pt1[1] - pt2[1]) < ytol;
89470}
89471
89472// distance in index units - uses the 3rd and 4th items in points
89473function ptDist(pt1, pt2) {
89474 var dx = pt1[2] - pt2[2];
89475 var dy = pt1[3] - pt2[3];
89476 return Math.sqrt(dx * dx + dy * dy);
89477}
89478
89479function makePath(pi, loc, edgeflag, xtol, ytol) {
89480 var locStr = loc.join(',');
89481 var mi = pi.crossings[locStr];
89482 var marchStep = getStartStep(mi, edgeflag, loc);
89483 // start by going backward a half step and finding the crossing point
89484 var pts = [getInterpPx(pi, loc, [-marchStep[0], -marchStep[1]])];
89485 var m = pi.z.length;
89486 var n = pi.z[0].length;
89487 var startLoc = loc.slice();
89488 var startStep = marchStep.slice();
89489 var cnt;
89490
89491 // now follow the path
89492 for(cnt = 0; cnt < 10000; cnt++) { // just to avoid infinite loops
89493 if(mi > 20) {
89494 mi = constants.CHOOSESADDLE[mi][(marchStep[0] || marchStep[1]) < 0 ? 0 : 1];
89495 pi.crossings[locStr] = constants.SADDLEREMAINDER[mi];
89496 } else {
89497 delete pi.crossings[locStr];
89498 }
89499
89500 marchStep = constants.NEWDELTA[mi];
89501 if(!marchStep) {
89502 Lib.log('Found bad marching index:', mi, loc, pi.level);
89503 break;
89504 }
89505
89506 // find the crossing a half step forward, and then take the full step
89507 pts.push(getInterpPx(pi, loc, marchStep));
89508 loc[0] += marchStep[0];
89509 loc[1] += marchStep[1];
89510 locStr = loc.join(',');
89511
89512 // don't include the same point multiple times
89513 if(equalPts(pts[pts.length - 1], pts[pts.length - 2], xtol, ytol)) pts.pop();
89514
89515 var atEdge = (marchStep[0] && (loc[0] < 0 || loc[0] > n - 2)) ||
89516 (marchStep[1] && (loc[1] < 0 || loc[1] > m - 2));
89517
89518 var closedLoop = loc[0] === startLoc[0] && loc[1] === startLoc[1] &&
89519 marchStep[0] === startStep[0] && marchStep[1] === startStep[1];
89520
89521 // have we completed a loop, or reached an edge?
89522 if((closedLoop) || (edgeflag && atEdge)) break;
89523
89524 mi = pi.crossings[locStr];
89525 }
89526
89527 if(cnt === 10000) {
89528 Lib.log('Infinite loop in contour?');
89529 }
89530 var closedpath = equalPts(pts[0], pts[pts.length - 1], xtol, ytol);
89531 var totaldist = 0;
89532 var distThresholdFactor = 0.2 * pi.smoothing;
89533 var alldists = [];
89534 var cropstart = 0;
89535 var distgroup, cnt2, cnt3, newpt, ptcnt, ptavg, thisdist,
89536 i, j, edgepathi, edgepathj;
89537
89538 /*
89539 * Check for points that are too close together (<1/5 the average dist
89540 * *in grid index units* (important for log axes and nonuniform grids),
89541 * less if less smoothed) and just take the center (or avg of center 2).
89542 * This cuts down on funny behavior when a point is very close to a
89543 * contour level.
89544 */
89545 for(cnt = 1; cnt < pts.length; cnt++) {
89546 thisdist = ptDist(pts[cnt], pts[cnt - 1]);
89547 totaldist += thisdist;
89548 alldists.push(thisdist);
89549 }
89550
89551 var distThreshold = totaldist / alldists.length * distThresholdFactor;
89552
89553 function getpt(i) { return pts[i % pts.length]; }
89554
89555 for(cnt = pts.length - 2; cnt >= cropstart; cnt--) {
89556 distgroup = alldists[cnt];
89557 if(distgroup < distThreshold) {
89558 cnt3 = 0;
89559 for(cnt2 = cnt - 1; cnt2 >= cropstart; cnt2--) {
89560 if(distgroup + alldists[cnt2] < distThreshold) {
89561 distgroup += alldists[cnt2];
89562 } else break;
89563 }
89564
89565 // closed path with close points wrapping around the boundary?
89566 if(closedpath && cnt === pts.length - 2) {
89567 for(cnt3 = 0; cnt3 < cnt2; cnt3++) {
89568 if(distgroup + alldists[cnt3] < distThreshold) {
89569 distgroup += alldists[cnt3];
89570 } else break;
89571 }
89572 }
89573 ptcnt = cnt - cnt2 + cnt3 + 1;
89574 ptavg = Math.floor((cnt + cnt2 + cnt3 + 2) / 2);
89575
89576 // either endpoint included: keep the endpoint
89577 if(!closedpath && cnt === pts.length - 2) newpt = pts[pts.length - 1];
89578 else if(!closedpath && cnt2 === -1) newpt = pts[0];
89579
89580 // odd # of points - just take the central one
89581 else if(ptcnt % 2) newpt = getpt(ptavg);
89582
89583 // even # of pts - average central two
89584 else {
89585 newpt = [(getpt(ptavg)[0] + getpt(ptavg + 1)[0]) / 2,
89586 (getpt(ptavg)[1] + getpt(ptavg + 1)[1]) / 2];
89587 }
89588
89589 pts.splice(cnt2 + 1, cnt - cnt2 + 1, newpt);
89590 cnt = cnt2 + 1;
89591 if(cnt3) cropstart = cnt3;
89592 if(closedpath) {
89593 if(cnt === pts.length - 2) pts[cnt3] = pts[pts.length - 1];
89594 else if(cnt === 0) pts[pts.length - 1] = pts[0];
89595 }
89596 }
89597 }
89598 pts.splice(0, cropstart);
89599
89600 // done with the index parts - remove them so path generation works right
89601 // because it depends on only having [xpx, ypx]
89602 for(cnt = 0; cnt < pts.length; cnt++) pts[cnt].length = 2;
89603
89604 // don't return single-point paths (ie all points were the same
89605 // so they got deleted?)
89606 if(pts.length < 2) return;
89607 else if(closedpath) {
89608 pts.pop();
89609 pi.paths.push(pts);
89610 } else {
89611 if(!edgeflag) {
89612 Lib.log('Unclosed interior contour?',
89613 pi.level, startLoc.join(','), pts.join('L'));
89614 }
89615
89616 // edge path - does it start where an existing edge path ends, or vice versa?
89617 var merged = false;
89618 for(i = 0; i < pi.edgepaths.length; i++) {
89619 edgepathi = pi.edgepaths[i];
89620 if(!merged && equalPts(edgepathi[0], pts[pts.length - 1], xtol, ytol)) {
89621 pts.pop();
89622 merged = true;
89623
89624 // now does it ALSO meet the end of another (or the same) path?
89625 var doublemerged = false;
89626 for(j = 0; j < pi.edgepaths.length; j++) {
89627 edgepathj = pi.edgepaths[j];
89628 if(equalPts(edgepathj[edgepathj.length - 1], pts[0], xtol, ytol)) {
89629 doublemerged = true;
89630 pts.shift();
89631 pi.edgepaths.splice(i, 1);
89632 if(j === i) {
89633 // the path is now closed
89634 pi.paths.push(pts.concat(edgepathj));
89635 } else {
89636 if(j > i) j--;
89637 pi.edgepaths[j] = edgepathj.concat(pts, edgepathi);
89638 }
89639 break;
89640 }
89641 }
89642 if(!doublemerged) {
89643 pi.edgepaths[i] = pts.concat(edgepathi);
89644 }
89645 }
89646 }
89647 for(i = 0; i < pi.edgepaths.length; i++) {
89648 if(merged) break;
89649 edgepathi = pi.edgepaths[i];
89650 if(equalPts(edgepathi[edgepathi.length - 1], pts[0], xtol, ytol)) {
89651 pts.shift();
89652 pi.edgepaths[i] = edgepathi.concat(pts);
89653 merged = true;
89654 }
89655 }
89656
89657 if(!merged) pi.edgepaths.push(pts);
89658 }
89659}
89660
89661// special function to get the marching step of the
89662// first point in the path (leading to loc)
89663function getStartStep(mi, edgeflag, loc) {
89664 var dx = 0;
89665 var dy = 0;
89666 if(mi > 20 && edgeflag) {
89667 // these saddles start at +/- x
89668 if(mi === 208 || mi === 1114) {
89669 // if we're starting at the left side, we must be going right
89670 dx = loc[0] === 0 ? 1 : -1;
89671 } else {
89672 // if we're starting at the bottom, we must be going up
89673 dy = loc[1] === 0 ? 1 : -1;
89674 }
89675 } else if(constants.BOTTOMSTART.indexOf(mi) !== -1) dy = 1;
89676 else if(constants.LEFTSTART.indexOf(mi) !== -1) dx = 1;
89677 else if(constants.TOPSTART.indexOf(mi) !== -1) dy = -1;
89678 else dx = -1;
89679 return [dx, dy];
89680}
89681
89682/*
89683 * Find the pixel coordinates of a particular crossing
89684 *
89685 * @param {object} pi: the pathinfo object at this level
89686 * @param {array} loc: the grid index [x, y] of the crossing
89687 * @param {array} step: the direction [dx, dy] we're moving on the grid
89688 *
89689 * @return {array} [xpx, ypx, xi, yi]: the first two are the pixel location,
89690 * the next two are the interpolated grid indices, which we use for
89691 * distance calculations to delete points that are too close together.
89692 * This is important when the grid is nonuniform (and most dramatically when
89693 * we're on log axes and include invalid (0 or negative) values.
89694 * It's crucial to delete these extra two before turning an array of these
89695 * points into a path, because those routines require length-2 points.
89696 */
89697function getInterpPx(pi, loc, step) {
89698 var locx = loc[0] + Math.max(step[0], 0);
89699 var locy = loc[1] + Math.max(step[1], 0);
89700 var zxy = pi.z[locy][locx];
89701 var xa = pi.xaxis;
89702 var ya = pi.yaxis;
89703
89704 if(step[1]) {
89705 var dx = (pi.level - zxy) / (pi.z[locy][locx + 1] - zxy);
89706
89707 return [xa.c2p((1 - dx) * pi.x[locx] + dx * pi.x[locx + 1], true),
89708 ya.c2p(pi.y[locy], true),
89709 locx + dx, locy];
89710 } else {
89711 var dy = (pi.level - zxy) / (pi.z[locy + 1][locx] - zxy);
89712 return [xa.c2p(pi.x[locx], true),
89713 ya.c2p((1 - dy) * pi.y[locy] + dy * pi.y[locy + 1], true),
89714 locx, locy + dy];
89715 }
89716}
89717
89718},{"../../lib":287,"./constants":419}],428:[function(_dereq_,module,exports){
89719'use strict';
89720
89721var Color = _dereq_('../../components/color');
89722
89723var heatmapHoverPoints = _dereq_('../heatmap/hover');
89724
89725module.exports = function hoverPoints(pointData, xval, yval, hovermode, opts) {
89726 if(!opts) opts = {};
89727 opts.isContour = true;
89728
89729 var hoverData = heatmapHoverPoints(pointData, xval, yval, hovermode, opts);
89730
89731 if(hoverData) {
89732 hoverData.forEach(function(hoverPt) {
89733 var trace = hoverPt.trace;
89734 if(trace.contours.type === 'constraint') {
89735 if(trace.fillcolor && Color.opacity(trace.fillcolor)) {
89736 hoverPt.color = Color.addOpacity(trace.fillcolor, 1);
89737 } else if(trace.contours.showlines && Color.opacity(trace.line.color)) {
89738 hoverPt.color = Color.addOpacity(trace.line.color, 1);
89739 }
89740 }
89741 });
89742 }
89743
89744 return hoverData;
89745};
89746
89747},{"../../components/color":157,"../heatmap/hover":444}],429:[function(_dereq_,module,exports){
89748'use strict';
89749
89750module.exports = {
89751 attributes: _dereq_('./attributes'),
89752 supplyDefaults: _dereq_('./defaults'),
89753 calc: _dereq_('./calc'),
89754 plot: _dereq_('./plot').plot,
89755 style: _dereq_('./style'),
89756 colorbar: _dereq_('./colorbar'),
89757 hoverPoints: _dereq_('./hover'),
89758
89759 moduleType: 'trace',
89760 name: 'contour',
89761 basePlotModule: _dereq_('../../plots/cartesian'),
89762 categories: ['cartesian', 'svg', '2dMap', 'contour', 'showLegend'],
89763 meta: {
89764 }
89765};
89766
89767},{"../../plots/cartesian":348,"./attributes":415,"./calc":416,"./colorbar":418,"./defaults":424,"./hover":428,"./plot":433,"./style":435}],430:[function(_dereq_,module,exports){
89768'use strict';
89769
89770var Lib = _dereq_('../../lib');
89771
89772module.exports = function handleLabelDefaults(coerce, layout, lineColor, opts) {
89773 if(!opts) opts = {};
89774 var showLabels = coerce('contours.showlabels');
89775 if(showLabels) {
89776 var globalFont = layout.font;
89777 Lib.coerceFont(coerce, 'contours.labelfont', {
89778 family: globalFont.family,
89779 size: globalFont.size,
89780 color: lineColor
89781 });
89782 coerce('contours.labelformat');
89783 }
89784
89785 if(opts.hasHover !== false) coerce('zhoverformat');
89786};
89787
89788},{"../../lib":287}],431:[function(_dereq_,module,exports){
89789'use strict';
89790
89791var d3 = _dereq_('@plotly/d3');
89792
89793var Colorscale = _dereq_('../../components/colorscale');
89794var endPlus = _dereq_('./end_plus');
89795
89796module.exports = function makeColorMap(trace) {
89797 var contours = trace.contours;
89798 var start = contours.start;
89799 var end = endPlus(contours);
89800 var cs = contours.size || 1;
89801 var nc = Math.floor((end - start) / cs) + 1;
89802 var extra = contours.coloring === 'lines' ? 0 : 1;
89803 var cOpts = Colorscale.extractOpts(trace);
89804
89805 if(!isFinite(cs)) {
89806 cs = 1;
89807 nc = 1;
89808 }
89809
89810 var scl = cOpts.reversescale ?
89811 Colorscale.flipScale(cOpts.colorscale) :
89812 cOpts.colorscale;
89813
89814 var len = scl.length;
89815 var domain = new Array(len);
89816 var range = new Array(len);
89817
89818 var si, i;
89819
89820 if(contours.coloring === 'heatmap') {
89821 var zmin0 = cOpts.min;
89822 var zmax0 = cOpts.max;
89823
89824 for(i = 0; i < len; i++) {
89825 si = scl[i];
89826 domain[i] = si[0] * (zmax0 - zmin0) + zmin0;
89827 range[i] = si[1];
89828 }
89829
89830 // do the contours extend beyond the colorscale?
89831 // if so, extend the colorscale with constants
89832 var zRange = d3.extent([
89833 zmin0,
89834 zmax0,
89835 contours.start,
89836 contours.start + cs * (nc - 1)
89837 ]);
89838 var zmin = zRange[zmin0 < zmax0 ? 0 : 1];
89839 var zmax = zRange[zmin0 < zmax0 ? 1 : 0];
89840
89841 if(zmin !== zmin0) {
89842 domain.splice(0, 0, zmin);
89843 range.splice(0, 0, range[0]);
89844 }
89845
89846 if(zmax !== zmax0) {
89847 domain.push(zmax);
89848 range.push(range[range.length - 1]);
89849 }
89850 } else {
89851 for(i = 0; i < len; i++) {
89852 si = scl[i];
89853 domain[i] = (si[0] * (nc + extra - 1) - (extra / 2)) * cs + start;
89854 range[i] = si[1];
89855 }
89856 }
89857
89858 return Colorscale.makeColorScaleFunc(
89859 {domain: domain, range: range},
89860 {noNumericCheck: true}
89861 );
89862};
89863
89864},{"../../components/colorscale":169,"./end_plus":426,"@plotly/d3":20}],432:[function(_dereq_,module,exports){
89865'use strict';
89866
89867var constants = _dereq_('./constants');
89868
89869// Calculate all the marching indices, for ALL levels at once.
89870// since we want to be exhaustive we'll check for contour crossings
89871// at every intersection, rather than just following a path
89872// TODO: shorten the inner loop to only the relevant levels
89873module.exports = function makeCrossings(pathinfo) {
89874 var z = pathinfo[0].z;
89875 var m = z.length;
89876 var n = z[0].length; // we already made sure z isn't ragged in interp2d
89877 var twoWide = m === 2 || n === 2;
89878 var xi;
89879 var yi;
89880 var startIndices;
89881 var ystartIndices;
89882 var label;
89883 var corners;
89884 var mi;
89885 var pi;
89886 var i;
89887
89888 for(yi = 0; yi < m - 1; yi++) {
89889 ystartIndices = [];
89890 if(yi === 0) ystartIndices = ystartIndices.concat(constants.BOTTOMSTART);
89891 if(yi === m - 2) ystartIndices = ystartIndices.concat(constants.TOPSTART);
89892
89893 for(xi = 0; xi < n - 1; xi++) {
89894 startIndices = ystartIndices.slice();
89895 if(xi === 0) startIndices = startIndices.concat(constants.LEFTSTART);
89896 if(xi === n - 2) startIndices = startIndices.concat(constants.RIGHTSTART);
89897
89898 label = xi + ',' + yi;
89899 corners = [[z[yi][xi], z[yi][xi + 1]],
89900 [z[yi + 1][xi], z[yi + 1][xi + 1]]];
89901 for(i = 0; i < pathinfo.length; i++) {
89902 pi = pathinfo[i];
89903 mi = getMarchingIndex(pi.level, corners);
89904 if(!mi) continue;
89905
89906 pi.crossings[label] = mi;
89907 if(startIndices.indexOf(mi) !== -1) {
89908 pi.starts.push([xi, yi]);
89909 if(twoWide && startIndices.indexOf(mi,
89910 startIndices.indexOf(mi) + 1) !== -1) {
89911 // the same square has starts from opposite sides
89912 // it's not possible to have starts on opposite edges
89913 // of a corner, only a start and an end...
89914 // but if the array is only two points wide (either way)
89915 // you can have starts on opposite sides.
89916 pi.starts.push([xi, yi]);
89917 }
89918 }
89919 }
89920 }
89921 }
89922};
89923
89924// modified marching squares algorithm,
89925// so we disambiguate the saddle points from the start
89926// and we ignore the cases with no crossings
89927// the index I'm using is based on:
89928// http://en.wikipedia.org/wiki/Marching_squares
89929// except that the saddles bifurcate and I represent them
89930// as the decimal combination of the two appropriate
89931// non-saddle indices
89932function getMarchingIndex(val, corners) {
89933 var mi = (corners[0][0] > val ? 0 : 1) +
89934 (corners[0][1] > val ? 0 : 2) +
89935 (corners[1][1] > val ? 0 : 4) +
89936 (corners[1][0] > val ? 0 : 8);
89937 if(mi === 5 || mi === 10) {
89938 var avg = (corners[0][0] + corners[0][1] +
89939 corners[1][0] + corners[1][1]) / 4;
89940 // two peaks with a big valley
89941 if(val > avg) return (mi === 5) ? 713 : 1114;
89942 // two valleys with a big ridge
89943 return (mi === 5) ? 104 : 208;
89944 }
89945 return (mi === 15) ? 0 : mi;
89946}
89947
89948},{"./constants":419}],433:[function(_dereq_,module,exports){
89949'use strict';
89950
89951var d3 = _dereq_('@plotly/d3');
89952
89953var Lib = _dereq_('../../lib');
89954var Drawing = _dereq_('../../components/drawing');
89955var Colorscale = _dereq_('../../components/colorscale');
89956var svgTextUtils = _dereq_('../../lib/svg_text_utils');
89957var Axes = _dereq_('../../plots/cartesian/axes');
89958var setConvert = _dereq_('../../plots/cartesian/set_convert');
89959
89960var heatmapPlot = _dereq_('../heatmap/plot');
89961var makeCrossings = _dereq_('./make_crossings');
89962var findAllPaths = _dereq_('./find_all_paths');
89963var emptyPathinfo = _dereq_('./empty_pathinfo');
89964var convertToConstraints = _dereq_('./convert_to_constraints');
89965var closeBoundaries = _dereq_('./close_boundaries');
89966var constants = _dereq_('./constants');
89967var costConstants = constants.LABELOPTIMIZER;
89968
89969exports.plot = function plot(gd, plotinfo, cdcontours, contourLayer) {
89970 var xa = plotinfo.xaxis;
89971 var ya = plotinfo.yaxis;
89972
89973 Lib.makeTraceGroups(contourLayer, cdcontours, 'contour').each(function(cd) {
89974 var plotGroup = d3.select(this);
89975 var cd0 = cd[0];
89976 var trace = cd0.trace;
89977 var x = cd0.x;
89978 var y = cd0.y;
89979 var contours = trace.contours;
89980 var pathinfo = emptyPathinfo(contours, plotinfo, cd0);
89981
89982 // use a heatmap to fill - draw it behind the lines
89983 var heatmapColoringLayer = Lib.ensureSingle(plotGroup, 'g', 'heatmapcoloring');
89984 var cdheatmaps = [];
89985 if(contours.coloring === 'heatmap') {
89986 cdheatmaps = [cd];
89987 }
89988 heatmapPlot(gd, plotinfo, cdheatmaps, heatmapColoringLayer);
89989
89990 makeCrossings(pathinfo);
89991 findAllPaths(pathinfo);
89992
89993 var leftedge = xa.c2p(x[0], true);
89994 var rightedge = xa.c2p(x[x.length - 1], true);
89995 var bottomedge = ya.c2p(y[0], true);
89996 var topedge = ya.c2p(y[y.length - 1], true);
89997 var perimeter = [
89998 [leftedge, topedge],
89999 [rightedge, topedge],
90000 [rightedge, bottomedge],
90001 [leftedge, bottomedge]
90002 ];
90003
90004 var fillPathinfo = pathinfo;
90005 if(contours.type === 'constraint') {
90006 // N.B. this also mutates pathinfo
90007 fillPathinfo = convertToConstraints(pathinfo, contours._operation);
90008 }
90009
90010 // draw everything
90011 makeBackground(plotGroup, perimeter, contours);
90012 makeFills(plotGroup, fillPathinfo, perimeter, contours);
90013 makeLinesAndLabels(plotGroup, pathinfo, gd, cd0, contours);
90014 clipGaps(plotGroup, plotinfo, gd, cd0, perimeter);
90015 });
90016};
90017
90018function makeBackground(plotgroup, perimeter, contours) {
90019 var bggroup = Lib.ensureSingle(plotgroup, 'g', 'contourbg');
90020
90021 var bgfill = bggroup.selectAll('path')
90022 .data(contours.coloring === 'fill' ? [0] : []);
90023 bgfill.enter().append('path');
90024 bgfill.exit().remove();
90025 bgfill
90026 .attr('d', 'M' + perimeter.join('L') + 'Z')
90027 .style('stroke', 'none');
90028}
90029
90030function makeFills(plotgroup, pathinfo, perimeter, contours) {
90031 var hasFills = contours.coloring === 'fill' || (contours.type === 'constraint' && contours._operation !== '=');
90032 var boundaryPath = 'M' + perimeter.join('L') + 'Z';
90033
90034 // fills prefixBoundary in pathinfo items
90035 if(hasFills) {
90036 closeBoundaries(pathinfo, contours);
90037 }
90038
90039 var fillgroup = Lib.ensureSingle(plotgroup, 'g', 'contourfill');
90040
90041 var fillitems = fillgroup.selectAll('path').data(hasFills ? pathinfo : []);
90042 fillitems.enter().append('path');
90043 fillitems.exit().remove();
90044 fillitems.each(function(pi) {
90045 // join all paths for this level together into a single path
90046 // first follow clockwise around the perimeter to close any open paths
90047 // if the whole perimeter is above this level, start with a path
90048 // enclosing the whole thing. With all that, the parity should mean
90049 // that we always fill everything above the contour, nothing below
90050 var fullpath = (pi.prefixBoundary ? boundaryPath : '') +
90051 joinAllPaths(pi, perimeter);
90052
90053 if(!fullpath) {
90054 d3.select(this).remove();
90055 } else {
90056 d3.select(this)
90057 .attr('d', fullpath)
90058 .style('stroke', 'none');
90059 }
90060 });
90061}
90062
90063function joinAllPaths(pi, perimeter) {
90064 var fullpath = '';
90065 var i = 0;
90066 var startsleft = pi.edgepaths.map(function(v, i) { return i; });
90067 var newloop = true;
90068 var endpt;
90069 var newendpt;
90070 var cnt;
90071 var nexti;
90072 var possiblei;
90073 var addpath;
90074
90075 function istop(pt) { return Math.abs(pt[1] - perimeter[0][1]) < 0.01; }
90076 function isbottom(pt) { return Math.abs(pt[1] - perimeter[2][1]) < 0.01; }
90077 function isleft(pt) { return Math.abs(pt[0] - perimeter[0][0]) < 0.01; }
90078 function isright(pt) { return Math.abs(pt[0] - perimeter[2][0]) < 0.01; }
90079
90080 while(startsleft.length) {
90081 addpath = Drawing.smoothopen(pi.edgepaths[i], pi.smoothing);
90082 fullpath += newloop ? addpath : addpath.replace(/^M/, 'L');
90083 startsleft.splice(startsleft.indexOf(i), 1);
90084 endpt = pi.edgepaths[i][pi.edgepaths[i].length - 1];
90085 nexti = -1;
90086
90087 // now loop through sides, moving our endpoint until we find a new start
90088 for(cnt = 0; cnt < 4; cnt++) { // just to prevent infinite loops
90089 if(!endpt) {
90090 Lib.log('Missing end?', i, pi);
90091 break;
90092 }
90093
90094 if(istop(endpt) && !isright(endpt)) newendpt = perimeter[1]; // right top
90095 else if(isleft(endpt)) newendpt = perimeter[0]; // left top
90096 else if(isbottom(endpt)) newendpt = perimeter[3]; // right bottom
90097 else if(isright(endpt)) newendpt = perimeter[2]; // left bottom
90098
90099 for(possiblei = 0; possiblei < pi.edgepaths.length; possiblei++) {
90100 var ptNew = pi.edgepaths[possiblei][0];
90101 // is ptNew on the (horz. or vert.) segment from endpt to newendpt?
90102 if(Math.abs(endpt[0] - newendpt[0]) < 0.01) {
90103 if(Math.abs(endpt[0] - ptNew[0]) < 0.01 &&
90104 (ptNew[1] - endpt[1]) * (newendpt[1] - ptNew[1]) >= 0) {
90105 newendpt = ptNew;
90106 nexti = possiblei;
90107 }
90108 } else if(Math.abs(endpt[1] - newendpt[1]) < 0.01) {
90109 if(Math.abs(endpt[1] - ptNew[1]) < 0.01 &&
90110 (ptNew[0] - endpt[0]) * (newendpt[0] - ptNew[0]) >= 0) {
90111 newendpt = ptNew;
90112 nexti = possiblei;
90113 }
90114 } else {
90115 Lib.log('endpt to newendpt is not vert. or horz.',
90116 endpt, newendpt, ptNew);
90117 }
90118 }
90119
90120 endpt = newendpt;
90121
90122 if(nexti >= 0) break;
90123 fullpath += 'L' + newendpt;
90124 }
90125
90126 if(nexti === pi.edgepaths.length) {
90127 Lib.log('unclosed perimeter path');
90128 break;
90129 }
90130
90131 i = nexti;
90132
90133 // if we closed back on a loop we already included,
90134 // close it and start a new loop
90135 newloop = (startsleft.indexOf(i) === -1);
90136 if(newloop) {
90137 i = startsleft[0];
90138 fullpath += 'Z';
90139 }
90140 }
90141
90142 // finally add the interior paths
90143 for(i = 0; i < pi.paths.length; i++) {
90144 fullpath += Drawing.smoothclosed(pi.paths[i], pi.smoothing);
90145 }
90146
90147 return fullpath;
90148}
90149
90150function makeLinesAndLabels(plotgroup, pathinfo, gd, cd0, contours) {
90151 var lineContainer = Lib.ensureSingle(plotgroup, 'g', 'contourlines');
90152 var showLines = contours.showlines !== false;
90153 var showLabels = contours.showlabels;
90154 var clipLinesForLabels = showLines && showLabels;
90155
90156 // Even if we're not going to show lines, we need to create them
90157 // if we're showing labels, because the fill paths include the perimeter
90158 // so can't be used to position the labels correctly.
90159 // In this case we'll remove the lines after making the labels.
90160 var linegroup = exports.createLines(lineContainer, showLines || showLabels, pathinfo);
90161
90162 var lineClip = exports.createLineClip(lineContainer, clipLinesForLabels, gd, cd0.trace.uid);
90163
90164 var labelGroup = plotgroup.selectAll('g.contourlabels')
90165 .data(showLabels ? [0] : []);
90166
90167 labelGroup.exit().remove();
90168
90169 labelGroup.enter().append('g')
90170 .classed('contourlabels', true);
90171
90172 if(showLabels) {
90173 var labelClipPathData = [];
90174 var labelData = [];
90175
90176 // invalidate the getTextLocation cache in case paths changed
90177 Lib.clearLocationCache();
90178
90179 var contourFormat = exports.labelFormatter(gd, cd0);
90180
90181 var dummyText = Drawing.tester.append('text')
90182 .attr('data-notex', 1)
90183 .call(Drawing.font, contours.labelfont);
90184
90185 var xa = pathinfo[0].xaxis;
90186 var ya = pathinfo[0].yaxis;
90187 var xLen = xa._length;
90188 var yLen = ya._length;
90189 var xRng = xa.range;
90190 var yRng = ya.range;
90191 var xMin = Lib.aggNums(Math.min, null, cd0.x);
90192 var xMax = Lib.aggNums(Math.max, null, cd0.x);
90193 var yMin = Lib.aggNums(Math.min, null, cd0.y);
90194 var yMax = Lib.aggNums(Math.max, null, cd0.y);
90195 var x0 = Math.max(xa.c2p(xMin, true), 0);
90196 var x1 = Math.min(xa.c2p(xMax, true), xLen);
90197 var y0 = Math.max(ya.c2p(yMax, true), 0);
90198 var y1 = Math.min(ya.c2p(yMin, true), yLen);
90199
90200 // visible bounds of the contour trace (and the midpoints, to
90201 // help with cost calculations)
90202 var bounds = {};
90203
90204 if(xRng[0] < xRng[1]) {
90205 bounds.left = x0;
90206 bounds.right = x1;
90207 } else {
90208 bounds.left = x1;
90209 bounds.right = x0;
90210 }
90211
90212 if(yRng[0] < yRng[1]) {
90213 bounds.top = y0;
90214 bounds.bottom = y1;
90215 } else {
90216 bounds.top = y1;
90217 bounds.bottom = y0;
90218 }
90219
90220 bounds.middle = (bounds.top + bounds.bottom) / 2;
90221 bounds.center = (bounds.left + bounds.right) / 2;
90222
90223 labelClipPathData.push([
90224 [bounds.left, bounds.top],
90225 [bounds.right, bounds.top],
90226 [bounds.right, bounds.bottom],
90227 [bounds.left, bounds.bottom]
90228 ]);
90229
90230 var plotDiagonal = Math.sqrt(xLen * xLen + yLen * yLen);
90231
90232 // the path length to use to scale the number of labels to draw:
90233 var normLength = constants.LABELDISTANCE * plotDiagonal /
90234 Math.max(1, pathinfo.length / constants.LABELINCREASE);
90235
90236 linegroup.each(function(d) {
90237 var textOpts = exports.calcTextOpts(d.level, contourFormat, dummyText, gd);
90238
90239 d3.select(this).selectAll('path').each(function() {
90240 var path = this;
90241 var pathBounds = Lib.getVisibleSegment(path, bounds, textOpts.height / 2);
90242 if(!pathBounds) return;
90243
90244 if(pathBounds.len < (textOpts.width + textOpts.height) * constants.LABELMIN) return;
90245
90246 var maxLabels = Math.min(Math.ceil(pathBounds.len / normLength),
90247 constants.LABELMAX);
90248
90249 for(var i = 0; i < maxLabels; i++) {
90250 var loc = exports.findBestTextLocation(path, pathBounds, textOpts,
90251 labelData, bounds);
90252
90253 if(!loc) break;
90254
90255 exports.addLabelData(loc, textOpts, labelData, labelClipPathData);
90256 }
90257 });
90258 });
90259
90260 dummyText.remove();
90261
90262 exports.drawLabels(labelGroup, labelData, gd, lineClip,
90263 clipLinesForLabels ? labelClipPathData : null);
90264 }
90265
90266 if(showLabels && !showLines) linegroup.remove();
90267}
90268
90269exports.createLines = function(lineContainer, makeLines, pathinfo) {
90270 var smoothing = pathinfo[0].smoothing;
90271
90272 var linegroup = lineContainer.selectAll('g.contourlevel')
90273 .data(makeLines ? pathinfo : []);
90274
90275 linegroup.exit().remove();
90276 linegroup.enter().append('g')
90277 .classed('contourlevel', true);
90278
90279 if(makeLines) {
90280 // pedgepaths / ppaths are used by contourcarpet, for the paths transformed from a/b to x/y
90281 // edgepaths / paths are used by contour since it's in x/y from the start
90282 var opencontourlines = linegroup.selectAll('path.openline')
90283 .data(function(d) { return d.pedgepaths || d.edgepaths; });
90284
90285 opencontourlines.exit().remove();
90286 opencontourlines.enter().append('path')
90287 .classed('openline', true);
90288
90289 opencontourlines
90290 .attr('d', function(d) {
90291 return Drawing.smoothopen(d, smoothing);
90292 })
90293 .style('stroke-miterlimit', 1)
90294 .style('vector-effect', 'non-scaling-stroke');
90295
90296 var closedcontourlines = linegroup.selectAll('path.closedline')
90297 .data(function(d) { return d.ppaths || d.paths; });
90298
90299 closedcontourlines.exit().remove();
90300 closedcontourlines.enter().append('path')
90301 .classed('closedline', true);
90302
90303 closedcontourlines
90304 .attr('d', function(d) {
90305 return Drawing.smoothclosed(d, smoothing);
90306 })
90307 .style('stroke-miterlimit', 1)
90308 .style('vector-effect', 'non-scaling-stroke');
90309 }
90310
90311 return linegroup;
90312};
90313
90314exports.createLineClip = function(lineContainer, clipLinesForLabels, gd, uid) {
90315 var clips = gd._fullLayout._clips;
90316 var clipId = clipLinesForLabels ? ('clipline' + uid) : null;
90317
90318 var lineClip = clips.selectAll('#' + clipId)
90319 .data(clipLinesForLabels ? [0] : []);
90320 lineClip.exit().remove();
90321
90322 lineClip.enter().append('clipPath')
90323 .classed('contourlineclip', true)
90324 .attr('id', clipId);
90325
90326 Drawing.setClipUrl(lineContainer, clipId, gd);
90327
90328 return lineClip;
90329};
90330
90331exports.labelFormatter = function(gd, cd0) {
90332 var fullLayout = gd._fullLayout;
90333 var trace = cd0.trace;
90334 var contours = trace.contours;
90335
90336 var formatAxis = {
90337 type: 'linear',
90338 _id: 'ycontour',
90339 showexponent: 'all',
90340 exponentformat: 'B'
90341 };
90342
90343 if(contours.labelformat) {
90344 formatAxis.tickformat = contours.labelformat;
90345 setConvert(formatAxis, fullLayout);
90346 } else {
90347 var cOpts = Colorscale.extractOpts(trace);
90348 if(cOpts && cOpts.colorbar && cOpts.colorbar._axis) {
90349 formatAxis = cOpts.colorbar._axis;
90350 } else {
90351 if(contours.type === 'constraint') {
90352 var value = contours.value;
90353 if(Array.isArray(value)) {
90354 formatAxis.range = [value[0], value[value.length - 1]];
90355 } else formatAxis.range = [value, value];
90356 } else {
90357 formatAxis.range = [contours.start, contours.end];
90358 formatAxis.nticks = (contours.end - contours.start) / contours.size;
90359 }
90360
90361 if(formatAxis.range[0] === formatAxis.range[1]) {
90362 formatAxis.range[1] += formatAxis.range[0] || 1;
90363 }
90364 if(!formatAxis.nticks) formatAxis.nticks = 1000;
90365
90366 setConvert(formatAxis, fullLayout);
90367 Axes.prepTicks(formatAxis);
90368 formatAxis._tmin = null;
90369 formatAxis._tmax = null;
90370 }
90371 }
90372
90373 return function(v) { return Axes.tickText(formatAxis, v).text; };
90374};
90375
90376exports.calcTextOpts = function(level, contourFormat, dummyText, gd) {
90377 var text = contourFormat(level);
90378 dummyText.text(text)
90379 .call(svgTextUtils.convertToTspans, gd);
90380
90381 var el = dummyText.node();
90382 var bBox = Drawing.bBox(el, true);
90383
90384 return {
90385 text: text,
90386 width: bBox.width,
90387 height: bBox.height,
90388 fontSize: +(el.style['font-size'].replace('px', '')),
90389 level: level,
90390 dy: (bBox.top + bBox.bottom) / 2
90391 };
90392};
90393
90394exports.findBestTextLocation = function(path, pathBounds, textOpts, labelData, plotBounds) {
90395 var textWidth = textOpts.width;
90396
90397 var p0, dp, pMax, pMin, loc;
90398 if(pathBounds.isClosed) {
90399 dp = pathBounds.len / costConstants.INITIALSEARCHPOINTS;
90400 p0 = pathBounds.min + dp / 2;
90401 pMax = pathBounds.max;
90402 } else {
90403 dp = (pathBounds.len - textWidth) / (costConstants.INITIALSEARCHPOINTS + 1);
90404 p0 = pathBounds.min + dp + textWidth / 2;
90405 pMax = pathBounds.max - (dp + textWidth) / 2;
90406 }
90407
90408 var cost = Infinity;
90409 for(var j = 0; j < costConstants.ITERATIONS; j++) {
90410 for(var p = p0; p < pMax; p += dp) {
90411 var newLocation = Lib.getTextLocation(path, pathBounds.total, p, textWidth);
90412 var newCost = locationCost(newLocation, textOpts, labelData, plotBounds);
90413 if(newCost < cost) {
90414 cost = newCost;
90415 loc = newLocation;
90416 pMin = p;
90417 }
90418 }
90419 if(cost > costConstants.MAXCOST * 2) break;
90420
90421 // subsequent iterations just look half steps away from the
90422 // best we found in the previous iteration
90423 if(j) dp /= 2;
90424 p0 = pMin - dp / 2;
90425 pMax = p0 + dp * 1.5;
90426 }
90427 if(cost <= costConstants.MAXCOST) return loc;
90428};
90429
90430/*
90431 * locationCost: a cost function for label locations
90432 * composed of three kinds of penalty:
90433 * - for open paths, being close to the end of the path
90434 * - the angle away from horizontal
90435 * - being too close to already placed neighbors
90436 */
90437function locationCost(loc, textOpts, labelData, bounds) {
90438 var halfWidth = textOpts.width / 2;
90439 var halfHeight = textOpts.height / 2;
90440 var x = loc.x;
90441 var y = loc.y;
90442 var theta = loc.theta;
90443 var dx = Math.cos(theta) * halfWidth;
90444 var dy = Math.sin(theta) * halfWidth;
90445
90446 // cost for being near an edge
90447 var normX = ((x > bounds.center) ? (bounds.right - x) : (x - bounds.left)) /
90448 (dx + Math.abs(Math.sin(theta) * halfHeight));
90449 var normY = ((y > bounds.middle) ? (bounds.bottom - y) : (y - bounds.top)) /
90450 (Math.abs(dy) + Math.cos(theta) * halfHeight);
90451 if(normX < 1 || normY < 1) return Infinity;
90452 var cost = costConstants.EDGECOST * (1 / (normX - 1) + 1 / (normY - 1));
90453
90454 // cost for not being horizontal
90455 cost += costConstants.ANGLECOST * theta * theta;
90456
90457 // cost for being close to other labels
90458 var x1 = x - dx;
90459 var y1 = y - dy;
90460 var x2 = x + dx;
90461 var y2 = y + dy;
90462 for(var i = 0; i < labelData.length; i++) {
90463 var labeli = labelData[i];
90464 var dxd = Math.cos(labeli.theta) * labeli.width / 2;
90465 var dyd = Math.sin(labeli.theta) * labeli.width / 2;
90466 var dist = Lib.segmentDistance(
90467 x1, y1,
90468 x2, y2,
90469 labeli.x - dxd, labeli.y - dyd,
90470 labeli.x + dxd, labeli.y + dyd
90471 ) * 2 / (textOpts.height + labeli.height);
90472
90473 var sameLevel = labeli.level === textOpts.level;
90474 var distOffset = sameLevel ? costConstants.SAMELEVELDISTANCE : 1;
90475
90476 if(dist <= distOffset) return Infinity;
90477
90478 var distFactor = costConstants.NEIGHBORCOST *
90479 (sameLevel ? costConstants.SAMELEVELFACTOR : 1);
90480
90481 cost += distFactor / (dist - distOffset);
90482 }
90483
90484 return cost;
90485}
90486
90487exports.addLabelData = function(loc, textOpts, labelData, labelClipPathData) {
90488 var fontSize = textOpts.fontSize;
90489 var w = textOpts.width + fontSize / 3;
90490 var h = Math.max(0, textOpts.height - fontSize / 3);
90491
90492 var x = loc.x;
90493 var y = loc.y;
90494 var theta = loc.theta;
90495
90496 var sin = Math.sin(theta);
90497 var cos = Math.cos(theta);
90498
90499 var rotateXY = function(dx, dy) {
90500 return [
90501 x + dx * cos - dy * sin,
90502 y + dx * sin + dy * cos
90503 ];
90504 };
90505
90506 var bBoxPts = [
90507 rotateXY(-w / 2, -h / 2),
90508 rotateXY(-w / 2, h / 2),
90509 rotateXY(w / 2, h / 2),
90510 rotateXY(w / 2, -h / 2)
90511 ];
90512
90513 labelData.push({
90514 text: textOpts.text,
90515 x: x,
90516 y: y,
90517 dy: textOpts.dy,
90518 theta: theta,
90519 level: textOpts.level,
90520 width: w,
90521 height: h
90522 });
90523
90524 labelClipPathData.push(bBoxPts);
90525};
90526
90527exports.drawLabels = function(labelGroup, labelData, gd, lineClip, labelClipPathData) {
90528 var labels = labelGroup.selectAll('text')
90529 .data(labelData, function(d) {
90530 return d.text + ',' + d.x + ',' + d.y + ',' + d.theta;
90531 });
90532
90533 labels.exit().remove();
90534
90535 labels.enter().append('text')
90536 .attr({
90537 'data-notex': 1,
90538 'text-anchor': 'middle'
90539 })
90540 .each(function(d) {
90541 var x = d.x + Math.sin(d.theta) * d.dy;
90542 var y = d.y - Math.cos(d.theta) * d.dy;
90543 d3.select(this)
90544 .text(d.text)
90545 .attr({
90546 x: x,
90547 y: y,
90548 transform: 'rotate(' + (180 * d.theta / Math.PI) + ' ' + x + ' ' + y + ')'
90549 })
90550 .call(svgTextUtils.convertToTspans, gd);
90551 });
90552
90553 if(labelClipPathData) {
90554 var clipPath = '';
90555 for(var i = 0; i < labelClipPathData.length; i++) {
90556 clipPath += 'M' + labelClipPathData[i].join('L') + 'Z';
90557 }
90558
90559 var lineClipPath = Lib.ensureSingle(lineClip, 'path', '');
90560 lineClipPath.attr('d', clipPath);
90561 }
90562};
90563
90564function clipGaps(plotGroup, plotinfo, gd, cd0, perimeter) {
90565 var trace = cd0.trace;
90566 var clips = gd._fullLayout._clips;
90567 var clipId = 'clip' + trace.uid;
90568
90569 var clipPath = clips.selectAll('#' + clipId)
90570 .data(trace.connectgaps ? [] : [0]);
90571 clipPath.enter().append('clipPath')
90572 .classed('contourclip', true)
90573 .attr('id', clipId);
90574 clipPath.exit().remove();
90575
90576 if(trace.connectgaps === false) {
90577 var clipPathInfo = {
90578 // fraction of the way from missing to present point
90579 // to draw the boundary.
90580 // if you make this 1 (or 1-epsilon) then a point in
90581 // a sea of missing data will disappear entirely.
90582 level: 0.9,
90583 crossings: {},
90584 starts: [],
90585 edgepaths: [],
90586 paths: [],
90587 xaxis: plotinfo.xaxis,
90588 yaxis: plotinfo.yaxis,
90589 x: cd0.x,
90590 y: cd0.y,
90591 // 0 = no data, 1 = data
90592 z: makeClipMask(cd0),
90593 smoothing: 0
90594 };
90595
90596 makeCrossings([clipPathInfo]);
90597 findAllPaths([clipPathInfo]);
90598 closeBoundaries([clipPathInfo], {type: 'levels'});
90599
90600 var path = Lib.ensureSingle(clipPath, 'path', '');
90601 path.attr('d',
90602 (clipPathInfo.prefixBoundary ? 'M' + perimeter.join('L') + 'Z' : '') +
90603 joinAllPaths(clipPathInfo, perimeter)
90604 );
90605 } else clipId = null;
90606
90607 Drawing.setClipUrl(plotGroup, clipId, gd);
90608}
90609
90610function makeClipMask(cd0) {
90611 var empties = cd0.trace._emptypoints;
90612 var z = [];
90613 var m = cd0.z.length;
90614 var n = cd0.z[0].length;
90615 var i;
90616 var row = [];
90617 var emptyPoint;
90618
90619 for(i = 0; i < n; i++) row.push(1);
90620 for(i = 0; i < m; i++) z.push(row.slice());
90621 for(i = 0; i < empties.length; i++) {
90622 emptyPoint = empties[i];
90623 z[emptyPoint[0]][emptyPoint[1]] = 0;
90624 }
90625 // save this mask to determine whether to show this data in hover
90626 cd0.zmask = z;
90627 return z;
90628}
90629
90630},{"../../components/colorscale":169,"../../components/drawing":179,"../../lib":287,"../../lib/svg_text_utils":310,"../../plots/cartesian/axes":334,"../../plots/cartesian/set_convert":355,"../heatmap/plot":448,"./close_boundaries":417,"./constants":419,"./convert_to_constraints":423,"./empty_pathinfo":425,"./find_all_paths":427,"./make_crossings":432,"@plotly/d3":20}],434:[function(_dereq_,module,exports){
90631'use strict';
90632
90633var Axes = _dereq_('../../plots/cartesian/axes');
90634var Lib = _dereq_('../../lib');
90635
90636module.exports = function setContours(trace, vals) {
90637 var contours = trace.contours;
90638
90639 // check if we need to auto-choose contour levels
90640 if(trace.autocontour) {
90641 // N.B. do not try to use coloraxis cmin/cmax,
90642 // these values here are meant to remain "per-trace" for now
90643 var zmin = trace.zmin;
90644 var zmax = trace.zmax;
90645 if(trace.zauto || zmin === undefined) {
90646 zmin = Lib.aggNums(Math.min, null, vals);
90647 }
90648 if(trace.zauto || zmax === undefined) {
90649 zmax = Lib.aggNums(Math.max, null, vals);
90650 }
90651
90652 var dummyAx = autoContours(zmin, zmax, trace.ncontours);
90653 contours.size = dummyAx.dtick;
90654 contours.start = Axes.tickFirst(dummyAx);
90655 dummyAx.range.reverse();
90656 contours.end = Axes.tickFirst(dummyAx);
90657
90658 if(contours.start === zmin) contours.start += contours.size;
90659 if(contours.end === zmax) contours.end -= contours.size;
90660
90661 // if you set a small ncontours, *and* the ends are exactly on zmin/zmax
90662 // there's an edge case where start > end now. Make sure there's at least
90663 // one meaningful contour, put it midway between the crossed values
90664 if(contours.start > contours.end) {
90665 contours.start = contours.end = (contours.start + contours.end) / 2;
90666 }
90667
90668 // copy auto-contour info back to the source data.
90669 // previously we copied the whole contours object back, but that had
90670 // other info (coloring, showlines) that should be left to supplyDefaults
90671 if(!trace._input.contours) trace._input.contours = {};
90672 Lib.extendFlat(trace._input.contours, {
90673 start: contours.start,
90674 end: contours.end,
90675 size: contours.size
90676 });
90677 trace._input.autocontour = true;
90678 } else if(contours.type !== 'constraint') {
90679 // sanity checks on manually-supplied start/end/size
90680 var start = contours.start;
90681 var end = contours.end;
90682 var inputContours = trace._input.contours;
90683
90684 if(start > end) {
90685 contours.start = inputContours.start = end;
90686 end = contours.end = inputContours.end = start;
90687 start = contours.start;
90688 }
90689
90690 if(!(contours.size > 0)) {
90691 var sizeOut;
90692 if(start === end) sizeOut = 1;
90693 else sizeOut = autoContours(start, end, trace.ncontours).dtick;
90694
90695 inputContours.size = contours.size = sizeOut;
90696 }
90697 }
90698};
90699
90700
90701/*
90702 * autoContours: make a dummy axis object with dtick we can use
90703 * as contours.size, and if needed we can use Axes.tickFirst
90704 * with this axis object to calculate the start and end too
90705 *
90706 * start: the value to start the contours at
90707 * end: the value to end at (must be > start)
90708 * ncontours: max number of contours to make, like roughDTick
90709 *
90710 * returns: an axis object
90711 */
90712function autoContours(start, end, ncontours) {
90713 var dummyAx = {
90714 type: 'linear',
90715 range: [start, end]
90716 };
90717
90718 Axes.autoTicks(
90719 dummyAx,
90720 (end - start) / (ncontours || 15)
90721 );
90722
90723 return dummyAx;
90724}
90725
90726},{"../../lib":287,"../../plots/cartesian/axes":334}],435:[function(_dereq_,module,exports){
90727'use strict';
90728
90729var d3 = _dereq_('@plotly/d3');
90730
90731var Drawing = _dereq_('../../components/drawing');
90732var heatmapStyle = _dereq_('../heatmap/style');
90733
90734var makeColorMap = _dereq_('./make_color_map');
90735
90736
90737module.exports = function style(gd) {
90738 var contours = d3.select(gd).selectAll('g.contour');
90739
90740 contours.style('opacity', function(d) {
90741 return d[0].trace.opacity;
90742 });
90743
90744 contours.each(function(d) {
90745 var c = d3.select(this);
90746 var trace = d[0].trace;
90747 var contours = trace.contours;
90748 var line = trace.line;
90749 var cs = contours.size || 1;
90750 var start = contours.start;
90751
90752 // for contourcarpet only - is this a constraint-type contour trace?
90753 var isConstraintType = contours.type === 'constraint';
90754 var colorLines = !isConstraintType && contours.coloring === 'lines';
90755 var colorFills = !isConstraintType && contours.coloring === 'fill';
90756
90757 var colorMap = (colorLines || colorFills) ? makeColorMap(trace) : null;
90758
90759 c.selectAll('g.contourlevel').each(function(d) {
90760 d3.select(this).selectAll('path')
90761 .call(Drawing.lineGroupStyle,
90762 line.width,
90763 colorLines ? colorMap(d.level) : line.color,
90764 line.dash);
90765 });
90766
90767 var labelFont = contours.labelfont;
90768 c.selectAll('g.contourlabels text').each(function(d) {
90769 Drawing.font(d3.select(this), {
90770 family: labelFont.family,
90771 size: labelFont.size,
90772 color: labelFont.color || (colorLines ? colorMap(d.level) : line.color)
90773 });
90774 });
90775
90776 if(isConstraintType) {
90777 c.selectAll('g.contourfill path')
90778 .style('fill', trace.fillcolor);
90779 } else if(colorFills) {
90780 var firstFill;
90781
90782 c.selectAll('g.contourfill path')
90783 .style('fill', function(d) {
90784 if(firstFill === undefined) firstFill = d.level;
90785 return colorMap(d.level + 0.5 * cs);
90786 });
90787
90788 if(firstFill === undefined) firstFill = start;
90789
90790 c.selectAll('g.contourbg path')
90791 .style('fill', colorMap(firstFill - 0.5 * cs));
90792 }
90793 });
90794
90795 heatmapStyle(gd);
90796};
90797
90798},{"../../components/drawing":179,"../heatmap/style":449,"./make_color_map":431,"@plotly/d3":20}],436:[function(_dereq_,module,exports){
90799'use strict';
90800
90801var colorscaleDefaults = _dereq_('../../components/colorscale/defaults');
90802var handleLabelDefaults = _dereq_('./label_defaults');
90803
90804
90805module.exports = function handleStyleDefaults(traceIn, traceOut, coerce, layout, opts) {
90806 var coloring = coerce('contours.coloring');
90807
90808 var showLines;
90809 var lineColor = '';
90810 if(coloring === 'fill') showLines = coerce('contours.showlines');
90811
90812 if(showLines !== false) {
90813 if(coloring !== 'lines') lineColor = coerce('line.color', '#000');
90814 coerce('line.width', 0.5);
90815 coerce('line.dash');
90816 }
90817
90818 if(coloring !== 'none') {
90819 // plots/plots always coerces showlegend to true, but in this case
90820 // we default to false and (by default) show a colorbar instead
90821 if(traceIn.showlegend !== true) traceOut.showlegend = false;
90822 traceOut._dfltShowLegend = false;
90823
90824 colorscaleDefaults(
90825 traceIn, traceOut, layout, coerce, {prefix: '', cLetter: 'z'}
90826 );
90827 }
90828
90829 coerce('line.smoothing');
90830
90831 handleLabelDefaults(coerce, layout, lineColor, opts);
90832};
90833
90834},{"../../components/colorscale/defaults":167,"./label_defaults":430}],437:[function(_dereq_,module,exports){
90835'use strict';
90836
90837var scatterAttrs = _dereq_('../scatter/attributes');
90838var baseAttrs = _dereq_('../../plots/attributes');
90839var axisHoverFormat = _dereq_('../../plots/cartesian/axis_format_attributes').axisHoverFormat;
90840var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs;
90841var colorScaleAttrs = _dereq_('../../components/colorscale/attributes');
90842
90843var extendFlat = _dereq_('../../lib/extend').extendFlat;
90844
90845module.exports = extendFlat({
90846 z: {
90847 valType: 'data_array',
90848 editType: 'calc',
90849 },
90850 x: extendFlat({}, scatterAttrs.x, {impliedEdits: {xtype: 'array'}}),
90851 x0: extendFlat({}, scatterAttrs.x0, {impliedEdits: {xtype: 'scaled'}}),
90852 dx: extendFlat({}, scatterAttrs.dx, {impliedEdits: {xtype: 'scaled'}}),
90853 y: extendFlat({}, scatterAttrs.y, {impliedEdits: {ytype: 'array'}}),
90854 y0: extendFlat({}, scatterAttrs.y0, {impliedEdits: {ytype: 'scaled'}}),
90855 dy: extendFlat({}, scatterAttrs.dy, {impliedEdits: {ytype: 'scaled'}}),
90856
90857 xperiod: extendFlat({}, scatterAttrs.xperiod, {impliedEdits: {xtype: 'scaled'}}),
90858 yperiod: extendFlat({}, scatterAttrs.yperiod, {impliedEdits: {ytype: 'scaled'}}),
90859 xperiod0: extendFlat({}, scatterAttrs.xperiod0, {impliedEdits: {xtype: 'scaled'}}),
90860 yperiod0: extendFlat({}, scatterAttrs.yperiod0, {impliedEdits: {ytype: 'scaled'}}),
90861 xperiodalignment: extendFlat({}, scatterAttrs.xperiodalignment, {impliedEdits: {xtype: 'scaled'}}),
90862 yperiodalignment: extendFlat({}, scatterAttrs.yperiodalignment, {impliedEdits: {ytype: 'scaled'}}),
90863
90864 text: {
90865 valType: 'data_array',
90866 editType: 'calc',
90867 },
90868 hovertext: {
90869 valType: 'data_array',
90870 editType: 'calc',
90871 },
90872 transpose: {
90873 valType: 'boolean',
90874 dflt: false,
90875 editType: 'calc',
90876 },
90877 xtype: {
90878 valType: 'enumerated',
90879 values: ['array', 'scaled'],
90880 editType: 'calc+clearAxisTypes',
90881 },
90882 ytype: {
90883 valType: 'enumerated',
90884 values: ['array', 'scaled'],
90885 editType: 'calc+clearAxisTypes',
90886 },
90887 zsmooth: {
90888 valType: 'enumerated',
90889 values: ['fast', 'best', false],
90890 dflt: false,
90891 editType: 'calc',
90892 },
90893 hoverongaps: {
90894 valType: 'boolean',
90895 dflt: true,
90896 editType: 'none',
90897 },
90898 connectgaps: {
90899 valType: 'boolean',
90900 editType: 'calc',
90901 },
90902 xgap: {
90903 valType: 'number',
90904 dflt: 0,
90905 min: 0,
90906 editType: 'plot',
90907 },
90908 ygap: {
90909 valType: 'number',
90910 dflt: 0,
90911 min: 0,
90912 editType: 'plot',
90913 },
90914 xhoverformat: axisHoverFormat('x'),
90915 yhoverformat: axisHoverFormat('y'),
90916 zhoverformat: axisHoverFormat('z', 1),
90917
90918 hovertemplate: hovertemplateAttrs(),
90919 showlegend: extendFlat({}, baseAttrs.showlegend, {dflt: false})
90920}, {
90921 transforms: undefined
90922},
90923 colorScaleAttrs('', {cLetter: 'z', autoColorDflt: false})
90924);
90925
90926},{"../../components/colorscale/attributes":164,"../../lib/extend":281,"../../plots/attributes":330,"../../plots/cartesian/axis_format_attributes":337,"../../plots/template_attributes":371,"../scatter/attributes":497}],438:[function(_dereq_,module,exports){
90927'use strict';
90928
90929var Registry = _dereq_('../../registry');
90930var Lib = _dereq_('../../lib');
90931var Axes = _dereq_('../../plots/cartesian/axes');
90932var alignPeriod = _dereq_('../../plots/cartesian/align_period');
90933
90934var histogram2dCalc = _dereq_('../histogram2d/calc');
90935var colorscaleCalc = _dereq_('../../components/colorscale/calc');
90936var convertColumnData = _dereq_('./convert_column_xyz');
90937var clean2dArray = _dereq_('./clean_2d_array');
90938var interp2d = _dereq_('./interp2d');
90939var findEmpties = _dereq_('./find_empties');
90940var makeBoundArray = _dereq_('./make_bound_array');
90941var BADNUM = _dereq_('../../constants/numerical').BADNUM;
90942
90943module.exports = function calc(gd, trace) {
90944 // prepare the raw data
90945 // run makeCalcdata on x and y even for heatmaps, in case of category mappings
90946 var xa = Axes.getFromId(gd, trace.xaxis || 'x');
90947 var ya = Axes.getFromId(gd, trace.yaxis || 'y');
90948 var isContour = Registry.traceIs(trace, 'contour');
90949 var isHist = Registry.traceIs(trace, 'histogram');
90950 var isGL2D = Registry.traceIs(trace, 'gl2d');
90951 var zsmooth = isContour ? 'best' : trace.zsmooth;
90952 var x, x0, dx, origX;
90953 var y, y0, dy, origY;
90954 var z, i, binned;
90955
90956 // cancel minimum tick spacings (only applies to bars and boxes)
90957 xa._minDtick = 0;
90958 ya._minDtick = 0;
90959
90960 if(isHist) {
90961 binned = histogram2dCalc(gd, trace);
90962 origX = binned.orig_x;
90963 x = binned.x;
90964 x0 = binned.x0;
90965 dx = binned.dx;
90966
90967 origY = binned.orig_y;
90968 y = binned.y;
90969 y0 = binned.y0;
90970 dy = binned.dy;
90971
90972 z = binned.z;
90973 } else {
90974 var zIn = trace.z;
90975 if(Lib.isArray1D(zIn)) {
90976 convertColumnData(trace, xa, ya, 'x', 'y', ['z']);
90977 x = trace._x;
90978 y = trace._y;
90979 zIn = trace._z;
90980 } else {
90981 origX = trace.x ? xa.makeCalcdata(trace, 'x') : [];
90982 origY = trace.y ? ya.makeCalcdata(trace, 'y') : [];
90983 x = alignPeriod(trace, xa, 'x', origX).vals;
90984 y = alignPeriod(trace, ya, 'y', origY).vals;
90985 trace._x = x;
90986 trace._y = y;
90987 }
90988
90989 x0 = trace.x0;
90990 dx = trace.dx;
90991 y0 = trace.y0;
90992 dy = trace.dy;
90993
90994 z = clean2dArray(zIn, trace, xa, ya);
90995 }
90996
90997 if(xa.rangebreaks || ya.rangebreaks) {
90998 z = dropZonBreaks(x, y, z);
90999
91000 if(!isHist) {
91001 x = skipBreaks(x);
91002 y = skipBreaks(y);
91003
91004 trace._x = x;
91005 trace._y = y;
91006 }
91007 }
91008
91009 if(!isHist && (isContour || trace.connectgaps)) {
91010 trace._emptypoints = findEmpties(z);
91011 interp2d(z, trace._emptypoints);
91012 }
91013
91014 function noZsmooth(msg) {
91015 zsmooth = trace._input.zsmooth = trace.zsmooth = false;
91016 Lib.warn('cannot use zsmooth: "fast": ' + msg);
91017 }
91018
91019 // check whether we really can smooth (ie all boxes are about the same size)
91020 if(zsmooth === 'fast') {
91021 if(xa.type === 'log' || ya.type === 'log') {
91022 noZsmooth('log axis found');
91023 } else if(!isHist) {
91024 if(x.length) {
91025 var avgdx = (x[x.length - 1] - x[0]) / (x.length - 1);
91026 var maxErrX = Math.abs(avgdx / 100);
91027 for(i = 0; i < x.length - 1; i++) {
91028 if(Math.abs(x[i + 1] - x[i] - avgdx) > maxErrX) {
91029 noZsmooth('x scale is not linear');
91030 break;
91031 }
91032 }
91033 }
91034 if(y.length && zsmooth === 'fast') {
91035 var avgdy = (y[y.length - 1] - y[0]) / (y.length - 1);
91036 var maxErrY = Math.abs(avgdy / 100);
91037 for(i = 0; i < y.length - 1; i++) {
91038 if(Math.abs(y[i + 1] - y[i] - avgdy) > maxErrY) {
91039 noZsmooth('y scale is not linear');
91040 break;
91041 }
91042 }
91043 }
91044 }
91045 }
91046
91047 // create arrays of brick boundaries, to be used by autorange and heatmap.plot
91048 var xlen = Lib.maxRowLength(z);
91049 var xIn = trace.xtype === 'scaled' ? '' : x;
91050 var xArray = makeBoundArray(trace, xIn, x0, dx, xlen, xa);
91051 var yIn = trace.ytype === 'scaled' ? '' : y;
91052 var yArray = makeBoundArray(trace, yIn, y0, dy, z.length, ya);
91053
91054 // handled in gl2d convert step
91055 if(!isGL2D) {
91056 trace._extremes[xa._id] = Axes.findExtremes(xa, xArray);
91057 trace._extremes[ya._id] = Axes.findExtremes(ya, yArray);
91058 }
91059
91060 var cd0 = {
91061 x: xArray,
91062 y: yArray,
91063 z: z,
91064 text: trace._text || trace.text,
91065 hovertext: trace._hovertext || trace.hovertext
91066 };
91067
91068 if(trace.xperiodalignment && origX) {
91069 cd0.orig_x = origX;
91070 }
91071 if(trace.yperiodalignment && origY) {
91072 cd0.orig_y = origY;
91073 }
91074
91075 if(xIn && xIn.length === xArray.length - 1) cd0.xCenter = xIn;
91076 if(yIn && yIn.length === yArray.length - 1) cd0.yCenter = yIn;
91077
91078 if(isHist) {
91079 cd0.xRanges = binned.xRanges;
91080 cd0.yRanges = binned.yRanges;
91081 cd0.pts = binned.pts;
91082 }
91083
91084 if(!isContour) {
91085 colorscaleCalc(gd, trace, {vals: z, cLetter: 'z'});
91086 }
91087
91088 if(isContour && trace.contours && trace.contours.coloring === 'heatmap') {
91089 var dummyTrace = {
91090 type: trace.type === 'contour' ? 'heatmap' : 'histogram2d',
91091 xcalendar: trace.xcalendar,
91092 ycalendar: trace.ycalendar
91093 };
91094 cd0.xfill = makeBoundArray(dummyTrace, xIn, x0, dx, xlen, xa);
91095 cd0.yfill = makeBoundArray(dummyTrace, yIn, y0, dy, z.length, ya);
91096 }
91097
91098 return [cd0];
91099};
91100
91101function skipBreaks(a) {
91102 var b = [];
91103 var len = a.length;
91104 for(var i = 0; i < len; i++) {
91105 var v = a[i];
91106 if(v !== BADNUM) b.push(v);
91107 }
91108 return b;
91109}
91110
91111function dropZonBreaks(x, y, z) {
91112 var newZ = [];
91113 var k = -1;
91114 for(var i = 0; i < z.length; i++) {
91115 if(y[i] === BADNUM) continue;
91116 k++;
91117 newZ[k] = [];
91118 for(var j = 0; j < z[i].length; j++) {
91119 if(x[j] === BADNUM) continue;
91120
91121 newZ[k].push(z[i][j]);
91122 }
91123 }
91124 return newZ;
91125}
91126
91127},{"../../components/colorscale/calc":165,"../../constants/numerical":267,"../../lib":287,"../../plots/cartesian/align_period":331,"../../plots/cartesian/axes":334,"../../registry":376,"../histogram2d/calc":466,"./clean_2d_array":439,"./convert_column_xyz":441,"./find_empties":443,"./interp2d":446,"./make_bound_array":447}],439:[function(_dereq_,module,exports){
91128'use strict';
91129
91130var isNumeric = _dereq_('fast-isnumeric');
91131var Lib = _dereq_('../../lib');
91132var BADNUM = _dereq_('../../constants/numerical').BADNUM;
91133
91134module.exports = function clean2dArray(zOld, trace, xa, ya) {
91135 var rowlen, collen, getCollen, old2new, i, j;
91136
91137 function cleanZvalue(v) {
91138 if(!isNumeric(v)) return undefined;
91139 return +v;
91140 }
91141
91142 if(trace && trace.transpose) {
91143 rowlen = 0;
91144 for(i = 0; i < zOld.length; i++) rowlen = Math.max(rowlen, zOld[i].length);
91145 if(rowlen === 0) return false;
91146 getCollen = function(zOld) { return zOld.length; };
91147 old2new = function(zOld, i, j) { return (zOld[j] || [])[i]; };
91148 } else {
91149 rowlen = zOld.length;
91150 getCollen = function(zOld, i) { return zOld[i].length; };
91151 old2new = function(zOld, i, j) { return (zOld[i] || [])[j]; };
91152 }
91153
91154 var padOld2new = function(zOld, i, j) {
91155 if(i === BADNUM || j === BADNUM) return BADNUM;
91156 return old2new(zOld, i, j);
91157 };
91158
91159 function axisMapping(ax) {
91160 if(trace && trace.type !== 'carpet' && trace.type !== 'contourcarpet' &&
91161 ax && ax.type === 'category' && trace['_' + ax._id.charAt(0)].length) {
91162 var axLetter = ax._id.charAt(0);
91163 var axMapping = {};
91164 var traceCategories = trace['_' + axLetter + 'CategoryMap'] || trace[axLetter];
91165 for(i = 0; i < traceCategories.length; i++) {
91166 axMapping[traceCategories[i]] = i;
91167 }
91168 return function(i) {
91169 var ind = axMapping[ax._categories[i]];
91170 return ind + 1 ? ind : BADNUM;
91171 };
91172 } else {
91173 return Lib.identity;
91174 }
91175 }
91176
91177 var xMap = axisMapping(xa);
91178 var yMap = axisMapping(ya);
91179
91180 if(ya && ya.type === 'category') rowlen = ya._categories.length;
91181 var zNew = new Array(rowlen);
91182
91183 for(i = 0; i < rowlen; i++) {
91184 if(xa && xa.type === 'category') {
91185 collen = xa._categories.length;
91186 } else {
91187 collen = getCollen(zOld, i);
91188 }
91189 zNew[i] = new Array(collen);
91190 for(j = 0; j < collen; j++) zNew[i][j] = cleanZvalue(padOld2new(zOld, yMap(i), xMap(j)));
91191 }
91192
91193 return zNew;
91194};
91195
91196},{"../../constants/numerical":267,"../../lib":287,"fast-isnumeric":33}],440:[function(_dereq_,module,exports){
91197'use strict';
91198
91199module.exports = {
91200 min: 'zmin',
91201 max: 'zmax'
91202};
91203
91204},{}],441:[function(_dereq_,module,exports){
91205'use strict';
91206
91207var Lib = _dereq_('../../lib');
91208var BADNUM = _dereq_('../../constants/numerical').BADNUM;
91209var alignPeriod = _dereq_('../../plots/cartesian/align_period');
91210
91211module.exports = function convertColumnData(trace, ax1, ax2, var1Name, var2Name, arrayVarNames) {
91212 var colLen = trace._length;
91213 var col1 = ax1.makeCalcdata(trace, var1Name);
91214 var col2 = ax2.makeCalcdata(trace, var2Name);
91215 col1 = alignPeriod(trace, ax1, var1Name, col1).vals;
91216 col2 = alignPeriod(trace, ax2, var2Name, col2).vals;
91217
91218 var textCol = trace.text;
91219 var hasColumnText = (textCol !== undefined && Lib.isArray1D(textCol));
91220 var hoverTextCol = trace.hovertext;
91221 var hasColumnHoverText = (hoverTextCol !== undefined && Lib.isArray1D(hoverTextCol));
91222 var i, j;
91223
91224 var col1dv = Lib.distinctVals(col1);
91225 var col1vals = col1dv.vals;
91226 var col2dv = Lib.distinctVals(col2);
91227 var col2vals = col2dv.vals;
91228 var newArrays = [];
91229 var text;
91230 var hovertext;
91231
91232 var nI = col2vals.length;
91233 var nJ = col1vals.length;
91234
91235 for(i = 0; i < arrayVarNames.length; i++) {
91236 newArrays[i] = Lib.init2dArray(nI, nJ);
91237 }
91238
91239 if(hasColumnText) {
91240 text = Lib.init2dArray(nI, nJ);
91241 }
91242 if(hasColumnHoverText) {
91243 hovertext = Lib.init2dArray(nI, nJ);
91244 }
91245
91246 var after2before = Lib.init2dArray(nI, nJ);
91247
91248 for(i = 0; i < colLen; i++) {
91249 if(col1[i] !== BADNUM && col2[i] !== BADNUM) {
91250 var i1 = Lib.findBin(col1[i] + col1dv.minDiff / 2, col1vals);
91251 var i2 = Lib.findBin(col2[i] + col2dv.minDiff / 2, col2vals);
91252
91253 for(j = 0; j < arrayVarNames.length; j++) {
91254 var arrayVarName = arrayVarNames[j];
91255 var arrayVar = trace[arrayVarName];
91256 var newArray = newArrays[j];
91257 newArray[i2][i1] = arrayVar[i];
91258 after2before[i2][i1] = i;
91259 }
91260
91261 if(hasColumnText) text[i2][i1] = textCol[i];
91262 if(hasColumnHoverText) hovertext[i2][i1] = hoverTextCol[i];
91263 }
91264 }
91265
91266 trace['_' + var1Name] = col1vals;
91267 trace['_' + var2Name] = col2vals;
91268 for(j = 0; j < arrayVarNames.length; j++) {
91269 trace['_' + arrayVarNames[j]] = newArrays[j];
91270 }
91271 if(hasColumnText) trace._text = text;
91272 if(hasColumnHoverText) trace._hovertext = hovertext;
91273
91274 if(ax1 && ax1.type === 'category') {
91275 trace['_' + var1Name + 'CategoryMap'] = col1vals.map(function(v) { return ax1._categories[v];});
91276 }
91277
91278 if(ax2 && ax2.type === 'category') {
91279 trace['_' + var2Name + 'CategoryMap'] = col2vals.map(function(v) { return ax2._categories[v];});
91280 }
91281
91282 trace._after2before = after2before;
91283};
91284
91285},{"../../constants/numerical":267,"../../lib":287,"../../plots/cartesian/align_period":331}],442:[function(_dereq_,module,exports){
91286'use strict';
91287
91288var Lib = _dereq_('../../lib');
91289
91290var handleXYZDefaults = _dereq_('./xyz_defaults');
91291var handlePeriodDefaults = _dereq_('../scatter/period_defaults');
91292var handleStyleDefaults = _dereq_('./style_defaults');
91293var colorscaleDefaults = _dereq_('../../components/colorscale/defaults');
91294var attributes = _dereq_('./attributes');
91295
91296
91297module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
91298 function coerce(attr, dflt) {
91299 return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
91300 }
91301
91302 var validData = handleXYZDefaults(traceIn, traceOut, coerce, layout);
91303 if(!validData) {
91304 traceOut.visible = false;
91305 return;
91306 }
91307
91308 handlePeriodDefaults(traceIn, traceOut, layout, coerce);
91309 coerce('xhoverformat');
91310 coerce('yhoverformat');
91311
91312 coerce('text');
91313 coerce('hovertext');
91314 coerce('hovertemplate');
91315
91316 handleStyleDefaults(traceIn, traceOut, coerce, layout);
91317
91318 coerce('hoverongaps');
91319 coerce('connectgaps', Lib.isArray1D(traceOut.z) && (traceOut.zsmooth !== false));
91320
91321 colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: '', cLetter: 'z'});
91322};
91323
91324},{"../../components/colorscale/defaults":167,"../../lib":287,"../scatter/period_defaults":517,"./attributes":437,"./style_defaults":450,"./xyz_defaults":451}],443:[function(_dereq_,module,exports){
91325'use strict';
91326
91327var maxRowLength = _dereq_('../../lib').maxRowLength;
91328
91329/* Return a list of empty points in 2D array z
91330 * each empty point z[i][j] gives an array [i, j, neighborCount]
91331 * neighborCount is the count of 4 nearest neighbors that DO exist
91332 * this is to give us an order of points to evaluate for interpolation.
91333 * if no neighbors exist, we iteratively look for neighbors that HAVE
91334 * neighbors, and add a fractional neighborCount
91335 */
91336module.exports = function findEmpties(z) {
91337 var empties = [];
91338 var neighborHash = {};
91339 var noNeighborList = [];
91340 var nextRow = z[0];
91341 var row = [];
91342 var blank = [0, 0, 0];
91343 var rowLength = maxRowLength(z);
91344 var prevRow;
91345 var i;
91346 var j;
91347 var thisPt;
91348 var p;
91349 var neighborCount;
91350 var newNeighborHash;
91351 var foundNewNeighbors;
91352
91353 for(i = 0; i < z.length; i++) {
91354 prevRow = row;
91355 row = nextRow;
91356 nextRow = z[i + 1] || [];
91357 for(j = 0; j < rowLength; j++) {
91358 if(row[j] === undefined) {
91359 neighborCount = (row[j - 1] !== undefined ? 1 : 0) +
91360 (row[j + 1] !== undefined ? 1 : 0) +
91361 (prevRow[j] !== undefined ? 1 : 0) +
91362 (nextRow[j] !== undefined ? 1 : 0);
91363
91364 if(neighborCount) {
91365 // for this purpose, don't count off-the-edge points
91366 // as undefined neighbors
91367 if(i === 0) neighborCount++;
91368 if(j === 0) neighborCount++;
91369 if(i === z.length - 1) neighborCount++;
91370 if(j === row.length - 1) neighborCount++;
91371
91372 // if all neighbors that could exist do, we don't
91373 // need this for finding farther neighbors
91374 if(neighborCount < 4) {
91375 neighborHash[[i, j]] = [i, j, neighborCount];
91376 }
91377
91378 empties.push([i, j, neighborCount]);
91379 } else noNeighborList.push([i, j]);
91380 }
91381 }
91382 }
91383
91384 while(noNeighborList.length) {
91385 newNeighborHash = {};
91386 foundNewNeighbors = false;
91387
91388 // look for cells that now have neighbors but didn't before
91389 for(p = noNeighborList.length - 1; p >= 0; p--) {
91390 thisPt = noNeighborList[p];
91391 i = thisPt[0];
91392 j = thisPt[1];
91393
91394 neighborCount = ((neighborHash[[i - 1, j]] || blank)[2] +
91395 (neighborHash[[i + 1, j]] || blank)[2] +
91396 (neighborHash[[i, j - 1]] || blank)[2] +
91397 (neighborHash[[i, j + 1]] || blank)[2]) / 20;
91398
91399 if(neighborCount) {
91400 newNeighborHash[thisPt] = [i, j, neighborCount];
91401 noNeighborList.splice(p, 1);
91402 foundNewNeighbors = true;
91403 }
91404 }
91405
91406 if(!foundNewNeighbors) {
91407 throw 'findEmpties iterated with no new neighbors';
91408 }
91409
91410 // put these new cells into the main neighbor list
91411 for(thisPt in newNeighborHash) {
91412 neighborHash[thisPt] = newNeighborHash[thisPt];
91413 empties.push(newNeighborHash[thisPt]);
91414 }
91415 }
91416
91417 // sort the full list in descending order of neighbor count
91418 return empties.sort(function(a, b) { return b[2] - a[2]; });
91419};
91420
91421},{"../../lib":287}],444:[function(_dereq_,module,exports){
91422'use strict';
91423
91424var Fx = _dereq_('../../components/fx');
91425var Lib = _dereq_('../../lib');
91426var Axes = _dereq_('../../plots/cartesian/axes');
91427var extractOpts = _dereq_('../../components/colorscale').extractOpts;
91428
91429module.exports = function hoverPoints(pointData, xval, yval, hovermode, opts) {
91430 if(!opts) opts = {};
91431 var isContour = opts.isContour;
91432
91433 var cd0 = pointData.cd[0];
91434 var trace = cd0.trace;
91435 var xa = pointData.xa;
91436 var ya = pointData.ya;
91437 var x = cd0.x;
91438 var y = cd0.y;
91439 var z = cd0.z;
91440 var xc = cd0.xCenter;
91441 var yc = cd0.yCenter;
91442 var zmask = cd0.zmask;
91443 var zhoverformat = trace.zhoverformat;
91444 var x2 = x;
91445 var y2 = y;
91446
91447 var xl, yl, nx, ny;
91448
91449 if(pointData.index !== false) {
91450 try {
91451 nx = Math.round(pointData.index[1]);
91452 ny = Math.round(pointData.index[0]);
91453 } catch(e) {
91454 Lib.error('Error hovering on heatmap, ' +
91455 'pointNumber must be [row,col], found:', pointData.index);
91456 return;
91457 }
91458 if(nx < 0 || nx >= z[0].length || ny < 0 || ny > z.length) {
91459 return;
91460 }
91461 } else if(Fx.inbox(xval - x[0], xval - x[x.length - 1], 0) > 0 ||
91462 Fx.inbox(yval - y[0], yval - y[y.length - 1], 0) > 0) {
91463 return;
91464 } else {
91465 if(isContour) {
91466 var i2;
91467 x2 = [2 * x[0] - x[1]];
91468
91469 for(i2 = 1; i2 < x.length; i2++) {
91470 x2.push((x[i2] + x[i2 - 1]) / 2);
91471 }
91472 x2.push([2 * x[x.length - 1] - x[x.length - 2]]);
91473
91474 y2 = [2 * y[0] - y[1]];
91475 for(i2 = 1; i2 < y.length; i2++) {
91476 y2.push((y[i2] + y[i2 - 1]) / 2);
91477 }
91478 y2.push([2 * y[y.length - 1] - y[y.length - 2]]);
91479 }
91480 nx = Math.max(0, Math.min(x2.length - 2, Lib.findBin(xval, x2)));
91481 ny = Math.max(0, Math.min(y2.length - 2, Lib.findBin(yval, y2)));
91482 }
91483
91484 var x0 = xa.c2p(x[nx]);
91485 var x1 = xa.c2p(x[nx + 1]);
91486 var y0 = ya.c2p(y[ny]);
91487 var y1 = ya.c2p(y[ny + 1]);
91488
91489 var _x, _y;
91490 if(isContour) {
91491 _x = cd0.orig_x || x;
91492 _y = cd0.orig_y || y;
91493
91494 x1 = x0;
91495 xl = _x[nx];
91496 y1 = y0;
91497 yl = _y[ny];
91498 } else {
91499 _x = cd0.orig_x || xc || x;
91500 _y = cd0.orig_y || yc || y;
91501
91502 xl = xc ? _x[nx] : ((_x[nx] + _x[nx + 1]) / 2);
91503 yl = yc ? _y[ny] : ((_y[ny] + _y[ny + 1]) / 2);
91504
91505 if(xa && xa.type === 'category') xl = x[nx];
91506 if(ya && ya.type === 'category') yl = y[ny];
91507
91508 if(trace.zsmooth) {
91509 x0 = x1 = xa.c2p(xl);
91510 y0 = y1 = ya.c2p(yl);
91511 }
91512 }
91513
91514 var zVal = z[ny][nx];
91515 if(zmask && !zmask[ny][nx]) zVal = undefined;
91516
91517 if(zVal === undefined && !trace.hoverongaps) return;
91518
91519 var text;
91520 if(Array.isArray(cd0.hovertext) && Array.isArray(cd0.hovertext[ny])) {
91521 text = cd0.hovertext[ny][nx];
91522 } else if(Array.isArray(cd0.text) && Array.isArray(cd0.text[ny])) {
91523 text = cd0.text[ny][nx];
91524 }
91525
91526 // dummy axis for formatting the z value
91527 var cOpts = extractOpts(trace);
91528 var dummyAx = {
91529 type: 'linear',
91530 range: [cOpts.min, cOpts.max],
91531 hoverformat: zhoverformat,
91532 _separators: xa._separators,
91533 _numFormat: xa._numFormat
91534 };
91535 var zLabel = Axes.tickText(dummyAx, zVal, 'hover').text;
91536
91537 return [Lib.extendFlat(pointData, {
91538 index: trace._after2before ? trace._after2before[ny][nx] : [ny, nx],
91539 // never let a 2D override 1D type as closest point
91540 distance: pointData.maxHoverDistance,
91541 spikeDistance: pointData.maxSpikeDistance,
91542 x0: x0,
91543 x1: x1,
91544 y0: y0,
91545 y1: y1,
91546 xLabelVal: xl,
91547 yLabelVal: yl,
91548 zLabelVal: zVal,
91549 zLabel: zLabel,
91550 text: text
91551 })];
91552};
91553
91554},{"../../components/colorscale":169,"../../components/fx":197,"../../lib":287,"../../plots/cartesian/axes":334}],445:[function(_dereq_,module,exports){
91555'use strict';
91556
91557module.exports = {
91558 attributes: _dereq_('./attributes'),
91559 supplyDefaults: _dereq_('./defaults'),
91560 calc: _dereq_('./calc'),
91561 plot: _dereq_('./plot'),
91562 colorbar: _dereq_('./colorbar'),
91563 style: _dereq_('./style'),
91564 hoverPoints: _dereq_('./hover'),
91565
91566 moduleType: 'trace',
91567 name: 'heatmap',
91568 basePlotModule: _dereq_('../../plots/cartesian'),
91569 categories: ['cartesian', 'svg', '2dMap', 'showLegend'],
91570 meta: {
91571 }
91572};
91573
91574},{"../../plots/cartesian":348,"./attributes":437,"./calc":438,"./colorbar":440,"./defaults":442,"./hover":444,"./plot":448,"./style":449}],446:[function(_dereq_,module,exports){
91575'use strict';
91576
91577var Lib = _dereq_('../../lib');
91578
91579var INTERPTHRESHOLD = 1e-2;
91580var NEIGHBORSHIFTS = [[-1, 0], [1, 0], [0, -1], [0, 1]];
91581
91582function correctionOvershoot(maxFractionalChange) {
91583 // start with less overshoot, until we know it's converging,
91584 // then ramp up the overshoot for faster convergence
91585 return 0.5 - 0.25 * Math.min(1, maxFractionalChange * 0.5);
91586}
91587
91588/*
91589 * interp2d: Fill in missing data from a 2D array using an iterative
91590 * poisson equation solver with zero-derivative BC at edges.
91591 * Amazingly, this just amounts to repeatedly averaging all the existing
91592 * nearest neighbors, at least if we don't take x/y scaling into account,
91593 * which is the right approach here where x and y may not even have the
91594 * same units.
91595 *
91596 * @param {array of arrays} z
91597 * The 2D array to fill in. Will be mutated here. Assumed to already be
91598 * cleaned, so all entries are numbers except gaps, which are `undefined`.
91599 * @param {array of arrays} emptyPoints
91600 * Each entry [i, j, neighborCount] for empty points z[i][j] and the number
91601 * of neighbors that are *not* missing. Assumed to be sorted from most to
91602 * least neighbors, as produced by heatmap/find_empties.
91603 */
91604module.exports = function interp2d(z, emptyPoints) {
91605 var maxFractionalChange = 1;
91606 var i;
91607
91608 // one pass to fill in a starting value for all the empties
91609 iterateInterp2d(z, emptyPoints);
91610
91611 // we're don't need to iterate lone empties - remove them
91612 for(i = 0; i < emptyPoints.length; i++) {
91613 if(emptyPoints[i][2] < 4) break;
91614 }
91615 // but don't remove these points from the original array,
91616 // we'll use them for masking, so make a copy.
91617 emptyPoints = emptyPoints.slice(i);
91618
91619 for(i = 0; i < 100 && maxFractionalChange > INTERPTHRESHOLD; i++) {
91620 maxFractionalChange = iterateInterp2d(z, emptyPoints,
91621 correctionOvershoot(maxFractionalChange));
91622 }
91623 if(maxFractionalChange > INTERPTHRESHOLD) {
91624 Lib.log('interp2d didn\'t converge quickly', maxFractionalChange);
91625 }
91626
91627 return z;
91628};
91629
91630function iterateInterp2d(z, emptyPoints, overshoot) {
91631 var maxFractionalChange = 0;
91632 var thisPt;
91633 var i;
91634 var j;
91635 var p;
91636 var q;
91637 var neighborShift;
91638 var neighborRow;
91639 var neighborVal;
91640 var neighborCount;
91641 var neighborSum;
91642 var initialVal;
91643 var minNeighbor;
91644 var maxNeighbor;
91645
91646 for(p = 0; p < emptyPoints.length; p++) {
91647 thisPt = emptyPoints[p];
91648 i = thisPt[0];
91649 j = thisPt[1];
91650 initialVal = z[i][j];
91651 neighborSum = 0;
91652 neighborCount = 0;
91653
91654 for(q = 0; q < 4; q++) {
91655 neighborShift = NEIGHBORSHIFTS[q];
91656 neighborRow = z[i + neighborShift[0]];
91657 if(!neighborRow) continue;
91658 neighborVal = neighborRow[j + neighborShift[1]];
91659 if(neighborVal !== undefined) {
91660 if(neighborSum === 0) {
91661 minNeighbor = maxNeighbor = neighborVal;
91662 } else {
91663 minNeighbor = Math.min(minNeighbor, neighborVal);
91664 maxNeighbor = Math.max(maxNeighbor, neighborVal);
91665 }
91666 neighborCount++;
91667 neighborSum += neighborVal;
91668 }
91669 }
91670
91671 if(neighborCount === 0) {
91672 throw 'iterateInterp2d order is wrong: no defined neighbors';
91673 }
91674
91675 // this is the laplace equation interpolation:
91676 // each point is just the average of its neighbors
91677 // note that this ignores differential x/y scaling
91678 // which I think is the right approach, since we
91679 // don't know what that scaling means
91680 z[i][j] = neighborSum / neighborCount;
91681
91682 if(initialVal === undefined) {
91683 if(neighborCount < 4) maxFractionalChange = 1;
91684 } else {
91685 // we can make large empty regions converge faster
91686 // if we overshoot the change vs the previous value
91687 z[i][j] = (1 + overshoot) * z[i][j] - overshoot * initialVal;
91688
91689 if(maxNeighbor > minNeighbor) {
91690 maxFractionalChange = Math.max(maxFractionalChange,
91691 Math.abs(z[i][j] - initialVal) / (maxNeighbor - minNeighbor));
91692 }
91693 }
91694 }
91695
91696 return maxFractionalChange;
91697}
91698
91699},{"../../lib":287}],447:[function(_dereq_,module,exports){
91700'use strict';
91701
91702var Registry = _dereq_('../../registry');
91703var isArrayOrTypedArray = _dereq_('../../lib').isArrayOrTypedArray;
91704
91705module.exports = function makeBoundArray(trace, arrayIn, v0In, dvIn, numbricks, ax) {
91706 var arrayOut = [];
91707 var isContour = Registry.traceIs(trace, 'contour');
91708 var isHist = Registry.traceIs(trace, 'histogram');
91709 var isGL2D = Registry.traceIs(trace, 'gl2d');
91710 var v0;
91711 var dv;
91712 var i;
91713
91714 var isArrayOfTwoItemsOrMore = isArrayOrTypedArray(arrayIn) && arrayIn.length > 1;
91715
91716 if(isArrayOfTwoItemsOrMore && !isHist && (ax.type !== 'category')) {
91717 var len = arrayIn.length;
91718
91719 // given vals are brick centers
91720 // hopefully length === numbricks, but use this method even if too few are supplied
91721 // and extend it linearly based on the last two points
91722 if(len <= numbricks) {
91723 // contour plots only want the centers
91724 if(isContour || isGL2D) arrayOut = arrayIn.slice(0, numbricks);
91725 else if(numbricks === 1) {
91726 arrayOut = [arrayIn[0] - 0.5, arrayIn[0] + 0.5];
91727 } else {
91728 arrayOut = [1.5 * arrayIn[0] - 0.5 * arrayIn[1]];
91729
91730 for(i = 1; i < len; i++) {
91731 arrayOut.push((arrayIn[i - 1] + arrayIn[i]) * 0.5);
91732 }
91733
91734 arrayOut.push(1.5 * arrayIn[len - 1] - 0.5 * arrayIn[len - 2]);
91735 }
91736
91737 if(len < numbricks) {
91738 var lastPt = arrayOut[arrayOut.length - 1];
91739 var delta = lastPt - arrayOut[arrayOut.length - 2];
91740
91741 for(i = len; i < numbricks; i++) {
91742 lastPt += delta;
91743 arrayOut.push(lastPt);
91744 }
91745 }
91746 } else {
91747 // hopefully length === numbricks+1, but do something regardless:
91748 // given vals are brick boundaries
91749 return isContour ?
91750 arrayIn.slice(0, numbricks) : // we must be strict for contours
91751 arrayIn.slice(0, numbricks + 1);
91752 }
91753 } else {
91754 var calendar = trace[ax._id.charAt(0) + 'calendar'];
91755
91756 if(isHist) {
91757 v0 = ax.r2c(v0In, 0, calendar);
91758 } else {
91759 if(isArrayOrTypedArray(arrayIn) && arrayIn.length === 1) {
91760 v0 = arrayIn[0];
91761 } else if(v0In === undefined) {
91762 v0 = 0;
91763 } else {
91764 var fn = ax.type === 'log' ? ax.d2c : ax.r2c;
91765 v0 = fn(v0In, 0, calendar);
91766 }
91767 }
91768
91769 dv = dvIn || 1;
91770
91771 for(i = (isContour || isGL2D) ? 0 : -0.5; i < numbricks; i++) {
91772 arrayOut.push(v0 + dv * i);
91773 }
91774 }
91775
91776 return arrayOut;
91777};
91778
91779},{"../../lib":287,"../../registry":376}],448:[function(_dereq_,module,exports){
91780'use strict';
91781
91782var d3 = _dereq_('@plotly/d3');
91783var tinycolor = _dereq_('tinycolor2');
91784
91785var Registry = _dereq_('../../registry');
91786var Lib = _dereq_('../../lib');
91787var makeColorScaleFuncFromTrace = _dereq_('../../components/colorscale').makeColorScaleFuncFromTrace;
91788var xmlnsNamespaces = _dereq_('../../constants/xmlns_namespaces');
91789
91790module.exports = function(gd, plotinfo, cdheatmaps, heatmapLayer) {
91791 var xa = plotinfo.xaxis;
91792 var ya = plotinfo.yaxis;
91793
91794 Lib.makeTraceGroups(heatmapLayer, cdheatmaps, 'hm').each(function(cd) {
91795 var plotGroup = d3.select(this);
91796 var cd0 = cd[0];
91797 var trace = cd0.trace;
91798
91799 var z = cd0.z;
91800 var x = cd0.x;
91801 var y = cd0.y;
91802 var xc = cd0.xCenter;
91803 var yc = cd0.yCenter;
91804 var isContour = Registry.traceIs(trace, 'contour');
91805 var zsmooth = isContour ? 'best' : trace.zsmooth;
91806
91807 // get z dims
91808 var m = z.length;
91809 var n = Lib.maxRowLength(z);
91810 var xrev = false;
91811 var yrev = false;
91812
91813 var left, right, temp, top, bottom, i;
91814
91815 // TODO: if there are multiple overlapping categorical heatmaps,
91816 // or if we allow category sorting, then the categories may not be
91817 // sequential... may need to reorder and/or expand z
91818
91819 // Get edges of png in pixels (xa.c2p() maps axes coordinates to pixel coordinates)
91820 // figure out if either axis is reversed (y is usually reversed, in pixel coords)
91821 // also clip the image to maximum 50% outside the visible plot area
91822 // bigger image lets you pan more naturally, but slows performance.
91823 // TODO: use low-resolution images outside the visible plot for panning
91824 // these while loops find the first and last brick bounds that are defined
91825 // (in case of log of a negative)
91826 i = 0;
91827 while(left === undefined && i < x.length - 1) {
91828 left = xa.c2p(x[i]);
91829 i++;
91830 }
91831 i = x.length - 1;
91832 while(right === undefined && i > 0) {
91833 right = xa.c2p(x[i]);
91834 i--;
91835 }
91836
91837 if(right < left) {
91838 temp = right;
91839 right = left;
91840 left = temp;
91841 xrev = true;
91842 }
91843
91844 i = 0;
91845 while(top === undefined && i < y.length - 1) {
91846 top = ya.c2p(y[i]);
91847 i++;
91848 }
91849 i = y.length - 1;
91850 while(bottom === undefined && i > 0) {
91851 bottom = ya.c2p(y[i]);
91852 i--;
91853 }
91854
91855 if(bottom < top) {
91856 temp = top;
91857 top = bottom;
91858 bottom = temp;
91859 yrev = true;
91860 }
91861
91862 // for contours with heatmap fill, we generate the boundaries based on
91863 // brick centers but then use the brick edges for drawing the bricks
91864 if(isContour) {
91865 xc = x;
91866 yc = y;
91867 x = cd0.xfill;
91868 y = cd0.yfill;
91869 }
91870
91871 // make an image that goes at most half a screen off either side, to keep
91872 // time reasonable when you zoom in. if zsmooth is true/fast, don't worry
91873 // about this, because zooming doesn't increase number of pixels
91874 // if zsmooth is best, don't include anything off screen because it takes too long
91875 if(zsmooth !== 'fast') {
91876 var extra = zsmooth === 'best' ? 0 : 0.5;
91877 left = Math.max(-extra * xa._length, left);
91878 right = Math.min((1 + extra) * xa._length, right);
91879 top = Math.max(-extra * ya._length, top);
91880 bottom = Math.min((1 + extra) * ya._length, bottom);
91881 }
91882
91883 var imageWidth = Math.round(right - left);
91884 var imageHeight = Math.round(bottom - top);
91885
91886 // setup image nodes
91887
91888 // if image is entirely off-screen, don't even draw it
91889 var isOffScreen = (imageWidth <= 0 || imageHeight <= 0);
91890
91891 if(isOffScreen) {
91892 var noImage = plotGroup.selectAll('image').data([]);
91893 noImage.exit().remove();
91894 return;
91895 }
91896
91897 // generate image data
91898
91899 var canvasW, canvasH;
91900 if(zsmooth === 'fast') {
91901 canvasW = n;
91902 canvasH = m;
91903 } else {
91904 canvasW = imageWidth;
91905 canvasH = imageHeight;
91906 }
91907
91908 var canvas = document.createElement('canvas');
91909 canvas.width = canvasW;
91910 canvas.height = canvasH;
91911 var context = canvas.getContext('2d');
91912
91913 var sclFunc = makeColorScaleFuncFromTrace(trace, {noNumericCheck: true, returnArray: true});
91914
91915 // map brick boundaries to image pixels
91916 var xpx,
91917 ypx;
91918 if(zsmooth === 'fast') {
91919 xpx = xrev ?
91920 function(index) { return n - 1 - index; } :
91921 Lib.identity;
91922 ypx = yrev ?
91923 function(index) { return m - 1 - index; } :
91924 Lib.identity;
91925 } else {
91926 xpx = function(index) {
91927 return Lib.constrain(Math.round(xa.c2p(x[index]) - left),
91928 0, imageWidth);
91929 };
91930 ypx = function(index) {
91931 return Lib.constrain(Math.round(ya.c2p(y[index]) - top),
91932 0, imageHeight);
91933 };
91934 }
91935
91936 // build the pixel map brick-by-brick
91937 // cruise through z-matrix row-by-row
91938 // build a brick at each z-matrix value
91939 var yi = ypx(0);
91940 var yb = [yi, yi];
91941 var xbi = xrev ? 0 : 1;
91942 var ybi = yrev ? 0 : 1;
91943 // for collecting an average luminosity of the heatmap
91944 var pixcount = 0;
91945 var rcount = 0;
91946 var gcount = 0;
91947 var bcount = 0;
91948
91949 var xb, j, xi, v, row, c;
91950
91951 function setColor(v, pixsize) {
91952 if(v !== undefined) {
91953 var c = sclFunc(v);
91954 c[0] = Math.round(c[0]);
91955 c[1] = Math.round(c[1]);
91956 c[2] = Math.round(c[2]);
91957
91958 pixcount += pixsize;
91959 rcount += c[0] * pixsize;
91960 gcount += c[1] * pixsize;
91961 bcount += c[2] * pixsize;
91962 return c;
91963 }
91964 return [0, 0, 0, 0];
91965 }
91966
91967 function interpColor(r0, r1, xinterp, yinterp) {
91968 var z00 = r0[xinterp.bin0];
91969 if(z00 === undefined) return setColor(undefined, 1);
91970
91971 var z01 = r0[xinterp.bin1];
91972 var z10 = r1[xinterp.bin0];
91973 var z11 = r1[xinterp.bin1];
91974 var dx = (z01 - z00) || 0;
91975 var dy = (z10 - z00) || 0;
91976 var dxy;
91977
91978 // the bilinear interpolation term needs different calculations
91979 // for all the different permutations of missing data
91980 // among the neighbors of the main point, to ensure
91981 // continuity across brick boundaries.
91982 if(z01 === undefined) {
91983 if(z11 === undefined) dxy = 0;
91984 else if(z10 === undefined) dxy = 2 * (z11 - z00);
91985 else dxy = (2 * z11 - z10 - z00) * 2 / 3;
91986 } else if(z11 === undefined) {
91987 if(z10 === undefined) dxy = 0;
91988 else dxy = (2 * z00 - z01 - z10) * 2 / 3;
91989 } else if(z10 === undefined) dxy = (2 * z11 - z01 - z00) * 2 / 3;
91990 else dxy = (z11 + z00 - z01 - z10);
91991
91992 return setColor(z00 + xinterp.frac * dx + yinterp.frac * (dy + xinterp.frac * dxy));
91993 }
91994
91995 if(zsmooth) { // best or fast, works fastest with imageData
91996 var pxIndex = 0;
91997 var pixels;
91998
91999 try {
92000 pixels = new Uint8Array(imageWidth * imageHeight * 4);
92001 } catch(e) {
92002 pixels = new Array(imageWidth * imageHeight * 4);
92003 }
92004
92005 if(zsmooth === 'best') {
92006 var xForPx = xc || x;
92007 var yForPx = yc || y;
92008 var xPixArray = new Array(xForPx.length);
92009 var yPixArray = new Array(yForPx.length);
92010 var xinterpArray = new Array(imageWidth);
92011 var findInterpX = xc ? findInterpFromCenters : findInterp;
92012 var findInterpY = yc ? findInterpFromCenters : findInterp;
92013 var yinterp, r0, r1;
92014
92015 // first make arrays of x and y pixel locations of brick boundaries
92016 for(i = 0; i < xForPx.length; i++) xPixArray[i] = Math.round(xa.c2p(xForPx[i]) - left);
92017 for(i = 0; i < yForPx.length; i++) yPixArray[i] = Math.round(ya.c2p(yForPx[i]) - top);
92018
92019 // then make arrays of interpolations
92020 // (bin0=closest, bin1=next, frac=fractional dist.)
92021 for(i = 0; i < imageWidth; i++) xinterpArray[i] = findInterpX(i, xPixArray);
92022
92023 // now do the interpolations and fill the png
92024 for(j = 0; j < imageHeight; j++) {
92025 yinterp = findInterpY(j, yPixArray);
92026 r0 = z[yinterp.bin0];
92027 r1 = z[yinterp.bin1];
92028 for(i = 0; i < imageWidth; i++, pxIndex += 4) {
92029 c = interpColor(r0, r1, xinterpArray[i], yinterp);
92030 putColor(pixels, pxIndex, c);
92031 }
92032 }
92033 } else { // zsmooth = fast
92034 for(j = 0; j < m; j++) {
92035 row = z[j];
92036 yb = ypx(j);
92037 for(i = 0; i < imageWidth; i++) {
92038 c = setColor(row[i], 1);
92039 pxIndex = (yb * imageWidth + xpx(i)) * 4;
92040 putColor(pixels, pxIndex, c);
92041 }
92042 }
92043 }
92044
92045 var imageData = context.createImageData(imageWidth, imageHeight);
92046 try {
92047 imageData.data.set(pixels);
92048 } catch(e) {
92049 var pxArray = imageData.data;
92050 var dlen = pxArray.length;
92051 for(j = 0; j < dlen; j ++) {
92052 pxArray[j] = pixels[j];
92053 }
92054 }
92055
92056 context.putImageData(imageData, 0, 0);
92057 } else { // zsmooth = false -> filling potentially large bricks works fastest with fillRect
92058 // gaps do not need to be exact integers, but if they *are* we will get
92059 // cleaner edges by rounding at least one edge
92060 var xGap = trace.xgap;
92061 var yGap = trace.ygap;
92062 var xGapLeft = Math.floor(xGap / 2);
92063 var yGapTop = Math.floor(yGap / 2);
92064
92065 for(j = 0; j < m; j++) {
92066 row = z[j];
92067 yb.reverse();
92068 yb[ybi] = ypx(j + 1);
92069 if(yb[0] === yb[1] || yb[0] === undefined || yb[1] === undefined) {
92070 continue;
92071 }
92072 xi = xpx(0);
92073 xb = [xi, xi];
92074 for(i = 0; i < n; i++) {
92075 // build one color brick!
92076 xb.reverse();
92077 xb[xbi] = xpx(i + 1);
92078 if(xb[0] === xb[1] || xb[0] === undefined || xb[1] === undefined) {
92079 continue;
92080 }
92081 v = row[i];
92082 c = setColor(v, (xb[1] - xb[0]) * (yb[1] - yb[0]));
92083 context.fillStyle = 'rgba(' + c.join(',') + ')';
92084
92085 context.fillRect(xb[0] + xGapLeft, yb[0] + yGapTop,
92086 xb[1] - xb[0] - xGap, yb[1] - yb[0] - yGap);
92087 }
92088 }
92089 }
92090
92091 rcount = Math.round(rcount / pixcount);
92092 gcount = Math.round(gcount / pixcount);
92093 bcount = Math.round(bcount / pixcount);
92094 var avgColor = tinycolor('rgb(' + rcount + ',' + gcount + ',' + bcount + ')');
92095
92096 gd._hmpixcount = (gd._hmpixcount||0) + pixcount;
92097 gd._hmlumcount = (gd._hmlumcount||0) + pixcount * avgColor.getLuminance();
92098
92099 var image3 = plotGroup.selectAll('image')
92100 .data(cd);
92101
92102 image3.enter().append('svg:image').attr({
92103 xmlns: xmlnsNamespaces.svg,
92104 preserveAspectRatio: 'none'
92105 });
92106
92107 image3.attr({
92108 height: imageHeight,
92109 width: imageWidth,
92110 x: left,
92111 y: top,
92112 'xlink:href': canvas.toDataURL('image/png')
92113 });
92114 });
92115};
92116
92117// get interpolated bin value. Returns {bin0:closest bin, frac:fractional dist to next, bin1:next bin}
92118function findInterp(pixel, pixArray) {
92119 var maxBin = pixArray.length - 2;
92120 var bin = Lib.constrain(Lib.findBin(pixel, pixArray), 0, maxBin);
92121 var pix0 = pixArray[bin];
92122 var pix1 = pixArray[bin + 1];
92123 var interp = Lib.constrain(bin + (pixel - pix0) / (pix1 - pix0) - 0.5, 0, maxBin);
92124 var bin0 = Math.round(interp);
92125 var frac = Math.abs(interp - bin0);
92126
92127 if(!interp || interp === maxBin || !frac) {
92128 return {
92129 bin0: bin0,
92130 bin1: bin0,
92131 frac: 0
92132 };
92133 }
92134 return {
92135 bin0: bin0,
92136 frac: frac,
92137 bin1: Math.round(bin0 + frac / (interp - bin0))
92138 };
92139}
92140
92141function findInterpFromCenters(pixel, centerPixArray) {
92142 var maxBin = centerPixArray.length - 1;
92143 var bin = Lib.constrain(Lib.findBin(pixel, centerPixArray), 0, maxBin);
92144 var pix0 = centerPixArray[bin];
92145 var pix1 = centerPixArray[bin + 1];
92146 var frac = ((pixel - pix0) / (pix1 - pix0)) || 0;
92147 if(frac <= 0) {
92148 return {
92149 bin0: bin,
92150 bin1: bin,
92151 frac: 0
92152 };
92153 }
92154 if(frac < 0.5) {
92155 return {
92156 bin0: bin,
92157 bin1: bin + 1,
92158 frac: frac
92159 };
92160 }
92161 return {
92162 bin0: bin + 1,
92163 bin1: bin,
92164 frac: 1 - frac
92165 };
92166}
92167
92168function putColor(pixels, pxIndex, c) {
92169 pixels[pxIndex] = c[0];
92170 pixels[pxIndex + 1] = c[1];
92171 pixels[pxIndex + 2] = c[2];
92172 pixels[pxIndex + 3] = Math.round(c[3] * 255);
92173}
92174
92175},{"../../components/colorscale":169,"../../constants/xmlns_namespaces":268,"../../lib":287,"../../registry":376,"@plotly/d3":20,"tinycolor2":121}],449:[function(_dereq_,module,exports){
92176'use strict';
92177
92178var d3 = _dereq_('@plotly/d3');
92179
92180module.exports = function style(gd) {
92181 d3.select(gd).selectAll('.hm image')
92182 .style('opacity', function(d) {
92183 return d.trace.opacity;
92184 });
92185};
92186
92187},{"@plotly/d3":20}],450:[function(_dereq_,module,exports){
92188'use strict';
92189
92190module.exports = function handleStyleDefaults(traceIn, traceOut, coerce) {
92191 var zsmooth = coerce('zsmooth');
92192 if(zsmooth === false) {
92193 // ensure that xgap and ygap are coerced only when zsmooth allows them to have an effect.
92194 coerce('xgap');
92195 coerce('ygap');
92196 }
92197
92198 coerce('zhoverformat');
92199};
92200
92201},{}],451:[function(_dereq_,module,exports){
92202'use strict';
92203
92204var isNumeric = _dereq_('fast-isnumeric');
92205var Lib = _dereq_('../../lib');
92206
92207var Registry = _dereq_('../../registry');
92208
92209module.exports = function handleXYZDefaults(traceIn, traceOut, coerce, layout, xName, yName) {
92210 var z = coerce('z');
92211 xName = xName || 'x';
92212 yName = yName || 'y';
92213 var x, y;
92214
92215 if(z === undefined || !z.length) return 0;
92216
92217 if(Lib.isArray1D(traceIn.z)) {
92218 x = coerce(xName);
92219 y = coerce(yName);
92220
92221 var xlen = Lib.minRowLength(x);
92222 var ylen = Lib.minRowLength(y);
92223
92224 // column z must be accompanied by xName and yName arrays
92225 if(xlen === 0 || ylen === 0) return 0;
92226
92227 traceOut._length = Math.min(xlen, ylen, z.length);
92228 } else {
92229 x = coordDefaults(xName, coerce);
92230 y = coordDefaults(yName, coerce);
92231
92232 // TODO put z validation elsewhere
92233 if(!isValidZ(z)) return 0;
92234
92235 coerce('transpose');
92236
92237 traceOut._length = null;
92238 }
92239
92240 if(traceIn.type === 'heatmapgl') return true; // skip calendars until we handle them in those traces
92241
92242 var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults');
92243 handleCalendarDefaults(traceIn, traceOut, [xName, yName], layout);
92244
92245 return true;
92246};
92247
92248function coordDefaults(coordStr, coerce) {
92249 var coord = coerce(coordStr);
92250 var coordType = coord ? coerce(coordStr + 'type', 'array') : 'scaled';
92251
92252 if(coordType === 'scaled') {
92253 coerce(coordStr + '0');
92254 coerce('d' + coordStr);
92255 }
92256
92257 return coord;
92258}
92259
92260function isValidZ(z) {
92261 var allRowsAreArrays = true;
92262 var oneRowIsFilled = false;
92263 var hasOneNumber = false;
92264 var zi;
92265
92266 /*
92267 * Without this step:
92268 *
92269 * hasOneNumber = false breaks contour but not heatmap
92270 * allRowsAreArrays = false breaks contour but not heatmap
92271 * oneRowIsFilled = false breaks both
92272 */
92273
92274 for(var i = 0; i < z.length; i++) {
92275 zi = z[i];
92276 if(!Lib.isArrayOrTypedArray(zi)) {
92277 allRowsAreArrays = false;
92278 break;
92279 }
92280 if(zi.length > 0) oneRowIsFilled = true;
92281 for(var j = 0; j < zi.length; j++) {
92282 if(isNumeric(zi[j])) {
92283 hasOneNumber = true;
92284 break;
92285 }
92286 }
92287 }
92288
92289 return (allRowsAreArrays && oneRowIsFilled && hasOneNumber);
92290}
92291
92292},{"../../lib":287,"../../registry":376,"fast-isnumeric":33}],452:[function(_dereq_,module,exports){
92293'use strict';
92294
92295var barAttrs = _dereq_('../bar/attributes');
92296var axisHoverFormat = _dereq_('../../plots/cartesian/axis_format_attributes').axisHoverFormat;
92297var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs;
92298var makeBinAttrs = _dereq_('./bin_attributes');
92299var constants = _dereq_('./constants');
92300var extendFlat = _dereq_('../../lib/extend').extendFlat;
92301
92302module.exports = {
92303 x: {
92304 valType: 'data_array',
92305 editType: 'calc+clearAxisTypes',
92306 },
92307 y: {
92308 valType: 'data_array',
92309 editType: 'calc+clearAxisTypes',
92310 },
92311
92312 xhoverformat: axisHoverFormat('x'),
92313 yhoverformat: axisHoverFormat('y'),
92314
92315 text: extendFlat({}, barAttrs.text, {
92316 }),
92317 hovertext: extendFlat({}, barAttrs.hovertext, {
92318 }),
92319 orientation: barAttrs.orientation,
92320
92321 histfunc: {
92322 valType: 'enumerated',
92323 values: ['count', 'sum', 'avg', 'min', 'max'],
92324 dflt: 'count',
92325 editType: 'calc',
92326 },
92327 histnorm: {
92328 valType: 'enumerated',
92329 values: ['', 'percent', 'probability', 'density', 'probability density'],
92330 dflt: '',
92331 editType: 'calc',
92332 },
92333
92334 cumulative: {
92335 enabled: {
92336 valType: 'boolean',
92337 dflt: false,
92338 editType: 'calc',
92339 },
92340
92341 direction: {
92342 valType: 'enumerated',
92343 values: ['increasing', 'decreasing'],
92344 dflt: 'increasing',
92345 editType: 'calc',
92346 },
92347
92348 currentbin: {
92349 valType: 'enumerated',
92350 values: ['include', 'exclude', 'half'],
92351 dflt: 'include',
92352 editType: 'calc',
92353 },
92354 editType: 'calc'
92355 },
92356 nbinsx: {
92357 valType: 'integer',
92358 min: 0,
92359 dflt: 0,
92360 editType: 'calc',
92361 },
92362 xbins: makeBinAttrs('x', true),
92363
92364 nbinsy: {
92365 valType: 'integer',
92366 min: 0,
92367 dflt: 0,
92368 editType: 'calc',
92369 },
92370 ybins: makeBinAttrs('y', true),
92371 autobinx: {
92372 valType: 'boolean',
92373 dflt: null,
92374 editType: 'calc',
92375 },
92376 autobiny: {
92377 valType: 'boolean',
92378 dflt: null,
92379 editType: 'calc',
92380 },
92381
92382 bingroup: {
92383 valType: 'string',
92384 dflt: '',
92385 editType: 'calc',
92386 },
92387
92388 hovertemplate: hovertemplateAttrs({}, {
92389 keys: constants.eventDataKeys
92390 }),
92391
92392 marker: barAttrs.marker,
92393
92394 offsetgroup: barAttrs.offsetgroup,
92395 alignmentgroup: barAttrs.alignmentgroup,
92396
92397 selected: barAttrs.selected,
92398 unselected: barAttrs.unselected,
92399
92400 _deprecated: {
92401 bardir: barAttrs._deprecated.bardir
92402 }
92403};
92404
92405},{"../../lib/extend":281,"../../plots/cartesian/axis_format_attributes":337,"../../plots/template_attributes":371,"../bar/attributes":386,"./bin_attributes":454,"./constants":458}],453:[function(_dereq_,module,exports){
92406'use strict';
92407
92408
92409module.exports = function doAvg(size, counts) {
92410 var nMax = size.length;
92411 var total = 0;
92412 for(var i = 0; i < nMax; i++) {
92413 if(counts[i]) {
92414 size[i] /= counts[i];
92415 total += size[i];
92416 } else size[i] = null;
92417 }
92418 return total;
92419};
92420
92421},{}],454:[function(_dereq_,module,exports){
92422'use strict';
92423
92424module.exports = function makeBinAttrs(axLetter, match) {
92425 return {
92426 start: {
92427 valType: 'any', // for date axes
92428 editType: 'calc',
92429 },
92430 end: {
92431 valType: 'any', // for date axes
92432 editType: 'calc',
92433 },
92434 size: {
92435 valType: 'any', // for date axes
92436 editType: 'calc',
92437 },
92438 editType: 'calc'
92439 };
92440};
92441
92442},{}],455:[function(_dereq_,module,exports){
92443'use strict';
92444
92445var isNumeric = _dereq_('fast-isnumeric');
92446
92447
92448module.exports = {
92449 count: function(n, i, size) {
92450 size[n]++;
92451 return 1;
92452 },
92453
92454 sum: function(n, i, size, counterData) {
92455 var v = counterData[i];
92456 if(isNumeric(v)) {
92457 v = Number(v);
92458 size[n] += v;
92459 return v;
92460 }
92461 return 0;
92462 },
92463
92464 avg: function(n, i, size, counterData, counts) {
92465 var v = counterData[i];
92466 if(isNumeric(v)) {
92467 v = Number(v);
92468 size[n] += v;
92469 counts[n]++;
92470 }
92471 return 0;
92472 },
92473
92474 min: function(n, i, size, counterData) {
92475 var v = counterData[i];
92476 if(isNumeric(v)) {
92477 v = Number(v);
92478 if(!isNumeric(size[n])) {
92479 size[n] = v;
92480 return v;
92481 } else if(size[n] > v) {
92482 var delta = v - size[n];
92483 size[n] = v;
92484 return delta;
92485 }
92486 }
92487 return 0;
92488 },
92489
92490 max: function(n, i, size, counterData) {
92491 var v = counterData[i];
92492 if(isNumeric(v)) {
92493 v = Number(v);
92494 if(!isNumeric(size[n])) {
92495 size[n] = v;
92496 return v;
92497 } else if(size[n] < v) {
92498 var delta = v - size[n];
92499 size[n] = v;
92500 return delta;
92501 }
92502 }
92503 return 0;
92504 }
92505};
92506
92507},{"fast-isnumeric":33}],456:[function(_dereq_,module,exports){
92508'use strict';
92509
92510var numConstants = _dereq_('../../constants/numerical');
92511var oneYear = numConstants.ONEAVGYEAR;
92512var oneMonth = numConstants.ONEAVGMONTH;
92513var oneDay = numConstants.ONEDAY;
92514var oneHour = numConstants.ONEHOUR;
92515var oneMin = numConstants.ONEMIN;
92516var oneSec = numConstants.ONESEC;
92517var tickIncrement = _dereq_('../../plots/cartesian/axes').tickIncrement;
92518
92519
92520/*
92521 * make a function that will find rounded bin edges
92522 * @param {number} leftGap: how far from the left edge of any bin is the closest data value?
92523 * @param {number} rightGap: how far from the right edge of any bin is the closest data value?
92524 * @param {Array[number]} binEdges: the actual edge values used in binning
92525 * @param {object} pa: the position axis
92526 * @param {string} calendar: the data calendar
92527 *
92528 * @return {function(v, isRightEdge)}:
92529 * find the start (isRightEdge is falsy) or end (truthy) label value for a bin edge `v`
92530 */
92531module.exports = function getBinSpanLabelRound(leftGap, rightGap, binEdges, pa, calendar) {
92532 // the rounding digit is the largest digit that changes in *all* of 4 regions:
92533 // - inside the rightGap before binEdges[0] (shifted 10% to the left)
92534 // - inside the leftGap after binEdges[0] (expanded by 10% of rightGap on each end)
92535 // - same for binEdges[1]
92536 var dv0 = -1.1 * rightGap;
92537 var dv1 = -0.1 * rightGap;
92538 var dv2 = leftGap - dv1;
92539 var edge0 = binEdges[0];
92540 var edge1 = binEdges[1];
92541 var leftDigit = Math.min(
92542 biggestDigitChanged(edge0 + dv1, edge0 + dv2, pa, calendar),
92543 biggestDigitChanged(edge1 + dv1, edge1 + dv2, pa, calendar)
92544 );
92545 var rightDigit = Math.min(
92546 biggestDigitChanged(edge0 + dv0, edge0 + dv1, pa, calendar),
92547 biggestDigitChanged(edge1 + dv0, edge1 + dv1, pa, calendar)
92548 );
92549
92550 // normally we try to make the label for the right edge different from
92551 // the left edge label, so it's unambiguous which bin gets data on the edge.
92552 // but if this results in more than 3 extra digits (or for dates, more than
92553 // 2 fields ie hr&min or min&sec, which is 3600x), it'll be more clutter than
92554 // useful so keep the label cleaner instead
92555 var digit, disambiguateEdges;
92556 if(leftDigit > rightDigit && rightDigit < Math.abs(edge1 - edge0) / 4000) {
92557 digit = leftDigit;
92558 disambiguateEdges = false;
92559 } else {
92560 digit = Math.min(leftDigit, rightDigit);
92561 disambiguateEdges = true;
92562 }
92563
92564 if(pa.type === 'date' && digit > oneDay) {
92565 var dashExclude = (digit === oneYear) ? 1 : 6;
92566 var increment = (digit === oneYear) ? 'M12' : 'M1';
92567
92568 return function(v, isRightEdge) {
92569 var dateStr = pa.c2d(v, oneYear, calendar);
92570 var dashPos = dateStr.indexOf('-', dashExclude);
92571 if(dashPos > 0) dateStr = dateStr.substr(0, dashPos);
92572 var roundedV = pa.d2c(dateStr, 0, calendar);
92573
92574 if(roundedV < v) {
92575 var nextV = tickIncrement(roundedV, increment, false, calendar);
92576 if((roundedV + nextV) / 2 < v + leftGap) roundedV = nextV;
92577 }
92578
92579 if(isRightEdge && disambiguateEdges) {
92580 return tickIncrement(roundedV, increment, true, calendar);
92581 }
92582
92583 return roundedV;
92584 };
92585 }
92586
92587 return function(v, isRightEdge) {
92588 var roundedV = digit * Math.round(v / digit);
92589 // if we rounded down and we could round up and still be < leftGap
92590 // (or what leftGap values round to), do that
92591 if(roundedV + (digit / 10) < v && roundedV + (digit * 0.9) < v + leftGap) {
92592 roundedV += digit;
92593 }
92594 // finally for the right edge back off one digit - but only if we can do that
92595 // and not clip off any data that's potentially in the bin
92596 if(isRightEdge && disambiguateEdges) {
92597 roundedV -= digit;
92598 }
92599 return roundedV;
92600 };
92601};
92602
92603/*
92604 * Find the largest digit that changes within a (calcdata) region [v1, v2]
92605 * if dates, "digit" means date/time part when it's bigger than a second
92606 * returns the unit value to round to this digit, eg 0.01 to round to hundredths, or
92607 * 100 to round to hundreds. returns oneMonth or oneYear for month or year rounding,
92608 * so that Math.min will work, rather than 'M1' and 'M12'
92609 */
92610function biggestDigitChanged(v1, v2, pa, calendar) {
92611 // are we crossing zero? can't say anything.
92612 // in principle this doesn't apply to dates but turns out this doesn't matter.
92613 if(v1 * v2 <= 0) return Infinity;
92614
92615 var dv = Math.abs(v2 - v1);
92616 var isDate = pa.type === 'date';
92617 var digit = biggestGuaranteedDigitChanged(dv, isDate);
92618 // see if a larger digit also changed
92619 for(var i = 0; i < 10; i++) {
92620 // numbers: next digit needs to be >10x but <100x then gets rounded down.
92621 // dates: next digit can be as much as 60x (then rounded down)
92622 var nextDigit = biggestGuaranteedDigitChanged(digit * 80, isDate);
92623 // if we get to years, the chain stops
92624 if(digit === nextDigit) break;
92625 if(didDigitChange(nextDigit, v1, v2, isDate, pa, calendar)) digit = nextDigit;
92626 else break;
92627 }
92628 return digit;
92629}
92630
92631/*
92632 * Find the largest digit that *definitely* changes in a region [v, v + dv] for any v
92633 * for nonuniform date regions (months/years) pick the largest
92634 */
92635function biggestGuaranteedDigitChanged(dv, isDate) {
92636 if(isDate && dv > oneSec) {
92637 // this is supposed to be the biggest *guaranteed* change
92638 // so compare to the longest month and year across any calendar,
92639 // and we'll iterate back up later
92640 // note: does not support rounding larger than one year. We could add
92641 // that if anyone wants it, but seems unusual and not strictly necessary.
92642 if(dv > oneDay) {
92643 if(dv > oneYear * 1.1) return oneYear;
92644 if(dv > oneMonth * 1.1) return oneMonth;
92645 return oneDay;
92646 }
92647
92648 if(dv > oneHour) return oneHour;
92649 if(dv > oneMin) return oneMin;
92650 return oneSec;
92651 }
92652 return Math.pow(10, Math.floor(Math.log(dv) / Math.LN10));
92653}
92654
92655function didDigitChange(digit, v1, v2, isDate, pa, calendar) {
92656 if(isDate && digit > oneDay) {
92657 var dateParts1 = dateParts(v1, pa, calendar);
92658 var dateParts2 = dateParts(v2, pa, calendar);
92659 var parti = (digit === oneYear) ? 0 : 1;
92660 return dateParts1[parti] !== dateParts2[parti];
92661 }
92662 return Math.floor(v2 / digit) - Math.floor(v1 / digit) > 0.1;
92663}
92664
92665function dateParts(v, pa, calendar) {
92666 var parts = pa.c2d(v, oneYear, calendar).split('-');
92667 if(parts[0] === '') {
92668 parts.unshift();
92669 parts[0] = '-' + parts[0];
92670 }
92671 return parts;
92672}
92673
92674},{"../../constants/numerical":267,"../../plots/cartesian/axes":334}],457:[function(_dereq_,module,exports){
92675'use strict';
92676
92677var isNumeric = _dereq_('fast-isnumeric');
92678
92679var Lib = _dereq_('../../lib');
92680var Registry = _dereq_('../../registry');
92681var Axes = _dereq_('../../plots/cartesian/axes');
92682
92683var arraysToCalcdata = _dereq_('../bar/arrays_to_calcdata');
92684var binFunctions = _dereq_('./bin_functions');
92685var normFunctions = _dereq_('./norm_functions');
92686var doAvg = _dereq_('./average');
92687var getBinSpanLabelRound = _dereq_('./bin_label_vals');
92688
92689function calc(gd, trace) {
92690 var pos = [];
92691 var size = [];
92692 var pa = Axes.getFromId(gd, trace.orientation === 'h' ? trace.yaxis : trace.xaxis);
92693 var mainData = trace.orientation === 'h' ? 'y' : 'x';
92694 var counterData = {x: 'y', y: 'x'}[mainData];
92695 var calendar = trace[mainData + 'calendar'];
92696 var cumulativeSpec = trace.cumulative;
92697 var i;
92698
92699 var binsAndPos = calcAllAutoBins(gd, trace, pa, mainData);
92700 var binSpec = binsAndPos[0];
92701 var pos0 = binsAndPos[1];
92702
92703 var nonuniformBins = typeof binSpec.size === 'string';
92704 var binEdges = [];
92705 var bins = nonuniformBins ? binEdges : binSpec;
92706 // make the empty bin array
92707 var inc = [];
92708 var counts = [];
92709 var inputPoints = [];
92710 var total = 0;
92711 var norm = trace.histnorm;
92712 var func = trace.histfunc;
92713 var densityNorm = norm.indexOf('density') !== -1;
92714 var i2, binEnd, n;
92715
92716 if(cumulativeSpec.enabled && densityNorm) {
92717 // we treat "cumulative" like it means "integral" if you use a density norm,
92718 // which in the end means it's the same as without "density"
92719 norm = norm.replace(/ ?density$/, '');
92720 densityNorm = false;
92721 }
92722
92723 var extremeFunc = func === 'max' || func === 'min';
92724 var sizeInit = extremeFunc ? null : 0;
92725 var binFunc = binFunctions.count;
92726 var normFunc = normFunctions[norm];
92727 var isAvg = false;
92728 var pr2c = function(v) { return pa.r2c(v, 0, calendar); };
92729 var rawCounterData;
92730
92731 if(Lib.isArrayOrTypedArray(trace[counterData]) && func !== 'count') {
92732 rawCounterData = trace[counterData];
92733 isAvg = func === 'avg';
92734 binFunc = binFunctions[func];
92735 }
92736
92737 // create the bins (and any extra arrays needed)
92738 // assume more than 1e6 bins is an error, so we don't crash the browser
92739 i = pr2c(binSpec.start);
92740
92741 // decrease end a little in case of rounding errors
92742 binEnd = pr2c(binSpec.end) + (i - Axes.tickIncrement(i, binSpec.size, false, calendar)) / 1e6;
92743
92744 while(i < binEnd && pos.length < 1e6) {
92745 i2 = Axes.tickIncrement(i, binSpec.size, false, calendar);
92746 pos.push((i + i2) / 2);
92747 size.push(sizeInit);
92748 inputPoints.push([]);
92749 // nonuniform bins (like months) we need to search,
92750 // rather than straight calculate the bin we're in
92751 binEdges.push(i);
92752 // nonuniform bins also need nonuniform normalization factors
92753 if(densityNorm) inc.push(1 / (i2 - i));
92754 if(isAvg) counts.push(0);
92755 // break to avoid infinite loops
92756 if(i2 <= i) break;
92757 i = i2;
92758 }
92759 binEdges.push(i);
92760
92761 // for date axes we need bin bounds to be calcdata. For nonuniform bins
92762 // we already have this, but uniform with start/end/size they're still strings.
92763 if(!nonuniformBins && pa.type === 'date') {
92764 bins = {
92765 start: pr2c(bins.start),
92766 end: pr2c(bins.end),
92767 size: bins.size
92768 };
92769 }
92770
92771 // stash left and right gaps by group
92772 if(!gd._fullLayout._roundFnOpts) gd._fullLayout._roundFnOpts = {};
92773 var groupName = trace['_' + mainData + 'bingroup'];
92774 var roundFnOpts = {leftGap: Infinity, rightGap: Infinity};
92775 if(groupName) {
92776 if(!gd._fullLayout._roundFnOpts[groupName]) gd._fullLayout._roundFnOpts[groupName] = roundFnOpts;
92777 roundFnOpts = gd._fullLayout._roundFnOpts[groupName];
92778 }
92779
92780 // bin the data
92781 // and make histogram-specific pt-number-to-cd-index map object
92782 var nMax = size.length;
92783 var uniqueValsPerBin = true;
92784 var leftGap = roundFnOpts.leftGap;
92785 var rightGap = roundFnOpts.rightGap;
92786 var ptNumber2cdIndex = {};
92787 for(i = 0; i < pos0.length; i++) {
92788 var posi = pos0[i];
92789 n = Lib.findBin(posi, bins);
92790 if(n >= 0 && n < nMax) {
92791 total += binFunc(n, i, size, rawCounterData, counts);
92792 if(uniqueValsPerBin && inputPoints[n].length && posi !== pos0[inputPoints[n][0]]) {
92793 uniqueValsPerBin = false;
92794 }
92795 inputPoints[n].push(i);
92796 ptNumber2cdIndex[i] = n;
92797
92798 leftGap = Math.min(leftGap, posi - binEdges[n]);
92799 rightGap = Math.min(rightGap, binEdges[n + 1] - posi);
92800 }
92801 }
92802 roundFnOpts.leftGap = leftGap;
92803 roundFnOpts.rightGap = rightGap;
92804
92805 var roundFn;
92806 if(!uniqueValsPerBin) {
92807 roundFn = function(v, isRightEdge) {
92808 return function() {
92809 var roundFnOpts = gd._fullLayout._roundFnOpts[groupName];
92810 return getBinSpanLabelRound(
92811 roundFnOpts.leftGap,
92812 roundFnOpts.rightGap,
92813 binEdges, pa, calendar
92814 )(v, isRightEdge);
92815 };
92816 };
92817 }
92818
92819 // average and/or normalize the data, if needed
92820 if(isAvg) total = doAvg(size, counts);
92821 if(normFunc) normFunc(size, total, inc);
92822
92823 // after all normalization etc, now we can accumulate if desired
92824 if(cumulativeSpec.enabled) cdf(size, cumulativeSpec.direction, cumulativeSpec.currentbin);
92825
92826 var seriesLen = Math.min(pos.length, size.length);
92827 var cd = [];
92828 var firstNonzero = 0;
92829 var lastNonzero = seriesLen - 1;
92830
92831 // look for empty bins at the ends to remove, so autoscale omits them
92832 for(i = 0; i < seriesLen; i++) {
92833 if(size[i]) {
92834 firstNonzero = i;
92835 break;
92836 }
92837 }
92838 for(i = seriesLen - 1; i >= firstNonzero; i--) {
92839 if(size[i]) {
92840 lastNonzero = i;
92841 break;
92842 }
92843 }
92844
92845 // create the "calculated data" to plot
92846 for(i = firstNonzero; i <= lastNonzero; i++) {
92847 if((isNumeric(pos[i]) && isNumeric(size[i]))) {
92848 var cdi = {
92849 p: pos[i],
92850 s: size[i],
92851 b: 0
92852 };
92853
92854 // setup hover and event data fields,
92855 // N.B. pts and "hover" positions ph0/ph1 don't seem to make much sense
92856 // for cumulative distributions
92857 if(!cumulativeSpec.enabled) {
92858 cdi.pts = inputPoints[i];
92859 if(uniqueValsPerBin) {
92860 cdi.ph0 = cdi.ph1 = (inputPoints[i].length) ? pos0[inputPoints[i][0]] : pos[i];
92861 } else {
92862 // Defer evaluation of ph(0|1) in crossTraceCalc
92863 trace._computePh = true;
92864 cdi.ph0 = roundFn(binEdges[i]);
92865 cdi.ph1 = roundFn(binEdges[i + 1], true);
92866 }
92867 }
92868 cd.push(cdi);
92869 }
92870 }
92871
92872 if(cd.length === 1) {
92873 // when we collapse to a single bin, calcdata no longer describes bin size
92874 // so we need to explicitly specify it
92875 cd[0].width1 = Axes.tickIncrement(cd[0].p, binSpec.size, false, calendar) - cd[0].p;
92876 }
92877
92878 arraysToCalcdata(cd, trace);
92879
92880 if(Lib.isArrayOrTypedArray(trace.selectedpoints)) {
92881 Lib.tagSelected(cd, trace, ptNumber2cdIndex);
92882 }
92883
92884 return cd;
92885}
92886
92887/*
92888 * calcAllAutoBins: we want all histograms inside the same bingroup
92889 * (see logic in Histogram.crossTraceDefaults) to share bin specs
92890 *
92891 * If the user has explicitly specified differing
92892 * bin specs, there's nothing we can do, but if possible we will try to use the
92893 * smallest bins of any of the auto values for all histograms inside the same
92894 * bingroup.
92895 */
92896function calcAllAutoBins(gd, trace, pa, mainData, _overlayEdgeCase) {
92897 var binAttr = mainData + 'bins';
92898 var fullLayout = gd._fullLayout;
92899 var groupName = trace['_' + mainData + 'bingroup'];
92900 var binOpts = fullLayout._histogramBinOpts[groupName];
92901 var isOverlay = fullLayout.barmode === 'overlay';
92902 var i, traces, tracei, calendar, pos0, autoVals, cumulativeSpec;
92903
92904 var r2c = function(v) { return pa.r2c(v, 0, calendar); };
92905 var c2r = function(v) { return pa.c2r(v, 0, calendar); };
92906
92907 var cleanBound = pa.type === 'date' ?
92908 function(v) { return (v || v === 0) ? Lib.cleanDate(v, null, calendar) : null; } :
92909 function(v) { return isNumeric(v) ? Number(v) : null; };
92910
92911 function setBound(attr, bins, newBins) {
92912 if(bins[attr + 'Found']) {
92913 bins[attr] = cleanBound(bins[attr]);
92914 if(bins[attr] === null) bins[attr] = newBins[attr];
92915 } else {
92916 autoVals[attr] = bins[attr] = newBins[attr];
92917 Lib.nestedProperty(traces[0], binAttr + '.' + attr).set(newBins[attr]);
92918 }
92919 }
92920
92921 // all but the first trace in this group has already been marked finished
92922 // clear this flag, so next time we run calc we will run autobin again
92923 if(trace['_' + mainData + 'autoBinFinished']) {
92924 delete trace['_' + mainData + 'autoBinFinished'];
92925 } else {
92926 traces = binOpts.traces;
92927 var allPos = [];
92928
92929 // Note: we're including `legendonly` traces here for autobin purposes,
92930 // so that showing & hiding from the legend won't affect bins.
92931 // But this complicates things a bit since those traces don't `calc`,
92932 // hence `isFirstVisible`.
92933 var isFirstVisible = true;
92934 var has2dMap = false;
92935 var hasHist2dContour = false;
92936 for(i = 0; i < traces.length; i++) {
92937 tracei = traces[i];
92938
92939 if(tracei.visible) {
92940 var mainDatai = binOpts.dirs[i];
92941 pos0 = tracei['_' + mainDatai + 'pos0'] = pa.makeCalcdata(tracei, mainDatai);
92942
92943 allPos = Lib.concat(allPos, pos0);
92944 delete tracei['_' + mainData + 'autoBinFinished'];
92945
92946 if(trace.visible === true) {
92947 if(isFirstVisible) {
92948 isFirstVisible = false;
92949 } else {
92950 delete tracei._autoBin;
92951 tracei['_' + mainData + 'autoBinFinished'] = 1;
92952 }
92953 if(Registry.traceIs(tracei, '2dMap')) {
92954 has2dMap = true;
92955 }
92956 if(tracei.type === 'histogram2dcontour') {
92957 hasHist2dContour = true;
92958 }
92959 }
92960 }
92961 }
92962
92963 calendar = traces[0][mainData + 'calendar'];
92964 var newBinSpec = Axes.autoBin(allPos, pa, binOpts.nbins, has2dMap, calendar, binOpts.sizeFound && binOpts.size);
92965
92966 var autoBin = traces[0]._autoBin = {};
92967 autoVals = autoBin[binOpts.dirs[0]] = {};
92968
92969 if(hasHist2dContour) {
92970 // the "true" 2nd argument reverses the tick direction (which we can't
92971 // just do with a minus sign because of month bins)
92972 if(!binOpts.size) {
92973 newBinSpec.start = c2r(Axes.tickIncrement(
92974 r2c(newBinSpec.start), newBinSpec.size, true, calendar));
92975 }
92976 if(binOpts.end === undefined) {
92977 newBinSpec.end = c2r(Axes.tickIncrement(
92978 r2c(newBinSpec.end), newBinSpec.size, false, calendar));
92979 }
92980 }
92981
92982 // Edge case: single-valued histogram overlaying others
92983 // Use them all together to calculate the bin size for the single-valued one
92984 if(isOverlay && !Registry.traceIs(trace, '2dMap') && newBinSpec._dataSpan === 0 &&
92985 pa.type !== 'category' && pa.type !== 'multicategory') {
92986 // Several single-valued histograms! Stop infinite recursion,
92987 // just return an extra flag that tells handleSingleValueOverlays
92988 // to sort out this trace too
92989 if(_overlayEdgeCase) return [newBinSpec, pos0, true];
92990
92991 newBinSpec = handleSingleValueOverlays(gd, trace, pa, mainData, binAttr);
92992 }
92993
92994 // adjust for CDF edge cases
92995 cumulativeSpec = tracei.cumulative || {};
92996 if(cumulativeSpec.enabled && (cumulativeSpec.currentbin !== 'include')) {
92997 if(cumulativeSpec.direction === 'decreasing') {
92998 newBinSpec.start = c2r(Axes.tickIncrement(
92999 r2c(newBinSpec.start), newBinSpec.size, true, calendar));
93000 } else {
93001 newBinSpec.end = c2r(Axes.tickIncrement(
93002 r2c(newBinSpec.end), newBinSpec.size, false, calendar));
93003 }
93004 }
93005
93006 binOpts.size = newBinSpec.size;
93007 if(!binOpts.sizeFound) {
93008 autoVals.size = newBinSpec.size;
93009 Lib.nestedProperty(traces[0], binAttr + '.size').set(newBinSpec.size);
93010 }
93011
93012 setBound('start', binOpts, newBinSpec);
93013 setBound('end', binOpts, newBinSpec);
93014 }
93015
93016 pos0 = trace['_' + mainData + 'pos0'];
93017 delete trace['_' + mainData + 'pos0'];
93018
93019 // Each trace can specify its own start/end, or if omitted
93020 // we ensure they're beyond the bounds of this trace's data,
93021 // and we need to make sure start is aligned with the main start
93022 var traceInputBins = trace._input[binAttr] || {};
93023 var traceBinOptsCalc = Lib.extendFlat({}, binOpts);
93024 var mainStart = binOpts.start;
93025 var startIn = pa.r2l(traceInputBins.start);
93026 var hasStart = startIn !== undefined;
93027 if((binOpts.startFound || hasStart) && startIn !== pa.r2l(mainStart)) {
93028 // We have an explicit start to reconcile across traces
93029 // if this trace has an explicit start, shift it down to a bin edge
93030 // if another trace had an explicit start, shift it down to a
93031 // bin edge past our data
93032 var traceStart = hasStart ?
93033 startIn :
93034 Lib.aggNums(Math.min, null, pos0);
93035
93036 var dummyAx = {
93037 type: (pa.type === 'category' || pa.type === 'multicategory') ? 'linear' : pa.type,
93038 r2l: pa.r2l,
93039 dtick: binOpts.size,
93040 tick0: mainStart,
93041 calendar: calendar,
93042 range: ([traceStart, Axes.tickIncrement(traceStart, binOpts.size, false, calendar)]).map(pa.l2r)
93043 };
93044 var newStart = Axes.tickFirst(dummyAx);
93045 if(newStart > pa.r2l(traceStart)) {
93046 newStart = Axes.tickIncrement(newStart, binOpts.size, true, calendar);
93047 }
93048 traceBinOptsCalc.start = pa.l2r(newStart);
93049 if(!hasStart) Lib.nestedProperty(trace, binAttr + '.start').set(traceBinOptsCalc.start);
93050 }
93051
93052 var mainEnd = binOpts.end;
93053 var endIn = pa.r2l(traceInputBins.end);
93054 var hasEnd = endIn !== undefined;
93055 if((binOpts.endFound || hasEnd) && endIn !== pa.r2l(mainEnd)) {
93056 // Reconciling an explicit end is easier, as it doesn't need to
93057 // match bin edges
93058 var traceEnd = hasEnd ?
93059 endIn :
93060 Lib.aggNums(Math.max, null, pos0);
93061
93062 traceBinOptsCalc.end = pa.l2r(traceEnd);
93063 if(!hasEnd) Lib.nestedProperty(trace, binAttr + '.start').set(traceBinOptsCalc.end);
93064 }
93065
93066 // Backward compatibility for one-time autobinning.
93067 // autobin: true is handled in cleanData, but autobin: false
93068 // needs to be here where we have determined the values.
93069 var autoBinAttr = 'autobin' + mainData;
93070 if(trace._input[autoBinAttr] === false) {
93071 trace._input[binAttr] = Lib.extendFlat({}, trace[binAttr] || {});
93072 delete trace._input[autoBinAttr];
93073 delete trace[autoBinAttr];
93074 }
93075
93076 return [traceBinOptsCalc, pos0];
93077}
93078
93079/*
93080 * Adjust single-value histograms in overlay mode to make as good a
93081 * guess as we can at autobin values the user would like.
93082 *
93083 * Returns the binSpec for the trace that sparked all this
93084 */
93085function handleSingleValueOverlays(gd, trace, pa, mainData, binAttr) {
93086 var fullLayout = gd._fullLayout;
93087 var overlaidTraceGroup = getConnectedHistograms(gd, trace);
93088 var pastThisTrace = false;
93089 var minSize = Infinity;
93090 var singleValuedTraces = [trace];
93091 var i, tracei, binOpts;
93092
93093 // first collect all the:
93094 // - min bin size from all multi-valued traces
93095 // - single-valued traces
93096 for(i = 0; i < overlaidTraceGroup.length; i++) {
93097 tracei = overlaidTraceGroup[i];
93098
93099 if(tracei === trace) {
93100 pastThisTrace = true;
93101 } else if(!pastThisTrace) {
93102 // This trace has already had its autobins calculated, so either:
93103 // - it is part of a bingroup
93104 // - it is NOT a single-valued trace
93105 binOpts = fullLayout._histogramBinOpts[tracei['_' + mainData + 'bingroup']];
93106 minSize = Math.min(minSize, binOpts.size || tracei[binAttr].size);
93107 } else {
93108 var resulti = calcAllAutoBins(gd, tracei, pa, mainData, true);
93109 var binSpeci = resulti[0];
93110 var isSingleValued = resulti[2];
93111
93112 // so we can use this result when we get to tracei in the normal
93113 // course of events, mark it as done and put _pos0 back
93114 tracei['_' + mainData + 'autoBinFinished'] = 1;
93115 tracei['_' + mainData + 'pos0'] = resulti[1];
93116
93117 if(isSingleValued) {
93118 singleValuedTraces.push(tracei);
93119 } else {
93120 minSize = Math.min(minSize, binSpeci.size);
93121 }
93122 }
93123 }
93124
93125 // find the real data values for each single-valued trace
93126 // hunt through pos0 for the first valid value
93127 var dataVals = new Array(singleValuedTraces.length);
93128 for(i = 0; i < singleValuedTraces.length; i++) {
93129 var pos0 = singleValuedTraces[i]['_' + mainData + 'pos0'];
93130 for(var j = 0; j < pos0.length; j++) {
93131 if(pos0[j] !== undefined) {
93132 dataVals[i] = pos0[j];
93133 break;
93134 }
93135 }
93136 }
93137
93138 // are ALL traces are single-valued? use the min difference between
93139 // all of their values (which defaults to 1 if there's still only one)
93140 if(!isFinite(minSize)) {
93141 minSize = Lib.distinctVals(dataVals).minDiff;
93142 }
93143
93144 // now apply the min size we found to all single-valued traces
93145 for(i = 0; i < singleValuedTraces.length; i++) {
93146 tracei = singleValuedTraces[i];
93147 var calendar = tracei[mainData + 'calendar'];
93148
93149 var newBins = {
93150 start: pa.c2r(dataVals[i] - minSize / 2, 0, calendar),
93151 end: pa.c2r(dataVals[i] + minSize / 2, 0, calendar),
93152 size: minSize
93153 };
93154
93155 tracei._input[binAttr] = tracei[binAttr] = newBins;
93156
93157 binOpts = fullLayout._histogramBinOpts[tracei['_' + mainData + 'bingroup']];
93158 if(binOpts) Lib.extendFlat(binOpts, newBins);
93159 }
93160
93161 return trace[binAttr];
93162}
93163
93164/*
93165 * Return an array of histograms that share axes and orientation.
93166 *
93167 * Only considers histograms. In principle we could include bars in a
93168 * similar way to how we do manually binned histograms, though this
93169 * would have tons of edge cases and value judgments to make.
93170 */
93171function getConnectedHistograms(gd, trace) {
93172 var xid = trace.xaxis;
93173 var yid = trace.yaxis;
93174 var orientation = trace.orientation;
93175
93176 var out = [];
93177 var fullData = gd._fullData;
93178 for(var i = 0; i < fullData.length; i++) {
93179 var tracei = fullData[i];
93180 if(tracei.type === 'histogram' &&
93181 tracei.visible === true &&
93182 tracei.orientation === orientation &&
93183 tracei.xaxis === xid && tracei.yaxis === yid
93184 ) {
93185 out.push(tracei);
93186 }
93187 }
93188
93189 return out;
93190}
93191
93192function cdf(size, direction, currentBin) {
93193 var i, vi, prevSum;
93194
93195 function firstHalfPoint(i) {
93196 prevSum = size[i];
93197 size[i] /= 2;
93198 }
93199
93200 function nextHalfPoint(i) {
93201 vi = size[i];
93202 size[i] = prevSum + vi / 2;
93203 prevSum += vi;
93204 }
93205
93206 if(currentBin === 'half') {
93207 if(direction === 'increasing') {
93208 firstHalfPoint(0);
93209 for(i = 1; i < size.length; i++) {
93210 nextHalfPoint(i);
93211 }
93212 } else {
93213 firstHalfPoint(size.length - 1);
93214 for(i = size.length - 2; i >= 0; i--) {
93215 nextHalfPoint(i);
93216 }
93217 }
93218 } else if(direction === 'increasing') {
93219 for(i = 1; i < size.length; i++) {
93220 size[i] += size[i - 1];
93221 }
93222
93223 // 'exclude' is identical to 'include' just shifted one bin over
93224 if(currentBin === 'exclude') {
93225 size.unshift(0);
93226 size.pop();
93227 }
93228 } else {
93229 for(i = size.length - 2; i >= 0; i--) {
93230 size[i] += size[i + 1];
93231 }
93232
93233 if(currentBin === 'exclude') {
93234 size.push(0);
93235 size.shift();
93236 }
93237 }
93238}
93239
93240module.exports = {
93241 calc: calc,
93242 calcAllAutoBins: calcAllAutoBins
93243};
93244
93245},{"../../lib":287,"../../plots/cartesian/axes":334,"../../registry":376,"../bar/arrays_to_calcdata":385,"./average":453,"./bin_functions":455,"./bin_label_vals":456,"./norm_functions":464,"fast-isnumeric":33}],458:[function(_dereq_,module,exports){
93246'use strict';
93247
93248module.exports = {
93249 eventDataKeys: ['binNumber']
93250};
93251
93252},{}],459:[function(_dereq_,module,exports){
93253'use strict';
93254
93255var Lib = _dereq_('../../lib');
93256var axisIds = _dereq_('../../plots/cartesian/axis_ids');
93257
93258var traceIs = _dereq_('../../registry').traceIs;
93259var handleGroupingDefaults = _dereq_('../bar/defaults').handleGroupingDefaults;
93260
93261var nestedProperty = Lib.nestedProperty;
93262var getAxisGroup = _dereq_('../../plots/cartesian/constraints').getAxisGroup;
93263
93264var BINATTRS = [
93265 {aStr: {x: 'xbins.start', y: 'ybins.start'}, name: 'start'},
93266 {aStr: {x: 'xbins.end', y: 'ybins.end'}, name: 'end'},
93267 {aStr: {x: 'xbins.size', y: 'ybins.size'}, name: 'size'},
93268 {aStr: {x: 'nbinsx', y: 'nbinsy'}, name: 'nbins'}
93269];
93270
93271var BINDIRECTIONS = ['x', 'y'];
93272
93273// handle bin attrs and relink auto-determined values so fullData is complete
93274module.exports = function crossTraceDefaults(fullData, fullLayout) {
93275 var allBinOpts = fullLayout._histogramBinOpts = {};
93276 var histTraces = [];
93277 var mustMatchTracesLookup = {};
93278 var otherTracesList = [];
93279
93280 var traceOut, traces, groupName, binDir;
93281 var i, j, k;
93282
93283 function coerce(attr, dflt) {
93284 return Lib.coerce(traceOut._input, traceOut, traceOut._module.attributes, attr, dflt);
93285 }
93286
93287 function orientation2binDir(traceOut) {
93288 return traceOut.orientation === 'v' ? 'x' : 'y';
93289 }
93290
93291 function getAxisType(traceOut, binDir) {
93292 var ax = axisIds.getFromTrace({_fullLayout: fullLayout}, traceOut, binDir);
93293 return ax.type;
93294 }
93295
93296 function fillBinOpts(traceOut, groupName, binDir) {
93297 // N.B. group traces that don't have a bingroup with themselves
93298 var fallbackGroupName = traceOut.uid + '__' + binDir;
93299 if(!groupName) groupName = fallbackGroupName;
93300
93301 var axType = getAxisType(traceOut, binDir);
93302 var calendar = traceOut[binDir + 'calendar'] || '';
93303 var binOpts = allBinOpts[groupName];
93304 var needsNewItem = true;
93305
93306 if(binOpts) {
93307 if(axType === binOpts.axType && calendar === binOpts.calendar) {
93308 needsNewItem = false;
93309 binOpts.traces.push(traceOut);
93310 binOpts.dirs.push(binDir);
93311 } else {
93312 groupName = fallbackGroupName;
93313
93314 if(axType !== binOpts.axType) {
93315 Lib.warn([
93316 'Attempted to group the bins of trace', traceOut.index,
93317 'set on a', 'type:' + axType, 'axis',
93318 'with bins on', 'type:' + binOpts.axType, 'axis.'
93319 ].join(' '));
93320 }
93321 if(calendar !== binOpts.calendar) {
93322 // prohibit bingroup for traces using different calendar,
93323 // there's probably a way to make this work, but skip for now
93324 Lib.warn([
93325 'Attempted to group the bins of trace', traceOut.index,
93326 'set with a', calendar, 'calendar',
93327 'with bins',
93328 (binOpts.calendar ? 'on a ' + binOpts.calendar + ' calendar' : 'w/o a set calendar')
93329 ].join(' '));
93330 }
93331 }
93332 }
93333
93334 if(needsNewItem) {
93335 allBinOpts[groupName] = {
93336 traces: [traceOut],
93337 dirs: [binDir],
93338 axType: axType,
93339 calendar: traceOut[binDir + 'calendar'] || ''
93340 };
93341 }
93342 traceOut['_' + binDir + 'bingroup'] = groupName;
93343 }
93344
93345 for(i = 0; i < fullData.length; i++) {
93346 traceOut = fullData[i];
93347
93348 if(traceIs(traceOut, 'histogram')) {
93349 histTraces.push(traceOut);
93350
93351 // TODO: this shouldn't be relinked as it's only used within calc
93352 // https://github.com/plotly/plotly.js/issues/749
93353 delete traceOut._xautoBinFinished;
93354 delete traceOut._yautoBinFinished;
93355
93356 // N.B. need to coerce *alignmentgroup* before *bingroup*, as traces
93357 // in same alignmentgroup "have to match"
93358 if(!traceIs(traceOut, '2dMap')) {
93359 handleGroupingDefaults(traceOut._input, traceOut, fullLayout, coerce);
93360 }
93361 }
93362 }
93363
93364 var alignmentOpts = fullLayout._alignmentOpts || {};
93365
93366 // Look for traces that "have to match", that is:
93367 // - 1d histogram traces on the same subplot with same orientation under barmode:stack,
93368 // - 1d histogram traces on the same subplot with same orientation under barmode:group
93369 // - 1d histogram traces on the same position axis with the same orientation
93370 // and the same *alignmentgroup* (coerced under barmode:group)
93371 // - Once `stackgroup` gets implemented (see https://github.com/plotly/plotly.js/issues/3614),
93372 // traces within the same stackgroup will also "have to match"
93373 for(i = 0; i < histTraces.length; i++) {
93374 traceOut = histTraces[i];
93375 groupName = '';
93376
93377 if(!traceIs(traceOut, '2dMap')) {
93378 binDir = orientation2binDir(traceOut);
93379
93380 if(fullLayout.barmode === 'group' && traceOut.alignmentgroup) {
93381 var pa = traceOut[binDir + 'axis'];
93382 var aGroupId = getAxisGroup(fullLayout, pa) + traceOut.orientation;
93383 if((alignmentOpts[aGroupId] || {})[traceOut.alignmentgroup]) {
93384 groupName = aGroupId;
93385 }
93386 }
93387
93388 if(!groupName && fullLayout.barmode !== 'overlay') {
93389 groupName = (
93390 getAxisGroup(fullLayout, traceOut.xaxis) +
93391 getAxisGroup(fullLayout, traceOut.yaxis) +
93392 orientation2binDir(traceOut)
93393 );
93394 }
93395 }
93396
93397 if(groupName) {
93398 if(!mustMatchTracesLookup[groupName]) {
93399 mustMatchTracesLookup[groupName] = [];
93400 }
93401 mustMatchTracesLookup[groupName].push(traceOut);
93402 } else {
93403 otherTracesList.push(traceOut);
93404 }
93405 }
93406
93407 // Setup binOpts for traces that have to match,
93408 // if the traces have a valid bingroup, use that
93409 // if not use axis+binDir groupName
93410 for(groupName in mustMatchTracesLookup) {
93411 traces = mustMatchTracesLookup[groupName];
93412
93413 // no need to 'force' anything when a single
93414 // trace is detected as "must match"
93415 if(traces.length === 1) {
93416 otherTracesList.push(traces[0]);
93417 continue;
93418 }
93419
93420 var binGroupFound = false;
93421 if(traces.length) {
93422 traceOut = traces[0];
93423 binGroupFound = coerce('bingroup');
93424 }
93425
93426 groupName = binGroupFound || groupName;
93427
93428 for(i = 0; i < traces.length; i++) {
93429 traceOut = traces[i];
93430 var bingroupIn = traceOut._input.bingroup;
93431 if(bingroupIn && bingroupIn !== groupName) {
93432 Lib.warn([
93433 'Trace', traceOut.index, 'must match',
93434 'within bingroup', groupName + '.',
93435 'Ignoring its bingroup:', bingroupIn, 'setting.'
93436 ].join(' '));
93437 }
93438 traceOut.bingroup = groupName;
93439
93440 // N.B. no need to worry about 2dMap case
93441 // (where both bin direction are set in each trace)
93442 // as 2dMap trace never "have to match"
93443 fillBinOpts(traceOut, groupName, orientation2binDir(traceOut));
93444 }
93445 }
93446
93447 // setup binOpts for traces that can but don't have to match,
93448 // notice that these traces can be matched with traces that have to match
93449 for(i = 0; i < otherTracesList.length; i++) {
93450 traceOut = otherTracesList[i];
93451
93452 var binGroup = coerce('bingroup');
93453
93454 if(traceIs(traceOut, '2dMap')) {
93455 for(k = 0; k < 2; k++) {
93456 binDir = BINDIRECTIONS[k];
93457 var binGroupInDir = coerce(binDir + 'bingroup',
93458 binGroup ? binGroup + '__' + binDir : null
93459 );
93460 fillBinOpts(traceOut, binGroupInDir, binDir);
93461 }
93462 } else {
93463 fillBinOpts(traceOut, binGroup, orientation2binDir(traceOut));
93464 }
93465 }
93466
93467 // coerce bin attrs!
93468 for(groupName in allBinOpts) {
93469 var binOpts = allBinOpts[groupName];
93470 traces = binOpts.traces;
93471
93472 for(j = 0; j < BINATTRS.length; j++) {
93473 var attrSpec = BINATTRS[j];
93474 var attr = attrSpec.name;
93475 var aStr;
93476 var autoVals;
93477
93478 // nbins(x|y) is moot if we have a size. This depends on
93479 // nbins coming after size in binAttrs.
93480 if(attr === 'nbins' && binOpts.sizeFound) continue;
93481
93482 for(i = 0; i < traces.length; i++) {
93483 traceOut = traces[i];
93484 binDir = binOpts.dirs[i];
93485 aStr = attrSpec.aStr[binDir];
93486
93487 if(nestedProperty(traceOut._input, aStr).get() !== undefined) {
93488 binOpts[attr] = coerce(aStr);
93489 binOpts[attr + 'Found'] = true;
93490 break;
93491 }
93492
93493 autoVals = (traceOut._autoBin || {})[binDir] || {};
93494 if(autoVals[attr]) {
93495 // if this is the *first* autoval
93496 nestedProperty(traceOut, aStr).set(autoVals[attr]);
93497 }
93498 }
93499
93500 // start and end we need to coerce anyway, after having collected the
93501 // first of each into binOpts, in case a trace wants to restrict its
93502 // data to a certain range
93503 if(attr === 'start' || attr === 'end') {
93504 for(; i < traces.length; i++) {
93505 traceOut = traces[i];
93506 if(traceOut['_' + binDir + 'bingroup']) {
93507 autoVals = (traceOut._autoBin || {})[binDir] || {};
93508 coerce(aStr, autoVals[attr]);
93509 }
93510 }
93511 }
93512
93513 if(attr === 'nbins' && !binOpts.sizeFound && !binOpts.nbinsFound) {
93514 traceOut = traces[0];
93515 binOpts[attr] = coerce(aStr);
93516 }
93517 }
93518 }
93519};
93520
93521},{"../../lib":287,"../../plots/cartesian/axis_ids":338,"../../plots/cartesian/constraints":342,"../../registry":376,"../bar/defaults":390}],460:[function(_dereq_,module,exports){
93522'use strict';
93523
93524var Registry = _dereq_('../../registry');
93525var Lib = _dereq_('../../lib');
93526var Color = _dereq_('../../components/color');
93527
93528var handleStyleDefaults = _dereq_('../bar/style_defaults');
93529var attributes = _dereq_('./attributes');
93530
93531module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
93532 function coerce(attr, dflt) {
93533 return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
93534 }
93535
93536 var x = coerce('x');
93537 var y = coerce('y');
93538
93539 var cumulative = coerce('cumulative.enabled');
93540 if(cumulative) {
93541 coerce('cumulative.direction');
93542 coerce('cumulative.currentbin');
93543 }
93544
93545 coerce('text');
93546 coerce('hovertext');
93547 coerce('hovertemplate');
93548 coerce('xhoverformat');
93549 coerce('yhoverformat');
93550
93551 var orientation = coerce('orientation', (y && !x) ? 'h' : 'v');
93552 var sampleLetter = orientation === 'v' ? 'x' : 'y';
93553 var aggLetter = orientation === 'v' ? 'y' : 'x';
93554
93555 var len = (x && y) ?
93556 Math.min(Lib.minRowLength(x) && Lib.minRowLength(y)) :
93557 Lib.minRowLength(traceOut[sampleLetter] || []);
93558
93559 if(!len) {
93560 traceOut.visible = false;
93561 return;
93562 }
93563
93564 traceOut._length = len;
93565
93566 var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults');
93567 handleCalendarDefaults(traceIn, traceOut, ['x', 'y'], layout);
93568
93569 var hasAggregationData = traceOut[aggLetter];
93570 if(hasAggregationData) coerce('histfunc');
93571 coerce('histnorm');
93572
93573 // Note: bin defaults are now handled in Histogram.crossTraceDefaults
93574 // autobin(x|y) are only included here to appease Plotly.validate
93575 coerce('autobin' + sampleLetter);
93576
93577 handleStyleDefaults(traceIn, traceOut, coerce, defaultColor, layout);
93578
93579 Lib.coerceSelectionMarkerOpacity(traceOut, coerce);
93580
93581 var lineColor = (traceOut.marker.line || {}).color;
93582
93583 // override defaultColor for error bars with defaultLine
93584 var errorBarsSupplyDefaults = Registry.getComponentMethod('errorbars', 'supplyDefaults');
93585 errorBarsSupplyDefaults(traceIn, traceOut, lineColor || Color.defaultLine, {axis: 'y'});
93586 errorBarsSupplyDefaults(traceIn, traceOut, lineColor || Color.defaultLine, {axis: 'x', inherit: 'y'});
93587};
93588
93589},{"../../components/color":157,"../../lib":287,"../../registry":376,"../bar/style_defaults":401,"./attributes":452}],461:[function(_dereq_,module,exports){
93590'use strict';
93591
93592module.exports = function eventData(out, pt, trace, cd, pointNumber) {
93593 // standard cartesian event data
93594 out.x = 'xVal' in pt ? pt.xVal : pt.x;
93595 out.y = 'yVal' in pt ? pt.yVal : pt.y;
93596
93597 // for 2d histograms
93598 if('zLabelVal' in pt) out.z = pt.zLabelVal;
93599
93600 if(pt.xa) out.xaxis = pt.xa;
93601 if(pt.ya) out.yaxis = pt.ya;
93602
93603 // specific to histogram - CDFs do not have pts (yet?)
93604 if(!(trace.cumulative || {}).enabled) {
93605 var pts = Array.isArray(pointNumber) ?
93606 cd[0].pts[pointNumber[0]][pointNumber[1]] :
93607 cd[pointNumber].pts;
93608
93609 out.pointNumbers = pts;
93610 out.binNumber = out.pointNumber;
93611 delete out.pointNumber;
93612 delete out.pointIndex;
93613
93614 var pointIndices;
93615 if(trace._indexToPoints) {
93616 pointIndices = [];
93617 for(var i = 0; i < pts.length; i++) {
93618 pointIndices = pointIndices.concat(trace._indexToPoints[pts[i]]);
93619 }
93620 } else {
93621 pointIndices = pts;
93622 }
93623
93624 out.pointIndices = pointIndices;
93625 }
93626
93627 return out;
93628};
93629
93630},{}],462:[function(_dereq_,module,exports){
93631'use strict';
93632
93633var barHover = _dereq_('../bar/hover').hoverPoints;
93634var hoverLabelText = _dereq_('../../plots/cartesian/axes').hoverLabelText;
93635
93636module.exports = function hoverPoints(pointData, xval, yval, hovermode, opts) {
93637 var pts = barHover(pointData, xval, yval, hovermode, opts);
93638
93639 if(!pts) return;
93640
93641 pointData = pts[0];
93642 var di = pointData.cd[pointData.index];
93643 var trace = pointData.cd[0].trace;
93644
93645 if(!trace.cumulative.enabled) {
93646 var posLetter = trace.orientation === 'h' ? 'y' : 'x';
93647
93648 pointData[posLetter + 'Label'] = hoverLabelText(pointData[posLetter + 'a'], [di.ph0, di.ph1], trace[posLetter + 'hoverformat']);
93649 }
93650
93651 return pts;
93652};
93653
93654},{"../../plots/cartesian/axes":334,"../bar/hover":393}],463:[function(_dereq_,module,exports){
93655'use strict';
93656
93657/**
93658 * Histogram has its own attribute, defaults and calc steps,
93659 * but uses bar's plot to display
93660 * and bar's crossTraceCalc (formerly known as setPositions) for stacking and grouping
93661 */
93662
93663/**
93664 * histogram errorBarsOK is debatable, but it's put in for backward compat.
93665 * there are use cases for it - sqrt for a simple histogram works right now,
93666 * constant and % work but they're not so meaningful. I guess it could be cool
93667 * to allow quadrature combination of errors in summed histograms...
93668 */
93669
93670module.exports = {
93671 attributes: _dereq_('./attributes'),
93672 layoutAttributes: _dereq_('../bar/layout_attributes'),
93673 supplyDefaults: _dereq_('./defaults'),
93674 crossTraceDefaults: _dereq_('./cross_trace_defaults'),
93675 supplyLayoutDefaults: _dereq_('../bar/layout_defaults'),
93676 calc: _dereq_('./calc').calc,
93677 crossTraceCalc: _dereq_('../bar/cross_trace_calc').crossTraceCalc,
93678 plot: _dereq_('../bar/plot').plot,
93679 layerName: 'barlayer',
93680 style: _dereq_('../bar/style').style,
93681 styleOnSelect: _dereq_('../bar/style').styleOnSelect,
93682 colorbar: _dereq_('../scatter/marker_colorbar'),
93683 hoverPoints: _dereq_('./hover'),
93684 selectPoints: _dereq_('../bar/select'),
93685 eventData: _dereq_('./event_data'),
93686
93687 moduleType: 'trace',
93688 name: 'histogram',
93689 basePlotModule: _dereq_('../../plots/cartesian'),
93690 categories: ['bar-like', 'cartesian', 'svg', 'bar', 'histogram', 'oriented', 'errorBarsOK', 'showLegend'],
93691 meta: {
93692 }
93693};
93694
93695},{"../../plots/cartesian":348,"../bar/cross_trace_calc":389,"../bar/layout_attributes":395,"../bar/layout_defaults":396,"../bar/plot":397,"../bar/select":398,"../bar/style":400,"../scatter/marker_colorbar":515,"./attributes":452,"./calc":457,"./cross_trace_defaults":459,"./defaults":460,"./event_data":461,"./hover":462}],464:[function(_dereq_,module,exports){
93696'use strict';
93697
93698
93699module.exports = {
93700 percent: function(size, total) {
93701 var nMax = size.length;
93702 var norm = 100 / total;
93703 for(var n = 0; n < nMax; n++) size[n] *= norm;
93704 },
93705 probability: function(size, total) {
93706 var nMax = size.length;
93707 for(var n = 0; n < nMax; n++) size[n] /= total;
93708 },
93709 density: function(size, total, inc, yinc) {
93710 var nMax = size.length;
93711 yinc = yinc || 1;
93712 for(var n = 0; n < nMax; n++) size[n] *= inc[n] * yinc;
93713 },
93714 'probability density': function(size, total, inc, yinc) {
93715 var nMax = size.length;
93716 if(yinc) total /= yinc;
93717 for(var n = 0; n < nMax; n++) size[n] *= inc[n] / total;
93718 }
93719};
93720
93721},{}],465:[function(_dereq_,module,exports){
93722'use strict';
93723
93724var histogramAttrs = _dereq_('../histogram/attributes');
93725var makeBinAttrs = _dereq_('../histogram/bin_attributes');
93726var heatmapAttrs = _dereq_('../heatmap/attributes');
93727var baseAttrs = _dereq_('../../plots/attributes');
93728var axisHoverFormat = _dereq_('../../plots/cartesian/axis_format_attributes').axisHoverFormat;
93729var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs;
93730var colorScaleAttrs = _dereq_('../../components/colorscale/attributes');
93731
93732var extendFlat = _dereq_('../../lib/extend').extendFlat;
93733
93734module.exports = extendFlat(
93735 {
93736 x: histogramAttrs.x,
93737 y: histogramAttrs.y,
93738
93739 z: {
93740 valType: 'data_array',
93741 editType: 'calc',
93742 },
93743 marker: {
93744 color: {
93745 valType: 'data_array',
93746 editType: 'calc',
93747 },
93748 editType: 'calc'
93749 },
93750
93751 histnorm: histogramAttrs.histnorm,
93752 histfunc: histogramAttrs.histfunc,
93753 nbinsx: histogramAttrs.nbinsx,
93754 xbins: makeBinAttrs('x'),
93755 nbinsy: histogramAttrs.nbinsy,
93756 ybins: makeBinAttrs('y'),
93757 autobinx: histogramAttrs.autobinx,
93758 autobiny: histogramAttrs.autobiny,
93759
93760 bingroup: extendFlat({}, histogramAttrs.bingroup, {
93761 }),
93762 xbingroup: extendFlat({}, histogramAttrs.bingroup, {
93763 }),
93764 ybingroup: extendFlat({}, histogramAttrs.bingroup, {
93765 }),
93766
93767 xgap: heatmapAttrs.xgap,
93768 ygap: heatmapAttrs.ygap,
93769 zsmooth: heatmapAttrs.zsmooth,
93770 xhoverformat: axisHoverFormat('x'),
93771 yhoverformat: axisHoverFormat('y'),
93772 zhoverformat: axisHoverFormat('z', 1),
93773 hovertemplate: hovertemplateAttrs({}, {keys: 'z'}),
93774 showlegend: extendFlat({}, baseAttrs.showlegend, {dflt: false})
93775 },
93776 colorScaleAttrs('', {cLetter: 'z', autoColorDflt: false})
93777);
93778
93779},{"../../components/colorscale/attributes":164,"../../lib/extend":281,"../../plots/attributes":330,"../../plots/cartesian/axis_format_attributes":337,"../../plots/template_attributes":371,"../heatmap/attributes":437,"../histogram/attributes":452,"../histogram/bin_attributes":454}],466:[function(_dereq_,module,exports){
93780'use strict';
93781
93782var Lib = _dereq_('../../lib');
93783var Axes = _dereq_('../../plots/cartesian/axes');
93784
93785var binFunctions = _dereq_('../histogram/bin_functions');
93786var normFunctions = _dereq_('../histogram/norm_functions');
93787var doAvg = _dereq_('../histogram/average');
93788var getBinSpanLabelRound = _dereq_('../histogram/bin_label_vals');
93789var calcAllAutoBins = _dereq_('../histogram/calc').calcAllAutoBins;
93790
93791module.exports = function calc(gd, trace) {
93792 var xa = Axes.getFromId(gd, trace.xaxis);
93793 var ya = Axes.getFromId(gd, trace.yaxis);
93794
93795 var xcalendar = trace.xcalendar;
93796 var ycalendar = trace.ycalendar;
93797 var xr2c = function(v) { return xa.r2c(v, 0, xcalendar); };
93798 var yr2c = function(v) { return ya.r2c(v, 0, ycalendar); };
93799 var xc2r = function(v) { return xa.c2r(v, 0, xcalendar); };
93800 var yc2r = function(v) { return ya.c2r(v, 0, ycalendar); };
93801
93802 var i, j, n, m;
93803
93804 // calculate the bins
93805 var xBinsAndPos = calcAllAutoBins(gd, trace, xa, 'x');
93806 var xBinSpec = xBinsAndPos[0];
93807 var xPos0 = xBinsAndPos[1];
93808 var yBinsAndPos = calcAllAutoBins(gd, trace, ya, 'y');
93809 var yBinSpec = yBinsAndPos[0];
93810 var yPos0 = yBinsAndPos[1];
93811
93812 var serieslen = trace._length;
93813 if(xPos0.length > serieslen) xPos0.splice(serieslen, xPos0.length - serieslen);
93814 if(yPos0.length > serieslen) yPos0.splice(serieslen, yPos0.length - serieslen);
93815
93816 // make the empty bin array & scale the map
93817 var z = [];
93818 var onecol = [];
93819 var zerocol = [];
93820 var nonuniformBinsX = typeof xBinSpec.size === 'string';
93821 var nonuniformBinsY = typeof yBinSpec.size === 'string';
93822 var xEdges = [];
93823 var yEdges = [];
93824 var xbins = nonuniformBinsX ? xEdges : xBinSpec;
93825 var ybins = nonuniformBinsY ? yEdges : yBinSpec;
93826 var total = 0;
93827 var counts = [];
93828 var inputPoints = [];
93829 var norm = trace.histnorm;
93830 var func = trace.histfunc;
93831 var densitynorm = norm.indexOf('density') !== -1;
93832 var extremefunc = func === 'max' || func === 'min';
93833 var sizeinit = extremefunc ? null : 0;
93834 var binfunc = binFunctions.count;
93835 var normfunc = normFunctions[norm];
93836 var doavg = false;
93837 var xinc = [];
93838 var yinc = [];
93839
93840 // set a binning function other than count?
93841 // for binning functions: check first for 'z',
93842 // then 'mc' in case we had a colored scatter plot
93843 // and want to transfer these colors to the 2D histo
93844 // TODO: axe this, make it the responsibility of the app changing type? or an impliedEdit?
93845 var rawCounterData = ('z' in trace) ?
93846 trace.z :
93847 (('marker' in trace && Array.isArray(trace.marker.color)) ?
93848 trace.marker.color : '');
93849 if(rawCounterData && func !== 'count') {
93850 doavg = func === 'avg';
93851 binfunc = binFunctions[func];
93852 }
93853
93854 // decrease end a little in case of rounding errors
93855 var xBinSize = xBinSpec.size;
93856 var xBinStart = xr2c(xBinSpec.start);
93857 var xBinEnd = xr2c(xBinSpec.end) +
93858 (xBinStart - Axes.tickIncrement(xBinStart, xBinSize, false, xcalendar)) / 1e6;
93859
93860 for(i = xBinStart; i < xBinEnd; i = Axes.tickIncrement(i, xBinSize, false, xcalendar)) {
93861 onecol.push(sizeinit);
93862 xEdges.push(i);
93863 if(doavg) zerocol.push(0);
93864 }
93865 xEdges.push(i);
93866
93867 var nx = onecol.length;
93868 var dx = (i - xBinStart) / nx;
93869 var x0 = xc2r(xBinStart + dx / 2);
93870
93871 var yBinSize = yBinSpec.size;
93872 var yBinStart = yr2c(yBinSpec.start);
93873 var yBinEnd = yr2c(yBinSpec.end) +
93874 (yBinStart - Axes.tickIncrement(yBinStart, yBinSize, false, ycalendar)) / 1e6;
93875
93876 for(i = yBinStart; i < yBinEnd; i = Axes.tickIncrement(i, yBinSize, false, ycalendar)) {
93877 z.push(onecol.slice());
93878 yEdges.push(i);
93879 var ipCol = new Array(nx);
93880 for(j = 0; j < nx; j++) ipCol[j] = [];
93881 inputPoints.push(ipCol);
93882 if(doavg) counts.push(zerocol.slice());
93883 }
93884 yEdges.push(i);
93885
93886 var ny = z.length;
93887 var dy = (i - yBinStart) / ny;
93888 var y0 = yc2r(yBinStart + dy / 2);
93889
93890 if(densitynorm) {
93891 xinc = makeIncrements(onecol.length, xbins, dx, nonuniformBinsX);
93892 yinc = makeIncrements(z.length, ybins, dy, nonuniformBinsY);
93893 }
93894
93895 // for date axes we need bin bounds to be calcdata. For nonuniform bins
93896 // we already have this, but uniform with start/end/size they're still strings.
93897 if(!nonuniformBinsX && xa.type === 'date') xbins = binsToCalc(xr2c, xbins);
93898 if(!nonuniformBinsY && ya.type === 'date') ybins = binsToCalc(yr2c, ybins);
93899
93900 // put data into bins
93901 var uniqueValsPerX = true;
93902 var uniqueValsPerY = true;
93903 var xVals = new Array(nx);
93904 var yVals = new Array(ny);
93905 var xGapLow = Infinity;
93906 var xGapHigh = Infinity;
93907 var yGapLow = Infinity;
93908 var yGapHigh = Infinity;
93909 for(i = 0; i < serieslen; i++) {
93910 var xi = xPos0[i];
93911 var yi = yPos0[i];
93912 n = Lib.findBin(xi, xbins);
93913 m = Lib.findBin(yi, ybins);
93914 if(n >= 0 && n < nx && m >= 0 && m < ny) {
93915 total += binfunc(n, i, z[m], rawCounterData, counts[m]);
93916 inputPoints[m][n].push(i);
93917
93918 if(uniqueValsPerX) {
93919 if(xVals[n] === undefined) xVals[n] = xi;
93920 else if(xVals[n] !== xi) uniqueValsPerX = false;
93921 }
93922 if(uniqueValsPerY) {
93923 if(yVals[m] === undefined) yVals[m] = yi;
93924 else if(yVals[m] !== yi) uniqueValsPerY = false;
93925 }
93926
93927 xGapLow = Math.min(xGapLow, xi - xEdges[n]);
93928 xGapHigh = Math.min(xGapHigh, xEdges[n + 1] - xi);
93929 yGapLow = Math.min(yGapLow, yi - yEdges[m]);
93930 yGapHigh = Math.min(yGapHigh, yEdges[m + 1] - yi);
93931 }
93932 }
93933 // normalize, if needed
93934 if(doavg) {
93935 for(m = 0; m < ny; m++) total += doAvg(z[m], counts[m]);
93936 }
93937 if(normfunc) {
93938 for(m = 0; m < ny; m++) normfunc(z[m], total, xinc, yinc[m]);
93939 }
93940
93941 return {
93942 x: xPos0,
93943 xRanges: getRanges(xEdges, uniqueValsPerX && xVals, xGapLow, xGapHigh, xa, xcalendar),
93944 x0: x0,
93945 dx: dx,
93946 y: yPos0,
93947 yRanges: getRanges(yEdges, uniqueValsPerY && yVals, yGapLow, yGapHigh, ya, ycalendar),
93948 y0: y0,
93949 dy: dy,
93950 z: z,
93951 pts: inputPoints
93952 };
93953};
93954
93955function makeIncrements(len, bins, dv, nonuniform) {
93956 var out = new Array(len);
93957 var i;
93958 if(nonuniform) {
93959 for(i = 0; i < len; i++) out[i] = 1 / (bins[i + 1] - bins[i]);
93960 } else {
93961 var inc = 1 / dv;
93962 for(i = 0; i < len; i++) out[i] = inc;
93963 }
93964 return out;
93965}
93966
93967function binsToCalc(r2c, bins) {
93968 return {
93969 start: r2c(bins.start),
93970 end: r2c(bins.end),
93971 size: bins.size
93972 };
93973}
93974
93975function getRanges(edges, uniqueVals, gapLow, gapHigh, ax, calendar) {
93976 var i;
93977 var len = edges.length - 1;
93978 var out = new Array(len);
93979 var roundFn = getBinSpanLabelRound(gapLow, gapHigh, edges, ax, calendar);
93980
93981 for(i = 0; i < len; i++) {
93982 var v = (uniqueVals || [])[i];
93983 out[i] = v === undefined ?
93984 [roundFn(edges[i]), roundFn(edges[i + 1], true)] :
93985 [v, v];
93986 }
93987 return out;
93988}
93989
93990},{"../../lib":287,"../../plots/cartesian/axes":334,"../histogram/average":453,"../histogram/bin_functions":455,"../histogram/bin_label_vals":456,"../histogram/calc":457,"../histogram/norm_functions":464}],467:[function(_dereq_,module,exports){
93991'use strict';
93992
93993var Lib = _dereq_('../../lib');
93994
93995var handleSampleDefaults = _dereq_('./sample_defaults');
93996var handleStyleDefaults = _dereq_('../heatmap/style_defaults');
93997var colorscaleDefaults = _dereq_('../../components/colorscale/defaults');
93998var attributes = _dereq_('./attributes');
93999
94000
94001module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
94002 function coerce(attr, dflt) {
94003 return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
94004 }
94005
94006 handleSampleDefaults(traceIn, traceOut, coerce, layout);
94007 if(traceOut.visible === false) return;
94008
94009 handleStyleDefaults(traceIn, traceOut, coerce, layout);
94010 colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: '', cLetter: 'z'});
94011 coerce('hovertemplate');
94012 coerce('xhoverformat');
94013 coerce('yhoverformat');
94014};
94015
94016},{"../../components/colorscale/defaults":167,"../../lib":287,"../heatmap/style_defaults":450,"./attributes":465,"./sample_defaults":470}],468:[function(_dereq_,module,exports){
94017'use strict';
94018
94019var heatmapHover = _dereq_('../heatmap/hover');
94020var hoverLabelText = _dereq_('../../plots/cartesian/axes').hoverLabelText;
94021
94022module.exports = function hoverPoints(pointData, xval, yval, hovermode, opts) {
94023 var pts = heatmapHover(pointData, xval, yval, hovermode, opts);
94024
94025 if(!pts) return;
94026
94027 pointData = pts[0];
94028 var indices = pointData.index;
94029 var ny = indices[0];
94030 var nx = indices[1];
94031 var cd0 = pointData.cd[0];
94032 var trace = cd0.trace;
94033 var xRange = cd0.xRanges[nx];
94034 var yRange = cd0.yRanges[ny];
94035
94036 pointData.xLabel = hoverLabelText(pointData.xa, [xRange[0], xRange[1]], trace.xhoverformat);
94037 pointData.yLabel = hoverLabelText(pointData.ya, [yRange[0], yRange[1]], trace.yhoverformat);
94038
94039 return pts;
94040};
94041
94042},{"../../plots/cartesian/axes":334,"../heatmap/hover":444}],469:[function(_dereq_,module,exports){
94043'use strict';
94044
94045module.exports = {
94046 attributes: _dereq_('./attributes'),
94047 supplyDefaults: _dereq_('./defaults'),
94048 crossTraceDefaults: _dereq_('../histogram/cross_trace_defaults'),
94049 calc: _dereq_('../heatmap/calc'),
94050 plot: _dereq_('../heatmap/plot'),
94051 layerName: 'heatmaplayer',
94052 colorbar: _dereq_('../heatmap/colorbar'),
94053 style: _dereq_('../heatmap/style'),
94054 hoverPoints: _dereq_('./hover'),
94055 eventData: _dereq_('../histogram/event_data'),
94056
94057 moduleType: 'trace',
94058 name: 'histogram2d',
94059 basePlotModule: _dereq_('../../plots/cartesian'),
94060 categories: ['cartesian', 'svg', '2dMap', 'histogram', 'showLegend'],
94061 meta: {
94062 }
94063};
94064
94065},{"../../plots/cartesian":348,"../heatmap/calc":438,"../heatmap/colorbar":440,"../heatmap/plot":448,"../heatmap/style":449,"../histogram/cross_trace_defaults":459,"../histogram/event_data":461,"./attributes":465,"./defaults":467,"./hover":468}],470:[function(_dereq_,module,exports){
94066'use strict';
94067
94068var Registry = _dereq_('../../registry');
94069var Lib = _dereq_('../../lib');
94070
94071module.exports = function handleSampleDefaults(traceIn, traceOut, coerce, layout) {
94072 var x = coerce('x');
94073 var y = coerce('y');
94074 var xlen = Lib.minRowLength(x);
94075 var ylen = Lib.minRowLength(y);
94076
94077 // we could try to accept x0 and dx, etc...
94078 // but that's a pretty weird use case.
94079 // for now require both x and y explicitly specified.
94080 if(!xlen || !ylen) {
94081 traceOut.visible = false;
94082 return;
94083 }
94084
94085 traceOut._length = Math.min(xlen, ylen);
94086
94087 var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults');
94088 handleCalendarDefaults(traceIn, traceOut, ['x', 'y'], layout);
94089
94090 // if marker.color is an array, we can use it in aggregation instead of z
94091 var hasAggregationData = coerce('z') || coerce('marker.color');
94092
94093 if(hasAggregationData) coerce('histfunc');
94094 coerce('histnorm');
94095
94096 // Note: bin defaults are now handled in Histogram2D.crossTraceDefaults
94097 // autobin(x|y) are only included here to appease Plotly.validate
94098 coerce('autobinx');
94099 coerce('autobiny');
94100};
94101
94102},{"../../lib":287,"../../registry":376}],471:[function(_dereq_,module,exports){
94103'use strict';
94104
94105var histogram2dAttrs = _dereq_('../histogram2d/attributes');
94106var contourAttrs = _dereq_('../contour/attributes');
94107var colorScaleAttrs = _dereq_('../../components/colorscale/attributes');
94108var axisHoverFormat = _dereq_('../../plots/cartesian/axis_format_attributes').axisHoverFormat;
94109
94110var extendFlat = _dereq_('../../lib/extend').extendFlat;
94111
94112module.exports = extendFlat({
94113 x: histogram2dAttrs.x,
94114 y: histogram2dAttrs.y,
94115 z: histogram2dAttrs.z,
94116 marker: histogram2dAttrs.marker,
94117
94118 histnorm: histogram2dAttrs.histnorm,
94119 histfunc: histogram2dAttrs.histfunc,
94120 nbinsx: histogram2dAttrs.nbinsx,
94121 xbins: histogram2dAttrs.xbins,
94122 nbinsy: histogram2dAttrs.nbinsy,
94123 ybins: histogram2dAttrs.ybins,
94124 autobinx: histogram2dAttrs.autobinx,
94125 autobiny: histogram2dAttrs.autobiny,
94126
94127 bingroup: histogram2dAttrs.bingroup,
94128 xbingroup: histogram2dAttrs.xbingroup,
94129 ybingroup: histogram2dAttrs.ybingroup,
94130
94131 autocontour: contourAttrs.autocontour,
94132 ncontours: contourAttrs.ncontours,
94133 contours: contourAttrs.contours,
94134 line: {
94135 color: contourAttrs.line.color,
94136 width: extendFlat({}, contourAttrs.line.width, {
94137 dflt: 0.5,
94138 }),
94139 dash: contourAttrs.line.dash,
94140 smoothing: contourAttrs.line.smoothing,
94141 editType: 'plot'
94142 },
94143 xhoverformat: axisHoverFormat('x'),
94144 yhoverformat: axisHoverFormat('y'),
94145 zhoverformat: axisHoverFormat('z', 1),
94146 hovertemplate: histogram2dAttrs.hovertemplate
94147},
94148 colorScaleAttrs('', {
94149 cLetter: 'z',
94150 editTypeOverride: 'calc'
94151 })
94152);
94153
94154},{"../../components/colorscale/attributes":164,"../../lib/extend":281,"../../plots/cartesian/axis_format_attributes":337,"../contour/attributes":415,"../histogram2d/attributes":465}],472:[function(_dereq_,module,exports){
94155'use strict';
94156
94157var Lib = _dereq_('../../lib');
94158
94159var handleSampleDefaults = _dereq_('../histogram2d/sample_defaults');
94160var handleContoursDefaults = _dereq_('../contour/contours_defaults');
94161var handleStyleDefaults = _dereq_('../contour/style_defaults');
94162var attributes = _dereq_('./attributes');
94163
94164
94165module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
94166 function coerce(attr, dflt) {
94167 return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
94168 }
94169
94170 function coerce2(attr) {
94171 return Lib.coerce2(traceIn, traceOut, attributes, attr);
94172 }
94173
94174 handleSampleDefaults(traceIn, traceOut, coerce, layout);
94175 if(traceOut.visible === false) return;
94176
94177 handleContoursDefaults(traceIn, traceOut, coerce, coerce2);
94178 handleStyleDefaults(traceIn, traceOut, coerce, layout);
94179 coerce('hovertemplate');
94180 coerce('xhoverformat');
94181 coerce('yhoverformat');
94182};
94183
94184},{"../../lib":287,"../contour/contours_defaults":422,"../contour/style_defaults":436,"../histogram2d/sample_defaults":470,"./attributes":471}],473:[function(_dereq_,module,exports){
94185'use strict';
94186
94187module.exports = {
94188 attributes: _dereq_('./attributes'),
94189 supplyDefaults: _dereq_('./defaults'),
94190 crossTraceDefaults: _dereq_('../histogram/cross_trace_defaults'),
94191 calc: _dereq_('../contour/calc'),
94192 plot: _dereq_('../contour/plot').plot,
94193 layerName: 'contourlayer',
94194 style: _dereq_('../contour/style'),
94195 colorbar: _dereq_('../contour/colorbar'),
94196 hoverPoints: _dereq_('../contour/hover'),
94197
94198 moduleType: 'trace',
94199 name: 'histogram2dcontour',
94200 basePlotModule: _dereq_('../../plots/cartesian'),
94201 categories: ['cartesian', 'svg', '2dMap', 'contour', 'histogram', 'showLegend'],
94202 meta: {
94203 }
94204};
94205
94206},{"../../plots/cartesian":348,"../contour/calc":416,"../contour/colorbar":418,"../contour/hover":428,"../contour/plot":433,"../contour/style":435,"../histogram/cross_trace_defaults":459,"./attributes":471,"./defaults":472}],474:[function(_dereq_,module,exports){
94207'use strict';
94208
94209var baseAttrs = _dereq_('../../plots/attributes');
94210var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs;
94211var extendFlat = _dereq_('../../lib/extend').extendFlat;
94212var colormodel = _dereq_('./constants').colormodel;
94213
94214var cm = ['rgb', 'rgba', 'rgba256', 'hsl', 'hsla'];
94215var zminDesc = [];
94216var zmaxDesc = [];
94217for(var i = 0; i < cm.length; i++) {
94218 var cr = colormodel[cm[i]];
94219 zminDesc.push('For the `' + cm[i] + '` colormodel, it is [' + (cr.zminDflt || cr.min).join(', ') + '].');
94220 zmaxDesc.push('For the `' + cm[i] + '` colormodel, it is [' + (cr.zmaxDflt || cr.max).join(', ') + '].');
94221}
94222
94223module.exports = extendFlat({
94224 source: {
94225 valType: 'string',
94226 editType: 'calc',
94227 },
94228 z: {
94229 valType: 'data_array',
94230 editType: 'calc',
94231 },
94232 colormodel: {
94233 valType: 'enumerated',
94234 values: cm,
94235 editType: 'calc',
94236 },
94237 zsmooth: {
94238 valType: 'enumerated',
94239 values: ['fast', false],
94240 dflt: false,
94241 editType: 'plot',
94242 },
94243 zmin: {
94244 valType: 'info_array',
94245 items: [
94246 {valType: 'number', editType: 'calc'},
94247 {valType: 'number', editType: 'calc'},
94248 {valType: 'number', editType: 'calc'},
94249 {valType: 'number', editType: 'calc'}
94250 ],
94251 editType: 'calc',
94252 },
94253 zmax: {
94254 valType: 'info_array',
94255 items: [
94256 {valType: 'number', editType: 'calc'},
94257 {valType: 'number', editType: 'calc'},
94258 {valType: 'number', editType: 'calc'},
94259 {valType: 'number', editType: 'calc'}
94260 ],
94261 editType: 'calc',
94262 },
94263 x0: {
94264 valType: 'any',
94265 dflt: 0,
94266 editType: 'calc+clearAxisTypes',
94267 },
94268 y0: {
94269 valType: 'any',
94270 dflt: 0,
94271 editType: 'calc+clearAxisTypes',
94272 },
94273 dx: {
94274 valType: 'number',
94275 dflt: 1,
94276 editType: 'calc',
94277 },
94278 dy: {
94279 valType: 'number',
94280 dflt: 1,
94281 editType: 'calc',
94282 },
94283 text: {
94284 valType: 'data_array',
94285 editType: 'plot',
94286 },
94287 hovertext: {
94288 valType: 'data_array',
94289 editType: 'plot',
94290 },
94291 hoverinfo: extendFlat({}, baseAttrs.hoverinfo, {
94292 flags: ['x', 'y', 'z', 'color', 'name', 'text'],
94293 dflt: 'x+y+z+text+name'
94294 }),
94295 hovertemplate: hovertemplateAttrs({}, {
94296 keys: ['z', 'color', 'colormodel']
94297 }),
94298
94299 transforms: undefined
94300});
94301
94302},{"../../lib/extend":281,"../../plots/attributes":330,"../../plots/template_attributes":371,"./constants":476}],475:[function(_dereq_,module,exports){
94303'use strict';
94304
94305var Lib = _dereq_('../../lib');
94306var constants = _dereq_('./constants');
94307var isNumeric = _dereq_('fast-isnumeric');
94308var Axes = _dereq_('../../plots/cartesian/axes');
94309var maxRowLength = _dereq_('../../lib').maxRowLength;
94310var getImageSize = _dereq_('./helpers').getImageSize;
94311
94312module.exports = function calc(gd, trace) {
94313 var h;
94314 var w;
94315 if(trace._hasZ) {
94316 h = trace.z.length;
94317 w = maxRowLength(trace.z);
94318 } else if(trace._hasSource) {
94319 var size = getImageSize(trace.source);
94320 h = size.height;
94321 w = size.width;
94322 }
94323
94324 var xa = Axes.getFromId(gd, trace.xaxis || 'x');
94325 var ya = Axes.getFromId(gd, trace.yaxis || 'y');
94326
94327 var x0 = xa.d2c(trace.x0) - trace.dx / 2;
94328 var y0 = ya.d2c(trace.y0) - trace.dy / 2;
94329
94330 // Set axis range
94331 var i;
94332 var xrange = [x0, x0 + w * trace.dx];
94333 var yrange = [y0, y0 + h * trace.dy];
94334 if(xa && xa.type === 'log') for(i = 0; i < w; i++) xrange.push(x0 + i * trace.dx);
94335 if(ya && ya.type === 'log') for(i = 0; i < h; i++) yrange.push(y0 + i * trace.dy);
94336 trace._extremes[xa._id] = Axes.findExtremes(xa, xrange);
94337 trace._extremes[ya._id] = Axes.findExtremes(ya, yrange);
94338 trace._scaler = makeScaler(trace);
94339
94340 var cd0 = {
94341 x0: x0,
94342 y0: y0,
94343 z: trace.z,
94344 w: w,
94345 h: h
94346 };
94347 return [cd0];
94348};
94349
94350function scale(zero, ratio, min, max) {
94351 return function(c) {
94352 return Lib.constrain((c - zero) * ratio, min, max);
94353 };
94354}
94355
94356function constrain(min, max) {
94357 return function(c) { return Lib.constrain(c, min, max);};
94358}
94359
94360// Generate a function to scale color components according to zmin/zmax and the colormodel
94361function makeScaler(trace) {
94362 var cr = constants.colormodel[trace.colormodel];
94363 var colormodel = (cr.colormodel || trace.colormodel);
94364 var n = colormodel.length;
94365
94366 trace._sArray = [];
94367 // Loop over all color components
94368 for(var k = 0; k < n; k++) {
94369 if(cr.min[k] !== trace.zmin[k] || cr.max[k] !== trace.zmax[k]) {
94370 trace._sArray.push(scale(
94371 trace.zmin[k],
94372 (cr.max[k] - cr.min[k]) / (trace.zmax[k] - trace.zmin[k]),
94373 cr.min[k],
94374 cr.max[k]
94375 ));
94376 } else {
94377 trace._sArray.push(constrain(cr.min[k], cr.max[k]));
94378 }
94379 }
94380
94381 return function(pixel) {
94382 var c = pixel.slice(0, n);
94383 for(var k = 0; k < n; k++) {
94384 var ck = c[k];
94385 if(!isNumeric(ck)) return false;
94386 c[k] = trace._sArray[k](ck);
94387 }
94388 return c;
94389 };
94390}
94391
94392},{"../../lib":287,"../../plots/cartesian/axes":334,"./constants":476,"./helpers":479,"fast-isnumeric":33}],476:[function(_dereq_,module,exports){
94393'use strict';
94394
94395module.exports = {
94396 colormodel: {
94397 // min and max define the numerical range accepted in CSS
94398 // If z(min|max)Dflt are not defined, z(min|max) will default to min/max
94399 rgb: {
94400 min: [0, 0, 0],
94401 max: [255, 255, 255],
94402 fmt: function(c) {return c.slice(0, 3);},
94403 suffix: ['', '', '']
94404 },
94405 rgba: {
94406 min: [0, 0, 0, 0],
94407 max: [255, 255, 255, 1],
94408 fmt: function(c) {return c.slice(0, 4);},
94409 suffix: ['', '', '', '']
94410 },
94411 rgba256: {
94412 colormodel: 'rgba', // because rgba256 is not an accept colormodel in CSS
94413 zminDflt: [0, 0, 0, 0],
94414 zmaxDflt: [255, 255, 255, 255],
94415 min: [0, 0, 0, 0],
94416 max: [255, 255, 255, 1],
94417 fmt: function(c) {return c.slice(0, 4);},
94418 suffix: ['', '', '', '']
94419 },
94420 hsl: {
94421 min: [0, 0, 0],
94422 max: [360, 100, 100],
94423 fmt: function(c) {
94424 var p = c.slice(0, 3);
94425 p[1] = p[1] + '%';
94426 p[2] = p[2] + '%';
94427 return p;
94428 },
94429 suffix: ['°', '%', '%']
94430 },
94431 hsla: {
94432 min: [0, 0, 0, 0],
94433 max: [360, 100, 100, 1],
94434 fmt: function(c) {
94435 var p = c.slice(0, 4);
94436 p[1] = p[1] + '%';
94437 p[2] = p[2] + '%';
94438 return p;
94439 },
94440 suffix: ['°', '%', '%', '']
94441 }
94442 },
94443 // For pixelated image rendering
94444 // http://phrogz.net/tmp/canvas_image_zoom.html
94445 // https://developer.mozilla.org/en-US/docs/Web/CSS/image-rendering
94446 pixelatedStyle: [
94447 'image-rendering: optimizeSpeed',
94448 'image-rendering: -moz-crisp-edges',
94449 'image-rendering: -o-crisp-edges',
94450 'image-rendering: -webkit-optimize-contrast',
94451 'image-rendering: optimize-contrast',
94452 'image-rendering: crisp-edges',
94453 'image-rendering: pixelated',
94454 ''
94455 ].join('; ')
94456};
94457
94458},{}],477:[function(_dereq_,module,exports){
94459'use strict';
94460
94461var Lib = _dereq_('../../lib');
94462var attributes = _dereq_('./attributes');
94463var constants = _dereq_('./constants');
94464var dataUri = _dereq_('../../snapshot/helpers').IMAGE_URL_PREFIX;
94465
94466module.exports = function supplyDefaults(traceIn, traceOut) {
94467 function coerce(attr, dflt) {
94468 return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
94469 }
94470 coerce('source');
94471 // sanitize source to only allow for data URI representing images
94472 if(traceOut.source && !traceOut.source.match(dataUri)) delete traceOut.source;
94473 traceOut._hasSource = !!traceOut.source;
94474
94475 var z = coerce('z');
94476 traceOut._hasZ = !(z === undefined || !z.length || !z[0] || !z[0].length);
94477 if(!traceOut._hasZ && !traceOut._hasSource) {
94478 traceOut.visible = false;
94479 return;
94480 }
94481
94482 coerce('x0');
94483 coerce('y0');
94484 coerce('dx');
94485 coerce('dy');
94486
94487 var cm;
94488 if(traceOut._hasZ) {
94489 coerce('colormodel', 'rgb');
94490 cm = constants.colormodel[traceOut.colormodel];
94491 coerce('zmin', (cm.zminDflt || cm.min));
94492 coerce('zmax', (cm.zmaxDflt || cm.max));
94493 } else if(traceOut._hasSource) {
94494 traceOut.colormodel = 'rgba256';
94495 cm = constants.colormodel[traceOut.colormodel];
94496 traceOut.zmin = cm.zminDflt;
94497 traceOut.zmax = cm.zmaxDflt;
94498 }
94499
94500 coerce('zsmooth');
94501 coerce('text');
94502 coerce('hovertext');
94503 coerce('hovertemplate');
94504
94505 traceOut._length = null;
94506};
94507
94508},{"../../lib":287,"../../snapshot/helpers":380,"./attributes":474,"./constants":476}],478:[function(_dereq_,module,exports){
94509'use strict';
94510
94511module.exports = function eventData(out, pt) {
94512 if('xVal' in pt) out.x = pt.xVal;
94513 if('yVal' in pt) out.y = pt.yVal;
94514 if(pt.xa) out.xaxis = pt.xa;
94515 if(pt.ya) out.yaxis = pt.ya;
94516 out.color = pt.color;
94517 out.colormodel = pt.trace.colormodel;
94518 if(!out.z) out.z = pt.color;
94519 return out;
94520};
94521
94522},{}],479:[function(_dereq_,module,exports){
94523'use strict';
94524
94525var probeSync = _dereq_('probe-image-size/sync');
94526var dataUri = _dereq_('../../snapshot/helpers').IMAGE_URL_PREFIX;
94527var Buffer = _dereq_('buffer/').Buffer; // note: the trailing slash is important!
94528
94529exports.getImageSize = function(src) {
94530 var data = src.replace(dataUri, '');
94531 var buff = new Buffer(data, 'base64');
94532 return probeSync(buff);
94533};
94534
94535},{"../../snapshot/helpers":380,"buffer/":28,"probe-image-size/sync":97}],480:[function(_dereq_,module,exports){
94536'use strict';
94537
94538var Fx = _dereq_('../../components/fx');
94539var Lib = _dereq_('../../lib');
94540var constants = _dereq_('./constants');
94541
94542module.exports = function hoverPoints(pointData, xval, yval) {
94543 var cd0 = pointData.cd[0];
94544 var trace = cd0.trace;
94545 var xa = pointData.xa;
94546 var ya = pointData.ya;
94547
94548 // Return early if not on image
94549 if(Fx.inbox(xval - cd0.x0, xval - (cd0.x0 + cd0.w * trace.dx), 0) > 0 ||
94550 Fx.inbox(yval - cd0.y0, yval - (cd0.y0 + cd0.h * trace.dy), 0) > 0) {
94551 return;
94552 }
94553
94554 // Find nearest pixel's index
94555 var nx = Math.floor((xval - cd0.x0) / trace.dx);
94556 var ny = Math.floor(Math.abs(yval - cd0.y0) / trace.dy);
94557
94558 var pixel;
94559 if(trace._hasZ) {
94560 pixel = cd0.z[ny][nx];
94561 } else if(trace._hasSource) {
94562 pixel = trace._canvas.el.getContext('2d').getImageData(nx, ny, 1, 1).data;
94563 }
94564
94565 // return early if pixel is undefined
94566 if(!pixel) return;
94567
94568 var hoverinfo = cd0.hi || trace.hoverinfo;
94569 var fmtColor;
94570 if(hoverinfo) {
94571 var parts = hoverinfo.split('+');
94572 if(parts.indexOf('all') !== -1) parts = ['color'];
94573 if(parts.indexOf('color') !== -1) fmtColor = true;
94574 }
94575
94576 var cr = constants.colormodel[trace.colormodel];
94577 var colormodel = cr.colormodel || trace.colormodel;
94578 var dims = colormodel.length;
94579 var c = trace._scaler(pixel);
94580 var s = cr.suffix;
94581
94582 var colorstring = [];
94583 if(trace.hovertemplate || fmtColor) {
94584 colorstring.push('[' + [c[0] + s[0], c[1] + s[1], c[2] + s[2]].join(', '));
94585 if(dims === 4) colorstring.push(', ' + c[3] + s[3]);
94586 colorstring.push(']');
94587 colorstring = colorstring.join('');
94588 pointData.extraText = colormodel.toUpperCase() + ': ' + colorstring;
94589 }
94590
94591 var text;
94592 if(Array.isArray(trace.hovertext) && Array.isArray(trace.hovertext[ny])) {
94593 text = trace.hovertext[ny][nx];
94594 } else if(Array.isArray(trace.text) && Array.isArray(trace.text[ny])) {
94595 text = trace.text[ny][nx];
94596 }
94597
94598 // TODO: for color model with 3 dims, display something useful for hovertemplate `%{color[3]}`
94599 var py = ya.c2p(cd0.y0 + (ny + 0.5) * trace.dy);
94600 var xVal = cd0.x0 + (nx + 0.5) * trace.dx;
94601 var yVal = cd0.y0 + (ny + 0.5) * trace.dy;
94602 var zLabel = '[' + pixel.slice(0, trace.colormodel.length).join(', ') + ']';
94603 return [Lib.extendFlat(pointData, {
94604 index: [ny, nx],
94605 x0: xa.c2p(cd0.x0 + nx * trace.dx),
94606 x1: xa.c2p(cd0.x0 + (nx + 1) * trace.dx),
94607 y0: py,
94608 y1: py,
94609 color: c,
94610 xVal: xVal,
94611 xLabelVal: xVal,
94612 yVal: yVal,
94613 yLabelVal: yVal,
94614 zLabelVal: zLabel,
94615 text: text,
94616 hovertemplateLabels: {
94617 'zLabel': zLabel,
94618 'colorLabel': colorstring,
94619 'color[0]Label': c[0] + s[0],
94620 'color[1]Label': c[1] + s[1],
94621 'color[2]Label': c[2] + s[2],
94622 'color[3]Label': c[3] + s[3]
94623 }
94624 })];
94625};
94626
94627},{"../../components/fx":197,"../../lib":287,"./constants":476}],481:[function(_dereq_,module,exports){
94628'use strict';
94629
94630module.exports = {
94631 attributes: _dereq_('./attributes'),
94632 supplyDefaults: _dereq_('./defaults'),
94633 calc: _dereq_('./calc'),
94634 plot: _dereq_('./plot'),
94635 style: _dereq_('./style'),
94636 hoverPoints: _dereq_('./hover'),
94637 eventData: _dereq_('./event_data'),
94638
94639 moduleType: 'trace',
94640 name: 'image',
94641 basePlotModule: _dereq_('../../plots/cartesian'),
94642 categories: ['cartesian', 'svg', '2dMap', 'noSortingByValue'],
94643 animatable: false,
94644 meta: {
94645 }
94646};
94647
94648},{"../../plots/cartesian":348,"./attributes":474,"./calc":475,"./defaults":477,"./event_data":478,"./hover":480,"./plot":482,"./style":483}],482:[function(_dereq_,module,exports){
94649'use strict';
94650
94651var d3 = _dereq_('@plotly/d3');
94652var Lib = _dereq_('../../lib');
94653var strTranslate = Lib.strTranslate;
94654var xmlnsNamespaces = _dereq_('../../constants/xmlns_namespaces');
94655var constants = _dereq_('./constants');
94656
94657var unsupportedBrowsers = Lib.isIOS() || Lib.isSafari() || Lib.isIE();
94658
94659module.exports = function plot(gd, plotinfo, cdimage, imageLayer) {
94660 var xa = plotinfo.xaxis;
94661 var ya = plotinfo.yaxis;
94662
94663 var supportsPixelatedImage = !(unsupportedBrowsers || gd._context._exportedPlot);
94664
94665 Lib.makeTraceGroups(imageLayer, cdimage, 'im').each(function(cd) {
94666 var plotGroup = d3.select(this);
94667 var cd0 = cd[0];
94668 var trace = cd0.trace;
94669 var realImage = (
94670 ((trace.zsmooth === 'fast') || (trace.zsmooth === false && supportsPixelatedImage)) &&
94671 !trace._hasZ && trace._hasSource && xa.type === 'linear' && ya.type === 'linear'
94672 );
94673 trace._realImage = realImage;
94674
94675 var z = cd0.z;
94676 var x0 = cd0.x0;
94677 var y0 = cd0.y0;
94678 var w = cd0.w;
94679 var h = cd0.h;
94680 var dx = trace.dx;
94681 var dy = trace.dy;
94682
94683 var left, right, temp, top, bottom, i;
94684 // in case of log of a negative
94685 i = 0;
94686 while(left === undefined && i < w) {
94687 left = xa.c2p(x0 + i * dx);
94688 i++;
94689 }
94690 i = w;
94691 while(right === undefined && i > 0) {
94692 right = xa.c2p(x0 + i * dx);
94693 i--;
94694 }
94695 i = 0;
94696 while(top === undefined && i < h) {
94697 top = ya.c2p(y0 + i * dy);
94698 i++;
94699 }
94700 i = h;
94701 while(bottom === undefined && i > 0) {
94702 bottom = ya.c2p(y0 + i * dy);
94703 i--;
94704 }
94705
94706 if(right < left) {
94707 temp = right;
94708 right = left;
94709 left = temp;
94710 }
94711
94712 if(bottom < top) {
94713 temp = top;
94714 top = bottom;
94715 bottom = temp;
94716 }
94717
94718 // Reduce image size when zoomed in to save memory
94719 if(!realImage) {
94720 var extra = 0.5; // half the axis size
94721 left = Math.max(-extra * xa._length, left);
94722 right = Math.min((1 + extra) * xa._length, right);
94723 top = Math.max(-extra * ya._length, top);
94724 bottom = Math.min((1 + extra) * ya._length, bottom);
94725 }
94726
94727 var imageWidth = Math.round(right - left);
94728 var imageHeight = Math.round(bottom - top);
94729
94730 // if image is entirely off-screen, don't even draw it
94731 var isOffScreen = (imageWidth <= 0 || imageHeight <= 0);
94732 if(isOffScreen) {
94733 var noImage = plotGroup.selectAll('image').data([]);
94734 noImage.exit().remove();
94735 return;
94736 }
94737
94738 // Create a new canvas and draw magnified pixels on it
94739 function drawMagnifiedPixelsOnCanvas(readPixel) {
94740 var canvas = document.createElement('canvas');
94741 canvas.width = imageWidth;
94742 canvas.height = imageHeight;
94743 var context = canvas.getContext('2d');
94744
94745 var ipx = function(i) {return Lib.constrain(Math.round(xa.c2p(x0 + i * dx) - left), 0, imageWidth);};
94746 var jpx = function(j) {return Lib.constrain(Math.round(ya.c2p(y0 + j * dy) - top), 0, imageHeight);};
94747
94748 var cr = constants.colormodel[trace.colormodel];
94749 var colormodel = (cr.colormodel || trace.colormodel);
94750 var fmt = cr.fmt;
94751 var c;
94752 for(i = 0; i < cd0.w; i++) {
94753 var ipx0 = ipx(i); var ipx1 = ipx(i + 1);
94754 if(ipx1 === ipx0 || isNaN(ipx1) || isNaN(ipx0)) continue;
94755 for(var j = 0; j < cd0.h; j++) {
94756 var jpx0 = jpx(j); var jpx1 = jpx(j + 1);
94757 if(jpx1 === jpx0 || isNaN(jpx1) || isNaN(jpx0) || !readPixel(i, j)) continue;
94758 c = trace._scaler(readPixel(i, j));
94759 if(c) {
94760 context.fillStyle = colormodel + '(' + fmt(c).join(',') + ')';
94761 } else {
94762 // Return a transparent pixel
94763 context.fillStyle = 'rgba(0,0,0,0)';
94764 }
94765 context.fillRect(ipx0, jpx0, ipx1 - ipx0, jpx1 - jpx0);
94766 }
94767 }
94768
94769 return canvas;
94770 }
94771
94772 var image3 = plotGroup.selectAll('image')
94773 .data([cd]);
94774
94775 image3.enter().append('svg:image').attr({
94776 xmlns: xmlnsNamespaces.svg,
94777 preserveAspectRatio: 'none'
94778 });
94779
94780 image3.exit().remove();
94781
94782 var style = (trace.zsmooth === false) ? constants.pixelatedStyle : '';
94783
94784 if(realImage) {
94785 var xRange = Lib.simpleMap(xa.range, xa.r2l);
94786 var yRange = Lib.simpleMap(ya.range, ya.r2l);
94787
94788 var flipX = xRange[1] < xRange[0];
94789 var flipY = yRange[1] > yRange[0];
94790 if(flipX || flipY) {
94791 var tx = left + imageWidth / 2;
94792 var ty = top + imageHeight / 2;
94793 style += 'transform:' +
94794 strTranslate(tx + 'px', ty + 'px') +
94795 'scale(' + (flipX ? -1 : 1) + ',' + (flipY ? -1 : 1) + ')' +
94796 strTranslate(-tx + 'px', -ty + 'px') + ';';
94797 }
94798 }
94799 image3.attr('style', style);
94800
94801 var p = new Promise(function(resolve) {
94802 if(trace._hasZ) {
94803 resolve();
94804 } else if(trace._hasSource) {
94805 // Check if canvas already exists and has the right data
94806 if(
94807 trace._canvas &&
94808 trace._canvas.el.width === w &&
94809 trace._canvas.el.height === h &&
94810 trace._canvas.source === trace.source
94811 ) {
94812 resolve();
94813 } else {
94814 // Create a canvas and transfer image onto it to access pixel information
94815 var canvas = document.createElement('canvas');
94816 canvas.width = w;
94817 canvas.height = h;
94818 var context = canvas.getContext('2d');
94819
94820 trace._image = trace._image || new Image();
94821 var image = trace._image;
94822 image.onload = function() {
94823 context.drawImage(image, 0, 0);
94824 trace._canvas = {
94825 el: canvas,
94826 source: trace.source
94827 };
94828 resolve();
94829 };
94830 image.setAttribute('src', trace.source);
94831 }
94832 }
94833 })
94834 .then(function() {
94835 var href, canvas;
94836 if(trace._hasZ) {
94837 canvas = drawMagnifiedPixelsOnCanvas(function(i, j) {return z[j][i];});
94838 href = canvas.toDataURL('image/png');
94839 } else if(trace._hasSource) {
94840 if(realImage) {
94841 href = trace.source;
94842 } else {
94843 var context = trace._canvas.el.getContext('2d');
94844 var data = context.getImageData(0, 0, w, h).data;
94845 canvas = drawMagnifiedPixelsOnCanvas(function(i, j) {
94846 var index = 4 * (j * w + i);
94847 return [
94848 data[index],
94849 data[index + 1],
94850 data[index + 2],
94851 data[index + 3]
94852 ];
94853 });
94854 href = canvas.toDataURL('image/png');
94855 }
94856 }
94857
94858 image3.attr({
94859 'xlink:href': href,
94860 height: imageHeight,
94861 width: imageWidth,
94862 x: left,
94863 y: top
94864 });
94865 });
94866
94867 gd._promises.push(p);
94868 });
94869};
94870
94871},{"../../constants/xmlns_namespaces":268,"../../lib":287,"./constants":476,"@plotly/d3":20}],483:[function(_dereq_,module,exports){
94872'use strict';
94873
94874var d3 = _dereq_('@plotly/d3');
94875
94876module.exports = function style(gd) {
94877 d3.select(gd).selectAll('.im image')
94878 .style('opacity', function(d) {
94879 return d[0].trace.opacity;
94880 });
94881};
94882
94883},{"@plotly/d3":20}],484:[function(_dereq_,module,exports){
94884'use strict';
94885
94886var baseAttrs = _dereq_('../../plots/attributes');
94887var domainAttrs = _dereq_('../../plots/domain').attributes;
94888var fontAttrs = _dereq_('../../plots/font_attributes');
94889var colorAttrs = _dereq_('../../components/color/attributes');
94890var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs;
94891var texttemplateAttrs = _dereq_('../../plots/template_attributes').texttemplateAttrs;
94892
94893var extendFlat = _dereq_('../../lib/extend').extendFlat;
94894
94895var textFontAttrs = fontAttrs({
94896 editType: 'plot',
94897 arrayOk: true,
94898 colorEditType: 'plot',
94899});
94900
94901module.exports = {
94902 labels: {
94903 valType: 'data_array',
94904 editType: 'calc',
94905 },
94906 // equivalent of x0 and dx, if label is missing
94907 label0: {
94908 valType: 'number',
94909 dflt: 0,
94910 editType: 'calc',
94911 },
94912 dlabel: {
94913 valType: 'number',
94914 dflt: 1,
94915 editType: 'calc',
94916 },
94917
94918 values: {
94919 valType: 'data_array',
94920 editType: 'calc',
94921 },
94922
94923 marker: {
94924 colors: {
94925 valType: 'data_array', // TODO 'color_array' ?
94926 editType: 'calc',
94927 },
94928
94929 line: {
94930 color: {
94931 valType: 'color',
94932 dflt: colorAttrs.defaultLine,
94933 arrayOk: true,
94934 editType: 'style',
94935 },
94936 width: {
94937 valType: 'number',
94938 min: 0,
94939 dflt: 0,
94940 arrayOk: true,
94941 editType: 'style',
94942 },
94943 editType: 'calc'
94944 },
94945 editType: 'calc'
94946 },
94947
94948 text: {
94949 valType: 'data_array',
94950 editType: 'plot',
94951 },
94952 hovertext: {
94953 valType: 'string',
94954 dflt: '',
94955 arrayOk: true,
94956 editType: 'style',
94957 },
94958
94959// 'see eg:'
94960// 'https://www.e-education.psu.edu/natureofgeoinfo/sites/www.e-education.psu.edu.natureofgeoinfo/files/image/hisp_pies.gif',
94961// '(this example involves a map too - may someday be a whole trace type',
94962// 'of its own. but the point is the size of the whole pie is important.)'
94963 scalegroup: {
94964 valType: 'string',
94965 dflt: '',
94966 editType: 'calc',
94967 },
94968
94969 // labels (legend is handled by plots.attributes.showlegend and layout.hiddenlabels)
94970 textinfo: {
94971 valType: 'flaglist',
94972 flags: ['label', 'text', 'value', 'percent'],
94973 extras: ['none'],
94974 editType: 'calc',
94975 },
94976 hoverinfo: extendFlat({}, baseAttrs.hoverinfo, {
94977 flags: ['label', 'text', 'value', 'percent', 'name']
94978 }),
94979 hovertemplate: hovertemplateAttrs({}, {
94980 keys: ['label', 'color', 'value', 'percent', 'text']
94981 }),
94982 texttemplate: texttemplateAttrs({editType: 'plot'}, {
94983 keys: ['label', 'color', 'value', 'percent', 'text']
94984 }),
94985 textposition: {
94986 valType: 'enumerated',
94987 values: ['inside', 'outside', 'auto', 'none'],
94988 dflt: 'auto',
94989 arrayOk: true,
94990 editType: 'plot',
94991 },
94992 textfont: extendFlat({}, textFontAttrs, {
94993 }),
94994 insidetextorientation: {
94995 valType: 'enumerated',
94996 values: ['horizontal', 'radial', 'tangential', 'auto'],
94997 dflt: 'auto',
94998 editType: 'plot',
94999 },
95000 insidetextfont: extendFlat({}, textFontAttrs, {
95001 }),
95002 outsidetextfont: extendFlat({}, textFontAttrs, {
95003 }),
95004 automargin: {
95005 valType: 'boolean',
95006 dflt: false,
95007 editType: 'plot',
95008 },
95009
95010 title: {
95011 text: {
95012 valType: 'string',
95013 dflt: '',
95014 editType: 'plot',
95015 },
95016 font: extendFlat({}, textFontAttrs, {
95017 }),
95018 position: {
95019 valType: 'enumerated',
95020 values: [
95021 'top left', 'top center', 'top right',
95022 'middle center',
95023 'bottom left', 'bottom center', 'bottom right'
95024 ],
95025 editType: 'plot',
95026 },
95027
95028 editType: 'plot'
95029 },
95030
95031 // position and shape
95032 domain: domainAttrs({name: 'pie', trace: true, editType: 'calc'}),
95033
95034 hole: {
95035 valType: 'number',
95036 min: 0,
95037 max: 1,
95038 dflt: 0,
95039 editType: 'calc',
95040 },
95041
95042 // ordering and direction
95043 sort: {
95044 valType: 'boolean',
95045 dflt: true,
95046 editType: 'calc',
95047 },
95048 direction: {
95049 /**
95050 * there are two common conventions, both of which place the first
95051 * (largest, if sorted) slice with its left edge at 12 o'clock but
95052 * succeeding slices follow either cw or ccw from there.
95053 *
95054 * see http://visage.co/data-visualization-101-pie-charts/
95055 */
95056 valType: 'enumerated',
95057 values: ['clockwise', 'counterclockwise'],
95058 dflt: 'counterclockwise',
95059 editType: 'calc',
95060 },
95061 rotation: {
95062 valType: 'number',
95063 min: -360,
95064 max: 360,
95065 dflt: 0,
95066 editType: 'calc',
95067 },
95068
95069 pull: {
95070 valType: 'number',
95071 min: 0,
95072 max: 1,
95073 dflt: 0,
95074 arrayOk: true,
95075 editType: 'calc',
95076 },
95077
95078 _deprecated: {
95079 title: {
95080 valType: 'string',
95081 dflt: '',
95082 editType: 'calc',
95083 },
95084 titlefont: extendFlat({}, textFontAttrs, {
95085 }),
95086 titleposition: {
95087 valType: 'enumerated',
95088 values: [
95089 'top left', 'top center', 'top right',
95090 'middle center',
95091 'bottom left', 'bottom center', 'bottom right'
95092 ],
95093 editType: 'calc',
95094 }
95095 }
95096};
95097
95098},{"../../components/color/attributes":156,"../../lib/extend":281,"../../plots/attributes":330,"../../plots/domain":362,"../../plots/font_attributes":363,"../../plots/template_attributes":371}],485:[function(_dereq_,module,exports){
95099'use strict';
95100
95101var plots = _dereq_('../../plots/plots');
95102
95103exports.name = 'pie';
95104
95105exports.plot = function(gd, traces, transitionOpts, makeOnCompleteCallback) {
95106 plots.plotBasePlot(exports.name, gd, traces, transitionOpts, makeOnCompleteCallback);
95107};
95108
95109exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) {
95110 plots.cleanBasePlot(exports.name, newFullData, newFullLayout, oldFullData, oldFullLayout);
95111};
95112
95113},{"../../plots/plots":369}],486:[function(_dereq_,module,exports){
95114'use strict';
95115
95116var isNumeric = _dereq_('fast-isnumeric');
95117var tinycolor = _dereq_('tinycolor2');
95118
95119var Color = _dereq_('../../components/color');
95120
95121var extendedColorWayList = {};
95122
95123function calc(gd, trace) {
95124 var cd = [];
95125
95126 var fullLayout = gd._fullLayout;
95127 var hiddenLabels = fullLayout.hiddenlabels || [];
95128
95129 var labels = trace.labels;
95130 var colors = trace.marker.colors || [];
95131 var vals = trace.values;
95132 var len = trace._length;
95133 var hasValues = trace._hasValues && len;
95134
95135 var i, pt;
95136
95137 if(trace.dlabel) {
95138 labels = new Array(len);
95139 for(i = 0; i < len; i++) {
95140 labels[i] = String(trace.label0 + i * trace.dlabel);
95141 }
95142 }
95143
95144 var allThisTraceLabels = {};
95145 var pullColor = makePullColorFn(fullLayout['_' + trace.type + 'colormap']);
95146 var vTotal = 0;
95147 var isAggregated = false;
95148
95149 for(i = 0; i < len; i++) {
95150 var v, label, hidden;
95151 if(hasValues) {
95152 v = vals[i];
95153 if(!isNumeric(v)) continue;
95154 v = +v;
95155 if(v < 0) continue;
95156 } else v = 1;
95157
95158 label = labels[i];
95159 if(label === undefined || label === '') label = i;
95160 label = String(label);
95161
95162 var thisLabelIndex = allThisTraceLabels[label];
95163 if(thisLabelIndex === undefined) {
95164 allThisTraceLabels[label] = cd.length;
95165
95166 hidden = hiddenLabels.indexOf(label) !== -1;
95167
95168 if(!hidden) vTotal += v;
95169
95170 cd.push({
95171 v: v,
95172 label: label,
95173 color: pullColor(colors[i], label),
95174 i: i,
95175 pts: [i],
95176 hidden: hidden
95177 });
95178 } else {
95179 isAggregated = true;
95180
95181 pt = cd[thisLabelIndex];
95182 pt.v += v;
95183 pt.pts.push(i);
95184 if(!pt.hidden) vTotal += v;
95185
95186 if(pt.color === false && colors[i]) {
95187 pt.color = pullColor(colors[i], label);
95188 }
95189 }
95190 }
95191
95192 var shouldSort = (trace.type === 'funnelarea') ? isAggregated : trace.sort;
95193 if(shouldSort) cd.sort(function(a, b) { return b.v - a.v; });
95194
95195 // include the sum of all values in the first point
95196 if(cd[0]) cd[0].vTotal = vTotal;
95197
95198 return cd;
95199}
95200
95201function makePullColorFn(colorMap) {
95202 return function pullColor(color, id) {
95203 if(!color) return false;
95204
95205 color = tinycolor(color);
95206 if(!color.isValid()) return false;
95207
95208 color = Color.addOpacity(color, color.getAlpha());
95209 if(!colorMap[id]) colorMap[id] = color;
95210
95211 return color;
95212 };
95213}
95214
95215/*
95216 * `calc` filled in (and collated) explicit colors.
95217 * Now we need to propagate these explicit colors to other traces,
95218 * and fill in default colors.
95219 * This is done after sorting, so we pick defaults
95220 * in the order slices will be displayed
95221 */
95222function crossTraceCalc(gd, plotinfo) { // TODO: should we name the second argument opts?
95223 var desiredType = (plotinfo || {}).type;
95224 if(!desiredType) desiredType = 'pie';
95225
95226 var fullLayout = gd._fullLayout;
95227 var calcdata = gd.calcdata;
95228 var colorWay = fullLayout[desiredType + 'colorway'];
95229 var colorMap = fullLayout['_' + desiredType + 'colormap'];
95230
95231 if(fullLayout['extend' + desiredType + 'colors']) {
95232 colorWay = generateExtendedColors(colorWay, extendedColorWayList);
95233 }
95234 var dfltColorCount = 0;
95235
95236 for(var i = 0; i < calcdata.length; i++) {
95237 var cd = calcdata[i];
95238 var traceType = cd[0].trace.type;
95239 if(traceType !== desiredType) continue;
95240
95241 for(var j = 0; j < cd.length; j++) {
95242 var pt = cd[j];
95243 if(pt.color === false) {
95244 // have we seen this label and assigned a color to it in a previous trace?
95245 if(colorMap[pt.label]) {
95246 pt.color = colorMap[pt.label];
95247 } else {
95248 colorMap[pt.label] = pt.color = colorWay[dfltColorCount % colorWay.length];
95249 dfltColorCount++;
95250 }
95251 }
95252 }
95253 }
95254}
95255
95256/**
95257 * pick a default color from the main default set, augmented by
95258 * itself lighter then darker before repeating
95259 */
95260function generateExtendedColors(colorList, extendedColorWays) {
95261 var i;
95262 var colorString = JSON.stringify(colorList);
95263 var colors = extendedColorWays[colorString];
95264 if(!colors) {
95265 colors = colorList.slice();
95266
95267 for(i = 0; i < colorList.length; i++) {
95268 colors.push(tinycolor(colorList[i]).lighten(20).toHexString());
95269 }
95270
95271 for(i = 0; i < colorList.length; i++) {
95272 colors.push(tinycolor(colorList[i]).darken(20).toHexString());
95273 }
95274 extendedColorWays[colorString] = colors;
95275 }
95276
95277 return colors;
95278}
95279
95280module.exports = {
95281 calc: calc,
95282 crossTraceCalc: crossTraceCalc,
95283
95284 makePullColorFn: makePullColorFn,
95285 generateExtendedColors: generateExtendedColors
95286};
95287
95288},{"../../components/color":157,"fast-isnumeric":33,"tinycolor2":121}],487:[function(_dereq_,module,exports){
95289'use strict';
95290
95291var isNumeric = _dereq_('fast-isnumeric');
95292var Lib = _dereq_('../../lib');
95293var attributes = _dereq_('./attributes');
95294var handleDomainDefaults = _dereq_('../../plots/domain').defaults;
95295var handleText = _dereq_('../bar/defaults').handleText;
95296
95297function handleLabelsAndValues(labels, values) {
95298 var hasLabels = Array.isArray(labels);
95299 var hasValues = Lib.isArrayOrTypedArray(values);
95300 var len = Math.min(
95301 hasLabels ? labels.length : Infinity,
95302 hasValues ? values.length : Infinity
95303 );
95304
95305 if(!isFinite(len)) len = 0;
95306
95307 if(len && hasValues) {
95308 var hasPositive;
95309 for(var i = 0; i < len; i++) {
95310 var v = values[i];
95311 if(isNumeric(v) && v > 0) {
95312 hasPositive = true;
95313 break;
95314 }
95315 }
95316 if(!hasPositive) len = 0;
95317 }
95318
95319 return {
95320 hasLabels: hasLabels,
95321 hasValues: hasValues,
95322 len: len
95323 };
95324}
95325
95326function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
95327 function coerce(attr, dflt) {
95328 return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
95329 }
95330
95331 var labels = coerce('labels');
95332 var values = coerce('values');
95333
95334 var res = handleLabelsAndValues(labels, values);
95335 var len = res.len;
95336 traceOut._hasLabels = res.hasLabels;
95337 traceOut._hasValues = res.hasValues;
95338
95339 if(!traceOut._hasLabels &&
95340 traceOut._hasValues
95341 ) {
95342 coerce('label0');
95343 coerce('dlabel');
95344 }
95345
95346 if(!len) {
95347 traceOut.visible = false;
95348 return;
95349 }
95350 traceOut._length = len;
95351
95352 var lineWidth = coerce('marker.line.width');
95353 if(lineWidth) coerce('marker.line.color');
95354
95355 coerce('marker.colors');
95356
95357 coerce('scalegroup');
95358 // TODO: hole needs to be coerced to the same value within a scaleegroup
95359
95360 var textData = coerce('text');
95361 var textTemplate = coerce('texttemplate');
95362 var textInfo;
95363 if(!textTemplate) textInfo = coerce('textinfo', Array.isArray(textData) ? 'text+percent' : 'percent');
95364
95365 coerce('hovertext');
95366 coerce('hovertemplate');
95367
95368 if(textTemplate || (textInfo && textInfo !== 'none')) {
95369 var textposition = coerce('textposition');
95370 handleText(traceIn, traceOut, layout, coerce, textposition, {
95371 moduleHasSelected: false,
95372 moduleHasUnselected: false,
95373 moduleHasConstrain: false,
95374 moduleHasCliponaxis: false,
95375 moduleHasTextangle: false,
95376 moduleHasInsideanchor: false
95377 });
95378
95379 var hasBoth = Array.isArray(textposition) || textposition === 'auto';
95380 var hasOutside = hasBoth || textposition === 'outside';
95381 if(hasOutside) {
95382 coerce('automargin');
95383 }
95384
95385 if(textposition === 'inside' || textposition === 'auto' || Array.isArray(textposition)) {
95386 coerce('insidetextorientation');
95387 }
95388 }
95389
95390 handleDomainDefaults(traceOut, layout, coerce);
95391
95392 var hole = coerce('hole');
95393 var title = coerce('title.text');
95394 if(title) {
95395 var titlePosition = coerce('title.position', hole ? 'middle center' : 'top center');
95396 if(!hole && titlePosition === 'middle center') traceOut.title.position = 'top center';
95397 Lib.coerceFont(coerce, 'title.font', layout.font);
95398 }
95399
95400 coerce('sort');
95401 coerce('direction');
95402 coerce('rotation');
95403 coerce('pull');
95404}
95405
95406module.exports = {
95407 handleLabelsAndValues: handleLabelsAndValues,
95408 supplyDefaults: supplyDefaults
95409};
95410
95411},{"../../lib":287,"../../plots/domain":362,"../bar/defaults":390,"./attributes":484,"fast-isnumeric":33}],488:[function(_dereq_,module,exports){
95412'use strict';
95413
95414var appendArrayMultiPointValues = _dereq_('../../components/fx/helpers').appendArrayMultiPointValues;
95415
95416// Note: like other eventData routines, this creates the data for hover/unhover/click events
95417// but it has a different API and goes through a totally different pathway.
95418// So to ensure it doesn't get misused, it's not attached to the Pie module.
95419module.exports = function eventData(pt, trace) {
95420 var out = {
95421 curveNumber: trace.index,
95422 pointNumbers: pt.pts,
95423 data: trace._input,
95424 fullData: trace,
95425 label: pt.label,
95426 color: pt.color,
95427 value: pt.v,
95428 percent: pt.percent,
95429 text: pt.text,
95430 bbox: pt.bbox,
95431
95432 // pt.v (and pt.i below) for backward compatibility
95433 v: pt.v
95434 };
95435
95436 // Only include pointNumber if it's unambiguous
95437 if(pt.pts.length === 1) out.pointNumber = out.i = pt.pts[0];
95438
95439 // Add extra data arrays to the output
95440 // notice that this is the multi-point version ('s' on the end!)
95441 // so added data will be arrays matching the pointNumbers array.
95442 appendArrayMultiPointValues(out, trace, pt.pts);
95443
95444 // don't include obsolete fields in new funnelarea traces
95445 if(trace.type === 'funnelarea') {
95446 delete out.v;
95447 delete out.i;
95448 }
95449
95450 return out;
95451};
95452
95453},{"../../components/fx/helpers":193}],489:[function(_dereq_,module,exports){
95454'use strict';
95455
95456var Lib = _dereq_('../../lib');
95457
95458function format(vRounded) {
95459 return (
95460 vRounded.indexOf('e') !== -1 ? vRounded.replace(/[.]?0+e/, 'e') :
95461 vRounded.indexOf('.') !== -1 ? vRounded.replace(/[.]?0+$/, '') :
95462 vRounded
95463 );
95464}
95465
95466exports.formatPiePercent = function formatPiePercent(v, separators) {
95467 var vRounded = format((v * 100).toPrecision(3));
95468 return Lib.numSeparate(vRounded, separators) + '%';
95469};
95470
95471exports.formatPieValue = function formatPieValue(v, separators) {
95472 var vRounded = format(v.toPrecision(10));
95473 return Lib.numSeparate(vRounded, separators);
95474};
95475
95476exports.getFirstFilled = function getFirstFilled(array, indices) {
95477 if(!Array.isArray(array)) return;
95478 for(var i = 0; i < indices.length; i++) {
95479 var v = array[indices[i]];
95480 if(v || v === 0 || v === '') return v;
95481 }
95482};
95483
95484exports.castOption = function castOption(item, indices) {
95485 if(Array.isArray(item)) return exports.getFirstFilled(item, indices);
95486 else if(item) return item;
95487};
95488
95489exports.getRotationAngle = function(rotation) {
95490 return (rotation === 'auto' ? 0 : rotation) * Math.PI / 180;
95491};
95492
95493},{"../../lib":287}],490:[function(_dereq_,module,exports){
95494'use strict';
95495
95496module.exports = {
95497 attributes: _dereq_('./attributes'),
95498 supplyDefaults: _dereq_('./defaults').supplyDefaults,
95499 supplyLayoutDefaults: _dereq_('./layout_defaults'),
95500 layoutAttributes: _dereq_('./layout_attributes'),
95501
95502 calc: _dereq_('./calc').calc,
95503 crossTraceCalc: _dereq_('./calc').crossTraceCalc,
95504
95505 plot: _dereq_('./plot').plot,
95506 style: _dereq_('./style'),
95507 styleOne: _dereq_('./style_one'),
95508
95509 moduleType: 'trace',
95510 name: 'pie',
95511 basePlotModule: _dereq_('./base_plot'),
95512 categories: ['pie-like', 'pie', 'showLegend'],
95513 meta: {
95514 }
95515};
95516
95517},{"./attributes":484,"./base_plot":485,"./calc":486,"./defaults":487,"./layout_attributes":491,"./layout_defaults":492,"./plot":493,"./style":494,"./style_one":495}],491:[function(_dereq_,module,exports){
95518'use strict';
95519
95520module.exports = {
95521 hiddenlabels: {
95522 valType: 'data_array',
95523 editType: 'calc',
95524 },
95525 piecolorway: {
95526 valType: 'colorlist',
95527 editType: 'calc',
95528 },
95529 extendpiecolors: {
95530 valType: 'boolean',
95531 dflt: true,
95532 editType: 'calc',
95533 }
95534};
95535
95536},{}],492:[function(_dereq_,module,exports){
95537'use strict';
95538
95539var Lib = _dereq_('../../lib');
95540
95541var layoutAttributes = _dereq_('./layout_attributes');
95542
95543module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) {
95544 function coerce(attr, dflt) {
95545 return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt);
95546 }
95547
95548 coerce('hiddenlabels');
95549 coerce('piecolorway', layoutOut.colorway);
95550 coerce('extendpiecolors');
95551};
95552
95553},{"../../lib":287,"./layout_attributes":491}],493:[function(_dereq_,module,exports){
95554'use strict';
95555
95556var d3 = _dereq_('@plotly/d3');
95557
95558var Plots = _dereq_('../../plots/plots');
95559var Fx = _dereq_('../../components/fx');
95560var Color = _dereq_('../../components/color');
95561var Drawing = _dereq_('../../components/drawing');
95562var Lib = _dereq_('../../lib');
95563var strScale = Lib.strScale;
95564var strTranslate = Lib.strTranslate;
95565var svgTextUtils = _dereq_('../../lib/svg_text_utils');
95566var uniformText = _dereq_('../bar/uniform_text');
95567var recordMinTextSize = uniformText.recordMinTextSize;
95568var clearMinTextSize = uniformText.clearMinTextSize;
95569var TEXTPAD = _dereq_('../bar/constants').TEXTPAD;
95570
95571var helpers = _dereq_('./helpers');
95572var eventData = _dereq_('./event_data');
95573var isValidTextValue = _dereq_('../../lib').isValidTextValue;
95574
95575function plot(gd, cdModule) {
95576 var fullLayout = gd._fullLayout;
95577 var gs = fullLayout._size;
95578
95579 clearMinTextSize('pie', fullLayout);
95580
95581 prerenderTitles(cdModule, gd);
95582 layoutAreas(cdModule, gs);
95583
95584 var plotGroups = Lib.makeTraceGroups(fullLayout._pielayer, cdModule, 'trace').each(function(cd) {
95585 var plotGroup = d3.select(this);
95586 var cd0 = cd[0];
95587 var trace = cd0.trace;
95588
95589 setCoords(cd);
95590
95591 // TODO: miter might look better but can sometimes cause problems
95592 // maybe miter with a small-ish stroke-miterlimit?
95593 plotGroup.attr('stroke-linejoin', 'round');
95594
95595 plotGroup.each(function() {
95596 var slices = d3.select(this).selectAll('g.slice').data(cd);
95597
95598 slices.enter().append('g')
95599 .classed('slice', true);
95600 slices.exit().remove();
95601
95602 var quadrants = [
95603 [[], []], // y<0: x<0, x>=0
95604 [[], []] // y>=0: x<0, x>=0
95605 ];
95606 var hasOutsideText = false;
95607
95608 slices.each(function(pt, i) {
95609 if(pt.hidden) {
95610 d3.select(this).selectAll('path,g').remove();
95611 return;
95612 }
95613
95614 // to have consistent event data compared to other traces
95615 pt.pointNumber = pt.i;
95616 pt.curveNumber = trace.index;
95617
95618 quadrants[pt.pxmid[1] < 0 ? 0 : 1][pt.pxmid[0] < 0 ? 0 : 1].push(pt);
95619
95620 var cx = cd0.cx;
95621 var cy = cd0.cy;
95622 var sliceTop = d3.select(this);
95623 var slicePath = sliceTop.selectAll('path.surface').data([pt]);
95624
95625 slicePath.enter().append('path')
95626 .classed('surface', true)
95627 .style({'pointer-events': 'all'});
95628
95629 sliceTop.call(attachFxHandlers, gd, cd);
95630
95631 if(trace.pull) {
95632 var pull = +helpers.castOption(trace.pull, pt.pts) || 0;
95633 if(pull > 0) {
95634 cx += pull * pt.pxmid[0];
95635 cy += pull * pt.pxmid[1];
95636 }
95637 }
95638
95639 pt.cxFinal = cx;
95640 pt.cyFinal = cy;
95641
95642 function arc(start, finish, cw, scale) {
95643 var dx = scale * (finish[0] - start[0]);
95644 var dy = scale * (finish[1] - start[1]);
95645
95646 return 'a' +
95647 (scale * cd0.r) + ',' + (scale * cd0.r) + ' 0 ' +
95648 pt.largeArc + (cw ? ' 1 ' : ' 0 ') + dx + ',' + dy;
95649 }
95650
95651 var hole = trace.hole;
95652 if(pt.v === cd0.vTotal) { // 100% fails bcs arc start and end are identical
95653 var outerCircle = 'M' + (cx + pt.px0[0]) + ',' + (cy + pt.px0[1]) +
95654 arc(pt.px0, pt.pxmid, true, 1) +
95655 arc(pt.pxmid, pt.px0, true, 1) + 'Z';
95656 if(hole) {
95657 slicePath.attr('d',
95658 'M' + (cx + hole * pt.px0[0]) + ',' + (cy + hole * pt.px0[1]) +
95659 arc(pt.px0, pt.pxmid, false, hole) +
95660 arc(pt.pxmid, pt.px0, false, hole) +
95661 'Z' + outerCircle);
95662 } else slicePath.attr('d', outerCircle);
95663 } else {
95664 var outerArc = arc(pt.px0, pt.px1, true, 1);
95665
95666 if(hole) {
95667 var rim = 1 - hole;
95668 slicePath.attr('d',
95669 'M' + (cx + hole * pt.px1[0]) + ',' + (cy + hole * pt.px1[1]) +
95670 arc(pt.px1, pt.px0, false, hole) +
95671 'l' + (rim * pt.px0[0]) + ',' + (rim * pt.px0[1]) +
95672 outerArc +
95673 'Z');
95674 } else {
95675 slicePath.attr('d',
95676 'M' + cx + ',' + cy +
95677 'l' + pt.px0[0] + ',' + pt.px0[1] +
95678 outerArc +
95679 'Z');
95680 }
95681 }
95682
95683 // add text
95684 formatSliceLabel(gd, pt, cd0);
95685 var textPosition = helpers.castOption(trace.textposition, pt.pts);
95686 var sliceTextGroup = sliceTop.selectAll('g.slicetext')
95687 .data(pt.text && (textPosition !== 'none') ? [0] : []);
95688
95689 sliceTextGroup.enter().append('g')
95690 .classed('slicetext', true);
95691 sliceTextGroup.exit().remove();
95692
95693 sliceTextGroup.each(function() {
95694 var sliceText = Lib.ensureSingle(d3.select(this), 'text', '', function(s) {
95695 // prohibit tex interpretation until we can handle
95696 // tex and regular text together
95697 s.attr('data-notex', 1);
95698 });
95699
95700 var font = Lib.ensureUniformFontSize(gd, textPosition === 'outside' ?
95701 determineOutsideTextFont(trace, pt, fullLayout.font) :
95702 determineInsideTextFont(trace, pt, fullLayout.font)
95703 );
95704
95705 sliceText.text(pt.text)
95706 .attr({
95707 'class': 'slicetext',
95708 transform: '',
95709 'text-anchor': 'middle'
95710 })
95711 .call(Drawing.font, font)
95712 .call(svgTextUtils.convertToTspans, gd);
95713
95714 // position the text relative to the slice
95715 var textBB = Drawing.bBox(sliceText.node());
95716 var transform;
95717
95718 if(textPosition === 'outside') {
95719 transform = transformOutsideText(textBB, pt);
95720 } else {
95721 transform = transformInsideText(textBB, pt, cd0);
95722 if(textPosition === 'auto' && transform.scale < 1) {
95723 var newFont = Lib.ensureUniformFontSize(gd, trace.outsidetextfont);
95724
95725 sliceText.call(Drawing.font, newFont);
95726 textBB = Drawing.bBox(sliceText.node());
95727
95728 transform = transformOutsideText(textBB, pt);
95729 }
95730 }
95731
95732 var textPosAngle = transform.textPosAngle;
95733 var textXY = textPosAngle === undefined ? pt.pxmid : getCoords(cd0.r, textPosAngle);
95734 transform.targetX = cx + textXY[0] * transform.rCenter + (transform.x || 0);
95735 transform.targetY = cy + textXY[1] * transform.rCenter + (transform.y || 0);
95736 computeTransform(transform, textBB);
95737
95738 // save some stuff to use later ensure no labels overlap
95739 if(transform.outside) {
95740 var targetY = transform.targetY;
95741 pt.yLabelMin = targetY - textBB.height / 2;
95742 pt.yLabelMid = targetY;
95743 pt.yLabelMax = targetY + textBB.height / 2;
95744 pt.labelExtraX = 0;
95745 pt.labelExtraY = 0;
95746 hasOutsideText = true;
95747 }
95748
95749 transform.fontSize = font.size;
95750 recordMinTextSize(trace.type, transform, fullLayout);
95751 cd[i].transform = transform;
95752
95753 sliceText.attr('transform', Lib.getTextTransform(transform));
95754 });
95755 });
95756
95757 // add the title
95758 var titleTextGroup = d3.select(this).selectAll('g.titletext')
95759 .data(trace.title.text ? [0] : []);
95760
95761 titleTextGroup.enter().append('g')
95762 .classed('titletext', true);
95763 titleTextGroup.exit().remove();
95764
95765 titleTextGroup.each(function() {
95766 var titleText = Lib.ensureSingle(d3.select(this), 'text', '', function(s) {
95767 // prohibit tex interpretation as above
95768 s.attr('data-notex', 1);
95769 });
95770
95771 var txt = trace.title.text;
95772 if(trace._meta) {
95773 txt = Lib.templateString(txt, trace._meta);
95774 }
95775
95776 titleText.text(txt)
95777 .attr({
95778 'class': 'titletext',
95779 transform: '',
95780 'text-anchor': 'middle',
95781 })
95782 .call(Drawing.font, trace.title.font)
95783 .call(svgTextUtils.convertToTspans, gd);
95784
95785 var transform;
95786
95787 if(trace.title.position === 'middle center') {
95788 transform = positionTitleInside(cd0);
95789 } else {
95790 transform = positionTitleOutside(cd0, gs);
95791 }
95792
95793 titleText.attr('transform',
95794 strTranslate(transform.x, transform.y) +
95795 strScale(Math.min(1, transform.scale)) +
95796 strTranslate(transform.tx, transform.ty));
95797 });
95798
95799 // now make sure no labels overlap (at least within one pie)
95800 if(hasOutsideText) scootLabels(quadrants, trace);
95801
95802 plotTextLines(slices, trace);
95803
95804 if(hasOutsideText && trace.automargin) {
95805 // TODO if we ever want to improve perf,
95806 // we could reuse the textBB computed above together
95807 // with the sliceText transform info
95808 var traceBbox = Drawing.bBox(plotGroup.node());
95809
95810 var domain = trace.domain;
95811 var vpw = gs.w * (domain.x[1] - domain.x[0]);
95812 var vph = gs.h * (domain.y[1] - domain.y[0]);
95813 var xgap = (0.5 * vpw - cd0.r) / gs.w;
95814 var ygap = (0.5 * vph - cd0.r) / gs.h;
95815
95816 Plots.autoMargin(gd, 'pie.' + trace.uid + '.automargin', {
95817 xl: domain.x[0] - xgap,
95818 xr: domain.x[1] + xgap,
95819 yb: domain.y[0] - ygap,
95820 yt: domain.y[1] + ygap,
95821 l: Math.max(cd0.cx - cd0.r - traceBbox.left, 0),
95822 r: Math.max(traceBbox.right - (cd0.cx + cd0.r), 0),
95823 b: Math.max(traceBbox.bottom - (cd0.cy + cd0.r), 0),
95824 t: Math.max(cd0.cy - cd0.r - traceBbox.top, 0),
95825 pad: 5
95826 });
95827 }
95828 });
95829 });
95830
95831 // This is for a bug in Chrome (as of 2015-07-22, and does not affect FF)
95832 // if insidetextfont and outsidetextfont are different sizes, sometimes the size
95833 // of an "em" gets taken from the wrong element at first so lines are
95834 // spaced wrong. You just have to tell it to try again later and it gets fixed.
95835 // I have no idea why we haven't seen this in other contexts. Also, sometimes
95836 // it gets the initial draw correct but on redraw it gets confused.
95837 setTimeout(function() {
95838 plotGroups.selectAll('tspan').each(function() {
95839 var s = d3.select(this);
95840 if(s.attr('dy')) s.attr('dy', s.attr('dy'));
95841 });
95842 }, 0);
95843}
95844
95845// TODO add support for transition
95846function plotTextLines(slices, trace) {
95847 slices.each(function(pt) {
95848 var sliceTop = d3.select(this);
95849
95850 if(!pt.labelExtraX && !pt.labelExtraY) {
95851 sliceTop.select('path.textline').remove();
95852 return;
95853 }
95854
95855 // first move the text to its new location
95856 var sliceText = sliceTop.select('g.slicetext text');
95857
95858 pt.transform.targetX += pt.labelExtraX;
95859 pt.transform.targetY += pt.labelExtraY;
95860
95861 sliceText.attr('transform', Lib.getTextTransform(pt.transform));
95862
95863 // then add a line to the new location
95864 var lineStartX = pt.cxFinal + pt.pxmid[0];
95865 var lineStartY = pt.cyFinal + pt.pxmid[1];
95866 var textLinePath = 'M' + lineStartX + ',' + lineStartY;
95867 var finalX = (pt.yLabelMax - pt.yLabelMin) * (pt.pxmid[0] < 0 ? -1 : 1) / 4;
95868
95869 if(pt.labelExtraX) {
95870 var yFromX = pt.labelExtraX * pt.pxmid[1] / pt.pxmid[0];
95871 var yNet = pt.yLabelMid + pt.labelExtraY - (pt.cyFinal + pt.pxmid[1]);
95872
95873 if(Math.abs(yFromX) > Math.abs(yNet)) {
95874 textLinePath +=
95875 'l' + (yNet * pt.pxmid[0] / pt.pxmid[1]) + ',' + yNet +
95876 'H' + (lineStartX + pt.labelExtraX + finalX);
95877 } else {
95878 textLinePath += 'l' + pt.labelExtraX + ',' + yFromX +
95879 'v' + (yNet - yFromX) +
95880 'h' + finalX;
95881 }
95882 } else {
95883 textLinePath +=
95884 'V' + (pt.yLabelMid + pt.labelExtraY) +
95885 'h' + finalX;
95886 }
95887
95888 Lib.ensureSingle(sliceTop, 'path', 'textline')
95889 .call(Color.stroke, trace.outsidetextfont.color)
95890 .attr({
95891 'stroke-width': Math.min(2, trace.outsidetextfont.size / 8),
95892 d: textLinePath,
95893 fill: 'none'
95894 });
95895 });
95896}
95897
95898function attachFxHandlers(sliceTop, gd, cd) {
95899 var cd0 = cd[0];
95900 var cx = cd0.cx;
95901 var cy = cd0.cy;
95902 var trace = cd0.trace;
95903 var isFunnelArea = trace.type === 'funnelarea';
95904
95905 // hover state vars
95906 // have we drawn a hover label, so it should be cleared later
95907 if(!('_hasHoverLabel' in trace)) trace._hasHoverLabel = false;
95908 // have we emitted a hover event, so later an unhover event should be emitted
95909 // note that click events do not depend on this - you can still get them
95910 // with hovermode: false or if you were earlier dragging, then clicked
95911 // in the same slice that you moused up in
95912 if(!('_hasHoverEvent' in trace)) trace._hasHoverEvent = false;
95913
95914 sliceTop.on('mouseover', function(pt) {
95915 // in case fullLayout or fullData has changed without a replot
95916 var fullLayout2 = gd._fullLayout;
95917 var trace2 = gd._fullData[trace.index];
95918
95919 if(gd._dragging || fullLayout2.hovermode === false) return;
95920
95921 var hoverinfo = trace2.hoverinfo;
95922 if(Array.isArray(hoverinfo)) {
95923 // super hacky: we need to pull out the *first* hoverinfo from
95924 // pt.pts, then put it back into an array in a dummy trace
95925 // and call castHoverinfo on that.
95926 // TODO: do we want to have Fx.castHoverinfo somehow handle this?
95927 // it already takes an array for index, for 2D, so this seems tricky.
95928 hoverinfo = Fx.castHoverinfo({
95929 hoverinfo: [helpers.castOption(hoverinfo, pt.pts)],
95930 _module: trace._module
95931 }, fullLayout2, 0);
95932 }
95933
95934 if(hoverinfo === 'all') hoverinfo = 'label+text+value+percent+name';
95935
95936 // in case we dragged over the pie from another subplot,
95937 // or if hover is turned off
95938 if(trace2.hovertemplate || (hoverinfo !== 'none' && hoverinfo !== 'skip' && hoverinfo)) {
95939 var rInscribed = pt.rInscribed || 0;
95940 var hoverCenterX = cx + pt.pxmid[0] * (1 - rInscribed);
95941 var hoverCenterY = cy + pt.pxmid[1] * (1 - rInscribed);
95942 var separators = fullLayout2.separators;
95943 var text = [];
95944
95945 if(hoverinfo && hoverinfo.indexOf('label') !== -1) text.push(pt.label);
95946 pt.text = helpers.castOption(trace2.hovertext || trace2.text, pt.pts);
95947 if(hoverinfo && hoverinfo.indexOf('text') !== -1) {
95948 var tx = pt.text;
95949 if(Lib.isValidTextValue(tx)) text.push(tx);
95950 }
95951 pt.value = pt.v;
95952 pt.valueLabel = helpers.formatPieValue(pt.v, separators);
95953 if(hoverinfo && hoverinfo.indexOf('value') !== -1) text.push(pt.valueLabel);
95954 pt.percent = pt.v / cd0.vTotal;
95955 pt.percentLabel = helpers.formatPiePercent(pt.percent, separators);
95956 if(hoverinfo && hoverinfo.indexOf('percent') !== -1) text.push(pt.percentLabel);
95957
95958 var hoverLabel = trace2.hoverlabel;
95959 var hoverFont = hoverLabel.font;
95960
95961 var bbox = [];
95962 Fx.loneHover({
95963 trace: trace,
95964 x0: hoverCenterX - rInscribed * cd0.r,
95965 x1: hoverCenterX + rInscribed * cd0.r,
95966 y: hoverCenterY,
95967 _x0: isFunnelArea ? cx + pt.TL[0] : hoverCenterX - rInscribed * cd0.r,
95968 _x1: isFunnelArea ? cx + pt.TR[0] : hoverCenterX + rInscribed * cd0.r,
95969 _y0: isFunnelArea ? cy + pt.TL[1] : hoverCenterY - rInscribed * cd0.r,
95970 _y1: isFunnelArea ? cy + pt.BL[1] : hoverCenterY + rInscribed * cd0.r,
95971 text: text.join('<br>'),
95972 name: (trace2.hovertemplate || hoverinfo.indexOf('name') !== -1) ? trace2.name : undefined,
95973 idealAlign: pt.pxmid[0] < 0 ? 'left' : 'right',
95974 color: helpers.castOption(hoverLabel.bgcolor, pt.pts) || pt.color,
95975 borderColor: helpers.castOption(hoverLabel.bordercolor, pt.pts),
95976 fontFamily: helpers.castOption(hoverFont.family, pt.pts),
95977 fontSize: helpers.castOption(hoverFont.size, pt.pts),
95978 fontColor: helpers.castOption(hoverFont.color, pt.pts),
95979 nameLength: helpers.castOption(hoverLabel.namelength, pt.pts),
95980 textAlign: helpers.castOption(hoverLabel.align, pt.pts),
95981 hovertemplate: helpers.castOption(trace2.hovertemplate, pt.pts),
95982 hovertemplateLabels: pt,
95983 eventData: [eventData(pt, trace2)]
95984 }, {
95985 container: fullLayout2._hoverlayer.node(),
95986 outerContainer: fullLayout2._paper.node(),
95987 gd: gd,
95988 inOut_bbox: bbox
95989 });
95990 pt.bbox = bbox[0];
95991
95992 trace._hasHoverLabel = true;
95993 }
95994
95995 trace._hasHoverEvent = true;
95996 gd.emit('plotly_hover', {
95997 points: [eventData(pt, trace2)],
95998 event: d3.event
95999 });
96000 });
96001
96002 sliceTop.on('mouseout', function(evt) {
96003 var fullLayout2 = gd._fullLayout;
96004 var trace2 = gd._fullData[trace.index];
96005 var pt = d3.select(this).datum();
96006
96007 if(trace._hasHoverEvent) {
96008 evt.originalEvent = d3.event;
96009 gd.emit('plotly_unhover', {
96010 points: [eventData(pt, trace2)],
96011 event: d3.event
96012 });
96013 trace._hasHoverEvent = false;
96014 }
96015
96016 if(trace._hasHoverLabel) {
96017 Fx.loneUnhover(fullLayout2._hoverlayer.node());
96018 trace._hasHoverLabel = false;
96019 }
96020 });
96021
96022 sliceTop.on('click', function(pt) {
96023 // TODO: this does not support right-click. If we want to support it, we
96024 // would likely need to change pie to use dragElement instead of straight
96025 // mapbox event binding. Or perhaps better, make a simple wrapper with the
96026 // right mousedown, mousemove, and mouseup handlers just for a left/right click
96027 // mapbox would use this too.
96028 var fullLayout2 = gd._fullLayout;
96029 var trace2 = gd._fullData[trace.index];
96030
96031 if(gd._dragging || fullLayout2.hovermode === false) return;
96032
96033 gd._hoverdata = [eventData(pt, trace2)];
96034 Fx.click(gd, d3.event);
96035 });
96036}
96037
96038function determineOutsideTextFont(trace, pt, layoutFont) {
96039 var color =
96040 helpers.castOption(trace.outsidetextfont.color, pt.pts) ||
96041 helpers.castOption(trace.textfont.color, pt.pts) ||
96042 layoutFont.color;
96043
96044 var family =
96045 helpers.castOption(trace.outsidetextfont.family, pt.pts) ||
96046 helpers.castOption(trace.textfont.family, pt.pts) ||
96047 layoutFont.family;
96048
96049 var size =
96050 helpers.castOption(trace.outsidetextfont.size, pt.pts) ||
96051 helpers.castOption(trace.textfont.size, pt.pts) ||
96052 layoutFont.size;
96053
96054 return {
96055 color: color,
96056 family: family,
96057 size: size
96058 };
96059}
96060
96061function determineInsideTextFont(trace, pt, layoutFont) {
96062 var customColor = helpers.castOption(trace.insidetextfont.color, pt.pts);
96063 if(!customColor && trace._input.textfont) {
96064 // Why not simply using trace.textfont? Because if not set, it
96065 // defaults to layout.font which has a default color. But if
96066 // textfont.color and insidetextfont.color don't supply a value,
96067 // a contrasting color shall be used.
96068 customColor = helpers.castOption(trace._input.textfont.color, pt.pts);
96069 }
96070
96071 var family =
96072 helpers.castOption(trace.insidetextfont.family, pt.pts) ||
96073 helpers.castOption(trace.textfont.family, pt.pts) ||
96074 layoutFont.family;
96075
96076 var size =
96077 helpers.castOption(trace.insidetextfont.size, pt.pts) ||
96078 helpers.castOption(trace.textfont.size, pt.pts) ||
96079 layoutFont.size;
96080
96081 return {
96082 color: customColor || Color.contrast(pt.color),
96083 family: family,
96084 size: size
96085 };
96086}
96087
96088function prerenderTitles(cdModule, gd) {
96089 var cd0, trace;
96090
96091 // Determine the width and height of the title for each pie.
96092 for(var i = 0; i < cdModule.length; i++) {
96093 cd0 = cdModule[i][0];
96094 trace = cd0.trace;
96095
96096 if(trace.title.text) {
96097 var txt = trace.title.text;
96098 if(trace._meta) {
96099 txt = Lib.templateString(txt, trace._meta);
96100 }
96101
96102 var dummyTitle = Drawing.tester.append('text')
96103 .attr('data-notex', 1)
96104 .text(txt)
96105 .call(Drawing.font, trace.title.font)
96106 .call(svgTextUtils.convertToTspans, gd);
96107 var bBox = Drawing.bBox(dummyTitle.node(), true);
96108 cd0.titleBox = {
96109 width: bBox.width,
96110 height: bBox.height,
96111 };
96112 dummyTitle.remove();
96113 }
96114 }
96115}
96116
96117function transformInsideText(textBB, pt, cd0) {
96118 var r = cd0.r || pt.rpx1;
96119 var rInscribed = pt.rInscribed;
96120
96121 var isEmpty = pt.startangle === pt.stopangle;
96122 if(isEmpty) {
96123 return {
96124 rCenter: 1 - rInscribed,
96125 scale: 0,
96126 rotate: 0,
96127 textPosAngle: 0
96128 };
96129 }
96130
96131 var ring = pt.ring;
96132 var isCircle = (ring === 1) && (Math.abs(pt.startangle - pt.stopangle) === Math.PI * 2);
96133
96134 var halfAngle = pt.halfangle;
96135 var midAngle = pt.midangle;
96136
96137 var orientation = cd0.trace.insidetextorientation;
96138 var isHorizontal = orientation === 'horizontal';
96139 var isTangential = orientation === 'tangential';
96140 var isRadial = orientation === 'radial';
96141 var isAuto = orientation === 'auto';
96142
96143 var allTransforms = [];
96144 var newT;
96145
96146 if(!isAuto) {
96147 // max size if text is placed (horizontally) at the top or bottom of the arc
96148
96149 var considerCrossing = function(angle, key) {
96150 if(isCrossing(pt, angle)) {
96151 var dStart = Math.abs(angle - pt.startangle);
96152 var dStop = Math.abs(angle - pt.stopangle);
96153
96154 var closestEdge = dStart < dStop ? dStart : dStop;
96155
96156 if(key === 'tan') {
96157 newT = calcTanTransform(textBB, r, ring, closestEdge, 0);
96158 } else { // case of 'rad'
96159 newT = calcRadTransform(textBB, r, ring, closestEdge, Math.PI / 2);
96160 }
96161 newT.textPosAngle = angle;
96162
96163 allTransforms.push(newT);
96164 }
96165 };
96166
96167 // to cover all cases with trace.rotation added
96168 var i;
96169 if(isHorizontal || isTangential) {
96170 // top
96171 for(i = 4; i >= -4; i -= 2) considerCrossing(Math.PI * i, 'tan');
96172 // bottom
96173 for(i = 4; i >= -4; i -= 2) considerCrossing(Math.PI * (i + 1), 'tan');
96174 }
96175 if(isHorizontal || isRadial) {
96176 // left
96177 for(i = 4; i >= -4; i -= 2) considerCrossing(Math.PI * (i + 1.5), 'rad');
96178 // right
96179 for(i = 4; i >= -4; i -= 2) considerCrossing(Math.PI * (i + 0.5), 'rad');
96180 }
96181 }
96182
96183 if(isCircle || isAuto || isHorizontal) {
96184 // max size text can be inserted inside without rotating it
96185 // this inscribes the text rectangle in a circle, which is then inscribed
96186 // in the slice, so it will be an underestimate, which some day we may want
96187 // to improve so this case can get more use
96188 var textDiameter = Math.sqrt(textBB.width * textBB.width + textBB.height * textBB.height);
96189
96190 newT = {
96191 scale: rInscribed * r * 2 / textDiameter,
96192
96193 // and the center position and rotation in this case
96194 rCenter: 1 - rInscribed,
96195 rotate: 0
96196 };
96197
96198 newT.textPosAngle = (pt.startangle + pt.stopangle) / 2;
96199 if(newT.scale >= 1) return newT;
96200
96201 allTransforms.push(newT);
96202 }
96203
96204 if(isAuto || isRadial) {
96205 newT = calcRadTransform(textBB, r, ring, halfAngle, midAngle);
96206 newT.textPosAngle = (pt.startangle + pt.stopangle) / 2;
96207 allTransforms.push(newT);
96208 }
96209
96210 if(isAuto || isTangential) {
96211 newT = calcTanTransform(textBB, r, ring, halfAngle, midAngle);
96212 newT.textPosAngle = (pt.startangle + pt.stopangle) / 2;
96213 allTransforms.push(newT);
96214 }
96215
96216 var id = 0;
96217 var maxScale = 0;
96218 for(var k = 0; k < allTransforms.length; k++) {
96219 var s = allTransforms[k].scale;
96220 if(maxScale < s) {
96221 maxScale = s;
96222 id = k;
96223 }
96224
96225 if(!isAuto && maxScale >= 1) {
96226 // respect test order for non-auto options
96227 break;
96228 }
96229 }
96230 return allTransforms[id];
96231}
96232
96233function isCrossing(pt, angle) {
96234 var start = pt.startangle;
96235 var stop = pt.stopangle;
96236 return (
96237 (start > angle && angle > stop) ||
96238 (start < angle && angle < stop)
96239 );
96240}
96241
96242function calcRadTransform(textBB, r, ring, halfAngle, midAngle) {
96243 r = Math.max(0, r - 2 * TEXTPAD);
96244
96245 // max size if text is rotated radially
96246 var a = textBB.width / textBB.height;
96247 var s = calcMaxHalfSize(a, halfAngle, r, ring);
96248 return {
96249 scale: s * 2 / textBB.height,
96250 rCenter: calcRCenter(a, s / r),
96251 rotate: calcRotate(midAngle)
96252 };
96253}
96254
96255function calcTanTransform(textBB, r, ring, halfAngle, midAngle) {
96256 r = Math.max(0, r - 2 * TEXTPAD);
96257
96258 // max size if text is rotated tangentially
96259 var a = textBB.height / textBB.width;
96260 var s = calcMaxHalfSize(a, halfAngle, r, ring);
96261 return {
96262 scale: s * 2 / textBB.width,
96263 rCenter: calcRCenter(a, s / r),
96264 rotate: calcRotate(midAngle + Math.PI / 2)
96265 };
96266}
96267
96268function calcRCenter(a, b) {
96269 return Math.cos(b) - a * b;
96270}
96271
96272function calcRotate(t) {
96273 return (180 / Math.PI * t + 720) % 180 - 90;
96274}
96275
96276function calcMaxHalfSize(a, halfAngle, r, ring) {
96277 var q = a + 1 / (2 * Math.tan(halfAngle));
96278 return r * Math.min(
96279 1 / (Math.sqrt(q * q + 0.5) + q),
96280 ring / (Math.sqrt(a * a + ring / 2) + a)
96281 );
96282}
96283
96284function getInscribedRadiusFraction(pt, cd0) {
96285 if(pt.v === cd0.vTotal && !cd0.trace.hole) return 1;// special case of 100% with no hole
96286
96287 return Math.min(1 / (1 + 1 / Math.sin(pt.halfangle)), pt.ring / 2);
96288}
96289
96290function transformOutsideText(textBB, pt) {
96291 var x = pt.pxmid[0];
96292 var y = pt.pxmid[1];
96293 var dx = textBB.width / 2;
96294 var dy = textBB.height / 2;
96295
96296 if(x < 0) dx *= -1;
96297 if(y < 0) dy *= -1;
96298
96299 return {
96300 scale: 1,
96301 rCenter: 1,
96302 rotate: 0,
96303 x: dx + Math.abs(dy) * (dx > 0 ? 1 : -1) / 2,
96304 y: dy / (1 + x * x / (y * y)),
96305 outside: true
96306 };
96307}
96308
96309function positionTitleInside(cd0) {
96310 var textDiameter =
96311 Math.sqrt(cd0.titleBox.width * cd0.titleBox.width + cd0.titleBox.height * cd0.titleBox.height);
96312 return {
96313 x: cd0.cx,
96314 y: cd0.cy,
96315 scale: cd0.trace.hole * cd0.r * 2 / textDiameter,
96316 tx: 0,
96317 ty: - cd0.titleBox.height / 2 + cd0.trace.title.font.size
96318 };
96319}
96320
96321function positionTitleOutside(cd0, plotSize) {
96322 var scaleX = 1;
96323 var scaleY = 1;
96324 var maxPull;
96325
96326 var trace = cd0.trace;
96327 // position of the baseline point of the text box in the plot, before scaling.
96328 // we anchored the text in the middle, so the baseline is on the bottom middle
96329 // of the first line of text.
96330 var topMiddle = {
96331 x: cd0.cx,
96332 y: cd0.cy
96333 };
96334 // relative translation of the text box after scaling
96335 var translate = {
96336 tx: 0,
96337 ty: 0
96338 };
96339
96340 // we reason below as if the baseline is the top middle point of the text box.
96341 // so we must add the font size to approximate the y-coord. of the top.
96342 // note that this correction must happen after scaling.
96343 translate.ty += trace.title.font.size;
96344 maxPull = getMaxPull(trace);
96345
96346 if(trace.title.position.indexOf('top') !== -1) {
96347 topMiddle.y -= (1 + maxPull) * cd0.r;
96348 translate.ty -= cd0.titleBox.height;
96349 } else if(trace.title.position.indexOf('bottom') !== -1) {
96350 topMiddle.y += (1 + maxPull) * cd0.r;
96351 }
96352
96353 var rx = applyAspectRatio(cd0.r, cd0.trace.aspectratio);
96354
96355 var maxWidth = plotSize.w * (trace.domain.x[1] - trace.domain.x[0]) / 2;
96356 if(trace.title.position.indexOf('left') !== -1) {
96357 // we start the text at the left edge of the pie
96358 maxWidth = maxWidth + rx;
96359 topMiddle.x -= (1 + maxPull) * rx;
96360 translate.tx += cd0.titleBox.width / 2;
96361 } else if(trace.title.position.indexOf('center') !== -1) {
96362 maxWidth *= 2;
96363 } else if(trace.title.position.indexOf('right') !== -1) {
96364 maxWidth = maxWidth + rx;
96365 topMiddle.x += (1 + maxPull) * rx;
96366 translate.tx -= cd0.titleBox.width / 2;
96367 }
96368 scaleX = maxWidth / cd0.titleBox.width;
96369 scaleY = getTitleSpace(cd0, plotSize) / cd0.titleBox.height;
96370 return {
96371 x: topMiddle.x,
96372 y: topMiddle.y,
96373 scale: Math.min(scaleX, scaleY),
96374 tx: translate.tx,
96375 ty: translate.ty
96376 };
96377}
96378
96379function applyAspectRatio(x, aspectratio) {
96380 return x / ((aspectratio === undefined) ? 1 : aspectratio);
96381}
96382
96383function getTitleSpace(cd0, plotSize) {
96384 var trace = cd0.trace;
96385 var pieBoxHeight = plotSize.h * (trace.domain.y[1] - trace.domain.y[0]);
96386 // use at most half of the plot for the title
96387 return Math.min(cd0.titleBox.height, pieBoxHeight / 2);
96388}
96389
96390function getMaxPull(trace) {
96391 var maxPull = trace.pull;
96392 if(!maxPull) return 0;
96393
96394 var j;
96395 if(Array.isArray(maxPull)) {
96396 maxPull = 0;
96397 for(j = 0; j < trace.pull.length; j++) {
96398 if(trace.pull[j] > maxPull) maxPull = trace.pull[j];
96399 }
96400 }
96401 return maxPull;
96402}
96403
96404function scootLabels(quadrants, trace) {
96405 var xHalf, yHalf, equatorFirst, farthestX, farthestY,
96406 xDiffSign, yDiffSign, thisQuad, oppositeQuad,
96407 wholeSide, i, thisQuadOutside, firstOppositeOutsidePt;
96408
96409 function topFirst(a, b) { return a.pxmid[1] - b.pxmid[1]; }
96410 function bottomFirst(a, b) { return b.pxmid[1] - a.pxmid[1]; }
96411
96412 function scootOneLabel(thisPt, prevPt) {
96413 if(!prevPt) prevPt = {};
96414
96415 var prevOuterY = prevPt.labelExtraY + (yHalf ? prevPt.yLabelMax : prevPt.yLabelMin);
96416 var thisInnerY = yHalf ? thisPt.yLabelMin : thisPt.yLabelMax;
96417 var thisOuterY = yHalf ? thisPt.yLabelMax : thisPt.yLabelMin;
96418 var thisSliceOuterY = thisPt.cyFinal + farthestY(thisPt.px0[1], thisPt.px1[1]);
96419 var newExtraY = prevOuterY - thisInnerY;
96420
96421 var xBuffer, i, otherPt, otherOuterY, otherOuterX, newExtraX;
96422
96423 // make sure this label doesn't overlap other labels
96424 // this *only* has us move these labels vertically
96425 if(newExtraY * yDiffSign > 0) thisPt.labelExtraY = newExtraY;
96426
96427 // make sure this label doesn't overlap any slices
96428 if(!Array.isArray(trace.pull)) return; // this can only happen with array pulls
96429
96430 for(i = 0; i < wholeSide.length; i++) {
96431 otherPt = wholeSide[i];
96432
96433 // overlap can only happen if the other point is pulled more than this one
96434 if(otherPt === thisPt || (
96435 (helpers.castOption(trace.pull, thisPt.pts) || 0) >=
96436 (helpers.castOption(trace.pull, otherPt.pts) || 0))
96437 ) {
96438 continue;
96439 }
96440
96441 if((thisPt.pxmid[1] - otherPt.pxmid[1]) * yDiffSign > 0) {
96442 // closer to the equator - by construction all of these happen first
96443 // move the text vertically to get away from these slices
96444 otherOuterY = otherPt.cyFinal + farthestY(otherPt.px0[1], otherPt.px1[1]);
96445 newExtraY = otherOuterY - thisInnerY - thisPt.labelExtraY;
96446
96447 if(newExtraY * yDiffSign > 0) thisPt.labelExtraY += newExtraY;
96448 } else if((thisOuterY + thisPt.labelExtraY - thisSliceOuterY) * yDiffSign > 0) {
96449 // farther from the equator - happens after we've done all the
96450 // vertical moving we're going to do
96451 // move horizontally to get away from these more polar slices
96452
96453 // if we're moving horz. based on a slice that's several slices away from this one
96454 // then we need some extra space for the lines to labels between them
96455 xBuffer = 3 * xDiffSign * Math.abs(i - wholeSide.indexOf(thisPt));
96456
96457 otherOuterX = otherPt.cxFinal + farthestX(otherPt.px0[0], otherPt.px1[0]);
96458 newExtraX = otherOuterX + xBuffer - (thisPt.cxFinal + thisPt.pxmid[0]) - thisPt.labelExtraX;
96459
96460 if(newExtraX * xDiffSign > 0) thisPt.labelExtraX += newExtraX;
96461 }
96462 }
96463 }
96464
96465 for(yHalf = 0; yHalf < 2; yHalf++) {
96466 equatorFirst = yHalf ? topFirst : bottomFirst;
96467 farthestY = yHalf ? Math.max : Math.min;
96468 yDiffSign = yHalf ? 1 : -1;
96469
96470 for(xHalf = 0; xHalf < 2; xHalf++) {
96471 farthestX = xHalf ? Math.max : Math.min;
96472 xDiffSign = xHalf ? 1 : -1;
96473
96474 // first sort the array
96475 // note this is a copy of cd, so cd itself doesn't get sorted
96476 // but we can still modify points in place.
96477 thisQuad = quadrants[yHalf][xHalf];
96478 thisQuad.sort(equatorFirst);
96479
96480 oppositeQuad = quadrants[1 - yHalf][xHalf];
96481 wholeSide = oppositeQuad.concat(thisQuad);
96482
96483 thisQuadOutside = [];
96484 for(i = 0; i < thisQuad.length; i++) {
96485 if(thisQuad[i].yLabelMid !== undefined) thisQuadOutside.push(thisQuad[i]);
96486 }
96487
96488 firstOppositeOutsidePt = false;
96489 for(i = 0; yHalf && i < oppositeQuad.length; i++) {
96490 if(oppositeQuad[i].yLabelMid !== undefined) {
96491 firstOppositeOutsidePt = oppositeQuad[i];
96492 break;
96493 }
96494 }
96495
96496 // each needs to avoid the previous
96497 for(i = 0; i < thisQuadOutside.length; i++) {
96498 var prevPt = i && thisQuadOutside[i - 1];
96499 // bottom half needs to avoid the first label of the top half
96500 // top half we still need to call scootOneLabel on the first slice
96501 // so we can avoid other slices, but we don't pass a prevPt
96502 if(firstOppositeOutsidePt && !i) prevPt = firstOppositeOutsidePt;
96503 scootOneLabel(thisQuadOutside[i], prevPt);
96504 }
96505 }
96506 }
96507}
96508
96509function layoutAreas(cdModule, plotSize) {
96510 var scaleGroups = [];
96511
96512 // figure out the center and maximum radius
96513 for(var i = 0; i < cdModule.length; i++) {
96514 var cd0 = cdModule[i][0];
96515 var trace = cd0.trace;
96516
96517 var domain = trace.domain;
96518 var width = plotSize.w * (domain.x[1] - domain.x[0]);
96519 var height = plotSize.h * (domain.y[1] - domain.y[0]);
96520 // leave some space for the title, if it will be displayed outside
96521 if(trace.title.text && trace.title.position !== 'middle center') {
96522 height -= getTitleSpace(cd0, plotSize);
96523 }
96524
96525 var rx = width / 2;
96526 var ry = height / 2;
96527 if(trace.type === 'funnelarea' && !trace.scalegroup) {
96528 ry /= trace.aspectratio;
96529 }
96530
96531 cd0.r = Math.min(rx, ry) / (1 + getMaxPull(trace));
96532
96533 cd0.cx = plotSize.l + plotSize.w * (trace.domain.x[1] + trace.domain.x[0]) / 2;
96534 cd0.cy = plotSize.t + plotSize.h * (1 - trace.domain.y[0]) - height / 2;
96535 if(trace.title.text && trace.title.position.indexOf('bottom') !== -1) {
96536 cd0.cy -= getTitleSpace(cd0, plotSize);
96537 }
96538
96539 if(trace.scalegroup && scaleGroups.indexOf(trace.scalegroup) === -1) {
96540 scaleGroups.push(trace.scalegroup);
96541 }
96542 }
96543
96544 groupScale(cdModule, scaleGroups);
96545}
96546
96547function groupScale(cdModule, scaleGroups) {
96548 var cd0, i, trace;
96549
96550 // scale those that are grouped
96551 for(var k = 0; k < scaleGroups.length; k++) {
96552 var min = Infinity;
96553 var g = scaleGroups[k];
96554
96555 for(i = 0; i < cdModule.length; i++) {
96556 cd0 = cdModule[i][0];
96557 trace = cd0.trace;
96558
96559 if(trace.scalegroup === g) {
96560 var area;
96561 if(trace.type === 'pie') {
96562 area = cd0.r * cd0.r;
96563 } else if(trace.type === 'funnelarea') {
96564 var rx, ry;
96565
96566 if(trace.aspectratio > 1) {
96567 rx = cd0.r;
96568 ry = rx / trace.aspectratio;
96569 } else {
96570 ry = cd0.r;
96571 rx = ry * trace.aspectratio;
96572 }
96573
96574 rx *= (1 + trace.baseratio) / 2;
96575
96576 area = rx * ry;
96577 }
96578
96579 min = Math.min(min, area / cd0.vTotal);
96580 }
96581 }
96582
96583 for(i = 0; i < cdModule.length; i++) {
96584 cd0 = cdModule[i][0];
96585 trace = cd0.trace;
96586 if(trace.scalegroup === g) {
96587 var v = min * cd0.vTotal;
96588 if(trace.type === 'funnelarea') {
96589 v /= (1 + trace.baseratio) / 2;
96590 v /= trace.aspectratio;
96591 }
96592
96593 cd0.r = Math.sqrt(v);
96594 }
96595 }
96596 }
96597}
96598
96599function setCoords(cd) {
96600 var cd0 = cd[0];
96601 var r = cd0.r;
96602 var trace = cd0.trace;
96603 var currentAngle = helpers.getRotationAngle(trace.rotation);
96604 var angleFactor = 2 * Math.PI / cd0.vTotal;
96605 var firstPt = 'px0';
96606 var lastPt = 'px1';
96607
96608 var i, cdi, currentCoords;
96609
96610 if(trace.direction === 'counterclockwise') {
96611 for(i = 0; i < cd.length; i++) {
96612 if(!cd[i].hidden) break; // find the first non-hidden slice
96613 }
96614 if(i === cd.length) return; // all slices hidden
96615
96616 currentAngle += angleFactor * cd[i].v;
96617 angleFactor *= -1;
96618 firstPt = 'px1';
96619 lastPt = 'px0';
96620 }
96621
96622 currentCoords = getCoords(r, currentAngle);
96623
96624 for(i = 0; i < cd.length; i++) {
96625 cdi = cd[i];
96626 if(cdi.hidden) continue;
96627
96628 cdi[firstPt] = currentCoords;
96629
96630 cdi.startangle = currentAngle;
96631 currentAngle += angleFactor * cdi.v / 2;
96632 cdi.pxmid = getCoords(r, currentAngle);
96633 cdi.midangle = currentAngle;
96634 currentAngle += angleFactor * cdi.v / 2;
96635 currentCoords = getCoords(r, currentAngle);
96636 cdi.stopangle = currentAngle;
96637
96638 cdi[lastPt] = currentCoords;
96639
96640 cdi.largeArc = (cdi.v > cd0.vTotal / 2) ? 1 : 0;
96641
96642 cdi.halfangle = Math.PI * Math.min(cdi.v / cd0.vTotal, 0.5);
96643 cdi.ring = 1 - trace.hole;
96644 cdi.rInscribed = getInscribedRadiusFraction(cdi, cd0);
96645 }
96646}
96647
96648function getCoords(r, angle) {
96649 return [r * Math.sin(angle), -r * Math.cos(angle)];
96650}
96651
96652function formatSliceLabel(gd, pt, cd0) {
96653 var fullLayout = gd._fullLayout;
96654 var trace = cd0.trace;
96655 // look for textemplate
96656 var texttemplate = trace.texttemplate;
96657
96658 // now insert text
96659 var textinfo = trace.textinfo;
96660 if(!texttemplate && textinfo && textinfo !== 'none') {
96661 var parts = textinfo.split('+');
96662 var hasFlag = function(flag) { return parts.indexOf(flag) !== -1; };
96663 var hasLabel = hasFlag('label');
96664 var hasText = hasFlag('text');
96665 var hasValue = hasFlag('value');
96666 var hasPercent = hasFlag('percent');
96667
96668 var separators = fullLayout.separators;
96669 var text;
96670
96671 text = hasLabel ? [pt.label] : [];
96672 if(hasText) {
96673 var tx = helpers.getFirstFilled(trace.text, pt.pts);
96674 if(isValidTextValue(tx)) text.push(tx);
96675 }
96676 if(hasValue) text.push(helpers.formatPieValue(pt.v, separators));
96677 if(hasPercent) text.push(helpers.formatPiePercent(pt.v / cd0.vTotal, separators));
96678 pt.text = text.join('<br>');
96679 }
96680
96681 function makeTemplateVariables(pt) {
96682 return {
96683 label: pt.label,
96684 value: pt.v,
96685 valueLabel: helpers.formatPieValue(pt.v, fullLayout.separators),
96686 percent: pt.v / cd0.vTotal,
96687 percentLabel: helpers.formatPiePercent(pt.v / cd0.vTotal, fullLayout.separators),
96688 color: pt.color,
96689 text: pt.text,
96690 customdata: Lib.castOption(trace, pt.i, 'customdata')
96691 };
96692 }
96693
96694 if(texttemplate) {
96695 var txt = Lib.castOption(trace, pt.i, 'texttemplate');
96696 if(!txt) {
96697 pt.text = '';
96698 } else {
96699 var obj = makeTemplateVariables(pt);
96700 var ptTx = helpers.getFirstFilled(trace.text, pt.pts);
96701 if(isValidTextValue(ptTx) || ptTx === '') obj.text = ptTx;
96702 pt.text = Lib.texttemplateString(txt, obj, gd._fullLayout._d3locale, obj, trace._meta || {});
96703 }
96704 }
96705}
96706
96707function computeTransform(
96708 transform, // inout
96709 textBB // in
96710) {
96711 var a = transform.rotate * Math.PI / 180;
96712 var cosA = Math.cos(a);
96713 var sinA = Math.sin(a);
96714 var midX = (textBB.left + textBB.right) / 2;
96715 var midY = (textBB.top + textBB.bottom) / 2;
96716 transform.textX = midX * cosA - midY * sinA;
96717 transform.textY = midX * sinA + midY * cosA;
96718 transform.noCenter = true;
96719}
96720
96721module.exports = {
96722 plot: plot,
96723 formatSliceLabel: formatSliceLabel,
96724 transformInsideText: transformInsideText,
96725 determineInsideTextFont: determineInsideTextFont,
96726 positionTitleOutside: positionTitleOutside,
96727 prerenderTitles: prerenderTitles,
96728 layoutAreas: layoutAreas,
96729 attachFxHandlers: attachFxHandlers,
96730 computeTransform: computeTransform
96731};
96732
96733},{"../../components/color":157,"../../components/drawing":179,"../../components/fx":197,"../../lib":287,"../../lib/svg_text_utils":310,"../../plots/plots":369,"../bar/constants":388,"../bar/uniform_text":402,"./event_data":488,"./helpers":489,"@plotly/d3":20}],494:[function(_dereq_,module,exports){
96734'use strict';
96735
96736var d3 = _dereq_('@plotly/d3');
96737
96738var styleOne = _dereq_('./style_one');
96739var resizeText = _dereq_('../bar/uniform_text').resizeText;
96740
96741module.exports = function style(gd) {
96742 var s = gd._fullLayout._pielayer.selectAll('.trace');
96743 resizeText(gd, s, 'pie');
96744
96745 s.each(function(cd) {
96746 var cd0 = cd[0];
96747 var trace = cd0.trace;
96748 var traceSelection = d3.select(this);
96749
96750 traceSelection.style({opacity: trace.opacity});
96751
96752 traceSelection.selectAll('path.surface').each(function(pt) {
96753 d3.select(this).call(styleOne, pt, trace);
96754 });
96755 });
96756};
96757
96758},{"../bar/uniform_text":402,"./style_one":495,"@plotly/d3":20}],495:[function(_dereq_,module,exports){
96759'use strict';
96760
96761var Color = _dereq_('../../components/color');
96762var castOption = _dereq_('./helpers').castOption;
96763
96764module.exports = function styleOne(s, pt, trace) {
96765 var line = trace.marker.line;
96766 var lineColor = castOption(line.color, pt.pts) || Color.defaultLine;
96767 var lineWidth = castOption(line.width, pt.pts) || 0;
96768
96769 s.style('stroke-width', lineWidth)
96770 .call(Color.fill, pt.color)
96771 .call(Color.stroke, lineColor);
96772};
96773
96774},{"../../components/color":157,"./helpers":489}],496:[function(_dereq_,module,exports){
96775'use strict';
96776
96777var Lib = _dereq_('../../lib');
96778
96779
96780// arrayOk attributes, merge them into calcdata array
96781module.exports = function arraysToCalcdata(cd, trace) {
96782 // so each point knows which index it originally came from
96783 for(var i = 0; i < cd.length; i++) cd[i].i = i;
96784
96785 Lib.mergeArray(trace.text, cd, 'tx');
96786 Lib.mergeArray(trace.texttemplate, cd, 'txt');
96787 Lib.mergeArray(trace.hovertext, cd, 'htx');
96788 Lib.mergeArray(trace.customdata, cd, 'data');
96789 Lib.mergeArray(trace.textposition, cd, 'tp');
96790 if(trace.textfont) {
96791 Lib.mergeArrayCastPositive(trace.textfont.size, cd, 'ts');
96792 Lib.mergeArray(trace.textfont.color, cd, 'tc');
96793 Lib.mergeArray(trace.textfont.family, cd, 'tf');
96794 }
96795
96796 var marker = trace.marker;
96797 if(marker) {
96798 Lib.mergeArrayCastPositive(marker.size, cd, 'ms');
96799 Lib.mergeArrayCastPositive(marker.opacity, cd, 'mo');
96800 Lib.mergeArray(marker.symbol, cd, 'mx');
96801 Lib.mergeArray(marker.color, cd, 'mc');
96802
96803 var markerLine = marker.line;
96804 if(marker.line) {
96805 Lib.mergeArray(markerLine.color, cd, 'mlc');
96806 Lib.mergeArrayCastPositive(markerLine.width, cd, 'mlw');
96807 }
96808
96809 var markerGradient = marker.gradient;
96810 if(markerGradient && markerGradient.type !== 'none') {
96811 Lib.mergeArray(markerGradient.type, cd, 'mgt');
96812 Lib.mergeArray(markerGradient.color, cd, 'mgc');
96813 }
96814 }
96815};
96816
96817},{"../../lib":287}],497:[function(_dereq_,module,exports){
96818'use strict';
96819
96820var axisHoverFormat = _dereq_('../../plots/cartesian/axis_format_attributes').axisHoverFormat;
96821var texttemplateAttrs = _dereq_('../../plots/template_attributes').texttemplateAttrs;
96822var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs;
96823var colorScaleAttrs = _dereq_('../../components/colorscale/attributes');
96824var fontAttrs = _dereq_('../../plots/font_attributes');
96825var dash = _dereq_('../../components/drawing/attributes').dash;
96826
96827var Drawing = _dereq_('../../components/drawing');
96828var constants = _dereq_('./constants');
96829
96830var extendFlat = _dereq_('../../lib/extend').extendFlat;
96831
96832function axisPeriod(axis) {
96833 return {
96834 valType: 'any',
96835 dflt: 0,
96836 editType: 'calc',
96837 };
96838}
96839
96840function axisPeriod0(axis) {
96841 return {
96842 valType: 'any',
96843 editType: 'calc',
96844 };
96845}
96846
96847function axisPeriodAlignment(axis) {
96848 return {
96849 valType: 'enumerated',
96850 values: [
96851 'start', 'middle', 'end'
96852 ],
96853 dflt: 'middle',
96854 editType: 'calc',
96855 };
96856}
96857
96858module.exports = {
96859 x: {
96860 valType: 'data_array',
96861 editType: 'calc+clearAxisTypes',
96862 anim: true,
96863 },
96864 x0: {
96865 valType: 'any',
96866 dflt: 0,
96867 editType: 'calc+clearAxisTypes',
96868 anim: true,
96869 },
96870 dx: {
96871 valType: 'number',
96872 dflt: 1,
96873 editType: 'calc',
96874 anim: true,
96875 },
96876 y: {
96877 valType: 'data_array',
96878 editType: 'calc+clearAxisTypes',
96879 anim: true,
96880 },
96881 y0: {
96882 valType: 'any',
96883 dflt: 0,
96884 editType: 'calc+clearAxisTypes',
96885 anim: true,
96886 },
96887 dy: {
96888 valType: 'number',
96889 dflt: 1,
96890 editType: 'calc',
96891 anim: true,
96892 },
96893
96894 xperiod: axisPeriod('x'),
96895 yperiod: axisPeriod('y'),
96896 xperiod0: axisPeriod0('x0'),
96897 yperiod0: axisPeriod0('y0'),
96898 xperiodalignment: axisPeriodAlignment('x'),
96899 yperiodalignment: axisPeriodAlignment('y'),
96900 xhoverformat: axisHoverFormat('x'),
96901 yhoverformat: axisHoverFormat('y'),
96902
96903 stackgroup: {
96904 valType: 'string',
96905 dflt: '',
96906 editType: 'calc',
96907 },
96908 orientation: {
96909 valType: 'enumerated',
96910 values: ['v', 'h'],
96911 editType: 'calc',
96912 },
96913 groupnorm: {
96914 valType: 'enumerated',
96915 values: ['', 'fraction', 'percent'],
96916 dflt: '',
96917 editType: 'calc',
96918 },
96919 stackgaps: {
96920 valType: 'enumerated',
96921 values: ['infer zero', 'interpolate'],
96922 dflt: 'infer zero',
96923 editType: 'calc',
96924 },
96925
96926 text: {
96927 valType: 'string',
96928 dflt: '',
96929 arrayOk: true,
96930 editType: 'calc',
96931 },
96932
96933 texttemplate: texttemplateAttrs({}, {
96934
96935 }),
96936 hovertext: {
96937 valType: 'string',
96938 dflt: '',
96939 arrayOk: true,
96940 editType: 'style',
96941 },
96942 mode: {
96943 valType: 'flaglist',
96944 flags: ['lines', 'markers', 'text'],
96945 extras: ['none'],
96946 editType: 'calc',
96947 },
96948 hoveron: {
96949 valType: 'flaglist',
96950 flags: ['points', 'fills'],
96951 editType: 'style',
96952 },
96953 hovertemplate: hovertemplateAttrs({}, {
96954 keys: constants.eventDataKeys
96955 }),
96956
96957 line: {
96958 color: {
96959 valType: 'color',
96960 editType: 'style',
96961 anim: true,
96962 },
96963 width: {
96964 valType: 'number',
96965 min: 0,
96966 dflt: 2,
96967 editType: 'style',
96968 anim: true,
96969 },
96970 shape: {
96971 valType: 'enumerated',
96972 values: ['linear', 'spline', 'hv', 'vh', 'hvh', 'vhv'],
96973 dflt: 'linear',
96974 editType: 'plot',
96975 },
96976 smoothing: {
96977 valType: 'number',
96978 min: 0,
96979 max: 1.3,
96980 dflt: 1,
96981 editType: 'plot',
96982 },
96983 dash: extendFlat({}, dash, {editType: 'style'}),
96984 simplify: {
96985 valType: 'boolean',
96986 dflt: true,
96987 editType: 'plot',
96988 },
96989 editType: 'plot'
96990 },
96991
96992 connectgaps: {
96993 valType: 'boolean',
96994 dflt: false,
96995 editType: 'calc',
96996 },
96997 cliponaxis: {
96998 valType: 'boolean',
96999 dflt: true,
97000 editType: 'plot',
97001 },
97002
97003 fill: {
97004 valType: 'enumerated',
97005 values: ['none', 'tozeroy', 'tozerox', 'tonexty', 'tonextx', 'toself', 'tonext'],
97006 editType: 'calc',
97007 },
97008 fillcolor: {
97009 valType: 'color',
97010 editType: 'style',
97011 anim: true,
97012 },
97013 marker: extendFlat({
97014 symbol: {
97015 valType: 'enumerated',
97016 values: Drawing.symbolList,
97017 dflt: 'circle',
97018 arrayOk: true,
97019 editType: 'style',
97020 },
97021 opacity: {
97022 valType: 'number',
97023 min: 0,
97024 max: 1,
97025 arrayOk: true,
97026 editType: 'style',
97027 anim: true,
97028 },
97029 size: {
97030 valType: 'number',
97031 min: 0,
97032 dflt: 6,
97033 arrayOk: true,
97034 editType: 'calc',
97035 anim: true,
97036 },
97037 maxdisplayed: {
97038 valType: 'number',
97039 min: 0,
97040 dflt: 0,
97041 editType: 'plot',
97042 },
97043 sizeref: {
97044 valType: 'number',
97045 dflt: 1,
97046 editType: 'calc',
97047 },
97048 sizemin: {
97049 valType: 'number',
97050 min: 0,
97051 dflt: 0,
97052 editType: 'calc',
97053 },
97054 sizemode: {
97055 valType: 'enumerated',
97056 values: ['diameter', 'area'],
97057 dflt: 'diameter',
97058 editType: 'calc',
97059 },
97060
97061 line: extendFlat({
97062 width: {
97063 valType: 'number',
97064 min: 0,
97065 arrayOk: true,
97066 editType: 'style',
97067 anim: true,
97068 },
97069 editType: 'calc'
97070 },
97071 colorScaleAttrs('marker.line', {anim: true})
97072 ),
97073 gradient: {
97074 type: {
97075 valType: 'enumerated',
97076 values: ['radial', 'horizontal', 'vertical', 'none'],
97077 arrayOk: true,
97078 dflt: 'none',
97079 editType: 'calc',
97080 },
97081 color: {
97082 valType: 'color',
97083 arrayOk: true,
97084 editType: 'calc',
97085 },
97086 editType: 'calc'
97087 },
97088 editType: 'calc'
97089 },
97090 colorScaleAttrs('marker', {anim: true})
97091 ),
97092 selected: {
97093 marker: {
97094 opacity: {
97095 valType: 'number',
97096 min: 0,
97097 max: 1,
97098 editType: 'style',
97099 },
97100 color: {
97101 valType: 'color',
97102 editType: 'style',
97103 },
97104 size: {
97105 valType: 'number',
97106 min: 0,
97107 editType: 'style',
97108 },
97109 editType: 'style'
97110 },
97111 textfont: {
97112 color: {
97113 valType: 'color',
97114 editType: 'style',
97115 },
97116 editType: 'style'
97117 },
97118 editType: 'style'
97119 },
97120 unselected: {
97121 marker: {
97122 opacity: {
97123 valType: 'number',
97124 min: 0,
97125 max: 1,
97126 editType: 'style',
97127 },
97128 color: {
97129 valType: 'color',
97130 editType: 'style',
97131 },
97132 size: {
97133 valType: 'number',
97134 min: 0,
97135 editType: 'style',
97136 },
97137 editType: 'style'
97138 },
97139 textfont: {
97140 color: {
97141 valType: 'color',
97142 editType: 'style',
97143 },
97144 editType: 'style'
97145 },
97146 editType: 'style'
97147 },
97148
97149 textposition: {
97150 valType: 'enumerated',
97151 values: [
97152 'top left', 'top center', 'top right',
97153 'middle left', 'middle center', 'middle right',
97154 'bottom left', 'bottom center', 'bottom right'
97155 ],
97156 dflt: 'middle center',
97157 arrayOk: true,
97158 editType: 'calc',
97159 },
97160 textfont: fontAttrs({
97161 editType: 'calc',
97162 colorEditType: 'style',
97163 arrayOk: true,
97164 }),
97165};
97166
97167},{"../../components/colorscale/attributes":164,"../../components/drawing":179,"../../components/drawing/attributes":178,"../../lib/extend":281,"../../plots/cartesian/axis_format_attributes":337,"../../plots/font_attributes":363,"../../plots/template_attributes":371,"./constants":501}],498:[function(_dereq_,module,exports){
97168'use strict';
97169
97170var isNumeric = _dereq_('fast-isnumeric');
97171var Lib = _dereq_('../../lib');
97172
97173var Axes = _dereq_('../../plots/cartesian/axes');
97174var alignPeriod = _dereq_('../../plots/cartesian/align_period');
97175var BADNUM = _dereq_('../../constants/numerical').BADNUM;
97176
97177var subTypes = _dereq_('./subtypes');
97178var calcColorscale = _dereq_('./colorscale_calc');
97179var arraysToCalcdata = _dereq_('./arrays_to_calcdata');
97180var calcSelection = _dereq_('./calc_selection');
97181
97182function calc(gd, trace) {
97183 var fullLayout = gd._fullLayout;
97184 var xa = Axes.getFromId(gd, trace.xaxis || 'x');
97185 var ya = Axes.getFromId(gd, trace.yaxis || 'y');
97186 var origX = xa.makeCalcdata(trace, 'x');
97187 var origY = ya.makeCalcdata(trace, 'y');
97188 var xObj = alignPeriod(trace, xa, 'x', origX);
97189 var yObj = alignPeriod(trace, ya, 'y', origY);
97190 var x = xObj.vals;
97191 var y = yObj.vals;
97192
97193 var serieslen = trace._length;
97194 var cd = new Array(serieslen);
97195 var ids = trace.ids;
97196 var stackGroupOpts = getStackOpts(trace, fullLayout, xa, ya);
97197 var interpolateGaps = false;
97198 var isV, i, j, k, interpolate, vali;
97199
97200 setFirstScatter(fullLayout, trace);
97201
97202 var xAttr = 'x';
97203 var yAttr = 'y';
97204 var posAttr;
97205 if(stackGroupOpts) {
97206 Lib.pushUnique(stackGroupOpts.traceIndices, trace._expandedIndex);
97207 isV = stackGroupOpts.orientation === 'v';
97208
97209 // size, like we use for bar
97210 if(isV) {
97211 yAttr = 's';
97212 posAttr = 'x';
97213 } else {
97214 xAttr = 's';
97215 posAttr = 'y';
97216 }
97217 interpolate = stackGroupOpts.stackgaps === 'interpolate';
97218 } else {
97219 var ppad = calcMarkerSize(trace, serieslen);
97220 calcAxisExpansion(gd, trace, xa, ya, x, y, ppad);
97221 }
97222
97223 var hasPeriodX = !!trace.xperiodalignment;
97224 var hasPeriodY = !!trace.yperiodalignment;
97225
97226 for(i = 0; i < serieslen; i++) {
97227 var cdi = cd[i] = {};
97228 var xValid = isNumeric(x[i]);
97229 var yValid = isNumeric(y[i]);
97230 if(xValid && yValid) {
97231 cdi[xAttr] = x[i];
97232 cdi[yAttr] = y[i];
97233
97234 if(hasPeriodX) {
97235 cdi.orig_x = origX[i]; // used by hover
97236 cdi.xEnd = xObj.ends[i];
97237 cdi.xStart = xObj.starts[i];
97238 }
97239 if(hasPeriodY) {
97240 cdi.orig_y = origY[i]; // used by hover
97241 cdi.yEnd = yObj.ends[i];
97242 cdi.yStart = yObj.starts[i];
97243 }
97244 } else if(stackGroupOpts && (isV ? xValid : yValid)) {
97245 // if we're stacking we need to hold on to all valid positions
97246 // even with invalid sizes
97247
97248 cdi[posAttr] = isV ? x[i] : y[i];
97249 cdi.gap = true;
97250 if(interpolate) {
97251 cdi.s = BADNUM;
97252 interpolateGaps = true;
97253 } else {
97254 cdi.s = 0;
97255 }
97256 } else {
97257 cdi[xAttr] = cdi[yAttr] = BADNUM;
97258 }
97259
97260 if(ids) {
97261 cdi.id = String(ids[i]);
97262 }
97263 }
97264
97265 arraysToCalcdata(cd, trace);
97266 calcColorscale(gd, trace);
97267 calcSelection(cd, trace);
97268
97269 if(stackGroupOpts) {
97270 // remove bad positions and sort
97271 // note that original indices get added to cd in arraysToCalcdata
97272 i = 0;
97273 while(i < cd.length) {
97274 if(cd[i][posAttr] === BADNUM) {
97275 cd.splice(i, 1);
97276 } else i++;
97277 }
97278
97279 Lib.sort(cd, function(a, b) {
97280 return (a[posAttr] - b[posAttr]) || (a.i - b.i);
97281 });
97282
97283 if(interpolateGaps) {
97284 // first fill the beginning with constant from the first point
97285 i = 0;
97286 while(i < cd.length - 1 && cd[i].gap) {
97287 i++;
97288 }
97289 vali = cd[i].s;
97290 if(!vali) vali = cd[i].s = 0; // in case of no data AT ALL in this trace - use 0
97291 for(j = 0; j < i; j++) {
97292 cd[j].s = vali;
97293 }
97294 // then fill the end with constant from the last point
97295 k = cd.length - 1;
97296 while(k > i && cd[k].gap) {
97297 k--;
97298 }
97299 vali = cd[k].s;
97300 for(j = cd.length - 1; j > k; j--) {
97301 cd[j].s = vali;
97302 }
97303 // now interpolate internal gaps linearly
97304 while(i < k) {
97305 i++;
97306 if(cd[i].gap) {
97307 j = i + 1;
97308 while(cd[j].gap) {
97309 j++;
97310 }
97311 var pos0 = cd[i - 1][posAttr];
97312 var size0 = cd[i - 1].s;
97313 var m = (cd[j].s - size0) / (cd[j][posAttr] - pos0);
97314 while(i < j) {
97315 cd[i].s = size0 + (cd[i][posAttr] - pos0) * m;
97316 i++;
97317 }
97318 }
97319 }
97320 }
97321 }
97322
97323 return cd;
97324}
97325
97326function calcAxisExpansion(gd, trace, xa, ya, x, y, ppad) {
97327 var serieslen = trace._length;
97328 var fullLayout = gd._fullLayout;
97329 var xId = xa._id;
97330 var yId = ya._id;
97331 var firstScatter = fullLayout._firstScatter[firstScatterGroup(trace)] === trace.uid;
97332 var stackOrientation = (getStackOpts(trace, fullLayout, xa, ya) || {}).orientation;
97333 var fill = trace.fill;
97334
97335 // cancel minimum tick spacings (only applies to bars and boxes)
97336 xa._minDtick = 0;
97337 ya._minDtick = 0;
97338
97339 // check whether bounds should be tight, padded, extended to zero...
97340 // most cases both should be padded on both ends, so start with that.
97341 var xOptions = {padded: true};
97342 var yOptions = {padded: true};
97343
97344 if(ppad) {
97345 xOptions.ppad = yOptions.ppad = ppad;
97346 }
97347
97348 // TODO: text size
97349
97350 var openEnded = serieslen < 2 || (x[0] !== x[serieslen - 1]) || (y[0] !== y[serieslen - 1]);
97351
97352 if(openEnded && (
97353 (fill === 'tozerox') ||
97354 ((fill === 'tonextx') && (firstScatter || stackOrientation === 'h'))
97355 )) {
97356 // include zero (tight) and extremes (padded) if fill to zero
97357 // (unless the shape is closed, then it's just filling the shape regardless)
97358
97359 xOptions.tozero = true;
97360 } else if(!(trace.error_y || {}).visible && (
97361 // if no error bars, markers or text, or fill to y=0 remove x padding
97362
97363 (fill === 'tonexty' || fill === 'tozeroy') ||
97364 (!subTypes.hasMarkers(trace) && !subTypes.hasText(trace))
97365 )) {
97366 xOptions.padded = false;
97367 xOptions.ppad = 0;
97368 }
97369
97370 if(openEnded && (
97371 (fill === 'tozeroy') ||
97372 ((fill === 'tonexty') && (firstScatter || stackOrientation === 'v'))
97373 )) {
97374 // now check for y - rather different logic, though still mostly padded both ends
97375 // include zero (tight) and extremes (padded) if fill to zero
97376 // (unless the shape is closed, then it's just filling the shape regardless)
97377
97378 yOptions.tozero = true;
97379 } else if(fill === 'tonextx' || fill === 'tozerox') {
97380 // tight y: any x fill
97381
97382 yOptions.padded = false;
97383 }
97384
97385 // N.B. asymmetric splom traces call this with blank {} xa or ya
97386 if(xId) trace._extremes[xId] = Axes.findExtremes(xa, x, xOptions);
97387 if(yId) trace._extremes[yId] = Axes.findExtremes(ya, y, yOptions);
97388}
97389
97390function calcMarkerSize(trace, serieslen) {
97391 if(!subTypes.hasMarkers(trace)) return;
97392
97393 // Treat size like x or y arrays --- Run d2c
97394 // this needs to go before ppad computation
97395 var marker = trace.marker;
97396 var sizeref = 1.6 * (trace.marker.sizeref || 1);
97397 var markerTrans;
97398
97399 if(trace.marker.sizemode === 'area') {
97400 markerTrans = function(v) {
97401 return Math.max(Math.sqrt((v || 0) / sizeref), 3);
97402 };
97403 } else {
97404 markerTrans = function(v) {
97405 return Math.max((v || 0) / sizeref, 3);
97406 };
97407 }
97408
97409 if(Lib.isArrayOrTypedArray(marker.size)) {
97410 // I tried auto-type but category and dates dont make much sense.
97411 var ax = {type: 'linear'};
97412 Axes.setConvert(ax);
97413
97414 var s = ax.makeCalcdata(trace.marker, 'size');
97415
97416 var sizeOut = new Array(serieslen);
97417 for(var i = 0; i < serieslen; i++) {
97418 sizeOut[i] = markerTrans(s[i]);
97419 }
97420 return sizeOut;
97421 } else {
97422 return markerTrans(marker.size);
97423 }
97424}
97425
97426/**
97427 * mark the first scatter trace for each subplot
97428 * note that scatter and scattergl each get their own first trace
97429 * note also that I'm doing this during calc rather than supplyDefaults
97430 * so I don't need to worry about transforms, but if we ever do
97431 * per-trace calc this will get confused.
97432 */
97433function setFirstScatter(fullLayout, trace) {
97434 var group = firstScatterGroup(trace);
97435 var firstScatter = fullLayout._firstScatter;
97436 if(!firstScatter[group]) firstScatter[group] = trace.uid;
97437}
97438
97439function firstScatterGroup(trace) {
97440 var stackGroup = trace.stackgroup;
97441 return trace.xaxis + trace.yaxis + trace.type +
97442 (stackGroup ? '-' + stackGroup : '');
97443}
97444
97445function getStackOpts(trace, fullLayout, xa, ya) {
97446 var stackGroup = trace.stackgroup;
97447 if(!stackGroup) return;
97448 var stackOpts = fullLayout._scatterStackOpts[xa._id + ya._id][stackGroup];
97449 var stackAx = stackOpts.orientation === 'v' ? ya : xa;
97450 // Allow stacking only on numeric axes
97451 // calc is a little late to be figuring this out, but during supplyDefaults
97452 // we don't know the axis type yet
97453 if(stackAx.type === 'linear' || stackAx.type === 'log') return stackOpts;
97454}
97455
97456module.exports = {
97457 calc: calc,
97458 calcMarkerSize: calcMarkerSize,
97459 calcAxisExpansion: calcAxisExpansion,
97460 setFirstScatter: setFirstScatter,
97461 getStackOpts: getStackOpts
97462};
97463
97464},{"../../constants/numerical":267,"../../lib":287,"../../plots/cartesian/align_period":331,"../../plots/cartesian/axes":334,"./arrays_to_calcdata":496,"./calc_selection":499,"./colorscale_calc":500,"./subtypes":522,"fast-isnumeric":33}],499:[function(_dereq_,module,exports){
97465'use strict';
97466
97467var Lib = _dereq_('../../lib');
97468
97469module.exports = function calcSelection(cd, trace) {
97470 if(Lib.isArrayOrTypedArray(trace.selectedpoints)) {
97471 Lib.tagSelected(cd, trace);
97472 }
97473};
97474
97475},{"../../lib":287}],500:[function(_dereq_,module,exports){
97476'use strict';
97477
97478var hasColorscale = _dereq_('../../components/colorscale/helpers').hasColorscale;
97479var calcColorscale = _dereq_('../../components/colorscale/calc');
97480
97481var subTypes = _dereq_('./subtypes');
97482
97483module.exports = function calcMarkerColorscale(gd, trace) {
97484 if(subTypes.hasLines(trace) && hasColorscale(trace, 'line')) {
97485 calcColorscale(gd, trace, {
97486 vals: trace.line.color,
97487 containerStr: 'line',
97488 cLetter: 'c'
97489 });
97490 }
97491
97492 if(subTypes.hasMarkers(trace)) {
97493 if(hasColorscale(trace, 'marker')) {
97494 calcColorscale(gd, trace, {
97495 vals: trace.marker.color,
97496 containerStr: 'marker',
97497 cLetter: 'c'
97498 });
97499 }
97500 if(hasColorscale(trace, 'marker.line')) {
97501 calcColorscale(gd, trace, {
97502 vals: trace.marker.line.color,
97503 containerStr: 'marker.line',
97504 cLetter: 'c'
97505 });
97506 }
97507 }
97508};
97509
97510},{"../../components/colorscale/calc":165,"../../components/colorscale/helpers":168,"./subtypes":522}],501:[function(_dereq_,module,exports){
97511'use strict';
97512
97513module.exports = {
97514 PTS_LINESONLY: 20,
97515
97516 // fixed parameters of clustering and clipping algorithms
97517
97518 // fraction of clustering tolerance "so close we don't even consider it a new point"
97519 minTolerance: 0.2,
97520 // how fast does clustering tolerance increase as you get away from the visible region
97521 toleranceGrowth: 10,
97522
97523 // number of viewport sizes away from the visible region
97524 // at which we clip all lines to the perimeter
97525 maxScreensAway: 20,
97526
97527 eventDataKeys: []
97528};
97529
97530},{}],502:[function(_dereq_,module,exports){
97531'use strict';
97532
97533var calc = _dereq_('./calc');
97534
97535/*
97536 * Scatter stacking & normalization calculations
97537 * runs per subplot, and can handle multiple stacking groups
97538 */
97539
97540module.exports = function crossTraceCalc(gd, plotinfo) {
97541 var xa = plotinfo.xaxis;
97542 var ya = plotinfo.yaxis;
97543 var subplot = xa._id + ya._id;
97544
97545 var subplotStackOpts = gd._fullLayout._scatterStackOpts[subplot];
97546 if(!subplotStackOpts) return;
97547
97548 var calcTraces = gd.calcdata;
97549
97550 var i, j, k, i2, cd, cd0, posj, sumj, norm;
97551 var groupOpts, interpolate, groupnorm, posAttr, valAttr;
97552 var hasAnyBlanks;
97553
97554 for(var stackGroup in subplotStackOpts) {
97555 groupOpts = subplotStackOpts[stackGroup];
97556 var indices = groupOpts.traceIndices;
97557
97558 // can get here with no indices if the stack axis is non-numeric
97559 if(!indices.length) continue;
97560
97561 interpolate = groupOpts.stackgaps === 'interpolate';
97562 groupnorm = groupOpts.groupnorm;
97563 if(groupOpts.orientation === 'v') {
97564 posAttr = 'x';
97565 valAttr = 'y';
97566 } else {
97567 posAttr = 'y';
97568 valAttr = 'x';
97569 }
97570 hasAnyBlanks = new Array(indices.length);
97571 for(i = 0; i < hasAnyBlanks.length; i++) {
97572 hasAnyBlanks[i] = false;
97573 }
97574
97575 // Collect the complete set of all positions across ALL traces.
97576 // Start with the first trace, then interleave items from later traces
97577 // as needed.
97578 // Fill in mising items as we go.
97579 cd0 = calcTraces[indices[0]];
97580 var allPositions = new Array(cd0.length);
97581 for(i = 0; i < cd0.length; i++) {
97582 allPositions[i] = cd0[i][posAttr];
97583 }
97584
97585 for(i = 1; i < indices.length; i++) {
97586 cd = calcTraces[indices[i]];
97587
97588 for(j = k = 0; j < cd.length; j++) {
97589 posj = cd[j][posAttr];
97590 for(; posj > allPositions[k] && k < allPositions.length; k++) {
97591 // the current trace is missing a position from some previous trace(s)
97592 insertBlank(cd, j, allPositions[k], i, hasAnyBlanks, interpolate, posAttr);
97593 j++;
97594 }
97595 if(posj !== allPositions[k]) {
97596 // previous trace(s) are missing a position from the current trace
97597 for(i2 = 0; i2 < i; i2++) {
97598 insertBlank(calcTraces[indices[i2]], k, posj, i2, hasAnyBlanks, interpolate, posAttr);
97599 }
97600 allPositions.splice(k, 0, posj);
97601 }
97602 k++;
97603 }
97604 for(; k < allPositions.length; k++) {
97605 insertBlank(cd, j, allPositions[k], i, hasAnyBlanks, interpolate, posAttr);
97606 j++;
97607 }
97608 }
97609
97610 var serieslen = allPositions.length;
97611
97612 // stack (and normalize)!
97613 for(j = 0; j < cd0.length; j++) {
97614 sumj = cd0[j][valAttr] = cd0[j].s;
97615 for(i = 1; i < indices.length; i++) {
97616 cd = calcTraces[indices[i]];
97617 cd[0].trace._rawLength = cd[0].trace._length;
97618 cd[0].trace._length = serieslen;
97619 sumj += cd[j].s;
97620 cd[j][valAttr] = sumj;
97621 }
97622
97623 if(groupnorm) {
97624 norm = ((groupnorm === 'fraction') ? sumj : (sumj / 100)) || 1;
97625 for(i = 0; i < indices.length; i++) {
97626 var cdj = calcTraces[indices[i]][j];
97627 cdj[valAttr] /= norm;
97628 cdj.sNorm = cdj.s / norm;
97629 }
97630 }
97631 }
97632
97633 // autorange
97634 for(i = 0; i < indices.length; i++) {
97635 cd = calcTraces[indices[i]];
97636 var trace = cd[0].trace;
97637 var ppad = calc.calcMarkerSize(trace, trace._rawLength);
97638 var arrayPad = Array.isArray(ppad);
97639 if((ppad && hasAnyBlanks[i]) || arrayPad) {
97640 var ppadRaw = ppad;
97641 ppad = new Array(serieslen);
97642 for(j = 0; j < serieslen; j++) {
97643 ppad[j] = cd[j].gap ? 0 : (arrayPad ? ppadRaw[cd[j].i] : ppadRaw);
97644 }
97645 }
97646 var x = new Array(serieslen);
97647 var y = new Array(serieslen);
97648 for(j = 0; j < serieslen; j++) {
97649 x[j] = cd[j].x;
97650 y[j] = cd[j].y;
97651 }
97652 calc.calcAxisExpansion(gd, trace, xa, ya, x, y, ppad);
97653
97654 // while we're here (in a loop over all traces in the stack)
97655 // record the orientation, so hover can find it easily
97656 cd[0].t.orientation = groupOpts.orientation;
97657 }
97658 }
97659};
97660
97661function insertBlank(calcTrace, index, position, traceIndex, hasAnyBlanks, interpolate, posAttr) {
97662 hasAnyBlanks[traceIndex] = true;
97663 var newEntry = {
97664 i: null,
97665 gap: true,
97666 s: 0
97667 };
97668 newEntry[posAttr] = position;
97669 calcTrace.splice(index, 0, newEntry);
97670 // Even if we're not interpolating, if one trace has multiple
97671 // values at the same position and this trace only has one value there,
97672 // we just duplicate that one value rather than insert a zero.
97673 // We also make it look like a real point - because it's ambiguous which
97674 // one really is the real one!
97675 if(index && position === calcTrace[index - 1][posAttr]) {
97676 var prevEntry = calcTrace[index - 1];
97677 newEntry.s = prevEntry.s;
97678 // TODO is it going to cause any problems to have multiple
97679 // calcdata points with the same index?
97680 newEntry.i = prevEntry.i;
97681 newEntry.gap = prevEntry.gap;
97682 } else if(interpolate) {
97683 newEntry.s = getInterp(calcTrace, index, position, posAttr);
97684 }
97685 if(!index) {
97686 // t and trace need to stay on the first cd entry
97687 calcTrace[0].t = calcTrace[1].t;
97688 calcTrace[0].trace = calcTrace[1].trace;
97689 delete calcTrace[1].t;
97690 delete calcTrace[1].trace;
97691 }
97692}
97693
97694function getInterp(calcTrace, index, position, posAttr) {
97695 var pt0 = calcTrace[index - 1];
97696 var pt1 = calcTrace[index + 1];
97697 if(!pt1) return pt0.s;
97698 if(!pt0) return pt1.s;
97699 return pt0.s + (pt1.s - pt0.s) * (position - pt0[posAttr]) / (pt1[posAttr] - pt0[posAttr]);
97700}
97701
97702},{"./calc":498}],503:[function(_dereq_,module,exports){
97703'use strict';
97704
97705
97706// remove opacity for any trace that has a fill or is filled to
97707module.exports = function crossTraceDefaults(fullData) {
97708 for(var i = 0; i < fullData.length; i++) {
97709 var tracei = fullData[i];
97710 if(tracei.type !== 'scatter') continue;
97711
97712 var filli = tracei.fill;
97713 if(filli === 'none' || filli === 'toself') continue;
97714
97715 tracei.opacity = undefined;
97716
97717 if(filli === 'tonexty' || filli === 'tonextx') {
97718 for(var j = i - 1; j >= 0; j--) {
97719 var tracej = fullData[j];
97720
97721 if((tracej.type === 'scatter') &&
97722 (tracej.xaxis === tracei.xaxis) &&
97723 (tracej.yaxis === tracei.yaxis)) {
97724 tracej.opacity = undefined;
97725 break;
97726 }
97727 }
97728 }
97729 }
97730};
97731
97732},{}],504:[function(_dereq_,module,exports){
97733'use strict';
97734
97735var Lib = _dereq_('../../lib');
97736var Registry = _dereq_('../../registry');
97737
97738var attributes = _dereq_('./attributes');
97739var constants = _dereq_('./constants');
97740var subTypes = _dereq_('./subtypes');
97741var handleXYDefaults = _dereq_('./xy_defaults');
97742var handlePeriodDefaults = _dereq_('./period_defaults');
97743var handleStackDefaults = _dereq_('./stack_defaults');
97744var handleMarkerDefaults = _dereq_('./marker_defaults');
97745var handleLineDefaults = _dereq_('./line_defaults');
97746var handleLineShapeDefaults = _dereq_('./line_shape_defaults');
97747var handleTextDefaults = _dereq_('./text_defaults');
97748var handleFillColorDefaults = _dereq_('./fillcolor_defaults');
97749
97750module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
97751 function coerce(attr, dflt) {
97752 return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
97753 }
97754
97755 var len = handleXYDefaults(traceIn, traceOut, layout, coerce);
97756 if(!len) traceOut.visible = false;
97757
97758 if(!traceOut.visible) return;
97759
97760 handlePeriodDefaults(traceIn, traceOut, layout, coerce);
97761 coerce('xhoverformat');
97762 coerce('yhoverformat');
97763
97764 var stackGroupOpts = handleStackDefaults(traceIn, traceOut, layout, coerce);
97765
97766 var defaultMode = !stackGroupOpts && (len < constants.PTS_LINESONLY) ?
97767 'lines+markers' : 'lines';
97768 coerce('text');
97769 coerce('hovertext');
97770 coerce('mode', defaultMode);
97771
97772 if(subTypes.hasLines(traceOut)) {
97773 handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce);
97774 handleLineShapeDefaults(traceIn, traceOut, coerce);
97775 coerce('connectgaps');
97776 coerce('line.simplify');
97777 }
97778
97779 if(subTypes.hasMarkers(traceOut)) {
97780 handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {gradient: true});
97781 }
97782
97783 if(subTypes.hasText(traceOut)) {
97784 coerce('texttemplate');
97785 handleTextDefaults(traceIn, traceOut, layout, coerce);
97786 }
97787
97788 var dfltHoverOn = [];
97789
97790 if(subTypes.hasMarkers(traceOut) || subTypes.hasText(traceOut)) {
97791 coerce('cliponaxis');
97792 coerce('marker.maxdisplayed');
97793 dfltHoverOn.push('points');
97794 }
97795
97796 // It's possible for this default to be changed by a later trace.
97797 // We handle that case in some hacky code inside handleStackDefaults.
97798 coerce('fill', stackGroupOpts ? stackGroupOpts.fillDflt : 'none');
97799 if(traceOut.fill !== 'none') {
97800 handleFillColorDefaults(traceIn, traceOut, defaultColor, coerce);
97801 if(!subTypes.hasLines(traceOut)) handleLineShapeDefaults(traceIn, traceOut, coerce);
97802 }
97803
97804 var lineColor = (traceOut.line || {}).color;
97805 var markerColor = (traceOut.marker || {}).color;
97806
97807 if(traceOut.fill === 'tonext' || traceOut.fill === 'toself') {
97808 dfltHoverOn.push('fills');
97809 }
97810 coerce('hoveron', dfltHoverOn.join('+') || 'points');
97811 if(traceOut.hoveron !== 'fills') coerce('hovertemplate');
97812 var errorBarsSupplyDefaults = Registry.getComponentMethod('errorbars', 'supplyDefaults');
97813 errorBarsSupplyDefaults(traceIn, traceOut, lineColor || markerColor || defaultColor, {axis: 'y'});
97814 errorBarsSupplyDefaults(traceIn, traceOut, lineColor || markerColor || defaultColor, {axis: 'x', inherit: 'y'});
97815
97816 Lib.coerceSelectionMarkerOpacity(traceOut, coerce);
97817};
97818
97819},{"../../lib":287,"../../registry":376,"./attributes":497,"./constants":501,"./fillcolor_defaults":505,"./line_defaults":510,"./line_shape_defaults":512,"./marker_defaults":516,"./period_defaults":517,"./stack_defaults":520,"./subtypes":522,"./text_defaults":523,"./xy_defaults":524}],505:[function(_dereq_,module,exports){
97820'use strict';
97821
97822var Color = _dereq_('../../components/color');
97823var isArrayOrTypedArray = _dereq_('../../lib').isArrayOrTypedArray;
97824
97825module.exports = function fillColorDefaults(traceIn, traceOut, defaultColor, coerce) {
97826 var inheritColorFromMarker = false;
97827
97828 if(traceOut.marker) {
97829 // don't try to inherit a color array
97830 var markerColor = traceOut.marker.color;
97831 var markerLineColor = (traceOut.marker.line || {}).color;
97832
97833 if(markerColor && !isArrayOrTypedArray(markerColor)) {
97834 inheritColorFromMarker = markerColor;
97835 } else if(markerLineColor && !isArrayOrTypedArray(markerLineColor)) {
97836 inheritColorFromMarker = markerLineColor;
97837 }
97838 }
97839
97840 coerce('fillcolor', Color.addOpacity(
97841 (traceOut.line || {}).color ||
97842 inheritColorFromMarker ||
97843 defaultColor, 0.5
97844 ));
97845};
97846
97847},{"../../components/color":157,"../../lib":287}],506:[function(_dereq_,module,exports){
97848'use strict';
97849
97850var Axes = _dereq_('../../plots/cartesian/axes');
97851
97852module.exports = function formatLabels(cdi, trace, fullLayout) {
97853 var labels = {};
97854
97855 var mockGd = {_fullLayout: fullLayout};
97856 var xa = Axes.getFromTrace(mockGd, trace, 'x');
97857 var ya = Axes.getFromTrace(mockGd, trace, 'y');
97858
97859 labels.xLabel = Axes.tickText(xa, xa.c2l(cdi.x), true).text;
97860 labels.yLabel = Axes.tickText(ya, ya.c2l(cdi.y), true).text;
97861
97862 return labels;
97863};
97864
97865},{"../../plots/cartesian/axes":334}],507:[function(_dereq_,module,exports){
97866'use strict';
97867
97868var Color = _dereq_('../../components/color');
97869var subtypes = _dereq_('./subtypes');
97870
97871
97872module.exports = function getTraceColor(trace, di) {
97873 var lc, tc;
97874
97875 // TODO: text modes
97876
97877 if(trace.mode === 'lines') {
97878 lc = trace.line.color;
97879 return (lc && Color.opacity(lc)) ?
97880 lc : trace.fillcolor;
97881 } else if(trace.mode === 'none') {
97882 return trace.fill ? trace.fillcolor : '';
97883 } else {
97884 var mc = di.mcc || (trace.marker || {}).color;
97885 var mlc = di.mlcc || ((trace.marker || {}).line || {}).color;
97886
97887 tc = (mc && Color.opacity(mc)) ? mc :
97888 (mlc && Color.opacity(mlc) &&
97889 (di.mlw || ((trace.marker || {}).line || {}).width)) ? mlc : '';
97890
97891 if(tc) {
97892 // make sure the points aren't TOO transparent
97893 if(Color.opacity(tc) < 0.3) {
97894 return Color.addOpacity(tc, 0.3);
97895 } else return tc;
97896 } else {
97897 lc = (trace.line || {}).color;
97898 return (lc && Color.opacity(lc) &&
97899 subtypes.hasLines(trace) && trace.line.width) ?
97900 lc : trace.fillcolor;
97901 }
97902 }
97903};
97904
97905},{"../../components/color":157,"./subtypes":522}],508:[function(_dereq_,module,exports){
97906'use strict';
97907
97908var Lib = _dereq_('../../lib');
97909var Fx = _dereq_('../../components/fx');
97910var Registry = _dereq_('../../registry');
97911var getTraceColor = _dereq_('./get_trace_color');
97912var Color = _dereq_('../../components/color');
97913var fillText = Lib.fillText;
97914
97915module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
97916 var cd = pointData.cd;
97917 var trace = cd[0].trace;
97918 var xa = pointData.xa;
97919 var ya = pointData.ya;
97920 var xpx = xa.c2p(xval);
97921 var ypx = ya.c2p(yval);
97922 var pt = [xpx, ypx];
97923 var hoveron = trace.hoveron || '';
97924 var minRad = (trace.mode.indexOf('markers') !== -1) ? 3 : 0.5;
97925
97926 var xPeriod = !!trace.xperiodalignment;
97927 var yPeriod = !!trace.yperiodalignment;
97928
97929 // look for points to hover on first, then take fills only if we
97930 // didn't find a point
97931
97932 if(hoveron.indexOf('points') !== -1) {
97933 // dx and dy are used in compare modes - here we want to always
97934 // prioritize the closest data point, at least as long as markers are
97935 // the same size or nonexistent, but still try to prioritize small markers too.
97936 var dx = function(di) {
97937 if(xPeriod) {
97938 var x0 = xa.c2p(di.xStart);
97939 var x1 = xa.c2p(di.xEnd);
97940
97941 return (
97942 xpx >= Math.min(x0, x1) &&
97943 xpx <= Math.max(x0, x1)
97944 ) ? 0 : Infinity;
97945 }
97946
97947 var rad = Math.max(3, di.mrc || 0);
97948 var kink = 1 - 1 / rad;
97949 var dxRaw = Math.abs(xa.c2p(di.x) - xpx);
97950 return (dxRaw < rad) ? (kink * dxRaw / rad) : (dxRaw - rad + kink);
97951 };
97952 var dy = function(di) {
97953 if(yPeriod) {
97954 var y0 = ya.c2p(di.yStart);
97955 var y1 = ya.c2p(di.yEnd);
97956
97957 return (
97958 ypx >= Math.min(y0, y1) &&
97959 ypx <= Math.max(y0, y1)
97960 ) ? 0 : Infinity;
97961 }
97962
97963 var rad = Math.max(3, di.mrc || 0);
97964 var kink = 1 - 1 / rad;
97965 var dyRaw = Math.abs(ya.c2p(di.y) - ypx);
97966 return (dyRaw < rad) ? (kink * dyRaw / rad) : (dyRaw - rad + kink);
97967 };
97968
97969 // scatter points: d.mrc is the calculated marker radius
97970 // adjust the distance so if you're inside the marker it
97971 // always will show up regardless of point size, but
97972 // prioritize smaller points
97973 var dxy = function(di) {
97974 var rad = Math.max(minRad, di.mrc || 0);
97975 var dx = xa.c2p(di.x) - xpx;
97976 var dy = ya.c2p(di.y) - ypx;
97977 return Math.max(Math.sqrt(dx * dx + dy * dy) - rad, 1 - minRad / rad);
97978 };
97979 var distfn = Fx.getDistanceFunction(hovermode, dx, dy, dxy);
97980
97981 Fx.getClosest(cd, distfn, pointData);
97982
97983 // skip the rest (for this trace) if we didn't find a close point
97984 if(pointData.index !== false) {
97985 // the closest data point
97986 var di = cd[pointData.index];
97987 var xc = xa.c2p(di.x, true);
97988 var yc = ya.c2p(di.y, true);
97989 var rad = di.mrc || 1;
97990
97991 // now we're done using the whole `calcdata` array, replace the
97992 // index with the original index (in case of inserted point from
97993 // stacked area)
97994 pointData.index = di.i;
97995
97996 var orientation = cd[0].t.orientation;
97997 // TODO: for scatter and bar, option to show (sub)totals and
97998 // raw data? Currently stacked and/or normalized bars just show
97999 // the normalized individual sizes, so that's what I'm doing here
98000 // for now.
98001 var sizeVal = orientation && (di.sNorm || di.s);
98002 var xLabelVal = (orientation === 'h') ? sizeVal : di.orig_x !== undefined ? di.orig_x : di.x;
98003 var yLabelVal = (orientation === 'v') ? sizeVal : di.orig_y !== undefined ? di.orig_y : di.y;
98004
98005 Lib.extendFlat(pointData, {
98006 color: getTraceColor(trace, di),
98007
98008 x0: xc - rad,
98009 x1: xc + rad,
98010 xLabelVal: xLabelVal,
98011
98012 y0: yc - rad,
98013 y1: yc + rad,
98014 yLabelVal: yLabelVal,
98015
98016 spikeDistance: dxy(di),
98017 hovertemplate: trace.hovertemplate
98018 });
98019
98020 fillText(di, trace, pointData);
98021 Registry.getComponentMethod('errorbars', 'hoverInfo')(di, trace, pointData);
98022
98023 return [pointData];
98024 }
98025 }
98026
98027 // even if hoveron is 'fills', only use it if we have polygons too
98028 if(hoveron.indexOf('fills') !== -1 && trace._polygons) {
98029 var polygons = trace._polygons;
98030 var polygonsIn = [];
98031 var inside = false;
98032 var xmin = Infinity;
98033 var xmax = -Infinity;
98034 var ymin = Infinity;
98035 var ymax = -Infinity;
98036
98037 var i, j, polygon, pts, xCross, x0, x1, y0, y1;
98038
98039 for(i = 0; i < polygons.length; i++) {
98040 polygon = polygons[i];
98041 // TODO: this is not going to work right for curved edges, it will
98042 // act as though they're straight. That's probably going to need
98043 // the elements themselves to capture the events. Worth it?
98044 if(polygon.contains(pt)) {
98045 inside = !inside;
98046 // TODO: need better than just the overall bounding box
98047 polygonsIn.push(polygon);
98048 ymin = Math.min(ymin, polygon.ymin);
98049 ymax = Math.max(ymax, polygon.ymax);
98050 }
98051 }
98052
98053 if(inside) {
98054 // constrain ymin/max to the visible plot, so the label goes
98055 // at the middle of the piece you can see
98056 ymin = Math.max(ymin, 0);
98057 ymax = Math.min(ymax, ya._length);
98058
98059 // find the overall left-most and right-most points of the
98060 // polygon(s) we're inside at their combined vertical midpoint.
98061 // This is where we will draw the hover label.
98062 // Note that this might not be the vertical midpoint of the
98063 // whole trace, if it's disjoint.
98064 var yAvg = (ymin + ymax) / 2;
98065 for(i = 0; i < polygonsIn.length; i++) {
98066 pts = polygonsIn[i].pts;
98067 for(j = 1; j < pts.length; j++) {
98068 y0 = pts[j - 1][1];
98069 y1 = pts[j][1];
98070 if((y0 > yAvg) !== (y1 >= yAvg)) {
98071 x0 = pts[j - 1][0];
98072 x1 = pts[j][0];
98073 if(y1 - y0) {
98074 xCross = x0 + (x1 - x0) * (yAvg - y0) / (y1 - y0);
98075 xmin = Math.min(xmin, xCross);
98076 xmax = Math.max(xmax, xCross);
98077 }
98078 }
98079 }
98080 }
98081
98082 // constrain xmin/max to the visible plot now too
98083 xmin = Math.max(xmin, 0);
98084 xmax = Math.min(xmax, xa._length);
98085
98086 // get only fill or line color for the hover color
98087 var color = Color.defaultLine;
98088 if(Color.opacity(trace.fillcolor)) color = trace.fillcolor;
98089 else if(Color.opacity((trace.line || {}).color)) {
98090 color = trace.line.color;
98091 }
98092
98093 Lib.extendFlat(pointData, {
98094 // never let a 2D override 1D type as closest point
98095 // also: no spikeDistance, it's not allowed for fills
98096 distance: pointData.maxHoverDistance,
98097 x0: xmin,
98098 x1: xmax,
98099 y0: yAvg,
98100 y1: yAvg,
98101 color: color,
98102 hovertemplate: false
98103 });
98104
98105 delete pointData.index;
98106
98107 if(trace.text && !Array.isArray(trace.text)) {
98108 pointData.text = String(trace.text);
98109 } else pointData.text = trace.name;
98110
98111 return [pointData];
98112 }
98113 }
98114};
98115
98116},{"../../components/color":157,"../../components/fx":197,"../../lib":287,"../../registry":376,"./get_trace_color":507}],509:[function(_dereq_,module,exports){
98117'use strict';
98118
98119var subtypes = _dereq_('./subtypes');
98120
98121module.exports = {
98122 hasLines: subtypes.hasLines,
98123 hasMarkers: subtypes.hasMarkers,
98124 hasText: subtypes.hasText,
98125 isBubble: subtypes.isBubble,
98126
98127 attributes: _dereq_('./attributes'),
98128 supplyDefaults: _dereq_('./defaults'),
98129 crossTraceDefaults: _dereq_('./cross_trace_defaults'),
98130 calc: _dereq_('./calc').calc,
98131 crossTraceCalc: _dereq_('./cross_trace_calc'),
98132 arraysToCalcdata: _dereq_('./arrays_to_calcdata'),
98133 plot: _dereq_('./plot'),
98134 colorbar: _dereq_('./marker_colorbar'),
98135 formatLabels: _dereq_('./format_labels'),
98136 style: _dereq_('./style').style,
98137 styleOnSelect: _dereq_('./style').styleOnSelect,
98138 hoverPoints: _dereq_('./hover'),
98139 selectPoints: _dereq_('./select'),
98140 animatable: true,
98141
98142 moduleType: 'trace',
98143 name: 'scatter',
98144 basePlotModule: _dereq_('../../plots/cartesian'),
98145 categories: [
98146 'cartesian', 'svg', 'symbols', 'errorBarsOK', 'showLegend', 'scatter-like',
98147 'zoomScale'
98148 ],
98149 meta: {
98150 }
98151};
98152
98153},{"../../plots/cartesian":348,"./arrays_to_calcdata":496,"./attributes":497,"./calc":498,"./cross_trace_calc":502,"./cross_trace_defaults":503,"./defaults":504,"./format_labels":506,"./hover":508,"./marker_colorbar":515,"./plot":518,"./select":519,"./style":521,"./subtypes":522}],510:[function(_dereq_,module,exports){
98154'use strict';
98155
98156var isArrayOrTypedArray = _dereq_('../../lib').isArrayOrTypedArray;
98157var hasColorscale = _dereq_('../../components/colorscale/helpers').hasColorscale;
98158var colorscaleDefaults = _dereq_('../../components/colorscale/defaults');
98159
98160module.exports = function lineDefaults(traceIn, traceOut, defaultColor, layout, coerce, opts) {
98161 var markerColor = (traceIn.marker || {}).color;
98162
98163 coerce('line.color', defaultColor);
98164
98165 if(hasColorscale(traceIn, 'line')) {
98166 colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: 'line.', cLetter: 'c'});
98167 } else {
98168 var lineColorDflt = (isArrayOrTypedArray(markerColor) ? false : markerColor) || defaultColor;
98169 coerce('line.color', lineColorDflt);
98170 }
98171
98172 coerce('line.width');
98173 if(!(opts || {}).noDash) coerce('line.dash');
98174};
98175
98176},{"../../components/colorscale/defaults":167,"../../components/colorscale/helpers":168,"../../lib":287}],511:[function(_dereq_,module,exports){
98177'use strict';
98178
98179var numConstants = _dereq_('../../constants/numerical');
98180var BADNUM = numConstants.BADNUM;
98181var LOG_CLIP = numConstants.LOG_CLIP;
98182var LOG_CLIP_PLUS = LOG_CLIP + 0.5;
98183var LOG_CLIP_MINUS = LOG_CLIP - 0.5;
98184var Lib = _dereq_('../../lib');
98185var segmentsIntersect = Lib.segmentsIntersect;
98186var constrain = Lib.constrain;
98187var constants = _dereq_('./constants');
98188
98189
98190module.exports = function linePoints(d, opts) {
98191 var xa = opts.xaxis;
98192 var ya = opts.yaxis;
98193 var xLog = xa.type === 'log';
98194 var yLog = ya.type === 'log';
98195 var xLen = xa._length;
98196 var yLen = ya._length;
98197 var connectGaps = opts.connectGaps;
98198 var baseTolerance = opts.baseTolerance;
98199 var shape = opts.shape;
98200 var linear = shape === 'linear';
98201 var fill = opts.fill && opts.fill !== 'none';
98202 var segments = [];
98203 var minTolerance = constants.minTolerance;
98204 var len = d.length;
98205 var pts = new Array(len);
98206 var pti = 0;
98207
98208 var i;
98209
98210 // pt variables are pixel coordinates [x,y] of one point
98211 // these four are the outputs of clustering on a line
98212 var clusterStartPt, clusterEndPt, clusterHighPt, clusterLowPt;
98213
98214 // "this" is the next point we're considering adding to the cluster
98215 var thisPt;
98216
98217 // did we encounter the high point first, then a low point, or vice versa?
98218 var clusterHighFirst;
98219
98220 // the first two points in the cluster determine its unit vector
98221 // so the second is always in the "High" direction
98222 var clusterUnitVector;
98223
98224 // the pixel delta from clusterStartPt
98225 var thisVector;
98226
98227 // val variables are (signed) pixel distances along the cluster vector
98228 var clusterRefDist, clusterHighVal, clusterLowVal, thisVal;
98229
98230 // deviation variables are (signed) pixel distances normal to the cluster vector
98231 var clusterMinDeviation, clusterMaxDeviation, thisDeviation;
98232
98233 // turn one calcdata point into pixel coordinates
98234 function getPt(index) {
98235 var di = d[index];
98236 if(!di) return false;
98237 var x = opts.linearized ? xa.l2p(di.x) : xa.c2p(di.x);
98238 var y = opts.linearized ? ya.l2p(di.y) : ya.c2p(di.y);
98239
98240 // if non-positive log values, set them VERY far off-screen
98241 // so the line looks essentially straight from the previous point.
98242 if(x === BADNUM) {
98243 if(xLog) x = xa.c2p(di.x, true);
98244 if(x === BADNUM) return false;
98245 // If BOTH were bad log values, make the line follow a constant
98246 // exponent rather than a constant slope
98247 if(yLog && y === BADNUM) {
98248 x *= Math.abs(xa._m * yLen * (xa._m > 0 ? LOG_CLIP_PLUS : LOG_CLIP_MINUS) /
98249 (ya._m * xLen * (ya._m > 0 ? LOG_CLIP_PLUS : LOG_CLIP_MINUS)));
98250 }
98251 x *= 1000;
98252 }
98253 if(y === BADNUM) {
98254 if(yLog) y = ya.c2p(di.y, true);
98255 if(y === BADNUM) return false;
98256 y *= 1000;
98257 }
98258 return [x, y];
98259 }
98260
98261 function crossesViewport(xFrac0, yFrac0, xFrac1, yFrac1) {
98262 var dx = xFrac1 - xFrac0;
98263 var dy = yFrac1 - yFrac0;
98264 var dx0 = 0.5 - xFrac0;
98265 var dy0 = 0.5 - yFrac0;
98266 var norm2 = dx * dx + dy * dy;
98267 var dot = dx * dx0 + dy * dy0;
98268 if(dot > 0 && dot < norm2) {
98269 var cross = dx0 * dy - dy0 * dx;
98270 if(cross * cross < norm2) return true;
98271 }
98272 }
98273
98274 var latestXFrac, latestYFrac;
98275 // if we're off-screen, increase tolerance over baseTolerance
98276 function getTolerance(pt, nextPt) {
98277 var xFrac = pt[0] / xLen;
98278 var yFrac = pt[1] / yLen;
98279 var offScreenFraction = Math.max(0, -xFrac, xFrac - 1, -yFrac, yFrac - 1);
98280 if(offScreenFraction && (latestXFrac !== undefined) &&
98281 crossesViewport(xFrac, yFrac, latestXFrac, latestYFrac)
98282 ) {
98283 offScreenFraction = 0;
98284 }
98285 if(offScreenFraction && nextPt &&
98286 crossesViewport(xFrac, yFrac, nextPt[0] / xLen, nextPt[1] / yLen)
98287 ) {
98288 offScreenFraction = 0;
98289 }
98290
98291 return (1 + constants.toleranceGrowth * offScreenFraction) * baseTolerance;
98292 }
98293
98294 function ptDist(pt1, pt2) {
98295 var dx = pt1[0] - pt2[0];
98296 var dy = pt1[1] - pt2[1];
98297 return Math.sqrt(dx * dx + dy * dy);
98298 }
98299
98300 // last bit of filtering: clip paths that are VERY far off-screen
98301 // so we don't get near the browser's hard limit (+/- 2^29 px in Chrome and FF)
98302
98303 var maxScreensAway = constants.maxScreensAway;
98304
98305 // find the intersections between the segment from pt1 to pt2
98306 // and the large rectangle maxScreensAway around the viewport
98307 // if one of pt1 and pt2 is inside and the other outside, there
98308 // will be only one intersection.
98309 // if both are outside there will be 0 or 2 intersections
98310 // (or 1 if it's right at a corner - we'll treat that like 0)
98311 // returns an array of intersection pts
98312 var xEdge0 = -xLen * maxScreensAway;
98313 var xEdge1 = xLen * (1 + maxScreensAway);
98314 var yEdge0 = -yLen * maxScreensAway;
98315 var yEdge1 = yLen * (1 + maxScreensAway);
98316 var edges = [
98317 [xEdge0, yEdge0, xEdge1, yEdge0],
98318 [xEdge1, yEdge0, xEdge1, yEdge1],
98319 [xEdge1, yEdge1, xEdge0, yEdge1],
98320 [xEdge0, yEdge1, xEdge0, yEdge0]
98321 ];
98322 var xEdge, yEdge, lastXEdge, lastYEdge, lastFarPt, edgePt;
98323
98324 // for linear line shape, edge intersections should be linearly interpolated
98325 // spline uses this too, which isn't precisely correct but is actually pretty
98326 // good, because Catmull-Rom weights far-away points less in creating the curvature
98327 function getLinearEdgeIntersections(pt1, pt2) {
98328 var out = [];
98329 var ptCount = 0;
98330 for(var i = 0; i < 4; i++) {
98331 var edge = edges[i];
98332 var ptInt = segmentsIntersect(
98333 pt1[0], pt1[1], pt2[0], pt2[1],
98334 edge[0], edge[1], edge[2], edge[3]
98335 );
98336 if(ptInt && (!ptCount ||
98337 Math.abs(ptInt.x - out[0][0]) > 1 ||
98338 Math.abs(ptInt.y - out[0][1]) > 1
98339 )) {
98340 ptInt = [ptInt.x, ptInt.y];
98341 // if we have 2 intersections, make sure the closest one to pt1 comes first
98342 if(ptCount && ptDist(ptInt, pt1) < ptDist(out[0], pt1)) out.unshift(ptInt);
98343 else out.push(ptInt);
98344 ptCount++;
98345 }
98346 }
98347 return out;
98348 }
98349
98350 function onlyConstrainedPoint(pt) {
98351 if(pt[0] < xEdge0 || pt[0] > xEdge1 || pt[1] < yEdge0 || pt[1] > yEdge1) {
98352 return [constrain(pt[0], xEdge0, xEdge1), constrain(pt[1], yEdge0, yEdge1)];
98353 }
98354 }
98355
98356 function sameEdge(pt1, pt2) {
98357 if(pt1[0] === pt2[0] && (pt1[0] === xEdge0 || pt1[0] === xEdge1)) return true;
98358 if(pt1[1] === pt2[1] && (pt1[1] === yEdge0 || pt1[1] === yEdge1)) return true;
98359 }
98360
98361 // for line shapes hv and vh, movement in the two dimensions is decoupled,
98362 // so all we need to do is constrain each dimension independently
98363 function getHVEdgeIntersections(pt1, pt2) {
98364 var out = [];
98365 var ptInt1 = onlyConstrainedPoint(pt1);
98366 var ptInt2 = onlyConstrainedPoint(pt2);
98367 if(ptInt1 && ptInt2 && sameEdge(ptInt1, ptInt2)) return out;
98368
98369 if(ptInt1) out.push(ptInt1);
98370 if(ptInt2) out.push(ptInt2);
98371 return out;
98372 }
98373
98374 // hvh and vhv we sometimes have to move one of the intersection points
98375 // out BEYOND the clipping rect, by a maximum of a factor of 2, so that
98376 // the midpoint line is drawn in the right place
98377 function getABAEdgeIntersections(dim, limit0, limit1) {
98378 return function(pt1, pt2) {
98379 var ptInt1 = onlyConstrainedPoint(pt1);
98380 var ptInt2 = onlyConstrainedPoint(pt2);
98381
98382 var out = [];
98383 if(ptInt1 && ptInt2 && sameEdge(ptInt1, ptInt2)) return out;
98384
98385 if(ptInt1) out.push(ptInt1);
98386 if(ptInt2) out.push(ptInt2);
98387
98388 var midShift = 2 * Lib.constrain((pt1[dim] + pt2[dim]) / 2, limit0, limit1) -
98389 ((ptInt1 || pt1)[dim] + (ptInt2 || pt2)[dim]);
98390 if(midShift) {
98391 var ptToAlter;
98392 if(ptInt1 && ptInt2) {
98393 ptToAlter = (midShift > 0 === ptInt1[dim] > ptInt2[dim]) ? ptInt1 : ptInt2;
98394 } else ptToAlter = ptInt1 || ptInt2;
98395
98396 ptToAlter[dim] += midShift;
98397 }
98398
98399 return out;
98400 };
98401 }
98402
98403 var getEdgeIntersections;
98404 if(shape === 'linear' || shape === 'spline') {
98405 getEdgeIntersections = getLinearEdgeIntersections;
98406 } else if(shape === 'hv' || shape === 'vh') {
98407 getEdgeIntersections = getHVEdgeIntersections;
98408 } else if(shape === 'hvh') getEdgeIntersections = getABAEdgeIntersections(0, xEdge0, xEdge1);
98409 else if(shape === 'vhv') getEdgeIntersections = getABAEdgeIntersections(1, yEdge0, yEdge1);
98410
98411 // a segment pt1->pt2 entirely outside the nearby region:
98412 // find the corner it gets closest to touching
98413 function getClosestCorner(pt1, pt2) {
98414 var dx = pt2[0] - pt1[0];
98415 var m = (pt2[1] - pt1[1]) / dx;
98416 var b = (pt1[1] * pt2[0] - pt2[1] * pt1[0]) / dx;
98417
98418 if(b > 0) return [m > 0 ? xEdge0 : xEdge1, yEdge1];
98419 else return [m > 0 ? xEdge1 : xEdge0, yEdge0];
98420 }
98421
98422 function updateEdge(pt) {
98423 var x = pt[0];
98424 var y = pt[1];
98425 var xSame = x === pts[pti - 1][0];
98426 var ySame = y === pts[pti - 1][1];
98427 // duplicate point?
98428 if(xSame && ySame) return;
98429 if(pti > 1) {
98430 // backtracking along an edge?
98431 var xSame2 = x === pts[pti - 2][0];
98432 var ySame2 = y === pts[pti - 2][1];
98433 if(xSame && (x === xEdge0 || x === xEdge1) && xSame2) {
98434 if(ySame2) pti--; // backtracking exactly - drop prev pt and don't add
98435 else pts[pti - 1] = pt; // not exact: replace the prev pt
98436 } else if(ySame && (y === yEdge0 || y === yEdge1) && ySame2) {
98437 if(xSame2) pti--;
98438 else pts[pti - 1] = pt;
98439 } else pts[pti++] = pt;
98440 } else pts[pti++] = pt;
98441 }
98442
98443 function updateEdgesForReentry(pt) {
98444 // if we're outside the nearby region and going back in,
98445 // we may need to loop around a corner point
98446 if(pts[pti - 1][0] !== pt[0] && pts[pti - 1][1] !== pt[1]) {
98447 updateEdge([lastXEdge, lastYEdge]);
98448 }
98449 updateEdge(pt);
98450 lastFarPt = null;
98451 lastXEdge = lastYEdge = 0;
98452 }
98453
98454 function addPt(pt) {
98455 latestXFrac = pt[0] / xLen;
98456 latestYFrac = pt[1] / yLen;
98457 // Are we more than maxScreensAway off-screen any direction?
98458 // if so, clip to this box, but in such a way that on-screen
98459 // drawing is unchanged
98460 xEdge = (pt[0] < xEdge0) ? xEdge0 : (pt[0] > xEdge1) ? xEdge1 : 0;
98461 yEdge = (pt[1] < yEdge0) ? yEdge0 : (pt[1] > yEdge1) ? yEdge1 : 0;
98462 if(xEdge || yEdge) {
98463 if(!pti) {
98464 // to get fills right - if first point is far, push it toward the
98465 // screen in whichever direction(s) are far
98466
98467 pts[pti++] = [xEdge || pt[0], yEdge || pt[1]];
98468 } else if(lastFarPt) {
98469 // both this point and the last are outside the nearby region
98470 // check if we're crossing the nearby region
98471 var intersections = getEdgeIntersections(lastFarPt, pt);
98472 if(intersections.length > 1) {
98473 updateEdgesForReentry(intersections[0]);
98474 pts[pti++] = intersections[1];
98475 }
98476 } else {
98477 // we're leaving the nearby region - add the point where we left it
98478
98479 edgePt = getEdgeIntersections(pts[pti - 1], pt)[0];
98480 pts[pti++] = edgePt;
98481 }
98482
98483 var lastPt = pts[pti - 1];
98484 if(xEdge && yEdge && (lastPt[0] !== xEdge || lastPt[1] !== yEdge)) {
98485 // we've gone out beyond a new corner: add the corner too
98486 // so that the next point will take the right winding
98487 if(lastFarPt) {
98488 if(lastXEdge !== xEdge && lastYEdge !== yEdge) {
98489 if(lastXEdge && lastYEdge) {
98490 // we've gone around to an opposite corner - we
98491 // need to add the correct extra corner
98492 // in order to get the right winding
98493 updateEdge(getClosestCorner(lastFarPt, pt));
98494 } else {
98495 // we're coming from a far edge - the extra corner
98496 // we need is determined uniquely by the sectors
98497 updateEdge([lastXEdge || xEdge, lastYEdge || yEdge]);
98498 }
98499 } else if(lastXEdge && lastYEdge) {
98500 updateEdge([lastXEdge, lastYEdge]);
98501 }
98502 }
98503 updateEdge([xEdge, yEdge]);
98504 } else if((lastXEdge - xEdge) && (lastYEdge - yEdge)) {
98505 // we're coming from an edge or far corner to an edge - again the
98506 // extra corner we need is uniquely determined by the sectors
98507 updateEdge([xEdge || lastXEdge, yEdge || lastYEdge]);
98508 }
98509 lastFarPt = pt;
98510 lastXEdge = xEdge;
98511 lastYEdge = yEdge;
98512 } else {
98513 if(lastFarPt) {
98514 // this point is in range but the previous wasn't: add its entry pt first
98515 updateEdgesForReentry(getEdgeIntersections(lastFarPt, pt)[0]);
98516 }
98517
98518 pts[pti++] = pt;
98519 }
98520 }
98521
98522 // loop over ALL points in this trace
98523 for(i = 0; i < len; i++) {
98524 clusterStartPt = getPt(i);
98525 if(!clusterStartPt) continue;
98526
98527 pti = 0;
98528 lastFarPt = null;
98529 addPt(clusterStartPt);
98530
98531 // loop over one segment of the trace
98532 for(i++; i < len; i++) {
98533 clusterHighPt = getPt(i);
98534 if(!clusterHighPt) {
98535 if(connectGaps) continue;
98536 else break;
98537 }
98538
98539 // can't decimate if nonlinear line shape
98540 // TODO: we *could* decimate [hv]{2,3} shapes if we restricted clusters to horz or vert again
98541 // but spline would be verrry awkward to decimate
98542 if(!linear || !opts.simplify) {
98543 addPt(clusterHighPt);
98544 continue;
98545 }
98546
98547 var nextPt = getPt(i + 1);
98548
98549 clusterRefDist = ptDist(clusterHighPt, clusterStartPt);
98550
98551 // #3147 - always include the very first and last points for fills
98552 if(!(fill && (pti === 0 || pti === len - 1)) &&
98553 clusterRefDist < getTolerance(clusterHighPt, nextPt) * minTolerance) continue;
98554
98555 clusterUnitVector = [
98556 (clusterHighPt[0] - clusterStartPt[0]) / clusterRefDist,
98557 (clusterHighPt[1] - clusterStartPt[1]) / clusterRefDist
98558 ];
98559
98560 clusterLowPt = clusterStartPt;
98561 clusterHighVal = clusterRefDist;
98562 clusterLowVal = clusterMinDeviation = clusterMaxDeviation = 0;
98563 clusterHighFirst = false;
98564 clusterEndPt = clusterHighPt;
98565
98566 // loop over one cluster of points that collapse onto one line
98567 for(i++; i < d.length; i++) {
98568 thisPt = nextPt;
98569 nextPt = getPt(i + 1);
98570 if(!thisPt) {
98571 if(connectGaps) continue;
98572 else break;
98573 }
98574 thisVector = [
98575 thisPt[0] - clusterStartPt[0],
98576 thisPt[1] - clusterStartPt[1]
98577 ];
98578 // cross product (or dot with normal to the cluster vector)
98579 thisDeviation = thisVector[0] * clusterUnitVector[1] - thisVector[1] * clusterUnitVector[0];
98580 clusterMinDeviation = Math.min(clusterMinDeviation, thisDeviation);
98581 clusterMaxDeviation = Math.max(clusterMaxDeviation, thisDeviation);
98582
98583 if(clusterMaxDeviation - clusterMinDeviation > getTolerance(thisPt, nextPt)) break;
98584
98585 clusterEndPt = thisPt;
98586 thisVal = thisVector[0] * clusterUnitVector[0] + thisVector[1] * clusterUnitVector[1];
98587
98588 if(thisVal > clusterHighVal) {
98589 clusterHighVal = thisVal;
98590 clusterHighPt = thisPt;
98591 clusterHighFirst = false;
98592 } else if(thisVal < clusterLowVal) {
98593 clusterLowVal = thisVal;
98594 clusterLowPt = thisPt;
98595 clusterHighFirst = true;
98596 }
98597 }
98598
98599 // insert this cluster into pts
98600 // we've already inserted the start pt, now check if we have high and low pts
98601 if(clusterHighFirst) {
98602 addPt(clusterHighPt);
98603 if(clusterEndPt !== clusterLowPt) addPt(clusterLowPt);
98604 } else {
98605 if(clusterLowPt !== clusterStartPt) addPt(clusterLowPt);
98606 if(clusterEndPt !== clusterHighPt) addPt(clusterHighPt);
98607 }
98608 // and finally insert the end pt
98609 addPt(clusterEndPt);
98610
98611 // have we reached the end of this segment?
98612 if(i >= d.length || !thisPt) break;
98613
98614 // otherwise we have an out-of-cluster point to insert as next clusterStartPt
98615 addPt(thisPt);
98616 clusterStartPt = thisPt;
98617 }
98618
98619 // to get fills right - repeat what we did at the start
98620 if(lastFarPt) updateEdge([lastXEdge || lastFarPt[0], lastYEdge || lastFarPt[1]]);
98621
98622 segments.push(pts.slice(0, pti));
98623 }
98624
98625 return segments;
98626};
98627
98628},{"../../constants/numerical":267,"../../lib":287,"./constants":501}],512:[function(_dereq_,module,exports){
98629'use strict';
98630
98631
98632// common to 'scatter' and 'scatterternary'
98633module.exports = function handleLineShapeDefaults(traceIn, traceOut, coerce) {
98634 var shape = coerce('line.shape');
98635 if(shape === 'spline') coerce('line.smoothing');
98636};
98637
98638},{}],513:[function(_dereq_,module,exports){
98639'use strict';
98640
98641var LINKEDFILLS = {tonextx: 1, tonexty: 1, tonext: 1};
98642
98643module.exports = function linkTraces(gd, plotinfo, cdscatter) {
98644 var trace, i, group, prevtrace, groupIndex;
98645
98646 // first sort traces to keep stacks & filled-together groups together
98647 var groupIndices = {};
98648 var needsSort = false;
98649 var prevGroupIndex = -1;
98650 var nextGroupIndex = 0;
98651 var prevUnstackedGroupIndex = -1;
98652 for(i = 0; i < cdscatter.length; i++) {
98653 trace = cdscatter[i][0].trace;
98654 group = trace.stackgroup || '';
98655 if(group) {
98656 if(group in groupIndices) {
98657 groupIndex = groupIndices[group];
98658 } else {
98659 groupIndex = groupIndices[group] = nextGroupIndex;
98660 nextGroupIndex++;
98661 }
98662 } else if(trace.fill in LINKEDFILLS && prevUnstackedGroupIndex >= 0) {
98663 groupIndex = prevUnstackedGroupIndex;
98664 } else {
98665 groupIndex = prevUnstackedGroupIndex = nextGroupIndex;
98666 nextGroupIndex++;
98667 }
98668
98669 if(groupIndex < prevGroupIndex) needsSort = true;
98670 trace._groupIndex = prevGroupIndex = groupIndex;
98671 }
98672
98673 var cdscatterSorted = cdscatter.slice();
98674 if(needsSort) {
98675 cdscatterSorted.sort(function(a, b) {
98676 var traceA = a[0].trace;
98677 var traceB = b[0].trace;
98678 return (traceA._groupIndex - traceB._groupIndex) ||
98679 (traceA.index - traceB.index);
98680 });
98681 }
98682
98683 // now link traces to each other
98684 var prevtraces = {};
98685 for(i = 0; i < cdscatterSorted.length; i++) {
98686 trace = cdscatterSorted[i][0].trace;
98687 group = trace.stackgroup || '';
98688
98689 // Note: The check which ensures all cdscatter here are for the same axis and
98690 // are either cartesian or scatterternary has been removed. This code assumes
98691 // the passed scattertraces have been filtered to the proper plot types and
98692 // the proper subplots.
98693 if(trace.visible === true) {
98694 trace._nexttrace = null;
98695
98696 if(trace.fill in LINKEDFILLS) {
98697 prevtrace = prevtraces[group];
98698 trace._prevtrace = prevtrace || null;
98699
98700 if(prevtrace) {
98701 prevtrace._nexttrace = trace;
98702 }
98703 }
98704
98705 trace._ownfill = (trace.fill && (
98706 trace.fill.substr(0, 6) === 'tozero' ||
98707 trace.fill === 'toself' ||
98708 (trace.fill.substr(0, 2) === 'to' && !trace._prevtrace)
98709 ));
98710
98711 prevtraces[group] = trace;
98712 } else {
98713 trace._prevtrace = trace._nexttrace = trace._ownfill = null;
98714 }
98715 }
98716
98717 return cdscatterSorted;
98718};
98719
98720},{}],514:[function(_dereq_,module,exports){
98721'use strict';
98722
98723var isNumeric = _dereq_('fast-isnumeric');
98724
98725
98726// used in the drawing step for 'scatter' and 'scattegeo' and
98727// in the convert step for 'scatter3d'
98728module.exports = function makeBubbleSizeFn(trace, factor) {
98729 if(!factor) {
98730 factor = 2;
98731 }
98732 var marker = trace.marker;
98733 var sizeRef = marker.sizeref || 1;
98734 var sizeMin = marker.sizemin || 0;
98735
98736 // for bubble charts, allow scaling the provided value linearly
98737 // and by area or diameter.
98738 // Note this only applies to the array-value sizes
98739
98740 var baseFn = (marker.sizemode === 'area') ?
98741 function(v) { return Math.sqrt(v / sizeRef); } :
98742 function(v) { return v / sizeRef; };
98743
98744 // TODO add support for position/negative bubbles?
98745 // TODO add 'sizeoffset' attribute?
98746 return function(v) {
98747 var baseSize = baseFn(v / factor);
98748
98749 // don't show non-numeric and negative sizes
98750 return (isNumeric(baseSize) && (baseSize > 0)) ?
98751 Math.max(baseSize, sizeMin) :
98752 0;
98753 };
98754};
98755
98756},{"fast-isnumeric":33}],515:[function(_dereq_,module,exports){
98757'use strict';
98758
98759module.exports = {
98760 container: 'marker',
98761 min: 'cmin',
98762 max: 'cmax'
98763};
98764
98765},{}],516:[function(_dereq_,module,exports){
98766'use strict';
98767
98768var Color = _dereq_('../../components/color');
98769var hasColorscale = _dereq_('../../components/colorscale/helpers').hasColorscale;
98770var colorscaleDefaults = _dereq_('../../components/colorscale/defaults');
98771
98772var subTypes = _dereq_('./subtypes');
98773
98774/*
98775 * opts: object of flags to control features not all marker users support
98776 * noLine: caller does not support marker lines
98777 * gradient: caller supports gradients
98778 * noSelect: caller does not support selected/unselected attribute containers
98779 */
98780module.exports = function markerDefaults(traceIn, traceOut, defaultColor, layout, coerce, opts) {
98781 var isBubble = subTypes.isBubble(traceIn);
98782 var lineColor = (traceIn.line || {}).color;
98783 var defaultMLC;
98784
98785 opts = opts || {};
98786
98787 // marker.color inherit from line.color (even if line.color is an array)
98788 if(lineColor) defaultColor = lineColor;
98789
98790 coerce('marker.symbol');
98791 coerce('marker.opacity', isBubble ? 0.7 : 1);
98792 coerce('marker.size');
98793
98794 coerce('marker.color', defaultColor);
98795 if(hasColorscale(traceIn, 'marker')) {
98796 colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: 'marker.', cLetter: 'c'});
98797 }
98798
98799 if(!opts.noSelect) {
98800 coerce('selected.marker.color');
98801 coerce('unselected.marker.color');
98802 coerce('selected.marker.size');
98803 coerce('unselected.marker.size');
98804 }
98805
98806 if(!opts.noLine) {
98807 // if there's a line with a different color than the marker, use
98808 // that line color as the default marker line color
98809 // (except when it's an array)
98810 // mostly this is for transparent markers to behave nicely
98811 if(lineColor && !Array.isArray(lineColor) && (traceOut.marker.color !== lineColor)) {
98812 defaultMLC = lineColor;
98813 } else if(isBubble) defaultMLC = Color.background;
98814 else defaultMLC = Color.defaultLine;
98815
98816 coerce('marker.line.color', defaultMLC);
98817 if(hasColorscale(traceIn, 'marker.line')) {
98818 colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: 'marker.line.', cLetter: 'c'});
98819 }
98820
98821 coerce('marker.line.width', isBubble ? 1 : 0);
98822 }
98823
98824 if(isBubble) {
98825 coerce('marker.sizeref');
98826 coerce('marker.sizemin');
98827 coerce('marker.sizemode');
98828 }
98829
98830 if(opts.gradient) {
98831 var gradientType = coerce('marker.gradient.type');
98832 if(gradientType !== 'none') {
98833 coerce('marker.gradient.color');
98834 }
98835 }
98836};
98837
98838},{"../../components/color":157,"../../components/colorscale/defaults":167,"../../components/colorscale/helpers":168,"./subtypes":522}],517:[function(_dereq_,module,exports){
98839'use strict';
98840
98841var dateTick0 = _dereq_('../../lib').dateTick0;
98842var numConstants = _dereq_('../../constants/numerical');
98843var ONEWEEK = numConstants.ONEWEEK;
98844
98845function getPeriod0Dflt(period, calendar) {
98846 if(period % ONEWEEK === 0) {
98847 return dateTick0(calendar, 1); // Sunday
98848 }
98849 return dateTick0(calendar, 0);
98850}
98851
98852module.exports = function handlePeriodDefaults(traceIn, traceOut, layout, coerce, opts) {
98853 if(!opts) {
98854 opts = {
98855 x: true,
98856 y: true
98857 };
98858 }
98859
98860 if(opts.x) {
98861 var xperiod = coerce('xperiod');
98862 if(xperiod) {
98863 coerce('xperiod0', getPeriod0Dflt(xperiod, traceOut.xcalendar));
98864 coerce('xperiodalignment');
98865 }
98866 }
98867
98868 if(opts.y) {
98869 var yperiod = coerce('yperiod');
98870 if(yperiod) {
98871 coerce('yperiod0', getPeriod0Dflt(yperiod, traceOut.ycalendar));
98872 coerce('yperiodalignment');
98873 }
98874 }
98875};
98876
98877},{"../../constants/numerical":267,"../../lib":287}],518:[function(_dereq_,module,exports){
98878'use strict';
98879
98880var d3 = _dereq_('@plotly/d3');
98881
98882var Registry = _dereq_('../../registry');
98883var Lib = _dereq_('../../lib');
98884var ensureSingle = Lib.ensureSingle;
98885var identity = Lib.identity;
98886var Drawing = _dereq_('../../components/drawing');
98887
98888var subTypes = _dereq_('./subtypes');
98889var linePoints = _dereq_('./line_points');
98890var linkTraces = _dereq_('./link_traces');
98891var polygonTester = _dereq_('../../lib/polygon').tester;
98892
98893module.exports = function plot(gd, plotinfo, cdscatter, scatterLayer, transitionOpts, makeOnCompleteCallback) {
98894 var join, onComplete;
98895
98896 // If transition config is provided, then it is only a partial replot and traces not
98897 // updated are removed.
98898 var isFullReplot = !transitionOpts;
98899 var hasTransition = !!transitionOpts && transitionOpts.duration > 0;
98900
98901 // Link traces so the z-order of fill layers is correct
98902 var cdscatterSorted = linkTraces(gd, plotinfo, cdscatter);
98903
98904 join = scatterLayer.selectAll('g.trace')
98905 .data(cdscatterSorted, function(d) { return d[0].trace.uid; });
98906
98907 // Append new traces:
98908 join.enter().append('g')
98909 .attr('class', function(d) {
98910 return 'trace scatter trace' + d[0].trace.uid;
98911 })
98912 .style('stroke-miterlimit', 2);
98913 join.order();
98914
98915 createFills(gd, join, plotinfo);
98916
98917 if(hasTransition) {
98918 if(makeOnCompleteCallback) {
98919 // If it was passed a callback to register completion, make a callback. If
98920 // this is created, then it must be executed on completion, otherwise the
98921 // pos-transition redraw will not execute:
98922 onComplete = makeOnCompleteCallback();
98923 }
98924
98925 var transition = d3.transition()
98926 .duration(transitionOpts.duration)
98927 .ease(transitionOpts.easing)
98928 .each('end', function() {
98929 onComplete && onComplete();
98930 })
98931 .each('interrupt', function() {
98932 onComplete && onComplete();
98933 });
98934
98935 transition.each(function() {
98936 // Must run the selection again since otherwise enters/updates get grouped together
98937 // and these get executed out of order. Except we need them in order!
98938 scatterLayer.selectAll('g.trace').each(function(d, i) {
98939 plotOne(gd, i, plotinfo, d, cdscatterSorted, this, transitionOpts);
98940 });
98941 });
98942 } else {
98943 join.each(function(d, i) {
98944 plotOne(gd, i, plotinfo, d, cdscatterSorted, this, transitionOpts);
98945 });
98946 }
98947
98948 if(isFullReplot) {
98949 join.exit().remove();
98950 }
98951
98952 // remove paths that didn't get used
98953 scatterLayer.selectAll('path:not([d])').remove();
98954};
98955
98956function createFills(gd, traceJoin, plotinfo) {
98957 traceJoin.each(function(d) {
98958 var fills = ensureSingle(d3.select(this), 'g', 'fills');
98959 Drawing.setClipUrl(fills, plotinfo.layerClipId, gd);
98960
98961 var trace = d[0].trace;
98962
98963 var fillData = [];
98964 if(trace._ownfill) fillData.push('_ownFill');
98965 if(trace._nexttrace) fillData.push('_nextFill');
98966
98967 var fillJoin = fills.selectAll('g').data(fillData, identity);
98968
98969 fillJoin.enter().append('g');
98970
98971 fillJoin.exit()
98972 .each(function(d) { trace[d] = null; })
98973 .remove();
98974
98975 fillJoin.order().each(function(d) {
98976 // make a path element inside the fill group, just so
98977 // we can give it its own data later on and the group can
98978 // keep its simple '_*Fill' data
98979 trace[d] = ensureSingle(d3.select(this), 'path', 'js-fill');
98980 });
98981 });
98982}
98983
98984function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transitionOpts) {
98985 var i;
98986
98987 // Since this has been reorganized and we're executing this on individual traces,
98988 // we need to pass it the full list of cdscatter as well as this trace's index (idx)
98989 // since it does an internal n^2 loop over comparisons with other traces:
98990 selectMarkers(gd, idx, plotinfo, cdscatter, cdscatterAll);
98991
98992 var hasTransition = !!transitionOpts && transitionOpts.duration > 0;
98993
98994 function transition(selection) {
98995 return hasTransition ? selection.transition() : selection;
98996 }
98997
98998 var xa = plotinfo.xaxis;
98999 var ya = plotinfo.yaxis;
99000
99001 var trace = cdscatter[0].trace;
99002 var line = trace.line;
99003 var tr = d3.select(element);
99004
99005 var errorBarGroup = ensureSingle(tr, 'g', 'errorbars');
99006 var lines = ensureSingle(tr, 'g', 'lines');
99007 var points = ensureSingle(tr, 'g', 'points');
99008 var text = ensureSingle(tr, 'g', 'text');
99009
99010 // error bars are at the bottom
99011 Registry.getComponentMethod('errorbars', 'plot')(gd, errorBarGroup, plotinfo, transitionOpts);
99012
99013 if(trace.visible !== true) return;
99014
99015 transition(tr).style('opacity', trace.opacity);
99016
99017 // BUILD LINES AND FILLS
99018 var ownFillEl3, tonext;
99019 var ownFillDir = trace.fill.charAt(trace.fill.length - 1);
99020 if(ownFillDir !== 'x' && ownFillDir !== 'y') ownFillDir = '';
99021
99022 // store node for tweaking by selectPoints
99023 cdscatter[0][plotinfo.isRangePlot ? 'nodeRangePlot3' : 'node3'] = tr;
99024
99025 var prevRevpath = '';
99026 var prevPolygons = [];
99027 var prevtrace = trace._prevtrace;
99028
99029 if(prevtrace) {
99030 prevRevpath = prevtrace._prevRevpath || '';
99031 tonext = prevtrace._nextFill;
99032 prevPolygons = prevtrace._polygons;
99033 }
99034
99035 var thispath;
99036 var thisrevpath;
99037 // fullpath is all paths for this curve, joined together straight
99038 // across gaps, for filling
99039 var fullpath = '';
99040 // revpath is fullpath reversed, for fill-to-next
99041 var revpath = '';
99042 // functions for converting a point array to a path
99043 var pathfn, revpathbase, revpathfn;
99044 // variables used before and after the data join
99045 var pt0, lastSegment, pt1, thisPolygons;
99046
99047 // initialize line join data / method
99048 var segments = [];
99049 var makeUpdate = Lib.noop;
99050
99051 ownFillEl3 = trace._ownFill;
99052
99053 if(subTypes.hasLines(trace) || trace.fill !== 'none') {
99054 if(tonext) {
99055 // This tells .style which trace to use for fill information:
99056 tonext.datum(cdscatter);
99057 }
99058
99059 if(['hv', 'vh', 'hvh', 'vhv'].indexOf(line.shape) !== -1) {
99060 pathfn = Drawing.steps(line.shape);
99061 revpathbase = Drawing.steps(
99062 line.shape.split('').reverse().join('')
99063 );
99064 } else if(line.shape === 'spline') {
99065 pathfn = revpathbase = function(pts) {
99066 var pLast = pts[pts.length - 1];
99067 if(pts.length > 1 && pts[0][0] === pLast[0] && pts[0][1] === pLast[1]) {
99068 // identical start and end points: treat it as a
99069 // closed curve so we don't get a kink
99070 return Drawing.smoothclosed(pts.slice(1), line.smoothing);
99071 } else {
99072 return Drawing.smoothopen(pts, line.smoothing);
99073 }
99074 };
99075 } else {
99076 pathfn = revpathbase = function(pts) {
99077 return 'M' + pts.join('L');
99078 };
99079 }
99080
99081 revpathfn = function(pts) {
99082 // note: this is destructive (reverses pts in place) so can't use pts after this
99083 return revpathbase(pts.reverse());
99084 };
99085
99086 segments = linePoints(cdscatter, {
99087 xaxis: xa,
99088 yaxis: ya,
99089 connectGaps: trace.connectgaps,
99090 baseTolerance: Math.max(line.width || 1, 3) / 4,
99091 shape: line.shape,
99092 simplify: line.simplify,
99093 fill: trace.fill
99094 });
99095
99096 // since we already have the pixel segments here, use them to make
99097 // polygons for hover on fill
99098 // TODO: can we skip this if hoveron!=fills? That would mean we
99099 // need to redraw when you change hoveron...
99100 thisPolygons = trace._polygons = new Array(segments.length);
99101 for(i = 0; i < segments.length; i++) {
99102 trace._polygons[i] = polygonTester(segments[i]);
99103 }
99104
99105 if(segments.length) {
99106 pt0 = segments[0][0];
99107 lastSegment = segments[segments.length - 1];
99108 pt1 = lastSegment[lastSegment.length - 1];
99109 }
99110
99111 makeUpdate = function(isEnter) {
99112 return function(pts) {
99113 thispath = pathfn(pts);
99114 thisrevpath = revpathfn(pts);
99115 if(!fullpath) {
99116 fullpath = thispath;
99117 revpath = thisrevpath;
99118 } else if(ownFillDir) {
99119 fullpath += 'L' + thispath.substr(1);
99120 revpath = thisrevpath + ('L' + revpath.substr(1));
99121 } else {
99122 fullpath += 'Z' + thispath;
99123 revpath = thisrevpath + 'Z' + revpath;
99124 }
99125
99126 if(subTypes.hasLines(trace) && pts.length > 1) {
99127 var el = d3.select(this);
99128
99129 // This makes the coloring work correctly:
99130 el.datum(cdscatter);
99131
99132 if(isEnter) {
99133 transition(el.style('opacity', 0)
99134 .attr('d', thispath)
99135 .call(Drawing.lineGroupStyle))
99136 .style('opacity', 1);
99137 } else {
99138 var sel = transition(el);
99139 sel.attr('d', thispath);
99140 Drawing.singleLineStyle(cdscatter, sel);
99141 }
99142 }
99143 };
99144 };
99145 }
99146
99147 var lineJoin = lines.selectAll('.js-line').data(segments);
99148
99149 transition(lineJoin.exit())
99150 .style('opacity', 0)
99151 .remove();
99152
99153 lineJoin.each(makeUpdate(false));
99154
99155 lineJoin.enter().append('path')
99156 .classed('js-line', true)
99157 .style('vector-effect', 'non-scaling-stroke')
99158 .call(Drawing.lineGroupStyle)
99159 .each(makeUpdate(true));
99160
99161 Drawing.setClipUrl(lineJoin, plotinfo.layerClipId, gd);
99162
99163 function clearFill(selection) {
99164 transition(selection).attr('d', 'M0,0Z');
99165 }
99166
99167 if(segments.length) {
99168 if(ownFillEl3) {
99169 ownFillEl3.datum(cdscatter);
99170 if(pt0 && pt1) {
99171 if(ownFillDir) {
99172 if(ownFillDir === 'y') {
99173 pt0[1] = pt1[1] = ya.c2p(0, true);
99174 } else if(ownFillDir === 'x') {
99175 pt0[0] = pt1[0] = xa.c2p(0, true);
99176 }
99177
99178 // fill to zero: full trace path, plus extension of
99179 // the endpoints to the appropriate axis
99180 // For the sake of animations, wrap the points around so that
99181 // the points on the axes are the first two points. Otherwise
99182 // animations get a little crazy if the number of points changes.
99183 transition(ownFillEl3).attr('d', 'M' + pt1 + 'L' + pt0 + 'L' + fullpath.substr(1))
99184 .call(Drawing.singleFillStyle);
99185 } else {
99186 // fill to self: just join the path to itself
99187 transition(ownFillEl3).attr('d', fullpath + 'Z')
99188 .call(Drawing.singleFillStyle);
99189 }
99190 }
99191 } else if(tonext) {
99192 if(trace.fill.substr(0, 6) === 'tonext' && fullpath && prevRevpath) {
99193 // fill to next: full trace path, plus the previous path reversed
99194 if(trace.fill === 'tonext') {
99195 // tonext: for use by concentric shapes, like manually constructed
99196 // contours, we just add the two paths closed on themselves.
99197 // This makes strange results if one path is *not* entirely
99198 // inside the other, but then that is a strange usage.
99199 transition(tonext).attr('d', fullpath + 'Z' + prevRevpath + 'Z')
99200 .call(Drawing.singleFillStyle);
99201 } else {
99202 // tonextx/y: for now just connect endpoints with lines. This is
99203 // the correct behavior if the endpoints are at the same value of
99204 // y/x, but if they *aren't*, we should ideally do more complicated
99205 // things depending on whether the new endpoint projects onto the
99206 // existing curve or off the end of it
99207 transition(tonext).attr('d', fullpath + 'L' + prevRevpath.substr(1) + 'Z')
99208 .call(Drawing.singleFillStyle);
99209 }
99210 trace._polygons = trace._polygons.concat(prevPolygons);
99211 } else {
99212 clearFill(tonext);
99213 trace._polygons = null;
99214 }
99215 }
99216 trace._prevRevpath = revpath;
99217 trace._prevPolygons = thisPolygons;
99218 } else {
99219 if(ownFillEl3) clearFill(ownFillEl3);
99220 else if(tonext) clearFill(tonext);
99221 trace._polygons = trace._prevRevpath = trace._prevPolygons = null;
99222 }
99223
99224
99225 function visFilter(d) {
99226 return d.filter(function(v) { return !v.gap && v.vis; });
99227 }
99228
99229 function visFilterWithGaps(d) {
99230 return d.filter(function(v) { return v.vis; });
99231 }
99232
99233 function gapFilter(d) {
99234 return d.filter(function(v) { return !v.gap; });
99235 }
99236
99237 function keyFunc(d) {
99238 return d.id;
99239 }
99240
99241 // Returns a function if the trace is keyed, otherwise returns undefined
99242 function getKeyFunc(trace) {
99243 if(trace.ids) {
99244 return keyFunc;
99245 }
99246 }
99247
99248 function hideFilter() {
99249 return false;
99250 }
99251
99252 function makePoints(points, text, cdscatter) {
99253 var join, selection, hasNode;
99254
99255 var trace = cdscatter[0].trace;
99256 var showMarkers = subTypes.hasMarkers(trace);
99257 var showText = subTypes.hasText(trace);
99258
99259 var keyFunc = getKeyFunc(trace);
99260 var markerFilter = hideFilter;
99261 var textFilter = hideFilter;
99262
99263 if(showMarkers || showText) {
99264 var showFilter = identity;
99265 // if we're stacking, "infer zero" gap mode gets markers in the
99266 // gap points - because we've inferred a zero there - but other
99267 // modes (currently "interpolate", later "interrupt" hopefully)
99268 // we don't draw generated markers
99269 var stackGroup = trace.stackgroup;
99270 var isInferZero = stackGroup && (
99271 gd._fullLayout._scatterStackOpts[xa._id + ya._id][stackGroup].stackgaps === 'infer zero');
99272 if(trace.marker.maxdisplayed || trace._needsCull) {
99273 showFilter = isInferZero ? visFilterWithGaps : visFilter;
99274 } else if(stackGroup && !isInferZero) {
99275 showFilter = gapFilter;
99276 }
99277
99278 if(showMarkers) markerFilter = showFilter;
99279 if(showText) textFilter = showFilter;
99280 }
99281
99282 // marker points
99283
99284 selection = points.selectAll('path.point');
99285
99286 join = selection.data(markerFilter, keyFunc);
99287
99288 var enter = join.enter().append('path')
99289 .classed('point', true);
99290
99291 if(hasTransition) {
99292 enter
99293 .call(Drawing.pointStyle, trace, gd)
99294 .call(Drawing.translatePoints, xa, ya)
99295 .style('opacity', 0)
99296 .transition()
99297 .style('opacity', 1);
99298 }
99299
99300 join.order();
99301
99302 var styleFns;
99303 if(showMarkers) {
99304 styleFns = Drawing.makePointStyleFns(trace);
99305 }
99306
99307 join.each(function(d) {
99308 var el = d3.select(this);
99309 var sel = transition(el);
99310 hasNode = Drawing.translatePoint(d, sel, xa, ya);
99311
99312 if(hasNode) {
99313 Drawing.singlePointStyle(d, sel, trace, styleFns, gd);
99314
99315 if(plotinfo.layerClipId) {
99316 Drawing.hideOutsideRangePoint(d, sel, xa, ya, trace.xcalendar, trace.ycalendar);
99317 }
99318
99319 if(trace.customdata) {
99320 el.classed('plotly-customdata', d.data !== null && d.data !== undefined);
99321 }
99322 } else {
99323 sel.remove();
99324 }
99325 });
99326
99327 if(hasTransition) {
99328 join.exit().transition()
99329 .style('opacity', 0)
99330 .remove();
99331 } else {
99332 join.exit().remove();
99333 }
99334
99335 // text points
99336 selection = text.selectAll('g');
99337 join = selection.data(textFilter, keyFunc);
99338
99339 // each text needs to go in its own 'g' in case
99340 // it gets converted to mathjax
99341 join.enter().append('g').classed('textpoint', true).append('text');
99342
99343 join.order();
99344
99345 join.each(function(d) {
99346 var g = d3.select(this);
99347 var sel = transition(g.select('text'));
99348 hasNode = Drawing.translatePoint(d, sel, xa, ya);
99349
99350 if(hasNode) {
99351 if(plotinfo.layerClipId) {
99352 Drawing.hideOutsideRangePoint(d, g, xa, ya, trace.xcalendar, trace.ycalendar);
99353 }
99354 } else {
99355 g.remove();
99356 }
99357 });
99358
99359 join.selectAll('text')
99360 .call(Drawing.textPointStyle, trace, gd)
99361 .each(function(d) {
99362 // This just *has* to be totally custom because of SVG text positioning :(
99363 // It's obviously copied from translatePoint; we just can't use that
99364 var x = xa.c2p(d.x);
99365 var y = ya.c2p(d.y);
99366
99367 d3.select(this).selectAll('tspan.line').each(function() {
99368 transition(d3.select(this)).attr({x: x, y: y});
99369 });
99370 });
99371
99372 join.exit().remove();
99373 }
99374
99375 points.datum(cdscatter);
99376 text.datum(cdscatter);
99377 makePoints(points, text, cdscatter);
99378
99379 // lastly, clip points groups of `cliponaxis !== false` traces
99380 // on `plotinfo._hasClipOnAxisFalse === true` subplots
99381 var hasClipOnAxisFalse = trace.cliponaxis === false;
99382 var clipUrl = hasClipOnAxisFalse ? null : plotinfo.layerClipId;
99383 Drawing.setClipUrl(points, clipUrl, gd);
99384 Drawing.setClipUrl(text, clipUrl, gd);
99385}
99386
99387function selectMarkers(gd, idx, plotinfo, cdscatter, cdscatterAll) {
99388 var xa = plotinfo.xaxis;
99389 var ya = plotinfo.yaxis;
99390 var xr = d3.extent(Lib.simpleMap(xa.range, xa.r2c));
99391 var yr = d3.extent(Lib.simpleMap(ya.range, ya.r2c));
99392
99393 var trace = cdscatter[0].trace;
99394 if(!subTypes.hasMarkers(trace)) return;
99395 // if marker.maxdisplayed is used, select a maximum of
99396 // mnum markers to show, from the set that are in the viewport
99397 var mnum = trace.marker.maxdisplayed;
99398
99399 // TODO: remove some as we get away from the viewport?
99400 if(mnum === 0) return;
99401
99402 var cd = cdscatter.filter(function(v) {
99403 return v.x >= xr[0] && v.x <= xr[1] && v.y >= yr[0] && v.y <= yr[1];
99404 });
99405 var inc = Math.ceil(cd.length / mnum);
99406 var tnum = 0;
99407 cdscatterAll.forEach(function(cdj, j) {
99408 var tracei = cdj[0].trace;
99409 if(subTypes.hasMarkers(tracei) &&
99410 tracei.marker.maxdisplayed > 0 && j < idx) {
99411 tnum++;
99412 }
99413 });
99414
99415 // if multiple traces use maxdisplayed, stagger which markers we
99416 // display this formula offsets successive traces by 1/3 of the
99417 // increment, adding an extra small amount after each triplet so
99418 // it's not quite periodic
99419 var i0 = Math.round(tnum * inc / 3 + Math.floor(tnum / 3) * inc / 7.1);
99420
99421 // for error bars: save in cd which markers to show
99422 // so we don't have to repeat this
99423 cdscatter.forEach(function(v) { delete v.vis; });
99424 cd.forEach(function(v, i) {
99425 if(Math.round((i + i0) % inc) === 0) v.vis = true;
99426 });
99427}
99428
99429},{"../../components/drawing":179,"../../lib":287,"../../lib/polygon":299,"../../registry":376,"./line_points":511,"./link_traces":513,"./subtypes":522,"@plotly/d3":20}],519:[function(_dereq_,module,exports){
99430'use strict';
99431
99432var subtypes = _dereq_('./subtypes');
99433
99434module.exports = function selectPoints(searchInfo, selectionTester) {
99435 var cd = searchInfo.cd;
99436 var xa = searchInfo.xaxis;
99437 var ya = searchInfo.yaxis;
99438 var selection = [];
99439 var trace = cd[0].trace;
99440 var i;
99441 var di;
99442 var x;
99443 var y;
99444
99445 var hasOnlyLines = (!subtypes.hasMarkers(trace) && !subtypes.hasText(trace));
99446 if(hasOnlyLines) return [];
99447
99448 if(selectionTester === false) { // clear selection
99449 for(i = 0; i < cd.length; i++) {
99450 cd[i].selected = 0;
99451 }
99452 } else {
99453 for(i = 0; i < cd.length; i++) {
99454 di = cd[i];
99455 x = xa.c2p(di.x);
99456 y = ya.c2p(di.y);
99457
99458 if((di.i !== null) && selectionTester.contains([x, y], false, i, searchInfo)) {
99459 selection.push({
99460 pointNumber: di.i,
99461 x: xa.c2d(di.x),
99462 y: ya.c2d(di.y)
99463 });
99464 di.selected = 1;
99465 } else {
99466 di.selected = 0;
99467 }
99468 }
99469 }
99470
99471 return selection;
99472};
99473
99474},{"./subtypes":522}],520:[function(_dereq_,module,exports){
99475'use strict';
99476
99477var perStackAttrs = ['orientation', 'groupnorm', 'stackgaps'];
99478
99479module.exports = function handleStackDefaults(traceIn, traceOut, layout, coerce) {
99480 var stackOpts = layout._scatterStackOpts;
99481
99482 var stackGroup = coerce('stackgroup');
99483 if(stackGroup) {
99484 // use independent stacking options per subplot
99485 var subplot = traceOut.xaxis + traceOut.yaxis;
99486 var subplotStackOpts = stackOpts[subplot];
99487 if(!subplotStackOpts) subplotStackOpts = stackOpts[subplot] = {};
99488
99489 var groupOpts = subplotStackOpts[stackGroup];
99490 var firstTrace = false;
99491 if(groupOpts) {
99492 groupOpts.traces.push(traceOut);
99493 } else {
99494 groupOpts = subplotStackOpts[stackGroup] = {
99495 // keep track of trace indices for use during stacking calculations
99496 // this will be filled in during `calc` and used during `crossTraceCalc`
99497 // so it's OK if we don't recreate it during a non-calc edit
99498 traceIndices: [],
99499 // Hold on to the whole set of prior traces
99500 // First one is most important, so we can clear defaults
99501 // there if we find explicit values only in later traces.
99502 // We're only going to *use* the values stored in groupOpts,
99503 // but for the editor and validate we want things self-consistent
99504 // The full set of traces is used only to fix `fill` default if
99505 // we find `orientation: 'h'` beyond the first trace
99506 traces: [traceOut]
99507 };
99508 firstTrace = true;
99509 }
99510 // TODO: how is this going to work with groupby transforms?
99511 // in principle it should be OK I guess, as long as explicit group styles
99512 // don't override explicit base-trace styles?
99513
99514 var dflts = {
99515 orientation: (traceOut.x && !traceOut.y) ? 'h' : 'v'
99516 };
99517
99518 for(var i = 0; i < perStackAttrs.length; i++) {
99519 var attr = perStackAttrs[i];
99520 var attrFound = attr + 'Found';
99521 if(!groupOpts[attrFound]) {
99522 var traceHasAttr = traceIn[attr] !== undefined;
99523 var isOrientation = attr === 'orientation';
99524 if(traceHasAttr || firstTrace) {
99525 groupOpts[attr] = coerce(attr, dflts[attr]);
99526
99527 if(isOrientation) {
99528 groupOpts.fillDflt = groupOpts[attr] === 'h' ?
99529 'tonextx' : 'tonexty';
99530 }
99531
99532 if(traceHasAttr) {
99533 // Note: this will show a value here even if it's invalid
99534 // in which case it will revert to default.
99535 groupOpts[attrFound] = true;
99536
99537 // Note: only one trace in the stack will get a _fullData
99538 // entry for a given stack-wide attribute. If no traces
99539 // (or the first trace) specify that attribute, the
99540 // first trace will get it. If the first trace does NOT
99541 // specify it but some later trace does, then it gets
99542 // removed from the first trace and only included in the
99543 // one that specified it. This is mostly important for
99544 // editors (that want to see the full values to know
99545 // what settings are available) and Plotly.react diffing.
99546 // Editors may want to use fullLayout._scatterStackOpts
99547 // directly and make these settings available from all
99548 // traces in the stack... then set the new value into
99549 // the first trace, and clear all later traces.
99550 if(!firstTrace) {
99551 delete groupOpts.traces[0][attr];
99552
99553 // orientation can affect default fill of previous traces
99554 if(isOrientation) {
99555 for(var j = 0; j < groupOpts.traces.length - 1; j++) {
99556 var trace2 = groupOpts.traces[j];
99557 if(trace2._input.fill !== trace2.fill) {
99558 trace2.fill = groupOpts.fillDflt;
99559 }
99560 }
99561 }
99562 }
99563 }
99564 }
99565 }
99566 }
99567 return groupOpts;
99568 }
99569};
99570
99571},{}],521:[function(_dereq_,module,exports){
99572'use strict';
99573
99574var d3 = _dereq_('@plotly/d3');
99575var Drawing = _dereq_('../../components/drawing');
99576var Registry = _dereq_('../../registry');
99577
99578function style(gd) {
99579 var s = d3.select(gd).selectAll('g.trace.scatter');
99580
99581 s.style('opacity', function(d) {
99582 return d[0].trace.opacity;
99583 });
99584
99585 s.selectAll('g.points').each(function(d) {
99586 var sel = d3.select(this);
99587 var trace = d.trace || d[0].trace;
99588 stylePoints(sel, trace, gd);
99589 });
99590
99591 s.selectAll('g.text').each(function(d) {
99592 var sel = d3.select(this);
99593 var trace = d.trace || d[0].trace;
99594 styleText(sel, trace, gd);
99595 });
99596
99597 s.selectAll('g.trace path.js-line')
99598 .call(Drawing.lineGroupStyle);
99599
99600 s.selectAll('g.trace path.js-fill')
99601 .call(Drawing.fillGroupStyle);
99602
99603 Registry.getComponentMethod('errorbars', 'style')(s);
99604}
99605
99606function stylePoints(sel, trace, gd) {
99607 Drawing.pointStyle(sel.selectAll('path.point'), trace, gd);
99608}
99609
99610function styleText(sel, trace, gd) {
99611 Drawing.textPointStyle(sel.selectAll('text'), trace, gd);
99612}
99613
99614function styleOnSelect(gd, cd, sel) {
99615 var trace = cd[0].trace;
99616
99617 if(trace.selectedpoints) {
99618 Drawing.selectedPointStyle(sel.selectAll('path.point'), trace);
99619 Drawing.selectedTextStyle(sel.selectAll('text'), trace);
99620 } else {
99621 stylePoints(sel, trace, gd);
99622 styleText(sel, trace, gd);
99623 }
99624}
99625
99626module.exports = {
99627 style: style,
99628 stylePoints: stylePoints,
99629 styleText: styleText,
99630 styleOnSelect: styleOnSelect
99631};
99632
99633},{"../../components/drawing":179,"../../registry":376,"@plotly/d3":20}],522:[function(_dereq_,module,exports){
99634'use strict';
99635
99636var Lib = _dereq_('../../lib');
99637
99638module.exports = {
99639 hasLines: function(trace) {
99640 return trace.visible && trace.mode &&
99641 trace.mode.indexOf('lines') !== -1;
99642 },
99643
99644 hasMarkers: function(trace) {
99645 return trace.visible && (
99646 (trace.mode && trace.mode.indexOf('markers') !== -1) ||
99647 // until splom implements 'mode'
99648 trace.type === 'splom'
99649 );
99650 },
99651
99652 hasText: function(trace) {
99653 return trace.visible && trace.mode &&
99654 trace.mode.indexOf('text') !== -1;
99655 },
99656
99657 isBubble: function(trace) {
99658 return Lib.isPlainObject(trace.marker) &&
99659 Lib.isArrayOrTypedArray(trace.marker.size);
99660 }
99661};
99662
99663},{"../../lib":287}],523:[function(_dereq_,module,exports){
99664'use strict';
99665
99666var Lib = _dereq_('../../lib');
99667
99668/*
99669 * opts: object of flags to control features not all text users support
99670 * noSelect: caller does not support selected/unselected attribute containers
99671 */
99672module.exports = function(traceIn, traceOut, layout, coerce, opts) {
99673 opts = opts || {};
99674
99675 coerce('textposition');
99676 Lib.coerceFont(coerce, 'textfont', layout.font);
99677
99678 if(!opts.noSelect) {
99679 coerce('selected.textfont.color');
99680 coerce('unselected.textfont.color');
99681 }
99682};
99683
99684},{"../../lib":287}],524:[function(_dereq_,module,exports){
99685'use strict';
99686
99687var Lib = _dereq_('../../lib');
99688var Registry = _dereq_('../../registry');
99689
99690module.exports = function handleXYDefaults(traceIn, traceOut, layout, coerce) {
99691 var x = coerce('x');
99692 var y = coerce('y');
99693 var len;
99694
99695 var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults');
99696 handleCalendarDefaults(traceIn, traceOut, ['x', 'y'], layout);
99697
99698 if(x) {
99699 var xlen = Lib.minRowLength(x);
99700 if(y) {
99701 len = Math.min(xlen, Lib.minRowLength(y));
99702 } else {
99703 len = xlen;
99704 coerce('y0');
99705 coerce('dy');
99706 }
99707 } else {
99708 if(!y) return 0;
99709
99710 len = Lib.minRowLength(y);
99711 coerce('x0');
99712 coerce('dx');
99713 }
99714
99715 traceOut._length = len;
99716
99717 return len;
99718};
99719
99720},{"../../lib":287,"../../registry":376}],525:[function(_dereq_,module,exports){
99721'use strict';
99722
99723var hovertemplateAttrs = _dereq_('../../plots/template_attributes').hovertemplateAttrs;
99724var texttemplateAttrs = _dereq_('../../plots/template_attributes').texttemplateAttrs;
99725var scatterAttrs = _dereq_('../scatter/attributes');
99726var baseAttrs = _dereq_('../../plots/attributes');
99727var colorScaleAttrs = _dereq_('../../components/colorscale/attributes');
99728var dash = _dereq_('../../components/drawing/attributes').dash;
99729
99730var extendFlat = _dereq_('../../lib/extend').extendFlat;
99731
99732var scatterMarkerAttrs = scatterAttrs.marker;
99733var scatterLineAttrs = scatterAttrs.line;
99734var scatterMarkerLineAttrs = scatterMarkerAttrs.line;
99735
99736module.exports = {
99737 a: {
99738 valType: 'data_array',
99739 editType: 'calc',
99740 },
99741 b: {
99742 valType: 'data_array',
99743 editType: 'calc',
99744 },
99745 c: {
99746 valType: 'data_array',
99747 editType: 'calc',
99748 },
99749 sum: {
99750 valType: 'number',
99751 dflt: 0,
99752 min: 0,
99753 editType: 'calc',
99754 },
99755 mode: extendFlat({}, scatterAttrs.mode, {dflt: 'markers'}),
99756 text: extendFlat({}, scatterAttrs.text, {
99757 }),
99758 texttemplate: texttemplateAttrs({editType: 'plot'}, {
99759 keys: ['a', 'b', 'c', 'text']
99760 }),
99761 hovertext: extendFlat({}, scatterAttrs.hovertext, {
99762 }),
99763 line: {
99764 color: scatterLineAttrs.color,
99765 width: scatterLineAttrs.width,
99766 dash: dash,
99767 shape: extendFlat({}, scatterLineAttrs.shape,
99768 {values: ['linear', 'spline']}),
99769 smoothing: scatterLineAttrs.smoothing,
99770 editType: 'calc'
99771 },
99772 connectgaps: scatterAttrs.connectgaps,
99773 cliponaxis: scatterAttrs.cliponaxis,
99774 fill: extendFlat({}, scatterAttrs.fill, {
99775 values: ['none', 'toself', 'tonext'],
99776 dflt: 'none',
99777 }),
99778 fillcolor: scatterAttrs.fillcolor,
99779 marker: extendFlat({
99780 symbol: scatterMarkerAttrs.symbol,
99781 opacity: scatterMarkerAttrs.opacity,
99782 maxdisplayed: scatterMarkerAttrs.maxdisplayed,
99783 size: scatterMarkerAttrs.size,
99784 sizeref: scatterMarkerAttrs.sizeref,
99785 sizemin: scatterMarkerAttrs.sizemin,
99786 sizemode: scatterMarkerAttrs.sizemode,
99787 line: extendFlat({
99788 width: scatterMarkerLineAttrs.width,
99789 editType: 'calc'
99790 },
99791 colorScaleAttrs('marker.line')
99792 ),
99793 gradient: scatterMarkerAttrs.gradient,
99794 editType: 'calc'
99795 },
99796 colorScaleAttrs('marker')
99797 ),
99798
99799 textfont: scatterAttrs.textfont,
99800 textposition: scatterAttrs.textposition,
99801
99802 selected: scatterAttrs.selected,
99803 unselected: scatterAttrs.unselected,
99804
99805 hoverinfo: extendFlat({}, baseAttrs.hoverinfo, {
99806 flags: ['a', 'b', 'c', 'text', 'name']
99807 }),
99808 hoveron: scatterAttrs.hoveron,
99809 hovertemplate: hovertemplateAttrs(),
99810};
99811
99812},{"../../components/colorscale/attributes":164,"../../components/drawing/attributes":178,"../../lib/extend":281,"../../plots/attributes":330,"../../plots/template_attributes":371,"../scatter/attributes":497}],526:[function(_dereq_,module,exports){
99813'use strict';
99814
99815var isNumeric = _dereq_('fast-isnumeric');
99816
99817var calcColorscale = _dereq_('../scatter/colorscale_calc');
99818var arraysToCalcdata = _dereq_('../scatter/arrays_to_calcdata');
99819var calcSelection = _dereq_('../scatter/calc_selection');
99820var calcMarkerSize = _dereq_('../scatter/calc').calcMarkerSize;
99821
99822var dataArrays = ['a', 'b', 'c'];
99823var arraysToFill = {a: ['b', 'c'], b: ['a', 'c'], c: ['a', 'b']};
99824
99825module.exports = function calc(gd, trace) {
99826 var ternary = gd._fullLayout[trace.subplot];
99827 var displaySum = ternary.sum;
99828 var normSum = trace.sum || displaySum;
99829 var arrays = {a: trace.a, b: trace.b, c: trace.c};
99830
99831 var i, j, dataArray, newArray, fillArray1, fillArray2;
99832
99833 // fill in one missing component
99834 for(i = 0; i < dataArrays.length; i++) {
99835 dataArray = dataArrays[i];
99836 if(arrays[dataArray]) continue;
99837
99838 fillArray1 = arrays[arraysToFill[dataArray][0]];
99839 fillArray2 = arrays[arraysToFill[dataArray][1]];
99840 newArray = new Array(fillArray1.length);
99841 for(j = 0; j < fillArray1.length; j++) {
99842 newArray[j] = normSum - fillArray1[j] - fillArray2[j];
99843 }
99844 arrays[dataArray] = newArray;
99845 }
99846
99847 // make the calcdata array
99848 var serieslen = trace._length;
99849 var cd = new Array(serieslen);
99850 var a, b, c, norm, x, y;
99851 for(i = 0; i < serieslen; i++) {
99852 a = arrays.a[i];
99853 b = arrays.b[i];
99854 c = arrays.c[i];
99855 if(isNumeric(a) && isNumeric(b) && isNumeric(c)) {
99856 a = +a;
99857 b = +b;
99858 c = +c;
99859 norm = displaySum / (a + b + c);
99860 if(norm !== 1) {
99861 a *= norm;
99862 b *= norm;
99863 c *= norm;
99864 }
99865 // map a, b, c onto x and y where the full scale of y
99866 // is [0, sum], and x is [-sum, sum]
99867 // TODO: this makes `a` always the top, `b` the bottom left,
99868 // and `c` the bottom right. Do we want options to rearrange
99869 // these?
99870 y = a;
99871 x = c - b;
99872 cd[i] = {x: x, y: y, a: a, b: b, c: c};
99873 } else cd[i] = {x: false, y: false};
99874 }
99875
99876 calcMarkerSize(trace, serieslen);
99877 calcColorscale(gd, trace);
99878 arraysToCalcdata(cd, trace);
99879 calcSelection(cd, trace);
99880
99881 return cd;
99882};
99883
99884},{"../scatter/arrays_to_calcdata":496,"../scatter/calc":498,"../scatter/calc_selection":499,"../scatter/colorscale_calc":500,"fast-isnumeric":33}],527:[function(_dereq_,module,exports){
99885'use strict';
99886
99887var Lib = _dereq_('../../lib');
99888
99889var constants = _dereq_('../scatter/constants');
99890var subTypes = _dereq_('../scatter/subtypes');
99891var handleMarkerDefaults = _dereq_('../scatter/marker_defaults');
99892var handleLineDefaults = _dereq_('../scatter/line_defaults');
99893var handleLineShapeDefaults = _dereq_('../scatter/line_shape_defaults');
99894var handleTextDefaults = _dereq_('../scatter/text_defaults');
99895var handleFillColorDefaults = _dereq_('../scatter/fillcolor_defaults');
99896
99897var attributes = _dereq_('./attributes');
99898
99899
99900module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
99901 function coerce(attr, dflt) {
99902 return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
99903 }
99904
99905 var a = coerce('a');
99906 var b = coerce('b');
99907 var c = coerce('c');
99908 var len;
99909
99910 // allow any one array to be missing, len is the minimum length of those
99911 // present. Note that after coerce data_array's are either Arrays (which
99912 // are truthy even if empty) or undefined. As in scatter, an empty array
99913 // is different from undefined, because it can signify that this data is
99914 // not known yet but expected in the future
99915 if(a) {
99916 len = a.length;
99917 if(b) {
99918 len = Math.min(len, b.length);
99919 if(c) len = Math.min(len, c.length);
99920 } else if(c) len = Math.min(len, c.length);
99921 else len = 0;
99922 } else if(b && c) {
99923 len = Math.min(b.length, c.length);
99924 }
99925
99926 if(!len) {
99927 traceOut.visible = false;
99928 return;
99929 }
99930
99931 traceOut._length = len;
99932
99933 coerce('sum');
99934
99935 coerce('text');
99936 coerce('hovertext');
99937 if(traceOut.hoveron !== 'fills') coerce('hovertemplate');
99938
99939 var defaultMode = len < constants.PTS_LINESONLY ? 'lines+markers' : 'lines';
99940 coerce('mode', defaultMode);
99941
99942 if(subTypes.hasLines(traceOut)) {
99943 handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce);
99944 handleLineShapeDefaults(traceIn, traceOut, coerce);
99945 coerce('connectgaps');
99946 }
99947
99948 if(subTypes.hasMarkers(traceOut)) {
99949 handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {gradient: true});
99950 }
99951
99952 if(subTypes.hasText(traceOut)) {
99953 coerce('texttemplate');
99954 handleTextDefaults(traceIn, traceOut, layout, coerce);
99955 }
99956
99957 var dfltHoverOn = [];
99958
99959 if(subTypes.hasMarkers(traceOut) || subTypes.hasText(traceOut)) {
99960 coerce('cliponaxis');
99961 coerce('marker.maxdisplayed');
99962 dfltHoverOn.push('points');
99963 }
99964
99965 coerce('fill');
99966 if(traceOut.fill !== 'none') {
99967 handleFillColorDefaults(traceIn, traceOut, defaultColor, coerce);
99968 if(!subTypes.hasLines(traceOut)) handleLineShapeDefaults(traceIn, traceOut, coerce);
99969 }
99970
99971 if(traceOut.fill === 'tonext' || traceOut.fill === 'toself') {
99972 dfltHoverOn.push('fills');
99973 }
99974 coerce('hoveron', dfltHoverOn.join('+') || 'points');
99975
99976 Lib.coerceSelectionMarkerOpacity(traceOut, coerce);
99977};
99978
99979},{"../../lib":287,"../scatter/constants":501,"../scatter/fillcolor_defaults":505,"../scatter/line_defaults":510,"../scatter/line_shape_defaults":512,"../scatter/marker_defaults":516,"../scatter/subtypes":522,"../scatter/text_defaults":523,"./attributes":525}],528:[function(_dereq_,module,exports){
99980'use strict';
99981
99982module.exports = function eventData(out, pt, trace, cd, pointNumber) {
99983 if(pt.xa) out.xaxis = pt.xa;
99984 if(pt.ya) out.yaxis = pt.ya;
99985
99986 if(cd[pointNumber]) {
99987 var cdi = cd[pointNumber];
99988
99989 // N.B. These are the normalized coordinates.
99990 out.a = cdi.a;
99991 out.b = cdi.b;
99992 out.c = cdi.c;
99993 } else {
99994 // for fill-hover only
99995 out.a = pt.a;
99996 out.b = pt.b;
99997 out.c = pt.c;
99998 }
99999
100000 return out;
100001};
100002
100003},{}],529:[function(_dereq_,module,exports){
100004'use strict';
100005
100006var Axes = _dereq_('../../plots/cartesian/axes');
100007
100008module.exports = function formatLabels(cdi, trace, fullLayout) {
100009 var labels = {};
100010
100011 var subplot = fullLayout[trace.subplot]._subplot;
100012 labels.aLabel = Axes.tickText(subplot.aaxis, cdi.a, true).text;
100013 labels.bLabel = Axes.tickText(subplot.baxis, cdi.b, true).text;
100014 labels.cLabel = Axes.tickText(subplot.caxis, cdi.c, true).text;
100015
100016 return labels;
100017};
100018
100019},{"../../plots/cartesian/axes":334}],530:[function(_dereq_,module,exports){
100020'use strict';
100021
100022var scatterHover = _dereq_('../scatter/hover');
100023
100024module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
100025 var scatterPointData = scatterHover(pointData, xval, yval, hovermode);
100026 if(!scatterPointData || scatterPointData[0].index === false) return;
100027
100028 var newPointData = scatterPointData[0];
100029
100030 // if hovering on a fill, we don't show any point data so the label is
100031 // unchanged from what scatter gives us - except that it needs to
100032 // be constrained to the trianglular plot area, not just the rectangular
100033 // area defined by the synthetic x and y axes
100034 // TODO: in some cases the vertical middle of the shape is not within
100035 // the triangular viewport at all, so the label can become disconnected
100036 // from the shape entirely. But calculating what portion of the shape
100037 // is actually visible, as constrained by the diagonal axis lines, is not
100038 // so easy and anyway we lost the information we would have needed to do
100039 // this inside scatterHover.
100040 if(newPointData.index === undefined) {
100041 var yFracUp = 1 - (newPointData.y0 / pointData.ya._length);
100042 var xLen = pointData.xa._length;
100043 var xMin = xLen * yFracUp / 2;
100044 var xMax = xLen - xMin;
100045 newPointData.x0 = Math.max(Math.min(newPointData.x0, xMax), xMin);
100046 newPointData.x1 = Math.max(Math.min(newPointData.x1, xMax), xMin);
100047 return scatterPointData;
100048 }
100049
100050 var cdi = newPointData.cd[newPointData.index];
100051 var trace = newPointData.trace;
100052 var subplot = newPointData.subplot;
100053
100054 newPointData.a = cdi.a;
100055 newPointData.b = cdi.b;
100056 newPointData.c = cdi.c;
100057
100058 newPointData.xLabelVal = undefined;
100059 newPointData.yLabelVal = undefined;
100060
100061 var fullLayout = {};
100062 fullLayout[trace.subplot] = {_subplot: subplot};
100063 var labels = trace._module.formatLabels(cdi, trace, fullLayout);
100064 newPointData.aLabel = labels.aLabel;
100065 newPointData.bLabel = labels.bLabel;
100066 newPointData.cLabel = labels.cLabel;
100067
100068 var hoverinfo = cdi.hi || trace.hoverinfo;
100069 var text = [];
100070 function textPart(ax, val) {
100071 text.push(ax._hovertitle + ': ' + val);
100072 }
100073 if(!trace.hovertemplate) {
100074 var parts = hoverinfo.split('+');
100075 if(parts.indexOf('all') !== -1) parts = ['a', 'b', 'c'];
100076 if(parts.indexOf('a') !== -1) textPart(subplot.aaxis, newPointData.aLabel);
100077 if(parts.indexOf('b') !== -1) textPart(subplot.baxis, newPointData.bLabel);
100078 if(parts.indexOf('c') !== -1) textPart(subplot.caxis, newPointData.cLabel);
100079 }
100080 newPointData.extraText = text.join('<br>');
100081 newPointData.hovertemplate = trace.hovertemplate;
100082 return scatterPointData;
100083};
100084
100085},{"../scatter/hover":508}],531:[function(_dereq_,module,exports){
100086'use strict';
100087
100088module.exports = {
100089 attributes: _dereq_('./attributes'),
100090 supplyDefaults: _dereq_('./defaults'),
100091 colorbar: _dereq_('../scatter/marker_colorbar'),
100092 formatLabels: _dereq_('./format_labels'),
100093 calc: _dereq_('./calc'),
100094 plot: _dereq_('./plot'),
100095 style: _dereq_('../scatter/style').style,
100096 styleOnSelect: _dereq_('../scatter/style').styleOnSelect,
100097 hoverPoints: _dereq_('./hover'),
100098 selectPoints: _dereq_('../scatter/select'),
100099 eventData: _dereq_('./event_data'),
100100
100101 moduleType: 'trace',
100102 name: 'scatterternary',
100103 basePlotModule: _dereq_('../../plots/ternary'),
100104 categories: ['ternary', 'symbols', 'showLegend', 'scatter-like'],
100105 meta: {
100106 }
100107};
100108
100109},{"../../plots/ternary":372,"../scatter/marker_colorbar":515,"../scatter/select":519,"../scatter/style":521,"./attributes":525,"./calc":526,"./defaults":527,"./event_data":528,"./format_labels":529,"./hover":530,"./plot":532}],532:[function(_dereq_,module,exports){
100110'use strict';
100111
100112var scatterPlot = _dereq_('../scatter/plot');
100113
100114module.exports = function plot(gd, ternary, moduleCalcData) {
100115 var plotContainer = ternary.plotContainer;
100116
100117 // remove all nodes inside the scatter layer
100118 plotContainer.select('.scatterlayer').selectAll('*').remove();
100119
100120 // mimic cartesian plotinfo
100121 var plotinfo = {
100122 xaxis: ternary.xaxis,
100123 yaxis: ternary.yaxis,
100124 plot: plotContainer,
100125 layerClipId: ternary._hasClipOnAxisFalse ? ternary.clipIdRelative : null
100126 };
100127
100128 var scatterLayer = ternary.layers.frontplot.select('g.scatterlayer');
100129
100130 scatterPlot(gd, plotinfo, moduleCalcData, scatterLayer);
100131};
100132
100133},{"../scatter/plot":518}],533:[function(_dereq_,module,exports){
100134'use strict';
100135
100136var boxAttrs = _dereq_('../box/attributes');
100137var extendFlat = _dereq_('../../lib/extend').extendFlat;
100138var axisHoverFormat = _dereq_('../../plots/cartesian/axis_format_attributes').axisHoverFormat;
100139
100140module.exports = {
100141 y: boxAttrs.y,
100142 x: boxAttrs.x,
100143 x0: boxAttrs.x0,
100144 y0: boxAttrs.y0,
100145
100146 xhoverformat: axisHoverFormat('x'),
100147 yhoverformat: axisHoverFormat('y'),
100148
100149 name: extendFlat({}, boxAttrs.name, {
100150 }),
100151 orientation: extendFlat({}, boxAttrs.orientation, {
100152 }),
100153
100154 bandwidth: {
100155 valType: 'number',
100156 min: 0,
100157 editType: 'calc',
100158 },
100159
100160 scalegroup: {
100161 valType: 'string',
100162 dflt: '',
100163 editType: 'calc',
100164 },
100165 scalemode: {
100166 valType: 'enumerated',
100167 values: ['width', 'count'],
100168 dflt: 'width',
100169 editType: 'calc',
100170 },
100171
100172 spanmode: {
100173 valType: 'enumerated',
100174 values: ['soft', 'hard', 'manual'],
100175 dflt: 'soft',
100176 editType: 'calc',
100177 },
100178 span: {
100179 valType: 'info_array',
100180 items: [
100181 {valType: 'any', editType: 'calc'},
100182 {valType: 'any', editType: 'calc'}
100183 ],
100184 editType: 'calc',
100185 },
100186
100187 line: {
100188 color: {
100189 valType: 'color',
100190 editType: 'style',
100191 },
100192 width: {
100193 valType: 'number',
100194 min: 0,
100195 dflt: 2,
100196 editType: 'style',
100197 },
100198 editType: 'plot'
100199 },
100200 fillcolor: boxAttrs.fillcolor,
100201
100202 points: extendFlat({}, boxAttrs.boxpoints, {
100203 }),
100204 jitter: extendFlat({}, boxAttrs.jitter, {
100205 }),
100206 pointpos: extendFlat({}, boxAttrs.pointpos, {
100207 }),
100208
100209 width: extendFlat({}, boxAttrs.width, {
100210 }),
100211
100212 marker: boxAttrs.marker,
100213 text: boxAttrs.text,
100214 hovertext: boxAttrs.hovertext,
100215 hovertemplate: boxAttrs.hovertemplate,
100216
100217 box: {
100218 visible: {
100219 valType: 'boolean',
100220 dflt: false,
100221 editType: 'plot',
100222 },
100223 width: {
100224 valType: 'number',
100225 min: 0,
100226 max: 1,
100227 dflt: 0.25,
100228 editType: 'plot',
100229 },
100230 fillcolor: {
100231 valType: 'color',
100232 editType: 'style',
100233 },
100234 line: {
100235 color: {
100236 valType: 'color',
100237 editType: 'style',
100238 },
100239 width: {
100240 valType: 'number',
100241 min: 0,
100242 editType: 'style',
100243 },
100244 editType: 'style'
100245 },
100246 editType: 'plot'
100247 },
100248
100249 meanline: {
100250 visible: {
100251 valType: 'boolean',
100252 dflt: false,
100253 editType: 'plot',
100254 },
100255 color: {
100256 valType: 'color',
100257 editType: 'style',
100258 },
100259 width: {
100260 valType: 'number',
100261 min: 0,
100262 editType: 'style',
100263 },
100264 editType: 'plot'
100265 },
100266
100267 side: {
100268 valType: 'enumerated',
100269 values: ['both', 'positive', 'negative'],
100270 dflt: 'both',
100271 editType: 'calc',
100272 },
100273
100274 offsetgroup: boxAttrs.offsetgroup,
100275 alignmentgroup: boxAttrs.alignmentgroup,
100276
100277 selected: boxAttrs.selected,
100278 unselected: boxAttrs.unselected,
100279
100280 hoveron: {
100281 valType: 'flaglist',
100282 flags: ['violins', 'points', 'kde'],
100283 dflt: 'violins+points+kde',
100284 extras: ['all'],
100285 editType: 'style',
100286 }
100287};
100288
100289},{"../../lib/extend":281,"../../plots/cartesian/axis_format_attributes":337,"../box/attributes":403}],534:[function(_dereq_,module,exports){
100290'use strict';
100291
100292var Lib = _dereq_('../../lib');
100293var Axes = _dereq_('../../plots/cartesian/axes');
100294var boxCalc = _dereq_('../box/calc');
100295var helpers = _dereq_('./helpers');
100296var BADNUM = _dereq_('../../constants/numerical').BADNUM;
100297
100298module.exports = function calc(gd, trace) {
100299 var cd = boxCalc(gd, trace);
100300
100301 if(cd[0].t.empty) return cd;
100302
100303 var fullLayout = gd._fullLayout;
100304 var valAxis = Axes.getFromId(
100305 gd,
100306 trace[trace.orientation === 'h' ? 'xaxis' : 'yaxis']
100307 );
100308
100309 var spanMin = Infinity;
100310 var spanMax = -Infinity;
100311 var maxKDE = 0;
100312 var maxCount = 0;
100313
100314 for(var i = 0; i < cd.length; i++) {
100315 var cdi = cd[i];
100316 var vals = cdi.pts.map(helpers.extractVal);
100317
100318 var bandwidth = cdi.bandwidth = calcBandwidth(trace, cdi, vals);
100319 var span = cdi.span = calcSpan(trace, cdi, valAxis, bandwidth);
100320
100321 if(cdi.min === cdi.max && bandwidth === 0) {
100322 // if span is zero and bandwidth is zero, we want a violin with zero width
100323 span = cdi.span = [cdi.min, cdi.max];
100324 cdi.density = [{v: 1, t: span[0]}];
100325 cdi.bandwidth = bandwidth;
100326 maxKDE = Math.max(maxKDE, 1);
100327 } else {
100328 // step that well covers the bandwidth and is multiple of span distance
100329 var dist = span[1] - span[0];
100330 var n = Math.ceil(dist / (bandwidth / 3));
100331 var step = dist / n;
100332
100333 if(!isFinite(step) || !isFinite(n)) {
100334 Lib.error('Something went wrong with computing the violin span');
100335 cd[0].t.empty = true;
100336 return cd;
100337 }
100338
100339 var kde = helpers.makeKDE(cdi, trace, vals);
100340 cdi.density = new Array(n);
100341
100342 for(var k = 0, t = span[0]; t < (span[1] + step / 2); k++, t += step) {
100343 var v = kde(t);
100344 cdi.density[k] = {v: v, t: t};
100345 maxKDE = Math.max(maxKDE, v);
100346 }
100347 }
100348
100349 maxCount = Math.max(maxCount, vals.length);
100350 spanMin = Math.min(spanMin, span[0]);
100351 spanMax = Math.max(spanMax, span[1]);
100352 }
100353
100354 var extremes = Axes.findExtremes(valAxis, [spanMin, spanMax], {padded: true});
100355 trace._extremes[valAxis._id] = extremes;
100356
100357 if(trace.width) {
100358 cd[0].t.maxKDE = maxKDE;
100359 } else {
100360 var violinScaleGroupStats = fullLayout._violinScaleGroupStats;
100361 var scaleGroup = trace.scalegroup;
100362 var groupStats = violinScaleGroupStats[scaleGroup];
100363
100364 if(groupStats) {
100365 groupStats.maxKDE = Math.max(groupStats.maxKDE, maxKDE);
100366 groupStats.maxCount = Math.max(groupStats.maxCount, maxCount);
100367 } else {
100368 violinScaleGroupStats[scaleGroup] = {
100369 maxKDE: maxKDE,
100370 maxCount: maxCount
100371 };
100372 }
100373 }
100374
100375 cd[0].t.labels.kde = Lib._(gd, 'kde:');
100376
100377 return cd;
100378};
100379
100380// Default to Silveman's rule of thumb
100381// - https://stats.stackexchange.com/a/6671
100382// - https://en.wikipedia.org/wiki/Kernel_density_estimation#A_rule-of-thumb_bandwidth_estimator
100383// - https://github.com/statsmodels/statsmodels/blob/master/statsmodels/nonparametric/bandwidths.py
100384function silvermanRule(len, ssd, iqr) {
100385 var a = Math.min(ssd, iqr / 1.349);
100386 return 1.059 * a * Math.pow(len, -0.2);
100387}
100388
100389function calcBandwidth(trace, cdi, vals) {
100390 var span = cdi.max - cdi.min;
100391
100392 // If span is zero
100393 if(!span) {
100394 if(trace.bandwidth) {
100395 return trace.bandwidth;
100396 } else {
100397 // if span is zero and no bandwidth is specified
100398 // it returns zero bandwidth which is a special case
100399 return 0;
100400 }
100401 }
100402
100403 // Limit how small the bandwidth can be.
100404 //
100405 // Silverman's rule of thumb can be "very" small
100406 // when IQR does a poor job at describing the spread
100407 // of the distribution.
100408 // We also want to limit custom bandwidths
100409 // to not blow up kde computations.
100410
100411 if(trace.bandwidth) {
100412 return Math.max(trace.bandwidth, span / 1e4);
100413 } else {
100414 var len = vals.length;
100415 var ssd = Lib.stdev(vals, len - 1, cdi.mean);
100416 return Math.max(
100417 silvermanRule(len, ssd, cdi.q3 - cdi.q1),
100418 span / 100
100419 );
100420 }
100421}
100422
100423function calcSpan(trace, cdi, valAxis, bandwidth) {
100424 var spanmode = trace.spanmode;
100425 var spanIn = trace.span || [];
100426 var spanTight = [cdi.min, cdi.max];
100427 var spanLoose = [cdi.min - 2 * bandwidth, cdi.max + 2 * bandwidth];
100428 var spanOut;
100429
100430 function calcSpanItem(index) {
100431 var s = spanIn[index];
100432 var sc = valAxis.type === 'multicategory' ?
100433 valAxis.r2c(s) :
100434 valAxis.d2c(s, 0, trace[cdi.valLetter + 'calendar']);
100435 return sc === BADNUM ? spanLoose[index] : sc;
100436 }
100437
100438 if(spanmode === 'soft') {
100439 spanOut = spanLoose;
100440 } else if(spanmode === 'hard') {
100441 spanOut = spanTight;
100442 } else {
100443 spanOut = [calcSpanItem(0), calcSpanItem(1)];
100444 }
100445
100446 // to reuse the equal-range-item block
100447 var dummyAx = {
100448 type: 'linear',
100449 range: spanOut
100450 };
100451 Axes.setConvert(dummyAx);
100452 dummyAx.cleanRange();
100453
100454 return spanOut;
100455}
100456
100457},{"../../constants/numerical":267,"../../lib":287,"../../plots/cartesian/axes":334,"../box/calc":404,"./helpers":537}],535:[function(_dereq_,module,exports){
100458'use strict';
100459
100460var setPositionOffset = _dereq_('../box/cross_trace_calc').setPositionOffset;
100461var orientations = ['v', 'h'];
100462
100463module.exports = function crossTraceCalc(gd, plotinfo) {
100464 var calcdata = gd.calcdata;
100465 var xa = plotinfo.xaxis;
100466 var ya = plotinfo.yaxis;
100467
100468 for(var i = 0; i < orientations.length; i++) {
100469 var orientation = orientations[i];
100470 var posAxis = orientation === 'h' ? ya : xa;
100471 var violinList = [];
100472
100473 for(var j = 0; j < calcdata.length; j++) {
100474 var cd = calcdata[j];
100475 var t = cd[0].t;
100476 var trace = cd[0].trace;
100477
100478 if(trace.visible === true && trace.type === 'violin' &&
100479 !t.empty &&
100480 trace.orientation === orientation &&
100481 trace.xaxis === xa._id &&
100482 trace.yaxis === ya._id
100483 ) {
100484 violinList.push(j);
100485 }
100486 }
100487
100488 setPositionOffset('violin', gd, violinList, posAxis);
100489 }
100490};
100491
100492},{"../box/cross_trace_calc":405}],536:[function(_dereq_,module,exports){
100493'use strict';
100494
100495var Lib = _dereq_('../../lib');
100496var Color = _dereq_('../../components/color');
100497
100498var boxDefaults = _dereq_('../box/defaults');
100499var attributes = _dereq_('./attributes');
100500
100501module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
100502 function coerce(attr, dflt) {
100503 return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
100504 }
100505 function coerce2(attr, dflt) {
100506 return Lib.coerce2(traceIn, traceOut, attributes, attr, dflt);
100507 }
100508
100509 boxDefaults.handleSampleDefaults(traceIn, traceOut, coerce, layout);
100510 if(traceOut.visible === false) return;
100511
100512 coerce('bandwidth');
100513 coerce('side');
100514
100515 var width = coerce('width');
100516 if(!width) {
100517 coerce('scalegroup', traceOut.name);
100518 coerce('scalemode');
100519 }
100520
100521 var span = coerce('span');
100522 var spanmodeDflt;
100523 if(Array.isArray(span)) spanmodeDflt = 'manual';
100524 coerce('spanmode', spanmodeDflt);
100525
100526 var lineColor = coerce('line.color', (traceIn.marker || {}).color || defaultColor);
100527 var lineWidth = coerce('line.width');
100528 var fillColor = coerce('fillcolor', Color.addOpacity(traceOut.line.color, 0.5));
100529
100530 boxDefaults.handlePointsDefaults(traceIn, traceOut, coerce, {prefix: ''});
100531
100532 var boxWidth = coerce2('box.width');
100533 var boxFillColor = coerce2('box.fillcolor', fillColor);
100534 var boxLineColor = coerce2('box.line.color', lineColor);
100535 var boxLineWidth = coerce2('box.line.width', lineWidth);
100536 var boxVisible = coerce('box.visible', Boolean(boxWidth || boxFillColor || boxLineColor || boxLineWidth));
100537 if(!boxVisible) traceOut.box = {visible: false};
100538
100539 var meanLineColor = coerce2('meanline.color', lineColor);
100540 var meanLineWidth = coerce2('meanline.width', lineWidth);
100541 var meanLineVisible = coerce('meanline.visible', Boolean(meanLineColor || meanLineWidth));
100542 if(!meanLineVisible) traceOut.meanline = {visible: false};
100543};
100544
100545},{"../../components/color":157,"../../lib":287,"../box/defaults":406,"./attributes":533}],537:[function(_dereq_,module,exports){
100546'use strict';
100547
100548var Lib = _dereq_('../../lib');
100549
100550// Maybe add kernels more down the road,
100551// but note that the default `spanmode: 'soft'` bounds might have
100552// to become kernel-dependent
100553var kernels = {
100554 gaussian: function(v) {
100555 return (1 / Math.sqrt(2 * Math.PI)) * Math.exp(-0.5 * v * v);
100556 }
100557};
100558
100559exports.makeKDE = function(calcItem, trace, vals) {
100560 var len = vals.length;
100561 var kernel = kernels.gaussian;
100562 var bandwidth = calcItem.bandwidth;
100563 var factor = 1 / (len * bandwidth);
100564
100565 // don't use Lib.aggNums to skip isNumeric checks
100566 return function(x) {
100567 var sum = 0;
100568 for(var i = 0; i < len; i++) {
100569 sum += kernel((x - vals[i]) / bandwidth);
100570 }
100571 return factor * sum;
100572 };
100573};
100574
100575exports.getPositionOnKdePath = function(calcItem, trace, valuePx) {
100576 var posLetter, valLetter;
100577
100578 if(trace.orientation === 'h') {
100579 posLetter = 'y';
100580 valLetter = 'x';
100581 } else {
100582 posLetter = 'x';
100583 valLetter = 'y';
100584 }
100585
100586 var pointOnPath = Lib.findPointOnPath(
100587 calcItem.path,
100588 valuePx,
100589 valLetter,
100590 {pathLength: calcItem.pathLength}
100591 );
100592
100593 var posCenterPx = calcItem.posCenterPx;
100594 var posOnPath0 = pointOnPath[posLetter];
100595 var posOnPath1 = trace.side === 'both' ?
100596 2 * posCenterPx - posOnPath0 :
100597 posCenterPx;
100598
100599 return [posOnPath0, posOnPath1];
100600};
100601
100602exports.getKdeValue = function(calcItem, trace, valueDist) {
100603 var vals = calcItem.pts.map(exports.extractVal);
100604 var kde = exports.makeKDE(calcItem, trace, vals);
100605 return kde(valueDist) / calcItem.posDensityScale;
100606};
100607
100608exports.extractVal = function(o) { return o.v; };
100609
100610},{"../../lib":287}],538:[function(_dereq_,module,exports){
100611'use strict';
100612
100613var Lib = _dereq_('../../lib');
100614var Axes = _dereq_('../../plots/cartesian/axes');
100615var boxHoverPoints = _dereq_('../box/hover');
100616var helpers = _dereq_('./helpers');
100617
100618module.exports = function hoverPoints(pointData, xval, yval, hovermode, opts) {
100619 if(!opts) opts = {};
100620 var hoverLayer = opts.hoverLayer;
100621
100622 var cd = pointData.cd;
100623 var trace = cd[0].trace;
100624 var hoveron = trace.hoveron;
100625 var hasHoveronViolins = hoveron.indexOf('violins') !== -1;
100626 var hasHoveronKDE = hoveron.indexOf('kde') !== -1;
100627 var closeData = [];
100628 var closePtData;
100629 var violinLineAttrs;
100630
100631 if(hasHoveronViolins || hasHoveronKDE) {
100632 var closeBoxData = boxHoverPoints.hoverOnBoxes(pointData, xval, yval, hovermode);
100633
100634 if(hasHoveronKDE && closeBoxData.length > 0) {
100635 var xa = pointData.xa;
100636 var ya = pointData.ya;
100637 var pLetter, vLetter, pAxis, vAxis, vVal;
100638
100639 if(trace.orientation === 'h') {
100640 vVal = xval;
100641 pLetter = 'y';
100642 pAxis = ya;
100643 vLetter = 'x';
100644 vAxis = xa;
100645 } else {
100646 vVal = yval;
100647 pLetter = 'x';
100648 pAxis = xa;
100649 vLetter = 'y';
100650 vAxis = ya;
100651 }
100652
100653 var di = cd[pointData.index];
100654
100655 if(vVal >= di.span[0] && vVal <= di.span[1]) {
100656 var kdePointData = Lib.extendFlat({}, pointData);
100657 var vValPx = vAxis.c2p(vVal, true);
100658 var kdeVal = helpers.getKdeValue(di, trace, vVal);
100659 var pOnPath = helpers.getPositionOnKdePath(di, trace, vValPx);
100660 var paOffset = pAxis._offset;
100661 var paLength = pAxis._length;
100662
100663 kdePointData[pLetter + '0'] = pOnPath[0];
100664 kdePointData[pLetter + '1'] = pOnPath[1];
100665 kdePointData[vLetter + '0'] = kdePointData[vLetter + '1'] = vValPx;
100666 kdePointData[vLetter + 'Label'] = vLetter + ': ' + Axes.hoverLabelText(vAxis, vVal, trace[vLetter + 'hoverformat']) + ', ' + cd[0].t.labels.kde + ' ' + kdeVal.toFixed(3);
100667
100668 // move the spike to the KDE point
100669 kdePointData.spikeDistance = closeBoxData[0].spikeDistance;
100670 var spikePosAttr = pLetter + 'Spike';
100671 kdePointData[spikePosAttr] = closeBoxData[0][spikePosAttr];
100672 closeBoxData[0].spikeDistance = undefined;
100673 closeBoxData[0][spikePosAttr] = undefined;
100674
100675 // no hovertemplate support yet
100676 kdePointData.hovertemplate = false;
100677
100678 closeData.push(kdePointData);
100679
100680 violinLineAttrs = {stroke: pointData.color};
100681 violinLineAttrs[pLetter + '1'] = Lib.constrain(paOffset + pOnPath[0], paOffset, paOffset + paLength);
100682 violinLineAttrs[pLetter + '2'] = Lib.constrain(paOffset + pOnPath[1], paOffset, paOffset + paLength);
100683 violinLineAttrs[vLetter + '1'] = violinLineAttrs[vLetter + '2'] = vAxis._offset + vValPx;
100684 }
100685 }
100686
100687 if(hasHoveronViolins) {
100688 closeData = closeData.concat(closeBoxData);
100689 }
100690 }
100691
100692 if(hoveron.indexOf('points') !== -1) {
100693 closePtData = boxHoverPoints.hoverOnPoints(pointData, xval, yval);
100694 }
100695
100696 // update violin line (if any)
100697 var violinLine = hoverLayer.selectAll('.violinline-' + trace.uid)
100698 .data(violinLineAttrs ? [0] : []);
100699 violinLine.enter().append('line')
100700 .classed('violinline-' + trace.uid, true)
100701 .attr('stroke-width', 1.5);
100702 violinLine.exit().remove();
100703 violinLine.attr(violinLineAttrs);
100704
100705 // same combine logic as box hoverPoints
100706 if(hovermode === 'closest') {
100707 if(closePtData) return [closePtData];
100708 return closeData;
100709 }
100710 if(closePtData) {
100711 closeData.push(closePtData);
100712 return closeData;
100713 }
100714 return closeData;
100715};
100716
100717},{"../../lib":287,"../../plots/cartesian/axes":334,"../box/hover":408,"./helpers":537}],539:[function(_dereq_,module,exports){
100718'use strict';
100719
100720module.exports = {
100721 attributes: _dereq_('./attributes'),
100722 layoutAttributes: _dereq_('./layout_attributes'),
100723 supplyDefaults: _dereq_('./defaults'),
100724 crossTraceDefaults: _dereq_('../box/defaults').crossTraceDefaults,
100725 supplyLayoutDefaults: _dereq_('./layout_defaults'),
100726 calc: _dereq_('./calc'),
100727 crossTraceCalc: _dereq_('./cross_trace_calc'),
100728 plot: _dereq_('./plot'),
100729 style: _dereq_('./style'),
100730 styleOnSelect: _dereq_('../scatter/style').styleOnSelect,
100731 hoverPoints: _dereq_('./hover'),
100732 selectPoints: _dereq_('../box/select'),
100733
100734 moduleType: 'trace',
100735 name: 'violin',
100736 basePlotModule: _dereq_('../../plots/cartesian'),
100737 categories: ['cartesian', 'svg', 'symbols', 'oriented', 'box-violin', 'showLegend', 'violinLayout', 'zoomScale'],
100738 meta: {
100739 }
100740};
100741
100742},{"../../plots/cartesian":348,"../box/defaults":406,"../box/select":413,"../scatter/style":521,"./attributes":533,"./calc":534,"./cross_trace_calc":535,"./defaults":536,"./hover":538,"./layout_attributes":540,"./layout_defaults":541,"./plot":542,"./style":543}],540:[function(_dereq_,module,exports){
100743'use strict';
100744
100745var boxLayoutAttrs = _dereq_('../box/layout_attributes');
100746var extendFlat = _dereq_('../../lib').extendFlat;
100747
100748module.exports = {
100749 violinmode: extendFlat({}, boxLayoutAttrs.boxmode, {
100750 }),
100751 violingap: extendFlat({}, boxLayoutAttrs.boxgap, {
100752 }),
100753 violingroupgap: extendFlat({}, boxLayoutAttrs.boxgroupgap, {
100754 })
100755};
100756
100757},{"../../lib":287,"../box/layout_attributes":410}],541:[function(_dereq_,module,exports){
100758'use strict';
100759
100760var Lib = _dereq_('../../lib');
100761var layoutAttributes = _dereq_('./layout_attributes');
100762var boxLayoutDefaults = _dereq_('../box/layout_defaults');
100763
100764module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
100765 function coerce(attr, dflt) {
100766 return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt);
100767 }
100768 boxLayoutDefaults._supply(layoutIn, layoutOut, fullData, coerce, 'violin');
100769};
100770
100771},{"../../lib":287,"../box/layout_defaults":411,"./layout_attributes":540}],542:[function(_dereq_,module,exports){
100772'use strict';
100773
100774var d3 = _dereq_('@plotly/d3');
100775var Lib = _dereq_('../../lib');
100776var Drawing = _dereq_('../../components/drawing');
100777
100778var boxPlot = _dereq_('../box/plot');
100779var linePoints = _dereq_('../scatter/line_points');
100780var helpers = _dereq_('./helpers');
100781
100782module.exports = function plot(gd, plotinfo, cdViolins, violinLayer) {
100783 var fullLayout = gd._fullLayout;
100784 var xa = plotinfo.xaxis;
100785 var ya = plotinfo.yaxis;
100786
100787 function makePath(pts) {
100788 var segments = linePoints(pts, {
100789 xaxis: xa,
100790 yaxis: ya,
100791 connectGaps: true,
100792 baseTolerance: 0.75,
100793 shape: 'spline',
100794 simplify: true,
100795 linearized: true
100796 });
100797 return Drawing.smoothopen(segments[0], 1);
100798 }
100799
100800 Lib.makeTraceGroups(violinLayer, cdViolins, 'trace violins').each(function(cd) {
100801 var plotGroup = d3.select(this);
100802 var cd0 = cd[0];
100803 var t = cd0.t;
100804 var trace = cd0.trace;
100805
100806 if(trace.visible !== true || t.empty) {
100807 plotGroup.remove();
100808 return;
100809 }
100810
100811 var bPos = t.bPos;
100812 var bdPos = t.bdPos;
100813 var valAxis = plotinfo[t.valLetter + 'axis'];
100814 var posAxis = plotinfo[t.posLetter + 'axis'];
100815 var hasBothSides = trace.side === 'both';
100816 var hasPositiveSide = hasBothSides || trace.side === 'positive';
100817 var hasNegativeSide = hasBothSides || trace.side === 'negative';
100818
100819 var violins = plotGroup.selectAll('path.violin').data(Lib.identity);
100820
100821 violins.enter().append('path')
100822 .style('vector-effect', 'non-scaling-stroke')
100823 .attr('class', 'violin');
100824
100825 violins.exit().remove();
100826
100827 violins.each(function(d) {
100828 var pathSel = d3.select(this);
100829 var density = d.density;
100830 var len = density.length;
100831 var posCenter = posAxis.c2l(d.pos + bPos, true);
100832 var posCenterPx = posAxis.l2p(posCenter);
100833
100834 var scale;
100835 if(trace.width) {
100836 scale = t.maxKDE / bdPos;
100837 } else {
100838 var groupStats = fullLayout._violinScaleGroupStats[trace.scalegroup];
100839 scale = trace.scalemode === 'count' ?
100840 (groupStats.maxKDE / bdPos) * (groupStats.maxCount / d.pts.length) :
100841 groupStats.maxKDE / bdPos;
100842 }
100843
100844 var pathPos, pathNeg, path;
100845 var i, k, pts, pt;
100846
100847 if(hasPositiveSide) {
100848 pts = new Array(len);
100849 for(i = 0; i < len; i++) {
100850 pt = pts[i] = {};
100851 pt[t.posLetter] = posCenter + (density[i].v / scale);
100852 pt[t.valLetter] = valAxis.c2l(density[i].t, true);
100853 }
100854 pathPos = makePath(pts);
100855 }
100856
100857 if(hasNegativeSide) {
100858 pts = new Array(len);
100859 for(k = 0, i = len - 1; k < len; k++, i--) {
100860 pt = pts[k] = {};
100861 pt[t.posLetter] = posCenter - (density[i].v / scale);
100862 pt[t.valLetter] = valAxis.c2l(density[i].t, true);
100863 }
100864 pathNeg = makePath(pts);
100865 }
100866
100867 if(hasBothSides) {
100868 path = pathPos + 'L' + pathNeg.substr(1) + 'Z';
100869 } else {
100870 var startPt = [posCenterPx, valAxis.c2p(density[0].t)];
100871 var endPt = [posCenterPx, valAxis.c2p(density[len - 1].t)];
100872
100873 if(trace.orientation === 'h') {
100874 startPt.reverse();
100875 endPt.reverse();
100876 }
100877
100878 if(hasPositiveSide) {
100879 path = 'M' + startPt + 'L' + pathPos.substr(1) + 'L' + endPt;
100880 } else {
100881 path = 'M' + endPt + 'L' + pathNeg.substr(1) + 'L' + startPt;
100882 }
100883 }
100884 pathSel.attr('d', path);
100885
100886 // save a few things used in getPositionOnKdePath, getKdeValue
100887 // on hover and for meanline draw block below
100888 d.posCenterPx = posCenterPx;
100889 d.posDensityScale = scale * bdPos;
100890 d.path = pathSel.node();
100891 d.pathLength = d.path.getTotalLength() / (hasBothSides ? 2 : 1);
100892 });
100893
100894 var boxAttrs = trace.box;
100895 var boxWidth = boxAttrs.width;
100896 var boxLineWidth = (boxAttrs.line || {}).width;
100897 var bdPosScaled;
100898 var bPosPxOffset;
100899
100900 if(hasBothSides) {
100901 bdPosScaled = bdPos * boxWidth;
100902 bPosPxOffset = 0;
100903 } else if(hasPositiveSide) {
100904 bdPosScaled = [0, bdPos * boxWidth / 2];
100905 bPosPxOffset = boxLineWidth * {x: 1, y: -1}[t.posLetter];
100906 } else {
100907 bdPosScaled = [bdPos * boxWidth / 2, 0];
100908 bPosPxOffset = boxLineWidth * {x: -1, y: 1}[t.posLetter];
100909 }
100910
100911 // inner box
100912 boxPlot.plotBoxAndWhiskers(plotGroup, {pos: posAxis, val: valAxis}, trace, {
100913 bPos: bPos,
100914 bdPos: bdPosScaled,
100915 bPosPxOffset: bPosPxOffset
100916 });
100917
100918 // meanline insider box
100919 boxPlot.plotBoxMean(plotGroup, {pos: posAxis, val: valAxis}, trace, {
100920 bPos: bPos,
100921 bdPos: bdPosScaled,
100922 bPosPxOffset: bPosPxOffset
100923 });
100924
100925 var fn;
100926 if(!trace.box.visible && trace.meanline.visible) {
100927 fn = Lib.identity;
100928 }
100929
100930 // N.B. use different class name than boxPlot.plotBoxMean,
100931 // to avoid selectAll conflict
100932 var meanPaths = plotGroup.selectAll('path.meanline').data(fn || []);
100933 meanPaths.enter().append('path')
100934 .attr('class', 'meanline')
100935 .style('fill', 'none')
100936 .style('vector-effect', 'non-scaling-stroke');
100937 meanPaths.exit().remove();
100938 meanPaths.each(function(d) {
100939 var v = valAxis.c2p(d.mean, true);
100940 var p = helpers.getPositionOnKdePath(d, trace, v);
100941
100942 d3.select(this).attr('d',
100943 trace.orientation === 'h' ?
100944 'M' + v + ',' + p[0] + 'V' + p[1] :
100945 'M' + p[0] + ',' + v + 'H' + p[1]
100946 );
100947 });
100948
100949 boxPlot.plotPoints(plotGroup, {x: xa, y: ya}, trace, t);
100950 });
100951};
100952
100953},{"../../components/drawing":179,"../../lib":287,"../box/plot":412,"../scatter/line_points":511,"./helpers":537,"@plotly/d3":20}],543:[function(_dereq_,module,exports){
100954'use strict';
100955
100956var d3 = _dereq_('@plotly/d3');
100957var Color = _dereq_('../../components/color');
100958var stylePoints = _dereq_('../scatter/style').stylePoints;
100959
100960module.exports = function style(gd) {
100961 var s = d3.select(gd).selectAll('g.trace.violins');
100962
100963 s.style('opacity', function(d) { return d[0].trace.opacity; });
100964
100965 s.each(function(d) {
100966 var trace = d[0].trace;
100967 var sel = d3.select(this);
100968 var box = trace.box || {};
100969 var boxLine = box.line || {};
100970 var meanline = trace.meanline || {};
100971 var meanLineWidth = meanline.width;
100972
100973 sel.selectAll('path.violin')
100974 .style('stroke-width', trace.line.width + 'px')
100975 .call(Color.stroke, trace.line.color)
100976 .call(Color.fill, trace.fillcolor);
100977
100978 sel.selectAll('path.box')
100979 .style('stroke-width', boxLine.width + 'px')
100980 .call(Color.stroke, boxLine.color)
100981 .call(Color.fill, box.fillcolor);
100982
100983 var meanLineStyle = {
100984 'stroke-width': meanLineWidth + 'px',
100985 'stroke-dasharray': (2 * meanLineWidth) + 'px,' + meanLineWidth + 'px'
100986 };
100987
100988 sel.selectAll('path.mean')
100989 .style(meanLineStyle)
100990 .call(Color.stroke, meanline.color);
100991
100992 sel.selectAll('path.meanline')
100993 .style(meanLineStyle)
100994 .call(Color.stroke, meanline.color);
100995
100996 stylePoints(sel, trace, gd);
100997 });
100998};
100999
101000},{"../../components/color":157,"../scatter/style":521,"@plotly/d3":20}],544:[function(_dereq_,module,exports){
101001'use strict';
101002
101003var Axes = _dereq_('../plots/cartesian/axes');
101004var Lib = _dereq_('../lib');
101005var PlotSchema = _dereq_('../plot_api/plot_schema');
101006var pointsAccessorFunction = _dereq_('./helpers').pointsAccessorFunction;
101007var BADNUM = _dereq_('../constants/numerical').BADNUM;
101008
101009exports.moduleType = 'transform';
101010
101011exports.name = 'aggregate';
101012
101013var attrs = exports.attributes = {
101014 enabled: {
101015 valType: 'boolean',
101016 dflt: true,
101017 editType: 'calc',
101018 },
101019 groups: {
101020 // TODO: groupby should support string or array grouping this way too
101021 // currently groupby only allows a grouping array
101022 valType: 'string',
101023 strict: true,
101024 noBlank: true,
101025 arrayOk: true,
101026 dflt: 'x',
101027 editType: 'calc',
101028 },
101029 aggregations: {
101030 _isLinkedToArray: 'aggregation',
101031 target: {
101032 valType: 'string',
101033 editType: 'calc',
101034 },
101035 func: {
101036 valType: 'enumerated',
101037 values: ['count', 'sum', 'avg', 'median', 'mode', 'rms', 'stddev', 'min', 'max', 'first', 'last', 'change', 'range'],
101038 dflt: 'first',
101039 editType: 'calc',
101040 },
101041 funcmode: {
101042 valType: 'enumerated',
101043 values: ['sample', 'population'],
101044 dflt: 'sample',
101045 editType: 'calc',
101046 },
101047 enabled: {
101048 valType: 'boolean',
101049 dflt: true,
101050 editType: 'calc',
101051 },
101052 editType: 'calc'
101053 },
101054 editType: 'calc'
101055};
101056
101057var aggAttrs = attrs.aggregations;
101058
101059/**
101060 * Supply transform attributes defaults
101061 *
101062 * @param {object} transformIn
101063 * object linked to trace.transforms[i] with 'func' set to exports.name
101064 * @param {object} traceOut
101065 * the _fullData trace this transform applies to
101066 * @param {object} layout
101067 * the plot's (not-so-full) layout
101068 * @param {object} traceIn
101069 * the input data trace this transform applies to
101070 *
101071 * @return {object} transformOut
101072 * copy of transformIn that contains attribute defaults
101073 */
101074exports.supplyDefaults = function(transformIn, traceOut) {
101075 var transformOut = {};
101076 var i;
101077
101078 function coerce(attr, dflt) {
101079 return Lib.coerce(transformIn, transformOut, attrs, attr, dflt);
101080 }
101081
101082 var enabled = coerce('enabled');
101083
101084 if(!enabled) return transformOut;
101085
101086 /*
101087 * Normally _arrayAttrs is calculated during doCalc, but that comes later.
101088 * Anyway this can change due to *count* aggregations (see below) so it's not
101089 * necessarily the same set.
101090 *
101091 * For performance we turn it into an object of truthy values
101092 * we'll use 1 for arrays we haven't aggregated yet, 0 for finished arrays,
101093 * as distinct from undefined which means this array isn't present in the input
101094 * missing arrays can still be aggregate outputs for *count* aggregations.
101095 */
101096 var arrayAttrArray = PlotSchema.findArrayAttributes(traceOut);
101097 var arrayAttrs = {};
101098 for(i = 0; i < arrayAttrArray.length; i++) arrayAttrs[arrayAttrArray[i]] = 1;
101099
101100 var groups = coerce('groups');
101101
101102 if(!Array.isArray(groups)) {
101103 if(!arrayAttrs[groups]) {
101104 transformOut.enabled = false;
101105 return transformOut;
101106 }
101107 arrayAttrs[groups] = 0;
101108 }
101109
101110 var aggregationsIn = transformIn.aggregations || [];
101111 var aggregationsOut = transformOut.aggregations = new Array(aggregationsIn.length);
101112 var aggregationOut;
101113
101114 function coercei(attr, dflt) {
101115 return Lib.coerce(aggregationsIn[i], aggregationOut, aggAttrs, attr, dflt);
101116 }
101117
101118 for(i = 0; i < aggregationsIn.length; i++) {
101119 aggregationOut = {_index: i};
101120 var target = coercei('target');
101121 var func = coercei('func');
101122 var enabledi = coercei('enabled');
101123
101124 // add this aggregation to the output only if it's the first instance
101125 // of a valid target attribute - or an unused target attribute with "count"
101126 if(enabledi && target && (arrayAttrs[target] || (func === 'count' && arrayAttrs[target] === undefined))) {
101127 if(func === 'stddev') coercei('funcmode');
101128
101129 arrayAttrs[target] = 0;
101130 aggregationsOut[i] = aggregationOut;
101131 } else aggregationsOut[i] = {enabled: false, _index: i};
101132 }
101133
101134 // any array attributes we haven't yet covered, fill them with the default aggregation
101135 for(i = 0; i < arrayAttrArray.length; i++) {
101136 if(arrayAttrs[arrayAttrArray[i]]) {
101137 aggregationsOut.push({
101138 target: arrayAttrArray[i],
101139 func: aggAttrs.func.dflt,
101140 enabled: true,
101141 _index: -1
101142 });
101143 }
101144 }
101145
101146 return transformOut;
101147};
101148
101149
101150exports.calcTransform = function(gd, trace, opts) {
101151 if(!opts.enabled) return;
101152
101153 var groups = opts.groups;
101154
101155 var groupArray = Lib.getTargetArray(trace, {target: groups});
101156 if(!groupArray) return;
101157
101158 var i, vi, groupIndex, newGrouping;
101159
101160 var groupIndices = {};
101161 var indexToPoints = {};
101162 var groupings = [];
101163
101164 var originalPointsAccessor = pointsAccessorFunction(trace.transforms, opts);
101165
101166 var len = groupArray.length;
101167 if(trace._length) len = Math.min(len, trace._length);
101168
101169 for(i = 0; i < len; i++) {
101170 vi = groupArray[i];
101171 groupIndex = groupIndices[vi];
101172 if(groupIndex === undefined) {
101173 groupIndices[vi] = groupings.length;
101174 newGrouping = [i];
101175 groupings.push(newGrouping);
101176 indexToPoints[groupIndices[vi]] = originalPointsAccessor(i);
101177 } else {
101178 groupings[groupIndex].push(i);
101179 indexToPoints[groupIndices[vi]] = (indexToPoints[groupIndices[vi]] || []).concat(originalPointsAccessor(i));
101180 }
101181 }
101182
101183 opts._indexToPoints = indexToPoints;
101184
101185 var aggregations = opts.aggregations;
101186
101187 for(i = 0; i < aggregations.length; i++) {
101188 aggregateOneArray(gd, trace, groupings, aggregations[i]);
101189 }
101190
101191 if(typeof groups === 'string') {
101192 aggregateOneArray(gd, trace, groupings, {
101193 target: groups,
101194 func: 'first',
101195 enabled: true
101196 });
101197 }
101198
101199 trace._length = groupings.length;
101200};
101201
101202function aggregateOneArray(gd, trace, groupings, aggregation) {
101203 if(!aggregation.enabled) return;
101204
101205 var attr = aggregation.target;
101206 var targetNP = Lib.nestedProperty(trace, attr);
101207 var arrayIn = targetNP.get();
101208 var conversions = Axes.getDataConversions(gd, trace, attr, arrayIn);
101209 var func = getAggregateFunction(aggregation, conversions);
101210
101211 var arrayOut = new Array(groupings.length);
101212 for(var i = 0; i < groupings.length; i++) {
101213 arrayOut[i] = func(arrayIn, groupings[i]);
101214 }
101215 targetNP.set(arrayOut);
101216
101217 if(aggregation.func === 'count') {
101218 // count does not depend on an input array, so it's likely not part of _arrayAttrs yet
101219 // but after this transform it most definitely *is* an array attribute.
101220 Lib.pushUnique(trace._arrayAttrs, attr);
101221 }
101222}
101223
101224function getAggregateFunction(opts, conversions) {
101225 var func = opts.func;
101226 var d2c = conversions.d2c;
101227 var c2d = conversions.c2d;
101228
101229 switch(func) {
101230 // count, first, and last don't depend on anything about the data
101231 // point back to pure functions for performance
101232 case 'count':
101233 return count;
101234 case 'first':
101235 return first;
101236 case 'last':
101237 return last;
101238
101239 case 'sum':
101240 // This will produce output in all cases even though it's nonsensical
101241 // for date or category data.
101242 return function(array, indices) {
101243 var total = 0;
101244 for(var i = 0; i < indices.length; i++) {
101245 var vi = d2c(array[indices[i]]);
101246 if(vi !== BADNUM) total += vi;
101247 }
101248 return c2d(total);
101249 };
101250
101251 case 'avg':
101252 // Generally meaningless for category data but it still does something.
101253 return function(array, indices) {
101254 var total = 0;
101255 var cnt = 0;
101256 for(var i = 0; i < indices.length; i++) {
101257 var vi = d2c(array[indices[i]]);
101258 if(vi !== BADNUM) {
101259 total += vi;
101260 cnt++;
101261 }
101262 }
101263 return cnt ? c2d(total / cnt) : BADNUM;
101264 };
101265
101266 case 'min':
101267 return function(array, indices) {
101268 var out = Infinity;
101269 for(var i = 0; i < indices.length; i++) {
101270 var vi = d2c(array[indices[i]]);
101271 if(vi !== BADNUM) out = Math.min(out, vi);
101272 }
101273 return (out === Infinity) ? BADNUM : c2d(out);
101274 };
101275
101276 case 'max':
101277 return function(array, indices) {
101278 var out = -Infinity;
101279 for(var i = 0; i < indices.length; i++) {
101280 var vi = d2c(array[indices[i]]);
101281 if(vi !== BADNUM) out = Math.max(out, vi);
101282 }
101283 return (out === -Infinity) ? BADNUM : c2d(out);
101284 };
101285
101286 case 'range':
101287 return function(array, indices) {
101288 var min = Infinity;
101289 var max = -Infinity;
101290 for(var i = 0; i < indices.length; i++) {
101291 var vi = d2c(array[indices[i]]);
101292 if(vi !== BADNUM) {
101293 min = Math.min(min, vi);
101294 max = Math.max(max, vi);
101295 }
101296 }
101297 return (max === -Infinity || min === Infinity) ? BADNUM : c2d(max - min);
101298 };
101299
101300 case 'change':
101301 return function(array, indices) {
101302 var first = d2c(array[indices[0]]);
101303 var last = d2c(array[indices[indices.length - 1]]);
101304 return (first === BADNUM || last === BADNUM) ? BADNUM : c2d(last - first);
101305 };
101306
101307 case 'median':
101308 return function(array, indices) {
101309 var sortCalc = [];
101310 for(var i = 0; i < indices.length; i++) {
101311 var vi = d2c(array[indices[i]]);
101312 if(vi !== BADNUM) sortCalc.push(vi);
101313 }
101314 if(!sortCalc.length) return BADNUM;
101315 sortCalc.sort(Lib.sorterAsc);
101316 var mid = (sortCalc.length - 1) / 2;
101317 return c2d((sortCalc[Math.floor(mid)] + sortCalc[Math.ceil(mid)]) / 2);
101318 };
101319
101320 case 'mode':
101321 return function(array, indices) {
101322 var counts = {};
101323 var maxCnt = 0;
101324 var out = BADNUM;
101325 for(var i = 0; i < indices.length; i++) {
101326 var vi = d2c(array[indices[i]]);
101327 if(vi !== BADNUM) {
101328 var counti = counts[vi] = (counts[vi] || 0) + 1;
101329 if(counti > maxCnt) {
101330 maxCnt = counti;
101331 out = vi;
101332 }
101333 }
101334 }
101335 return maxCnt ? c2d(out) : BADNUM;
101336 };
101337
101338 case 'rms':
101339 return function(array, indices) {
101340 var total = 0;
101341 var cnt = 0;
101342 for(var i = 0; i < indices.length; i++) {
101343 var vi = d2c(array[indices[i]]);
101344 if(vi !== BADNUM) {
101345 total += vi * vi;
101346 cnt++;
101347 }
101348 }
101349 return cnt ? c2d(Math.sqrt(total / cnt)) : BADNUM;
101350 };
101351
101352 case 'stddev':
101353 return function(array, indices) {
101354 // balance numerical stability with performance:
101355 // so that we call d2c once per element but don't need to
101356 // store them, reference all to the first element
101357 var total = 0;
101358 var total2 = 0;
101359 var cnt = 1;
101360 var v0 = BADNUM;
101361 var i;
101362 for(i = 0; i < indices.length && v0 === BADNUM; i++) {
101363 v0 = d2c(array[indices[i]]);
101364 }
101365 if(v0 === BADNUM) return BADNUM;
101366
101367 for(; i < indices.length; i++) {
101368 var vi = d2c(array[indices[i]]);
101369 if(vi !== BADNUM) {
101370 var dv = vi - v0;
101371 total += dv;
101372 total2 += dv * dv;
101373 cnt++;
101374 }
101375 }
101376
101377 // This is population std dev, if we want sample std dev
101378 // we would need (...) / (cnt - 1)
101379 // Also note there's no c2d here - that means for dates the result
101380 // is a number of milliseconds, and for categories it's a number
101381 // of category differences, which is not generically meaningful but
101382 // as in other cases we don't forbid it.
101383 var norm = (opts.funcmode === 'sample') ? (cnt - 1) : cnt;
101384 // this is debatable: should a count of 1 return sample stddev of
101385 // 0 or undefined?
101386 if(!norm) return 0;
101387 return Math.sqrt((total2 - (total * total / cnt)) / norm);
101388 };
101389 }
101390}
101391
101392function count(array, indices) {
101393 return indices.length;
101394}
101395
101396function first(array, indices) {
101397 return array[indices[0]];
101398}
101399
101400function last(array, indices) {
101401 return array[indices[indices.length - 1]];
101402}
101403
101404},{"../constants/numerical":267,"../lib":287,"../plot_api/plot_schema":322,"../plots/cartesian/axes":334,"./helpers":547}],545:[function(_dereq_,module,exports){
101405'use strict';
101406
101407var Lib = _dereq_('../lib');
101408var Registry = _dereq_('../registry');
101409var Axes = _dereq_('../plots/cartesian/axes');
101410var pointsAccessorFunction = _dereq_('./helpers').pointsAccessorFunction;
101411
101412var filterOps = _dereq_('../constants/filter_ops');
101413var COMPARISON_OPS = filterOps.COMPARISON_OPS;
101414var INTERVAL_OPS = filterOps.INTERVAL_OPS;
101415var SET_OPS = filterOps.SET_OPS;
101416
101417exports.moduleType = 'transform';
101418
101419exports.name = 'filter';
101420
101421exports.attributes = {
101422 enabled: {
101423 valType: 'boolean',
101424 dflt: true,
101425 editType: 'calc',
101426 },
101427 target: {
101428 valType: 'string',
101429 strict: true,
101430 noBlank: true,
101431 arrayOk: true,
101432 dflt: 'x',
101433 editType: 'calc',
101434 },
101435 operation: {
101436 valType: 'enumerated',
101437 values: []
101438 .concat(COMPARISON_OPS)
101439 .concat(INTERVAL_OPS)
101440 .concat(SET_OPS),
101441 dflt: '=',
101442 editType: 'calc',
101443 },
101444 value: {
101445 valType: 'any',
101446 dflt: 0,
101447 editType: 'calc',
101448 },
101449 preservegaps: {
101450 valType: 'boolean',
101451 dflt: false,
101452 editType: 'calc',
101453 },
101454 editType: 'calc'
101455};
101456
101457exports.supplyDefaults = function(transformIn) {
101458 var transformOut = {};
101459
101460 function coerce(attr, dflt) {
101461 return Lib.coerce(transformIn, transformOut, exports.attributes, attr, dflt);
101462 }
101463
101464 var enabled = coerce('enabled');
101465
101466 if(enabled) {
101467 var target = coerce('target');
101468
101469 if(Lib.isArrayOrTypedArray(target) && target.length === 0) {
101470 transformOut.enabled = false;
101471 return transformOut;
101472 }
101473
101474 coerce('preservegaps');
101475 coerce('operation');
101476 coerce('value');
101477
101478 var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleDefaults');
101479 handleCalendarDefaults(transformIn, transformOut, 'valuecalendar', null);
101480 handleCalendarDefaults(transformIn, transformOut, 'targetcalendar', null);
101481 }
101482
101483 return transformOut;
101484};
101485
101486exports.calcTransform = function(gd, trace, opts) {
101487 if(!opts.enabled) return;
101488
101489 var targetArray = Lib.getTargetArray(trace, opts);
101490 if(!targetArray) return;
101491
101492 var target = opts.target;
101493
101494 var len = targetArray.length;
101495 if(trace._length) len = Math.min(len, trace._length);
101496
101497 var targetCalendar = opts.targetcalendar;
101498 var arrayAttrs = trace._arrayAttrs;
101499 var preservegaps = opts.preservegaps;
101500
101501 // even if you provide targetcalendar, if target is a string and there
101502 // is a calendar attribute matching target it will get used instead.
101503 if(typeof target === 'string') {
101504 var attrTargetCalendar = Lib.nestedProperty(trace, target + 'calendar').get();
101505 if(attrTargetCalendar) targetCalendar = attrTargetCalendar;
101506 }
101507
101508 var d2c = Axes.getDataToCoordFunc(gd, trace, target, targetArray);
101509 var filterFunc = getFilterFunc(opts, d2c, targetCalendar);
101510 var originalArrays = {};
101511 var indexToPoints = {};
101512 var index = 0;
101513
101514 function forAllAttrs(fn, index) {
101515 for(var j = 0; j < arrayAttrs.length; j++) {
101516 var np = Lib.nestedProperty(trace, arrayAttrs[j]);
101517 fn(np, index);
101518 }
101519 }
101520
101521 var initFn;
101522 var fillFn;
101523 if(preservegaps) {
101524 initFn = function(np) {
101525 originalArrays[np.astr] = Lib.extendDeep([], np.get());
101526 np.set(new Array(len));
101527 };
101528 fillFn = function(np, index) {
101529 var val = originalArrays[np.astr][index];
101530 np.get()[index] = val;
101531 };
101532 } else {
101533 initFn = function(np) {
101534 originalArrays[np.astr] = Lib.extendDeep([], np.get());
101535 np.set([]);
101536 };
101537 fillFn = function(np, index) {
101538 var val = originalArrays[np.astr][index];
101539 np.get().push(val);
101540 };
101541 }
101542
101543 // copy all original array attribute values, and clear arrays in trace
101544 forAllAttrs(initFn);
101545
101546 var originalPointsAccessor = pointsAccessorFunction(trace.transforms, opts);
101547
101548 // loop through filter array, fill trace arrays if passed
101549 for(var i = 0; i < len; i++) {
101550 var passed = filterFunc(targetArray[i]);
101551 if(passed) {
101552 forAllAttrs(fillFn, i);
101553 indexToPoints[index++] = originalPointsAccessor(i);
101554 } else if(preservegaps) index++;
101555 }
101556
101557 opts._indexToPoints = indexToPoints;
101558 trace._length = index;
101559};
101560
101561function getFilterFunc(opts, d2c, targetCalendar) {
101562 var operation = opts.operation;
101563 var value = opts.value;
101564 var hasArrayValue = Array.isArray(value);
101565
101566 function isOperationIn(array) {
101567 return array.indexOf(operation) !== -1;
101568 }
101569
101570 var d2cValue = function(v) { return d2c(v, 0, opts.valuecalendar); };
101571 var d2cTarget = function(v) { return d2c(v, 0, targetCalendar); };
101572
101573 var coercedValue;
101574
101575 if(isOperationIn(COMPARISON_OPS)) {
101576 coercedValue = hasArrayValue ? d2cValue(value[0]) : d2cValue(value);
101577 } else if(isOperationIn(INTERVAL_OPS)) {
101578 coercedValue = hasArrayValue ?
101579 [d2cValue(value[0]), d2cValue(value[1])] :
101580 [d2cValue(value), d2cValue(value)];
101581 } else if(isOperationIn(SET_OPS)) {
101582 coercedValue = hasArrayValue ? value.map(d2cValue) : [d2cValue(value)];
101583 }
101584
101585 switch(operation) {
101586 case '=':
101587 return function(v) { return d2cTarget(v) === coercedValue; };
101588
101589 case '!=':
101590 return function(v) { return d2cTarget(v) !== coercedValue; };
101591
101592 case '<':
101593 return function(v) { return d2cTarget(v) < coercedValue; };
101594
101595 case '<=':
101596 return function(v) { return d2cTarget(v) <= coercedValue; };
101597
101598 case '>':
101599 return function(v) { return d2cTarget(v) > coercedValue; };
101600
101601 case '>=':
101602 return function(v) { return d2cTarget(v) >= coercedValue; };
101603
101604 case '[]':
101605 return function(v) {
101606 var cv = d2cTarget(v);
101607 return cv >= coercedValue[0] && cv <= coercedValue[1];
101608 };
101609
101610 case '()':
101611 return function(v) {
101612 var cv = d2cTarget(v);
101613 return cv > coercedValue[0] && cv < coercedValue[1];
101614 };
101615
101616 case '[)':
101617 return function(v) {
101618 var cv = d2cTarget(v);
101619 return cv >= coercedValue[0] && cv < coercedValue[1];
101620 };
101621
101622 case '(]':
101623 return function(v) {
101624 var cv = d2cTarget(v);
101625 return cv > coercedValue[0] && cv <= coercedValue[1];
101626 };
101627
101628 case '][':
101629 return function(v) {
101630 var cv = d2cTarget(v);
101631 return cv <= coercedValue[0] || cv >= coercedValue[1];
101632 };
101633
101634 case ')(':
101635 return function(v) {
101636 var cv = d2cTarget(v);
101637 return cv < coercedValue[0] || cv > coercedValue[1];
101638 };
101639
101640 case '](':
101641 return function(v) {
101642 var cv = d2cTarget(v);
101643 return cv <= coercedValue[0] || cv > coercedValue[1];
101644 };
101645
101646 case ')[':
101647 return function(v) {
101648 var cv = d2cTarget(v);
101649 return cv < coercedValue[0] || cv >= coercedValue[1];
101650 };
101651
101652 case '{}':
101653 return function(v) {
101654 return coercedValue.indexOf(d2cTarget(v)) !== -1;
101655 };
101656
101657 case '}{':
101658 return function(v) {
101659 return coercedValue.indexOf(d2cTarget(v)) === -1;
101660 };
101661 }
101662}
101663
101664},{"../constants/filter_ops":265,"../lib":287,"../plots/cartesian/axes":334,"../registry":376,"./helpers":547}],546:[function(_dereq_,module,exports){
101665'use strict';
101666
101667var Lib = _dereq_('../lib');
101668var PlotSchema = _dereq_('../plot_api/plot_schema');
101669var Plots = _dereq_('../plots/plots');
101670var pointsAccessorFunction = _dereq_('./helpers').pointsAccessorFunction;
101671
101672exports.moduleType = 'transform';
101673
101674exports.name = 'groupby';
101675
101676exports.attributes = {
101677 enabled: {
101678 valType: 'boolean',
101679 dflt: true,
101680 editType: 'calc',
101681 },
101682 groups: {
101683 valType: 'data_array',
101684 dflt: [],
101685 editType: 'calc',
101686 },
101687 nameformat: {
101688 valType: 'string',
101689 editType: 'calc',
101690 },
101691 styles: {
101692 _isLinkedToArray: 'style',
101693 target: {
101694 valType: 'string',
101695 editType: 'calc',
101696 },
101697 value: {
101698 valType: 'any',
101699 dflt: {},
101700 editType: 'calc',
101701 _compareAsJSON: true
101702 },
101703 editType: 'calc'
101704 },
101705 editType: 'calc'
101706};
101707
101708/**
101709 * Supply transform attributes defaults
101710 *
101711 * @param {object} transformIn
101712 * object linked to trace.transforms[i] with 'type' set to exports.name
101713 * @param {object} traceOut
101714 * the _fullData trace this transform applies to
101715 * @param {object} layout
101716 * the plot's (not-so-full) layout
101717 * @param {object} traceIn
101718 * the input data trace this transform applies to
101719 *
101720 * @return {object} transformOut
101721 * copy of transformIn that contains attribute defaults
101722 */
101723exports.supplyDefaults = function(transformIn, traceOut, layout) {
101724 var i;
101725 var transformOut = {};
101726
101727 function coerce(attr, dflt) {
101728 return Lib.coerce(transformIn, transformOut, exports.attributes, attr, dflt);
101729 }
101730
101731 var enabled = coerce('enabled');
101732
101733 if(!enabled) return transformOut;
101734
101735 coerce('groups');
101736 coerce('nameformat', layout._dataLength > 1 ? '%{group} (%{trace})' : '%{group}');
101737
101738 var styleIn = transformIn.styles;
101739 var styleOut = transformOut.styles = [];
101740
101741 if(styleIn) {
101742 for(i = 0; i < styleIn.length; i++) {
101743 var thisStyle = styleOut[i] = {};
101744 Lib.coerce(styleIn[i], styleOut[i], exports.attributes.styles, 'target');
101745 var value = Lib.coerce(styleIn[i], styleOut[i], exports.attributes.styles, 'value');
101746
101747 // so that you can edit value in place and have Plotly.react notice it, or
101748 // rebuild it every time and have Plotly.react NOT think it changed:
101749 // use _compareAsJSON to say we should diff the _JSON_value
101750 if(Lib.isPlainObject(value)) thisStyle.value = Lib.extendDeep({}, value);
101751 else if(value) delete thisStyle.value;
101752 }
101753 }
101754
101755 return transformOut;
101756};
101757
101758
101759/**
101760 * Apply transform !!!
101761 *
101762 * @param {array} data
101763 * array of transformed traces (is [fullTrace] upon first transform)
101764 *
101765 * @param {object} state
101766 * state object which includes:
101767 * - transform {object} full transform attributes
101768 * - fullTrace {object} full trace object which is being transformed
101769 * - fullData {array} full pre-transform(s) data array
101770 * - layout {object} the plot's (not-so-full) layout
101771 *
101772 * @return {object} newData
101773 * array of transformed traces
101774 */
101775exports.transform = function(data, state) {
101776 var newTraces, i, j;
101777 var newData = [];
101778
101779 for(i = 0; i < data.length; i++) {
101780 newTraces = transformOne(data[i], state);
101781
101782 for(j = 0; j < newTraces.length; j++) {
101783 newData.push(newTraces[j]);
101784 }
101785 }
101786
101787 return newData;
101788};
101789
101790function transformOne(trace, state) {
101791 var i, j, k, attr, srcArray, groupName, newTrace, transforms, arrayLookup;
101792 var groupNameObj;
101793
101794 var opts = state.transform;
101795 var transformIndex = state.transformIndex;
101796 var groups = trace.transforms[transformIndex].groups;
101797 var originalPointsAccessor = pointsAccessorFunction(trace.transforms, opts);
101798
101799 if(!(Lib.isArrayOrTypedArray(groups)) || groups.length === 0) {
101800 return [trace];
101801 }
101802
101803 var groupNames = Lib.filterUnique(groups);
101804 var newData = new Array(groupNames.length);
101805 var len = groups.length;
101806
101807 var arrayAttrs = PlotSchema.findArrayAttributes(trace);
101808
101809 var styles = opts.styles || [];
101810 var styleLookup = {};
101811 for(i = 0; i < styles.length; i++) {
101812 styleLookup[styles[i].target] = styles[i].value;
101813 }
101814
101815 if(opts.styles) {
101816 groupNameObj = Lib.keyedContainer(opts, 'styles', 'target', 'value.name');
101817 }
101818
101819 // An index to map group name --> expanded trace index
101820 var indexLookup = {};
101821 var indexCnts = {};
101822
101823 for(i = 0; i < groupNames.length; i++) {
101824 groupName = groupNames[i];
101825 indexLookup[groupName] = i;
101826 indexCnts[groupName] = 0;
101827
101828 // Start with a deep extend that just copies array references.
101829 newTrace = newData[i] = Lib.extendDeepNoArrays({}, trace);
101830 newTrace._group = groupName;
101831 newTrace.transforms[transformIndex]._indexToPoints = {};
101832
101833 var suppliedName = null;
101834 if(groupNameObj) {
101835 suppliedName = groupNameObj.get(groupName);
101836 }
101837
101838 if(suppliedName || suppliedName === '') {
101839 newTrace.name = suppliedName;
101840 } else {
101841 newTrace.name = Lib.templateString(opts.nameformat, {
101842 trace: trace.name,
101843 group: groupName
101844 });
101845 }
101846
101847 // In order for groups to apply correctly to other transform data (e.g.
101848 // a filter transform), we have to break the connection and clone the
101849 // transforms so that each group writes grouped values into a different
101850 // destination. This function does not break the array reference
101851 // connection between the split transforms it creates. That's handled in
101852 // initialize, which creates a new empty array for each arrayAttr.
101853 transforms = newTrace.transforms;
101854 newTrace.transforms = [];
101855 for(j = 0; j < transforms.length; j++) {
101856 newTrace.transforms[j] = Lib.extendDeepNoArrays({}, transforms[j]);
101857 }
101858
101859 // Initialize empty arrays for the arrayAttrs, to be split in the next step
101860 for(j = 0; j < arrayAttrs.length; j++) {
101861 Lib.nestedProperty(newTrace, arrayAttrs[j]).set([]);
101862 }
101863 }
101864
101865 // For each array attribute including those nested inside this and other
101866 // transforms (small note that we technically only need to do this for
101867 // transforms that have not yet been applied):
101868 for(k = 0; k < arrayAttrs.length; k++) {
101869 attr = arrayAttrs[k];
101870
101871 // Cache all the arrays to which we'll push:
101872 for(j = 0, arrayLookup = []; j < groupNames.length; j++) {
101873 arrayLookup[j] = Lib.nestedProperty(newData[j], attr).get();
101874 }
101875
101876 // Get the input data:
101877 srcArray = Lib.nestedProperty(trace, attr).get();
101878
101879 // Send each data point to the appropriate expanded trace:
101880 for(j = 0; j < len; j++) {
101881 // Map group data --> trace index --> array and push data onto it
101882 arrayLookup[indexLookup[groups[j]]].push(srcArray[j]);
101883 }
101884 }
101885
101886 for(j = 0; j < len; j++) {
101887 newTrace = newData[indexLookup[groups[j]]];
101888
101889 var indexToPoints = newTrace.transforms[transformIndex]._indexToPoints;
101890 indexToPoints[indexCnts[groups[j]]] = originalPointsAccessor(j);
101891 indexCnts[groups[j]]++;
101892 }
101893
101894 for(i = 0; i < groupNames.length; i++) {
101895 groupName = groupNames[i];
101896 newTrace = newData[i];
101897
101898 Plots.clearExpandedTraceDefaultColors(newTrace);
101899
101900 // there's no need to coerce styleLookup[groupName] here
101901 // as another round of supplyDefaults is done on the transformed traces
101902 newTrace = Lib.extendDeepNoArrays(newTrace, styleLookup[groupName] || {});
101903 }
101904
101905 return newData;
101906}
101907
101908},{"../lib":287,"../plot_api/plot_schema":322,"../plots/plots":369,"./helpers":547}],547:[function(_dereq_,module,exports){
101909'use strict';
101910
101911exports.pointsAccessorFunction = function(transforms, opts) {
101912 var tr;
101913 var prevIndexToPoints;
101914 for(var i = 0; i < transforms.length; i++) {
101915 tr = transforms[i];
101916 if(tr === opts) break;
101917 if(!tr._indexToPoints || tr.enabled === false) continue;
101918 prevIndexToPoints = tr._indexToPoints;
101919 }
101920 var originalPointsAccessor = prevIndexToPoints ?
101921 function(i) {return prevIndexToPoints[i];} :
101922 function(i) {return [i];};
101923 return originalPointsAccessor;
101924};
101925
101926},{}],548:[function(_dereq_,module,exports){
101927'use strict';
101928
101929var Lib = _dereq_('../lib');
101930var Axes = _dereq_('../plots/cartesian/axes');
101931var pointsAccessorFunction = _dereq_('./helpers').pointsAccessorFunction;
101932
101933var BADNUM = _dereq_('../constants/numerical').BADNUM;
101934
101935exports.moduleType = 'transform';
101936
101937exports.name = 'sort';
101938
101939exports.attributes = {
101940 enabled: {
101941 valType: 'boolean',
101942 dflt: true,
101943 editType: 'calc',
101944 },
101945 target: {
101946 valType: 'string',
101947 strict: true,
101948 noBlank: true,
101949 arrayOk: true,
101950 dflt: 'x',
101951 editType: 'calc',
101952 },
101953 order: {
101954 valType: 'enumerated',
101955 values: ['ascending', 'descending'],
101956 dflt: 'ascending',
101957 editType: 'calc',
101958 },
101959 editType: 'calc'
101960};
101961
101962exports.supplyDefaults = function(transformIn) {
101963 var transformOut = {};
101964
101965 function coerce(attr, dflt) {
101966 return Lib.coerce(transformIn, transformOut, exports.attributes, attr, dflt);
101967 }
101968
101969 var enabled = coerce('enabled');
101970
101971 if(enabled) {
101972 coerce('target');
101973 coerce('order');
101974 }
101975
101976 return transformOut;
101977};
101978
101979exports.calcTransform = function(gd, trace, opts) {
101980 if(!opts.enabled) return;
101981
101982 var targetArray = Lib.getTargetArray(trace, opts);
101983 if(!targetArray) return;
101984
101985 var target = opts.target;
101986
101987 var len = targetArray.length;
101988 if(trace._length) len = Math.min(len, trace._length);
101989
101990 var arrayAttrs = trace._arrayAttrs;
101991 var d2c = Axes.getDataToCoordFunc(gd, trace, target, targetArray);
101992 var indices = getIndices(opts, targetArray, d2c, len);
101993 var originalPointsAccessor = pointsAccessorFunction(trace.transforms, opts);
101994 var indexToPoints = {};
101995 var i, j;
101996
101997 for(i = 0; i < arrayAttrs.length; i++) {
101998 var np = Lib.nestedProperty(trace, arrayAttrs[i]);
101999 var arrayOld = np.get();
102000 var arrayNew = new Array(len);
102001
102002 for(j = 0; j < len; j++) {
102003 arrayNew[j] = arrayOld[indices[j]];
102004 }
102005
102006 np.set(arrayNew);
102007 }
102008
102009 for(j = 0; j < len; j++) {
102010 indexToPoints[j] = originalPointsAccessor(indices[j]);
102011 }
102012
102013 opts._indexToPoints = indexToPoints;
102014 trace._length = len;
102015};
102016
102017function getIndices(opts, targetArray, d2c, len) {
102018 var sortedArray = new Array(len);
102019 var indices = new Array(len);
102020 var i;
102021
102022 for(i = 0; i < len; i++) {
102023 sortedArray[i] = {v: targetArray[i], i: i};
102024 }
102025
102026 sortedArray.sort(getSortFunc(opts, d2c));
102027
102028 for(i = 0; i < len; i++) {
102029 indices[i] = sortedArray[i].i;
102030 }
102031
102032 return indices;
102033}
102034
102035function getSortFunc(opts, d2c) {
102036 switch(opts.order) {
102037 case 'ascending':
102038 return function(a, b) {
102039 var ac = d2c(a.v);
102040 var bc = d2c(b.v);
102041 if(ac === BADNUM) {
102042 return 1;
102043 }
102044 if(bc === BADNUM) {
102045 return -1;
102046 }
102047 return ac - bc;
102048 };
102049 case 'descending':
102050 return function(a, b) {
102051 var ac = d2c(a.v);
102052 var bc = d2c(b.v);
102053 if(ac === BADNUM) {
102054 return 1;
102055 }
102056 if(bc === BADNUM) {
102057 return -1;
102058 }
102059 return bc - ac;
102060 };
102061 }
102062}
102063
102064},{"../constants/numerical":267,"../lib":287,"../plots/cartesian/axes":334,"./helpers":547}],549:[function(_dereq_,module,exports){
102065'use strict';
102066
102067// package version injected by `npm run preprocess`
102068exports.version = '2.4.1';
102069
102070},{}]},{},[15])(15)
102071});