UNPKG

106 kBJavaScriptView Raw
1(function (global, factory) {
2 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
3 typeof define === 'function' && define.amd ? define(factory) :
4 (global = global || self, global.controls = factory());
5}(this, (function () { 'use strict';
6
7 var xhtml = "http://www.w3.org/1999/xhtml";
8
9 var namespaces = {
10 svg: "http://www.w3.org/2000/svg",
11 xhtml: xhtml,
12 xlink: "http://www.w3.org/1999/xlink",
13 xml: "http://www.w3.org/XML/1998/namespace",
14 xmlns: "http://www.w3.org/2000/xmlns/"
15 };
16
17 function namespace(name) {
18 var prefix = name += "", i = prefix.indexOf(":");
19 if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") name = name.slice(i + 1);
20 return namespaces.hasOwnProperty(prefix) ? {space: namespaces[prefix], local: name} : name;
21 }
22
23 function creatorInherit(name) {
24 return function() {
25 var document = this.ownerDocument,
26 uri = this.namespaceURI;
27 return uri === xhtml && document.documentElement.namespaceURI === xhtml
28 ? document.createElement(name)
29 : document.createElementNS(uri, name);
30 };
31 }
32
33 function creatorFixed(fullname) {
34 return function() {
35 return this.ownerDocument.createElementNS(fullname.space, fullname.local);
36 };
37 }
38
39 function creator(name) {
40 var fullname = namespace(name);
41 return (fullname.local
42 ? creatorFixed
43 : creatorInherit)(fullname);
44 }
45
46 function none() {}
47
48 function selector(selector) {
49 return selector == null ? none : function() {
50 return this.querySelector(selector);
51 };
52 }
53
54 function selection_select(select) {
55 if (typeof select !== "function") select = selector(select);
56
57 for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
58 for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {
59 if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {
60 if ("__data__" in node) subnode.__data__ = node.__data__;
61 subgroup[i] = subnode;
62 }
63 }
64 }
65
66 return new Selection(subgroups, this._parents);
67 }
68
69 function empty() {
70 return [];
71 }
72
73 function selectorAll(selector) {
74 return selector == null ? empty : function() {
75 return this.querySelectorAll(selector);
76 };
77 }
78
79 function selection_selectAll(select) {
80 if (typeof select !== "function") select = selectorAll(select);
81
82 for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {
83 for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
84 if (node = group[i]) {
85 subgroups.push(select.call(node, node.__data__, i, group));
86 parents.push(node);
87 }
88 }
89 }
90
91 return new Selection(subgroups, parents);
92 }
93
94 function matcher(selector) {
95 return function() {
96 return this.matches(selector);
97 };
98 }
99
100 function selection_filter(match) {
101 if (typeof match !== "function") match = matcher(match);
102
103 for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
104 for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {
105 if ((node = group[i]) && match.call(node, node.__data__, i, group)) {
106 subgroup.push(node);
107 }
108 }
109 }
110
111 return new Selection(subgroups, this._parents);
112 }
113
114 function sparse(update) {
115 return new Array(update.length);
116 }
117
118 function selection_enter() {
119 return new Selection(this._enter || this._groups.map(sparse), this._parents);
120 }
121
122 function EnterNode(parent, datum) {
123 this.ownerDocument = parent.ownerDocument;
124 this.namespaceURI = parent.namespaceURI;
125 this._next = null;
126 this._parent = parent;
127 this.__data__ = datum;
128 }
129
130 EnterNode.prototype = {
131 constructor: EnterNode,
132 appendChild: function(child) { return this._parent.insertBefore(child, this._next); },
133 insertBefore: function(child, next) { return this._parent.insertBefore(child, next); },
134 querySelector: function(selector) { return this._parent.querySelector(selector); },
135 querySelectorAll: function(selector) { return this._parent.querySelectorAll(selector); }
136 };
137
138 function constant(x) {
139 return function() {
140 return x;
141 };
142 }
143
144 var keyPrefix = "$"; // Protect against keys like “__proto__”.
145
146 function bindIndex(parent, group, enter, update, exit, data) {
147 var i = 0,
148 node,
149 groupLength = group.length,
150 dataLength = data.length;
151
152 // Put any non-null nodes that fit into update.
153 // Put any null nodes into enter.
154 // Put any remaining data into enter.
155 for (; i < dataLength; ++i) {
156 if (node = group[i]) {
157 node.__data__ = data[i];
158 update[i] = node;
159 } else {
160 enter[i] = new EnterNode(parent, data[i]);
161 }
162 }
163
164 // Put any non-null nodes that don’t fit into exit.
165 for (; i < groupLength; ++i) {
166 if (node = group[i]) {
167 exit[i] = node;
168 }
169 }
170 }
171
172 function bindKey(parent, group, enter, update, exit, data, key) {
173 var i,
174 node,
175 nodeByKeyValue = {},
176 groupLength = group.length,
177 dataLength = data.length,
178 keyValues = new Array(groupLength),
179 keyValue;
180
181 // Compute the key for each node.
182 // If multiple nodes have the same key, the duplicates are added to exit.
183 for (i = 0; i < groupLength; ++i) {
184 if (node = group[i]) {
185 keyValues[i] = keyValue = keyPrefix + key.call(node, node.__data__, i, group);
186 if (keyValue in nodeByKeyValue) {
187 exit[i] = node;
188 } else {
189 nodeByKeyValue[keyValue] = node;
190 }
191 }
192 }
193
194 // Compute the key for each datum.
195 // If there a node associated with this key, join and add it to update.
196 // If there is not (or the key is a duplicate), add it to enter.
197 for (i = 0; i < dataLength; ++i) {
198 keyValue = keyPrefix + key.call(parent, data[i], i, data);
199 if (node = nodeByKeyValue[keyValue]) {
200 update[i] = node;
201 node.__data__ = data[i];
202 nodeByKeyValue[keyValue] = null;
203 } else {
204 enter[i] = new EnterNode(parent, data[i]);
205 }
206 }
207
208 // Add any remaining nodes that were not bound to data to exit.
209 for (i = 0; i < groupLength; ++i) {
210 if ((node = group[i]) && (nodeByKeyValue[keyValues[i]] === node)) {
211 exit[i] = node;
212 }
213 }
214 }
215
216 function selection_data(value, key) {
217 if (!value) {
218 data = new Array(this.size()), j = -1;
219 this.each(function(d) { data[++j] = d; });
220 return data;
221 }
222
223 var bind = key ? bindKey : bindIndex,
224 parents = this._parents,
225 groups = this._groups;
226
227 if (typeof value !== "function") value = constant(value);
228
229 for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) {
230 var parent = parents[j],
231 group = groups[j],
232 groupLength = group.length,
233 data = value.call(parent, parent && parent.__data__, j, parents),
234 dataLength = data.length,
235 enterGroup = enter[j] = new Array(dataLength),
236 updateGroup = update[j] = new Array(dataLength),
237 exitGroup = exit[j] = new Array(groupLength);
238
239 bind(parent, group, enterGroup, updateGroup, exitGroup, data, key);
240
241 // Now connect the enter nodes to their following update node, such that
242 // appendChild can insert the materialized enter node before this node,
243 // rather than at the end of the parent node.
244 for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) {
245 if (previous = enterGroup[i0]) {
246 if (i0 >= i1) i1 = i0 + 1;
247 while (!(next = updateGroup[i1]) && ++i1 < dataLength);
248 previous._next = next || null;
249 }
250 }
251 }
252
253 update = new Selection(update, parents);
254 update._enter = enter;
255 update._exit = exit;
256 return update;
257 }
258
259 function selection_exit() {
260 return new Selection(this._exit || this._groups.map(sparse), this._parents);
261 }
262
263 function selection_join(onenter, onupdate, onexit) {
264 var enter = this.enter(), update = this, exit = this.exit();
265 enter = typeof onenter === "function" ? onenter(enter) : enter.append(onenter + "");
266 if (onupdate != null) update = onupdate(update);
267 if (onexit == null) exit.remove(); else onexit(exit);
268 return enter && update ? enter.merge(update).order() : update;
269 }
270
271 function selection_merge(selection) {
272
273 for (var groups0 = this._groups, groups1 = selection._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) {
274 for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {
275 if (node = group0[i] || group1[i]) {
276 merge[i] = node;
277 }
278 }
279 }
280
281 for (; j < m0; ++j) {
282 merges[j] = groups0[j];
283 }
284
285 return new Selection(merges, this._parents);
286 }
287
288 function selection_order() {
289
290 for (var groups = this._groups, j = -1, m = groups.length; ++j < m;) {
291 for (var group = groups[j], i = group.length - 1, next = group[i], node; --i >= 0;) {
292 if (node = group[i]) {
293 if (next && node.compareDocumentPosition(next) ^ 4) next.parentNode.insertBefore(node, next);
294 next = node;
295 }
296 }
297 }
298
299 return this;
300 }
301
302 function selection_sort(compare) {
303 if (!compare) compare = ascending;
304
305 function compareNode(a, b) {
306 return a && b ? compare(a.__data__, b.__data__) : !a - !b;
307 }
308
309 for (var groups = this._groups, m = groups.length, sortgroups = new Array(m), j = 0; j < m; ++j) {
310 for (var group = groups[j], n = group.length, sortgroup = sortgroups[j] = new Array(n), node, i = 0; i < n; ++i) {
311 if (node = group[i]) {
312 sortgroup[i] = node;
313 }
314 }
315 sortgroup.sort(compareNode);
316 }
317
318 return new Selection(sortgroups, this._parents).order();
319 }
320
321 function ascending(a, b) {
322 return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
323 }
324
325 function selection_call() {
326 var callback = arguments[0];
327 arguments[0] = this;
328 callback.apply(null, arguments);
329 return this;
330 }
331
332 function selection_nodes() {
333 var nodes = new Array(this.size()), i = -1;
334 this.each(function() { nodes[++i] = this; });
335 return nodes;
336 }
337
338 function selection_node() {
339
340 for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
341 for (var group = groups[j], i = 0, n = group.length; i < n; ++i) {
342 var node = group[i];
343 if (node) return node;
344 }
345 }
346
347 return null;
348 }
349
350 function selection_size() {
351 var size = 0;
352 this.each(function() { ++size; });
353 return size;
354 }
355
356 function selection_empty() {
357 return !this.node();
358 }
359
360 function selection_each(callback) {
361
362 for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
363 for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) {
364 if (node = group[i]) callback.call(node, node.__data__, i, group);
365 }
366 }
367
368 return this;
369 }
370
371 function attrRemove(name) {
372 return function() {
373 this.removeAttribute(name);
374 };
375 }
376
377 function attrRemoveNS(fullname) {
378 return function() {
379 this.removeAttributeNS(fullname.space, fullname.local);
380 };
381 }
382
383 function attrConstant(name, value) {
384 return function() {
385 this.setAttribute(name, value);
386 };
387 }
388
389 function attrConstantNS(fullname, value) {
390 return function() {
391 this.setAttributeNS(fullname.space, fullname.local, value);
392 };
393 }
394
395 function attrFunction(name, value) {
396 return function() {
397 var v = value.apply(this, arguments);
398 if (v == null) this.removeAttribute(name);
399 else this.setAttribute(name, v);
400 };
401 }
402
403 function attrFunctionNS(fullname, value) {
404 return function() {
405 var v = value.apply(this, arguments);
406 if (v == null) this.removeAttributeNS(fullname.space, fullname.local);
407 else this.setAttributeNS(fullname.space, fullname.local, v);
408 };
409 }
410
411 function selection_attr(name, value) {
412 var fullname = namespace(name);
413
414 if (arguments.length < 2) {
415 var node = this.node();
416 return fullname.local
417 ? node.getAttributeNS(fullname.space, fullname.local)
418 : node.getAttribute(fullname);
419 }
420
421 return this.each((value == null
422 ? (fullname.local ? attrRemoveNS : attrRemove) : (typeof value === "function"
423 ? (fullname.local ? attrFunctionNS : attrFunction)
424 : (fullname.local ? attrConstantNS : attrConstant)))(fullname, value));
425 }
426
427 function defaultView(node) {
428 return (node.ownerDocument && node.ownerDocument.defaultView) // node is a Node
429 || (node.document && node) // node is a Window
430 || node.defaultView; // node is a Document
431 }
432
433 function styleRemove(name) {
434 return function() {
435 this.style.removeProperty(name);
436 };
437 }
438
439 function styleConstant(name, value, priority) {
440 return function() {
441 this.style.setProperty(name, value, priority);
442 };
443 }
444
445 function styleFunction(name, value, priority) {
446 return function() {
447 var v = value.apply(this, arguments);
448 if (v == null) this.style.removeProperty(name);
449 else this.style.setProperty(name, v, priority);
450 };
451 }
452
453 function selection_style(name, value, priority) {
454 return arguments.length > 1
455 ? this.each((value == null
456 ? styleRemove : typeof value === "function"
457 ? styleFunction
458 : styleConstant)(name, value, priority == null ? "" : priority))
459 : styleValue(this.node(), name);
460 }
461
462 function styleValue(node, name) {
463 return node.style.getPropertyValue(name)
464 || defaultView(node).getComputedStyle(node, null).getPropertyValue(name);
465 }
466
467 function propertyRemove(name) {
468 return function() {
469 delete this[name];
470 };
471 }
472
473 function propertyConstant(name, value) {
474 return function() {
475 this[name] = value;
476 };
477 }
478
479 function propertyFunction(name, value) {
480 return function() {
481 var v = value.apply(this, arguments);
482 if (v == null) delete this[name];
483 else this[name] = v;
484 };
485 }
486
487 function selection_property(name, value) {
488 return arguments.length > 1
489 ? this.each((value == null
490 ? propertyRemove : typeof value === "function"
491 ? propertyFunction
492 : propertyConstant)(name, value))
493 : this.node()[name];
494 }
495
496 function classArray(string) {
497 return string.trim().split(/^|\s+/);
498 }
499
500 function classList(node) {
501 return node.classList || new ClassList(node);
502 }
503
504 function ClassList(node) {
505 this._node = node;
506 this._names = classArray(node.getAttribute("class") || "");
507 }
508
509 ClassList.prototype = {
510 add: function(name) {
511 var i = this._names.indexOf(name);
512 if (i < 0) {
513 this._names.push(name);
514 this._node.setAttribute("class", this._names.join(" "));
515 }
516 },
517 remove: function(name) {
518 var i = this._names.indexOf(name);
519 if (i >= 0) {
520 this._names.splice(i, 1);
521 this._node.setAttribute("class", this._names.join(" "));
522 }
523 },
524 contains: function(name) {
525 return this._names.indexOf(name) >= 0;
526 }
527 };
528
529 function classedAdd(node, names) {
530 var list = classList(node), i = -1, n = names.length;
531 while (++i < n) list.add(names[i]);
532 }
533
534 function classedRemove(node, names) {
535 var list = classList(node), i = -1, n = names.length;
536 while (++i < n) list.remove(names[i]);
537 }
538
539 function classedTrue(names) {
540 return function() {
541 classedAdd(this, names);
542 };
543 }
544
545 function classedFalse(names) {
546 return function() {
547 classedRemove(this, names);
548 };
549 }
550
551 function classedFunction(names, value) {
552 return function() {
553 (value.apply(this, arguments) ? classedAdd : classedRemove)(this, names);
554 };
555 }
556
557 function selection_classed(name, value) {
558 var names = classArray(name + "");
559
560 if (arguments.length < 2) {
561 var list = classList(this.node()), i = -1, n = names.length;
562 while (++i < n) if (!list.contains(names[i])) return false;
563 return true;
564 }
565
566 return this.each((typeof value === "function"
567 ? classedFunction : value
568 ? classedTrue
569 : classedFalse)(names, value));
570 }
571
572 function textRemove() {
573 this.textContent = "";
574 }
575
576 function textConstant(value) {
577 return function() {
578 this.textContent = value;
579 };
580 }
581
582 function textFunction(value) {
583 return function() {
584 var v = value.apply(this, arguments);
585 this.textContent = v == null ? "" : v;
586 };
587 }
588
589 function selection_text(value) {
590 return arguments.length
591 ? this.each(value == null
592 ? textRemove : (typeof value === "function"
593 ? textFunction
594 : textConstant)(value))
595 : this.node().textContent;
596 }
597
598 function htmlRemove() {
599 this.innerHTML = "";
600 }
601
602 function htmlConstant(value) {
603 return function() {
604 this.innerHTML = value;
605 };
606 }
607
608 function htmlFunction(value) {
609 return function() {
610 var v = value.apply(this, arguments);
611 this.innerHTML = v == null ? "" : v;
612 };
613 }
614
615 function selection_html(value) {
616 return arguments.length
617 ? this.each(value == null
618 ? htmlRemove : (typeof value === "function"
619 ? htmlFunction
620 : htmlConstant)(value))
621 : this.node().innerHTML;
622 }
623
624 function raise() {
625 if (this.nextSibling) this.parentNode.appendChild(this);
626 }
627
628 function selection_raise() {
629 return this.each(raise);
630 }
631
632 function lower() {
633 if (this.previousSibling) this.parentNode.insertBefore(this, this.parentNode.firstChild);
634 }
635
636 function selection_lower() {
637 return this.each(lower);
638 }
639
640 function selection_append(name) {
641 var create = typeof name === "function" ? name : creator(name);
642 return this.select(function() {
643 return this.appendChild(create.apply(this, arguments));
644 });
645 }
646
647 function constantNull() {
648 return null;
649 }
650
651 function selection_insert(name, before) {
652 var create = typeof name === "function" ? name : creator(name),
653 select = before == null ? constantNull : typeof before === "function" ? before : selector(before);
654 return this.select(function() {
655 return this.insertBefore(create.apply(this, arguments), select.apply(this, arguments) || null);
656 });
657 }
658
659 function remove() {
660 var parent = this.parentNode;
661 if (parent) parent.removeChild(this);
662 }
663
664 function selection_remove() {
665 return this.each(remove);
666 }
667
668 function selection_cloneShallow() {
669 return this.parentNode.insertBefore(this.cloneNode(false), this.nextSibling);
670 }
671
672 function selection_cloneDeep() {
673 return this.parentNode.insertBefore(this.cloneNode(true), this.nextSibling);
674 }
675
676 function selection_clone(deep) {
677 return this.select(deep ? selection_cloneDeep : selection_cloneShallow);
678 }
679
680 function selection_datum(value) {
681 return arguments.length
682 ? this.property("__data__", value)
683 : this.node().__data__;
684 }
685
686 var filterEvents = {};
687
688 var event$1 = null;
689
690 if (typeof document !== "undefined") {
691 var element = document.documentElement;
692 if (!("onmouseenter" in element)) {
693 filterEvents = {mouseenter: "mouseover", mouseleave: "mouseout"};
694 }
695 }
696
697 function filterContextListener(listener, index, group) {
698 listener = contextListener(listener, index, group);
699 return function(event) {
700 var related = event.relatedTarget;
701 if (!related || (related !== this && !(related.compareDocumentPosition(this) & 8))) {
702 listener.call(this, event);
703 }
704 };
705 }
706
707 function contextListener(listener, index, group) {
708 return function(event1) {
709 var event0 = event$1; // Events can be reentrant (e.g., focus).
710 event$1 = event1;
711 try {
712 listener.call(this, this.__data__, index, group);
713 } finally {
714 event$1 = event0;
715 }
716 };
717 }
718
719 function parseTypenames(typenames) {
720 return typenames.trim().split(/^|\s+/).map(function(t) {
721 var name = "", i = t.indexOf(".");
722 if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);
723 return {type: t, name: name};
724 });
725 }
726
727 function onRemove(typename) {
728 return function() {
729 var on = this.__on;
730 if (!on) return;
731 for (var j = 0, i = -1, m = on.length, o; j < m; ++j) {
732 if (o = on[j], (!typename.type || o.type === typename.type) && o.name === typename.name) {
733 this.removeEventListener(o.type, o.listener, o.capture);
734 } else {
735 on[++i] = o;
736 }
737 }
738 if (++i) on.length = i;
739 else delete this.__on;
740 };
741 }
742
743 function onAdd(typename, value, capture) {
744 var wrap = filterEvents.hasOwnProperty(typename.type) ? filterContextListener : contextListener;
745 return function(d, i, group) {
746 var on = this.__on, o, listener = wrap(value, i, group);
747 if (on) for (var j = 0, m = on.length; j < m; ++j) {
748 if ((o = on[j]).type === typename.type && o.name === typename.name) {
749 this.removeEventListener(o.type, o.listener, o.capture);
750 this.addEventListener(o.type, o.listener = listener, o.capture = capture);
751 o.value = value;
752 return;
753 }
754 }
755 this.addEventListener(typename.type, listener, capture);
756 o = {type: typename.type, name: typename.name, value: value, listener: listener, capture: capture};
757 if (!on) this.__on = [o];
758 else on.push(o);
759 };
760 }
761
762 function selection_on(typename, value, capture) {
763 var typenames = parseTypenames(typename + ""), i, n = typenames.length, t;
764
765 if (arguments.length < 2) {
766 var on = this.node().__on;
767 if (on) for (var j = 0, m = on.length, o; j < m; ++j) {
768 for (i = 0, o = on[j]; i < n; ++i) {
769 if ((t = typenames[i]).type === o.type && t.name === o.name) {
770 return o.value;
771 }
772 }
773 }
774 return;
775 }
776
777 on = value ? onAdd : onRemove;
778 if (capture == null) capture = false;
779 for (i = 0; i < n; ++i) this.each(on(typenames[i], value, capture));
780 return this;
781 }
782
783 function dispatchEvent(node, type, params) {
784 var window = defaultView(node),
785 event = window.CustomEvent;
786
787 if (typeof event === "function") {
788 event = new event(type, params);
789 } else {
790 event = window.document.createEvent("Event");
791 if (params) event.initEvent(type, params.bubbles, params.cancelable), event.detail = params.detail;
792 else event.initEvent(type, false, false);
793 }
794
795 node.dispatchEvent(event);
796 }
797
798 function dispatchConstant(type, params) {
799 return function() {
800 return dispatchEvent(this, type, params);
801 };
802 }
803
804 function dispatchFunction(type, params) {
805 return function() {
806 return dispatchEvent(this, type, params.apply(this, arguments));
807 };
808 }
809
810 function selection_dispatch(type, params) {
811 return this.each((typeof params === "function"
812 ? dispatchFunction
813 : dispatchConstant)(type, params));
814 }
815
816 var root = [null];
817
818 function Selection(groups, parents) {
819 this._groups = groups;
820 this._parents = parents;
821 }
822
823 function selection() {
824 return new Selection([[document.documentElement]], root);
825 }
826
827 Selection.prototype = selection.prototype = {
828 constructor: Selection,
829 select: selection_select,
830 selectAll: selection_selectAll,
831 filter: selection_filter,
832 data: selection_data,
833 enter: selection_enter,
834 exit: selection_exit,
835 join: selection_join,
836 merge: selection_merge,
837 order: selection_order,
838 sort: selection_sort,
839 call: selection_call,
840 nodes: selection_nodes,
841 node: selection_node,
842 size: selection_size,
843 empty: selection_empty,
844 each: selection_each,
845 attr: selection_attr,
846 style: selection_style,
847 property: selection_property,
848 classed: selection_classed,
849 text: selection_text,
850 html: selection_html,
851 raise: selection_raise,
852 lower: selection_lower,
853 append: selection_append,
854 insert: selection_insert,
855 remove: selection_remove,
856 clone: selection_clone,
857 datum: selection_datum,
858 on: selection_on,
859 dispatch: selection_dispatch
860 };
861
862 function select(selector) {
863 return typeof selector === "string"
864 ? new Selection([[document.querySelector(selector)]], [document.documentElement])
865 : new Selection([[selector]], root);
866 }
867
868 function sourceEvent() {
869 var current = event$1, source;
870 while (source = current.sourceEvent) current = source;
871 return current;
872 }
873
874 function point(node, event) {
875 var svg = node.ownerSVGElement || node;
876
877 if (svg.createSVGPoint) {
878 var point = svg.createSVGPoint();
879 point.x = event.clientX, point.y = event.clientY;
880 point = point.matrixTransform(node.getScreenCTM().inverse());
881 return [point.x, point.y];
882 }
883
884 var rect = node.getBoundingClientRect();
885 return [event.clientX - rect.left - node.clientLeft, event.clientY - rect.top - node.clientTop];
886 }
887
888 function d3_mouse(node) {
889 var event = sourceEvent();
890 if (event.changedTouches) event = event.changedTouches[0];
891 return point(node, event);
892 }
893
894 function ascending$1(a, b) {
895 return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
896 }
897
898 function sortArray(uarr, sort, parser, formatter) {
899 var sarr = uarr.map(function(d, i) {
900 var parsed_value = parser(d);
901 return {
902 value: d,
903 options_index: i,
904 parsed: parsed_value,
905 display: formatter(d)
906 };
907 });
908
909 if (!sort) return sarr;
910 return sarr.sort(function(a, b) { return ascending$1(a.parsed, b.parsed); });
911 }
912
913 function Stylesheet() {
914 this.declarations = [];
915 return this;
916 }
917
918 Stylesheet.prototype.select = function(selector) {
919 if (!selector) return this;
920 var declaration = new Declaration(selector, this);
921 declaration.parent = this;
922 this.addDeclaration(declaration);
923
924 return declaration;
925 };
926
927 Stylesheet.prototype.addDeclaration = function(declaration) {
928 this.declarations.push(declaration);
929 return this;
930 };
931
932 Stylesheet.prototype.print = function() {
933 var text = "";
934 this.declarations.forEach(function(declaration) {
935 text += declaration.selector + " {\n";
936 declaration.styles.forEach(function(style) {
937 text += "\t" + style[0] + ": " + style[1] + ";\n";
938 });
939 text += "}\n\n";
940 });
941 return text;
942 };
943
944 Stylesheet.prototype.clear = function() {
945 this.declarations = [];
946 return this;
947 };
948
949 function Declaration(selector) {
950 this.selector = selector;
951 this.styles = [];
952 return this;
953 }
954
955 Declaration.prototype.style = function(property, _value) {
956 var value = typeof value_ == "function" ? _value() : _value;
957 if (value !== "" && value !== null && value !== undefined) this.styles.push([property, value]);
958 return this;
959 };
960
961 Declaration.prototype.select = function(selector) {
962 return this.parent.select(this.selector + " " + selector);
963 };
964
965 function createCssString() {
966 var s = new Stylesheet();
967
968 s.select(".fl-controls-container")
969 .style("display", "inline-block")
970 .style("line-height", "1em");
971
972 s.select(".fl-controls-title")
973 .style("display", "inline-block")
974 .style("margin", "0px 0.5em 0px 0px");
975
976 s.select(".fl-controls-container, .fl-controls-container *")
977 .style("box-sizing", "border-box");
978
979 s.select(".slider-holder")
980 .style("margin-bottom", "20px");
981
982 s.select(".fl-controls-slider, .slider-play")
983 .style("pointer-events", "all")
984 .style("display", "inline-block")
985 .style("vertical-align", "middle");
986
987 s.select(".slider-play svg")
988 .style("height", "100%")
989 .style("width", "100%")
990 .style("cursor", " pointer", "");
991
992 s.select(".slider-play:hover")
993 .style("opacity", "0.6");
994
995 s.select(".fl-control-slider")
996 .style("width", "100%")
997 .style("bottom", "0");
998
999 s.select(".fl-control")
1000 .style("position", "relative");
1001
1002 s.select(".fl-control.hidden")
1003 .style("display", "none");
1004
1005 s.select(".fl-control .button")
1006 .style("display", "inline-block")
1007 .style("background", "#eee")
1008 .style("padding", "0.5em")
1009 .style("margin-right", "0.25em")
1010 .style("margin-bottom", "0.25em")
1011 .style("line-height", "1em");
1012
1013 s.select(".fl-control.grouped:not(.hidden)")
1014 .style("display", "inline-table")
1015 .style("table-layout", "fixed")
1016 .select(".button")
1017 .style("display", "table-cell")
1018 .style("margin", "0")
1019 .style("text-align", "center");
1020
1021 s.select(".fl-control .button.selected")
1022 .style("background", "#ddd");
1023
1024 s.select(".fl-control-dropdown")
1025 .style("line-height", "1em")
1026 .select(".list")
1027 .style("display", "none")
1028 .style("position", "absolute")
1029 .style("background-color", "white")
1030 .style("z-index", "100")
1031 .style("border", "1px solid #eee")
1032 .select(".list-item")
1033 .style("text-align", "start")
1034 .style("cursor", "pointer")
1035 .style("padding", "0.5rem");
1036
1037 s.select(".fl-control-dropdown.open .list")
1038 .style("display", "block");
1039
1040 s.select(".fl-control-dropdown .main")
1041 .style("position", "relative")
1042 .style("text-align", "start")
1043 .style("display", "flex")
1044 .style("justify-content", "space-between");
1045
1046 s.select(".fl-control-dropdown .symbol")
1047 .style("float", "right")
1048 .select("div")
1049 .style("border-top-color", "#333333");
1050
1051 return s.print();
1052 }
1053
1054 var css_injected = false;
1055
1056 function injectCSS() {
1057 if (css_injected || typeof document === "undefined") return;
1058
1059 var css_string = createCssString();
1060
1061 var head = document.head || document.getElementsByTagName("head")[0];
1062 var style = document.createElement("style");
1063 style.type = "text/css";
1064 style.className = "flourish-controls";
1065 head.appendChild(style);
1066 if (style.styleSheet) {
1067 style.styleSheet.cssText = css_string;
1068 }
1069 else {
1070 style.appendChild(document.createTextNode(css_string));
1071 }
1072 css_injected = true;
1073 }
1074
1075 var getTextWidth = (function() {
1076 var context = document.createElement("canvas").getContext("2d");
1077 return function(text, font) {
1078 context.font = font || "10px sans-serif";
1079 var metrics = context.measureText(text);
1080 return metrics.width;
1081 };
1082 })();
1083
1084 var remToPx;
1085
1086 function getRemToPx() {
1087 var font_size = parseFloat(getComputedStyle(document.documentElement).fontSize) || 16;
1088 remToPx = function (rem) { return rem * font_size; };
1089 }
1090
1091 var MIN_HEIGHT = 90;
1092 function createDropdown(control_obj, state, container) {
1093 var dropdown_obj = {};
1094 var bounding_container = document.body;
1095
1096 // Add dropdown elements to container
1097 var dropdown = select(container).append("div").attr("class", "fl-control fl-control-dropdown");
1098 var dropdown_node = dropdown.node();
1099 var dropdown_main = dropdown.append("div").attr("class", "main");
1100 var dropdown_current = dropdown_main.append("span").attr("class", "current");
1101 dropdown_main.append("span").attr("class", "symbol").style("width", "10px")
1102 .append("div")
1103 .style("border-left", "5px solid transparent")
1104 .style("border-right", "5px solid transparent")
1105 .style("border-bottom", "5px solid transparent")
1106 .style("border-top-style", "solid")
1107 .style("border-top-width", "5px")
1108 .style("top", "50%")
1109 .style("position", "absolute")
1110 .style("margin-top", "-2.5px");
1111 var dropdown_list = dropdown.append("div").attr("class", "list");
1112
1113 var showDropdownList = function() {
1114 dropdown.classed("open", true);
1115 dropdown_list.style("top", "100%");
1116 dropdown_list.style("bottom", null);
1117 dropdown_list.style("display", null);
1118 dropdown_list.style("overflow", "auto");
1119 var bounding_bb = bounding_container.getBoundingClientRect();
1120 var list_bb = dropdown_list.node().getBoundingClientRect();
1121 var overspill = list_bb.bottom - bounding_bb.bottom;
1122 if (overspill > 0) {
1123 var new_height = bounding_bb.bottom - list_bb.top - 30;
1124 if (new_height > MIN_HEIGHT) dropdown_list.style("max-height", new_height + "px");
1125 else dropdown_list
1126 .style("top", "auto")
1127 .style("bottom", "100%");
1128 }
1129 if (list_bb.right > window.innerWidth) {
1130 dropdown_list.style("right", 0);
1131 }
1132 };
1133
1134 var hideDropdownList = function() {
1135 dropdown.classed("open", false);
1136 dropdown_list.style("right", null);
1137 dropdown_list.style("max-height", null);
1138 dropdown_list.style("display", "none");
1139 };
1140
1141 var toggleDropdownList = function() {
1142 if (dropdown.classed("open")) hideDropdownList();
1143 else showDropdownList();
1144 };
1145
1146 dropdown_main.on("click", function() { toggleDropdownList(); });
1147
1148 var clickHandler = function() {
1149 if (!dropdown.classed("open")) return; // If already closed, nothing to close
1150 var el = event.target;
1151 var parent = el.parentElement;
1152 while (parent) {
1153 if (el === dropdown_node) return; // We've clicked the dropdown, don't close it here
1154 el = parent;
1155 parent = el.parentElement;
1156 }
1157 hideDropdownList(); // Clicked somewhere else, hide the dropdown
1158 };
1159
1160 var showControl = function(longest_text_width) {
1161 var dropdown_width = "100%";
1162 if (state.dropdown_width_mode == "auto") {
1163 dropdown_width = Math.min(longest_text_width + 40, remToPx(20)) + "px";
1164 }
1165 else if (state.dropdown_width_mode == "fixed") {
1166 dropdown_width = remToPx(state.dropdown_width_fixed) + "px";
1167 }
1168 container.style.width = state.dropdown_width_mode == "full" ? dropdown_width : "";
1169 dropdown.style("width", dropdown_width).style("display", state.dropdown_width_mode !== "full" ? "inline-table" : null);
1170 dropdown.select(".main").style("width", dropdown_width);
1171 };
1172
1173 var hideControl = function() {
1174 hideDropdownList();
1175 dropdown.style("display", "none");
1176 };
1177
1178
1179 dropdown_obj.appendedToDOM = function(_bounding_container) {
1180 if (_bounding_container) bounding_container = _bounding_container;
1181 document.querySelector("body").addEventListener("click", clickHandler, false);
1182 return dropdown_obj;
1183 };
1184
1185
1186 dropdown_obj.removedFromDOM = function() {
1187 document.querySelector("body").removeEventListener("click", clickHandler);
1188 return dropdown_obj;
1189 };
1190
1191 dropdown_obj.show = showControl;
1192 dropdown_obj.hide = hideControl;
1193
1194
1195 dropdown_obj.update = function(sorted_options) {
1196 dropdown_list.text("");
1197 var dropdown_font_size = window.getComputedStyle(dropdown.node()).fontSize;
1198 if (!control_obj.n_options || state.control_type !== "dropdown") {
1199 hideControl();
1200 return dropdown_obj;
1201 }
1202
1203 var longest_text = "";
1204 dropdown_list.text("")
1205 .selectAll(".list-item")
1206 .data(sorted_options)
1207 .enter()
1208 .append("div")
1209 .attr("class", "list-item")
1210 .text(function(d) {
1211 if (d.display.length > longest_text.length) longest_text = d.display;
1212 return d.display;
1213 })
1214 .on("click", function(d) {
1215 hideDropdownList();
1216 var i = d.options_index;
1217 if (i === control_obj.index()) return;
1218 control_obj.index(i);
1219 dropdown_current.text(d.display).attr("title", d.display);
1220 control_obj.trigger("change");
1221 });
1222 var longest_text_width = getTextWidth(longest_text, dropdown_font_size + " sans-serif");
1223 var sorted_index = control_obj.getSortedIndex();
1224 var value = sorted_options[sorted_index].display;
1225 dropdown_current.text(value).attr("title", value);
1226
1227 showControl(longest_text_width);
1228
1229 return dropdown_obj;
1230 };
1231
1232
1233 return dropdown_obj;
1234 }
1235
1236 function createButtons(control_obj, state, container) {
1237 var button_obj = {};
1238 var button_container = select(container).append("div").attr("class", "fl-control fl-control-buttons");
1239
1240 var showControl = function() {
1241 button_container.classed("hidden", false);
1242 };
1243
1244 var hideControl = function() {
1245 button_container.classed("hidden", true);
1246 };
1247
1248 button_obj.show = showControl;
1249 button_obj.hide = hideControl;
1250
1251 button_obj.update = function(sorted_options) {
1252 if (!control_obj.n_options || state.control_type !== "buttons") {
1253 hideControl();
1254 return button_obj;
1255 }
1256
1257 var index = control_obj.index();
1258
1259 button_container.classed("grouped", state.button_group);
1260 button_container.classed("fixed-width", state.button_group_width_mode == "fixed" || state.button_group_width_mode == "full");
1261 button_container.style("width", state.button_group && state.button_group_width_mode == "fixed" ? remToPx(state.button_group_width_fixed) + "px" : state.button_group_width_mode == "full" ? "100%" : null);
1262
1263 var clickHandler = function(d) {
1264 var i = d.options_index;
1265 if (i === control_obj.index()) return;
1266 control_obj.index(i);
1267 control_obj.trigger("change");
1268 };
1269
1270 var buttons = button_container.selectAll(".button")
1271 .data(sorted_options, function(d) { return d.display; });
1272
1273 var buttons_enter = buttons.enter()
1274 .append("div")
1275 .attr("class", "button")
1276 .style("cursor", "pointer")
1277 .attr("role", "button")
1278 .attr("tabindex", 0)
1279 .on("click", function(d) { clickHandler(d); })
1280 .on("keyup", function(d) {
1281 if (event$1.code === "Space" || event$1.code === "Enter") {
1282 event$1.preventDefault();
1283 clickHandler(d);
1284 }
1285 });
1286
1287 buttons_enter.append("span")
1288 .text(function(d) { return d.display; });
1289
1290 showControl();
1291
1292 buttons.merge(buttons_enter)
1293 .classed("selected", function(d) { return d.options_index === index; })
1294 .attr("aria-pressed", function(d) { return d.options_index === index; });
1295
1296 buttons.exit().remove();
1297
1298 showControl();
1299 };
1300
1301 return button_obj;
1302 }
1303
1304 var slice = Array.prototype.slice;
1305
1306 function identity(x) {
1307 return x;
1308 }
1309
1310 var top = 1,
1311 right = 2,
1312 bottom = 3,
1313 left = 4,
1314 epsilon = 1e-6;
1315
1316 function translateX(x) {
1317 return "translate(" + (x + 0.5) + ",0)";
1318 }
1319
1320 function translateY(y) {
1321 return "translate(0," + (y + 0.5) + ")";
1322 }
1323
1324 function number(scale) {
1325 return function(d) {
1326 return +scale(d);
1327 };
1328 }
1329
1330 function center(scale) {
1331 var offset = Math.max(0, scale.bandwidth() - 1) / 2; // Adjust for 0.5px offset.
1332 if (scale.round()) offset = Math.round(offset);
1333 return function(d) {
1334 return +scale(d) + offset;
1335 };
1336 }
1337
1338 function entering() {
1339 return !this.__axis;
1340 }
1341
1342 function axis(orient, scale) {
1343 var tickArguments = [],
1344 tickValues = null,
1345 tickFormat = null,
1346 tickSizeInner = 6,
1347 tickSizeOuter = 6,
1348 tickPadding = 3,
1349 k = orient === top || orient === left ? -1 : 1,
1350 x = orient === left || orient === right ? "x" : "y",
1351 transform = orient === top || orient === bottom ? translateX : translateY;
1352
1353 function axis(context) {
1354 var values = tickValues == null ? (scale.ticks ? scale.ticks.apply(scale, tickArguments) : scale.domain()) : tickValues,
1355 format = tickFormat == null ? (scale.tickFormat ? scale.tickFormat.apply(scale, tickArguments) : identity) : tickFormat,
1356 spacing = Math.max(tickSizeInner, 0) + tickPadding,
1357 range = scale.range(),
1358 range0 = +range[0] + 0.5,
1359 range1 = +range[range.length - 1] + 0.5,
1360 position = (scale.bandwidth ? center : number)(scale.copy()),
1361 selection = context.selection ? context.selection() : context,
1362 path = selection.selectAll(".domain").data([null]),
1363 tick = selection.selectAll(".tick").data(values, scale).order(),
1364 tickExit = tick.exit(),
1365 tickEnter = tick.enter().append("g").attr("class", "tick"),
1366 line = tick.select("line"),
1367 text = tick.select("text");
1368
1369 path = path.merge(path.enter().insert("path", ".tick")
1370 .attr("class", "domain")
1371 .attr("stroke", "currentColor"));
1372
1373 tick = tick.merge(tickEnter);
1374
1375 line = line.merge(tickEnter.append("line")
1376 .attr("stroke", "currentColor")
1377 .attr(x + "2", k * tickSizeInner));
1378
1379 text = text.merge(tickEnter.append("text")
1380 .attr("fill", "currentColor")
1381 .attr(x, k * spacing)
1382 .attr("dy", orient === top ? "0em" : orient === bottom ? "0.71em" : "0.32em"));
1383
1384 if (context !== selection) {
1385 path = path.transition(context);
1386 tick = tick.transition(context);
1387 line = line.transition(context);
1388 text = text.transition(context);
1389
1390 tickExit = tickExit.transition(context)
1391 .attr("opacity", epsilon)
1392 .attr("transform", function(d) { return isFinite(d = position(d)) ? transform(d) : this.getAttribute("transform"); });
1393
1394 tickEnter
1395 .attr("opacity", epsilon)
1396 .attr("transform", function(d) { var p = this.parentNode.__axis; return transform(p && isFinite(p = p(d)) ? p : position(d)); });
1397 }
1398
1399 tickExit.remove();
1400
1401 path
1402 .attr("d", orient === left || orient == right
1403 ? (tickSizeOuter ? "M" + k * tickSizeOuter + "," + range0 + "H0.5V" + range1 + "H" + k * tickSizeOuter : "M0.5," + range0 + "V" + range1)
1404 : (tickSizeOuter ? "M" + range0 + "," + k * tickSizeOuter + "V0.5H" + range1 + "V" + k * tickSizeOuter : "M" + range0 + ",0.5H" + range1));
1405
1406 tick
1407 .attr("opacity", 1)
1408 .attr("transform", function(d) { return transform(position(d)); });
1409
1410 line
1411 .attr(x + "2", k * tickSizeInner);
1412
1413 text
1414 .attr(x, k * spacing)
1415 .text(format);
1416
1417 selection.filter(entering)
1418 .attr("fill", "none")
1419 .attr("font-size", 10)
1420 .attr("font-family", "sans-serif")
1421 .attr("text-anchor", orient === right ? "start" : orient === left ? "end" : "middle");
1422
1423 selection
1424 .each(function() { this.__axis = position; });
1425 }
1426
1427 axis.scale = function(_) {
1428 return arguments.length ? (scale = _, axis) : scale;
1429 };
1430
1431 axis.ticks = function() {
1432 return tickArguments = slice.call(arguments), axis;
1433 };
1434
1435 axis.tickArguments = function(_) {
1436 return arguments.length ? (tickArguments = _ == null ? [] : slice.call(_), axis) : tickArguments.slice();
1437 };
1438
1439 axis.tickValues = function(_) {
1440 return arguments.length ? (tickValues = _ == null ? null : slice.call(_), axis) : tickValues && tickValues.slice();
1441 };
1442
1443 axis.tickFormat = function(_) {
1444 return arguments.length ? (tickFormat = _, axis) : tickFormat;
1445 };
1446
1447 axis.tickSize = function(_) {
1448 return arguments.length ? (tickSizeInner = tickSizeOuter = +_, axis) : tickSizeInner;
1449 };
1450
1451 axis.tickSizeInner = function(_) {
1452 return arguments.length ? (tickSizeInner = +_, axis) : tickSizeInner;
1453 };
1454
1455 axis.tickSizeOuter = function(_) {
1456 return arguments.length ? (tickSizeOuter = +_, axis) : tickSizeOuter;
1457 };
1458
1459 axis.tickPadding = function(_) {
1460 return arguments.length ? (tickPadding = +_, axis) : tickPadding;
1461 };
1462
1463 return axis;
1464 }
1465
1466 function axisBottom(scale) {
1467 return axis(bottom, scale);
1468 }
1469
1470 function ascending$2(a, b) {
1471 return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
1472 }
1473
1474 function bisector(compare) {
1475 if (compare.length === 1) compare = ascendingComparator(compare);
1476 return {
1477 left: function(a, x, lo, hi) {
1478 if (lo == null) lo = 0;
1479 if (hi == null) hi = a.length;
1480 while (lo < hi) {
1481 var mid = lo + hi >>> 1;
1482 if (compare(a[mid], x) < 0) lo = mid + 1;
1483 else hi = mid;
1484 }
1485 return lo;
1486 },
1487 right: function(a, x, lo, hi) {
1488 if (lo == null) lo = 0;
1489 if (hi == null) hi = a.length;
1490 while (lo < hi) {
1491 var mid = lo + hi >>> 1;
1492 if (compare(a[mid], x) > 0) hi = mid;
1493 else lo = mid + 1;
1494 }
1495 return lo;
1496 }
1497 };
1498 }
1499
1500 function ascendingComparator(f) {
1501 return function(d, x) {
1502 return ascending$2(f(d), x);
1503 };
1504 }
1505
1506 var ascendingBisect = bisector(ascending$2);
1507 var bisectRight = ascendingBisect.right;
1508
1509 var e10 = Math.sqrt(50),
1510 e5 = Math.sqrt(10),
1511 e2 = Math.sqrt(2);
1512
1513 function ticks(start, stop, count) {
1514 var reverse,
1515 i = -1,
1516 n,
1517 ticks,
1518 step;
1519
1520 stop = +stop, start = +start, count = +count;
1521 if (start === stop && count > 0) return [start];
1522 if (reverse = stop < start) n = start, start = stop, stop = n;
1523 if ((step = tickIncrement(start, stop, count)) === 0 || !isFinite(step)) return [];
1524
1525 if (step > 0) {
1526 start = Math.ceil(start / step);
1527 stop = Math.floor(stop / step);
1528 ticks = new Array(n = Math.ceil(stop - start + 1));
1529 while (++i < n) ticks[i] = (start + i) * step;
1530 } else {
1531 start = Math.floor(start * step);
1532 stop = Math.ceil(stop * step);
1533 ticks = new Array(n = Math.ceil(start - stop + 1));
1534 while (++i < n) ticks[i] = (start - i) / step;
1535 }
1536
1537 if (reverse) ticks.reverse();
1538
1539 return ticks;
1540 }
1541
1542 function tickIncrement(start, stop, count) {
1543 var step = (stop - start) / Math.max(0, count),
1544 power = Math.floor(Math.log(step) / Math.LN10),
1545 error = step / Math.pow(10, power);
1546 return power >= 0
1547 ? (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1) * Math.pow(10, power)
1548 : -Math.pow(10, -power) / (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1);
1549 }
1550
1551 function tickStep(start, stop, count) {
1552 var step0 = Math.abs(stop - start) / Math.max(0, count),
1553 step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)),
1554 error = step0 / step1;
1555 if (error >= e10) step1 *= 10;
1556 else if (error >= e5) step1 *= 5;
1557 else if (error >= e2) step1 *= 2;
1558 return stop < start ? -step1 : step1;
1559 }
1560
1561 function define(constructor, factory, prototype) {
1562 constructor.prototype = factory.prototype = prototype;
1563 prototype.constructor = constructor;
1564 }
1565
1566 function extend(parent, definition) {
1567 var prototype = Object.create(parent.prototype);
1568 for (var key in definition) prototype[key] = definition[key];
1569 return prototype;
1570 }
1571
1572 function Color() {}
1573
1574 var darker = 0.7;
1575 var brighter = 1 / darker;
1576
1577 var reI = "\\s*([+-]?\\d+)\\s*",
1578 reN = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*",
1579 reP = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*",
1580 reHex3 = /^#([0-9a-f]{3})$/,
1581 reHex6 = /^#([0-9a-f]{6})$/,
1582 reRgbInteger = new RegExp("^rgb\\(" + [reI, reI, reI] + "\\)$"),
1583 reRgbPercent = new RegExp("^rgb\\(" + [reP, reP, reP] + "\\)$"),
1584 reRgbaInteger = new RegExp("^rgba\\(" + [reI, reI, reI, reN] + "\\)$"),
1585 reRgbaPercent = new RegExp("^rgba\\(" + [reP, reP, reP, reN] + "\\)$"),
1586 reHslPercent = new RegExp("^hsl\\(" + [reN, reP, reP] + "\\)$"),
1587 reHslaPercent = new RegExp("^hsla\\(" + [reN, reP, reP, reN] + "\\)$");
1588
1589 var named = {
1590 aliceblue: 0xf0f8ff,
1591 antiquewhite: 0xfaebd7,
1592 aqua: 0x00ffff,
1593 aquamarine: 0x7fffd4,
1594 azure: 0xf0ffff,
1595 beige: 0xf5f5dc,
1596 bisque: 0xffe4c4,
1597 black: 0x000000,
1598 blanchedalmond: 0xffebcd,
1599 blue: 0x0000ff,
1600 blueviolet: 0x8a2be2,
1601 brown: 0xa52a2a,
1602 burlywood: 0xdeb887,
1603 cadetblue: 0x5f9ea0,
1604 chartreuse: 0x7fff00,
1605 chocolate: 0xd2691e,
1606 coral: 0xff7f50,
1607 cornflowerblue: 0x6495ed,
1608 cornsilk: 0xfff8dc,
1609 crimson: 0xdc143c,
1610 cyan: 0x00ffff,
1611 darkblue: 0x00008b,
1612 darkcyan: 0x008b8b,
1613 darkgoldenrod: 0xb8860b,
1614 darkgray: 0xa9a9a9,
1615 darkgreen: 0x006400,
1616 darkgrey: 0xa9a9a9,
1617 darkkhaki: 0xbdb76b,
1618 darkmagenta: 0x8b008b,
1619 darkolivegreen: 0x556b2f,
1620 darkorange: 0xff8c00,
1621 darkorchid: 0x9932cc,
1622 darkred: 0x8b0000,
1623 darksalmon: 0xe9967a,
1624 darkseagreen: 0x8fbc8f,
1625 darkslateblue: 0x483d8b,
1626 darkslategray: 0x2f4f4f,
1627 darkslategrey: 0x2f4f4f,
1628 darkturquoise: 0x00ced1,
1629 darkviolet: 0x9400d3,
1630 deeppink: 0xff1493,
1631 deepskyblue: 0x00bfff,
1632 dimgray: 0x696969,
1633 dimgrey: 0x696969,
1634 dodgerblue: 0x1e90ff,
1635 firebrick: 0xb22222,
1636 floralwhite: 0xfffaf0,
1637 forestgreen: 0x228b22,
1638 fuchsia: 0xff00ff,
1639 gainsboro: 0xdcdcdc,
1640 ghostwhite: 0xf8f8ff,
1641 gold: 0xffd700,
1642 goldenrod: 0xdaa520,
1643 gray: 0x808080,
1644 green: 0x008000,
1645 greenyellow: 0xadff2f,
1646 grey: 0x808080,
1647 honeydew: 0xf0fff0,
1648 hotpink: 0xff69b4,
1649 indianred: 0xcd5c5c,
1650 indigo: 0x4b0082,
1651 ivory: 0xfffff0,
1652 khaki: 0xf0e68c,
1653 lavender: 0xe6e6fa,
1654 lavenderblush: 0xfff0f5,
1655 lawngreen: 0x7cfc00,
1656 lemonchiffon: 0xfffacd,
1657 lightblue: 0xadd8e6,
1658 lightcoral: 0xf08080,
1659 lightcyan: 0xe0ffff,
1660 lightgoldenrodyellow: 0xfafad2,
1661 lightgray: 0xd3d3d3,
1662 lightgreen: 0x90ee90,
1663 lightgrey: 0xd3d3d3,
1664 lightpink: 0xffb6c1,
1665 lightsalmon: 0xffa07a,
1666 lightseagreen: 0x20b2aa,
1667 lightskyblue: 0x87cefa,
1668 lightslategray: 0x778899,
1669 lightslategrey: 0x778899,
1670 lightsteelblue: 0xb0c4de,
1671 lightyellow: 0xffffe0,
1672 lime: 0x00ff00,
1673 limegreen: 0x32cd32,
1674 linen: 0xfaf0e6,
1675 magenta: 0xff00ff,
1676 maroon: 0x800000,
1677 mediumaquamarine: 0x66cdaa,
1678 mediumblue: 0x0000cd,
1679 mediumorchid: 0xba55d3,
1680 mediumpurple: 0x9370db,
1681 mediumseagreen: 0x3cb371,
1682 mediumslateblue: 0x7b68ee,
1683 mediumspringgreen: 0x00fa9a,
1684 mediumturquoise: 0x48d1cc,
1685 mediumvioletred: 0xc71585,
1686 midnightblue: 0x191970,
1687 mintcream: 0xf5fffa,
1688 mistyrose: 0xffe4e1,
1689 moccasin: 0xffe4b5,
1690 navajowhite: 0xffdead,
1691 navy: 0x000080,
1692 oldlace: 0xfdf5e6,
1693 olive: 0x808000,
1694 olivedrab: 0x6b8e23,
1695 orange: 0xffa500,
1696 orangered: 0xff4500,
1697 orchid: 0xda70d6,
1698 palegoldenrod: 0xeee8aa,
1699 palegreen: 0x98fb98,
1700 paleturquoise: 0xafeeee,
1701 palevioletred: 0xdb7093,
1702 papayawhip: 0xffefd5,
1703 peachpuff: 0xffdab9,
1704 peru: 0xcd853f,
1705 pink: 0xffc0cb,
1706 plum: 0xdda0dd,
1707 powderblue: 0xb0e0e6,
1708 purple: 0x800080,
1709 rebeccapurple: 0x663399,
1710 red: 0xff0000,
1711 rosybrown: 0xbc8f8f,
1712 royalblue: 0x4169e1,
1713 saddlebrown: 0x8b4513,
1714 salmon: 0xfa8072,
1715 sandybrown: 0xf4a460,
1716 seagreen: 0x2e8b57,
1717 seashell: 0xfff5ee,
1718 sienna: 0xa0522d,
1719 silver: 0xc0c0c0,
1720 skyblue: 0x87ceeb,
1721 slateblue: 0x6a5acd,
1722 slategray: 0x708090,
1723 slategrey: 0x708090,
1724 snow: 0xfffafa,
1725 springgreen: 0x00ff7f,
1726 steelblue: 0x4682b4,
1727 tan: 0xd2b48c,
1728 teal: 0x008080,
1729 thistle: 0xd8bfd8,
1730 tomato: 0xff6347,
1731 turquoise: 0x40e0d0,
1732 violet: 0xee82ee,
1733 wheat: 0xf5deb3,
1734 white: 0xffffff,
1735 whitesmoke: 0xf5f5f5,
1736 yellow: 0xffff00,
1737 yellowgreen: 0x9acd32
1738 };
1739
1740 define(Color, color, {
1741 displayable: function() {
1742 return this.rgb().displayable();
1743 },
1744 hex: function() {
1745 return this.rgb().hex();
1746 },
1747 toString: function() {
1748 return this.rgb() + "";
1749 }
1750 });
1751
1752 function color(format) {
1753 var m;
1754 format = (format + "").trim().toLowerCase();
1755 return (m = reHex3.exec(format)) ? (m = parseInt(m[1], 16), new Rgb((m >> 8 & 0xf) | (m >> 4 & 0x0f0), (m >> 4 & 0xf) | (m & 0xf0), ((m & 0xf) << 4) | (m & 0xf), 1)) // #f00
1756 : (m = reHex6.exec(format)) ? rgbn(parseInt(m[1], 16)) // #ff0000
1757 : (m = reRgbInteger.exec(format)) ? new Rgb(m[1], m[2], m[3], 1) // rgb(255, 0, 0)
1758 : (m = reRgbPercent.exec(format)) ? new Rgb(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, 1) // rgb(100%, 0%, 0%)
1759 : (m = reRgbaInteger.exec(format)) ? rgba(m[1], m[2], m[3], m[4]) // rgba(255, 0, 0, 1)
1760 : (m = reRgbaPercent.exec(format)) ? rgba(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, m[4]) // rgb(100%, 0%, 0%, 1)
1761 : (m = reHslPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, 1) // hsl(120, 50%, 50%)
1762 : (m = reHslaPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) // hsla(120, 50%, 50%, 1)
1763 : named.hasOwnProperty(format) ? rgbn(named[format])
1764 : format === "transparent" ? new Rgb(NaN, NaN, NaN, 0)
1765 : null;
1766 }
1767
1768 function rgbn(n) {
1769 return new Rgb(n >> 16 & 0xff, n >> 8 & 0xff, n & 0xff, 1);
1770 }
1771
1772 function rgba(r, g, b, a) {
1773 if (a <= 0) r = g = b = NaN;
1774 return new Rgb(r, g, b, a);
1775 }
1776
1777 function rgbConvert(o) {
1778 if (!(o instanceof Color)) o = color(o);
1779 if (!o) return new Rgb;
1780 o = o.rgb();
1781 return new Rgb(o.r, o.g, o.b, o.opacity);
1782 }
1783
1784 function rgb(r, g, b, opacity) {
1785 return arguments.length === 1 ? rgbConvert(r) : new Rgb(r, g, b, opacity == null ? 1 : opacity);
1786 }
1787
1788 function Rgb(r, g, b, opacity) {
1789 this.r = +r;
1790 this.g = +g;
1791 this.b = +b;
1792 this.opacity = +opacity;
1793 }
1794
1795 define(Rgb, rgb, extend(Color, {
1796 brighter: function(k) {
1797 k = k == null ? brighter : Math.pow(brighter, k);
1798 return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
1799 },
1800 darker: function(k) {
1801 k = k == null ? darker : Math.pow(darker, k);
1802 return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
1803 },
1804 rgb: function() {
1805 return this;
1806 },
1807 displayable: function() {
1808 return (0 <= this.r && this.r <= 255)
1809 && (0 <= this.g && this.g <= 255)
1810 && (0 <= this.b && this.b <= 255)
1811 && (0 <= this.opacity && this.opacity <= 1);
1812 },
1813 hex: function() {
1814 return "#" + hex(this.r) + hex(this.g) + hex(this.b);
1815 },
1816 toString: function() {
1817 var a = this.opacity; a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));
1818 return (a === 1 ? "rgb(" : "rgba(")
1819 + Math.max(0, Math.min(255, Math.round(this.r) || 0)) + ", "
1820 + Math.max(0, Math.min(255, Math.round(this.g) || 0)) + ", "
1821 + Math.max(0, Math.min(255, Math.round(this.b) || 0))
1822 + (a === 1 ? ")" : ", " + a + ")");
1823 }
1824 }));
1825
1826 function hex(value) {
1827 value = Math.max(0, Math.min(255, Math.round(value) || 0));
1828 return (value < 16 ? "0" : "") + value.toString(16);
1829 }
1830
1831 function hsla(h, s, l, a) {
1832 if (a <= 0) h = s = l = NaN;
1833 else if (l <= 0 || l >= 1) h = s = NaN;
1834 else if (s <= 0) h = NaN;
1835 return new Hsl(h, s, l, a);
1836 }
1837
1838 function hslConvert(o) {
1839 if (o instanceof Hsl) return new Hsl(o.h, o.s, o.l, o.opacity);
1840 if (!(o instanceof Color)) o = color(o);
1841 if (!o) return new Hsl;
1842 if (o instanceof Hsl) return o;
1843 o = o.rgb();
1844 var r = o.r / 255,
1845 g = o.g / 255,
1846 b = o.b / 255,
1847 min = Math.min(r, g, b),
1848 max = Math.max(r, g, b),
1849 h = NaN,
1850 s = max - min,
1851 l = (max + min) / 2;
1852 if (s) {
1853 if (r === max) h = (g - b) / s + (g < b) * 6;
1854 else if (g === max) h = (b - r) / s + 2;
1855 else h = (r - g) / s + 4;
1856 s /= l < 0.5 ? max + min : 2 - max - min;
1857 h *= 60;
1858 } else {
1859 s = l > 0 && l < 1 ? 0 : h;
1860 }
1861 return new Hsl(h, s, l, o.opacity);
1862 }
1863
1864 function hsl(h, s, l, opacity) {
1865 return arguments.length === 1 ? hslConvert(h) : new Hsl(h, s, l, opacity == null ? 1 : opacity);
1866 }
1867
1868 function Hsl(h, s, l, opacity) {
1869 this.h = +h;
1870 this.s = +s;
1871 this.l = +l;
1872 this.opacity = +opacity;
1873 }
1874
1875 define(Hsl, hsl, extend(Color, {
1876 brighter: function(k) {
1877 k = k == null ? brighter : Math.pow(brighter, k);
1878 return new Hsl(this.h, this.s, this.l * k, this.opacity);
1879 },
1880 darker: function(k) {
1881 k = k == null ? darker : Math.pow(darker, k);
1882 return new Hsl(this.h, this.s, this.l * k, this.opacity);
1883 },
1884 rgb: function() {
1885 var h = this.h % 360 + (this.h < 0) * 360,
1886 s = isNaN(h) || isNaN(this.s) ? 0 : this.s,
1887 l = this.l,
1888 m2 = l + (l < 0.5 ? l : 1 - l) * s,
1889 m1 = 2 * l - m2;
1890 return new Rgb(
1891 hsl2rgb(h >= 240 ? h - 240 : h + 120, m1, m2),
1892 hsl2rgb(h, m1, m2),
1893 hsl2rgb(h < 120 ? h + 240 : h - 120, m1, m2),
1894 this.opacity
1895 );
1896 },
1897 displayable: function() {
1898 return (0 <= this.s && this.s <= 1 || isNaN(this.s))
1899 && (0 <= this.l && this.l <= 1)
1900 && (0 <= this.opacity && this.opacity <= 1);
1901 }
1902 }));
1903
1904 /* From FvD 13.37, CSS Color Module Level 3 */
1905 function hsl2rgb(h, m1, m2) {
1906 return (h < 60 ? m1 + (m2 - m1) * h / 60
1907 : h < 180 ? m2
1908 : h < 240 ? m1 + (m2 - m1) * (240 - h) / 60
1909 : m1) * 255;
1910 }
1911
1912 var deg2rad = Math.PI / 180;
1913 var rad2deg = 180 / Math.PI;
1914
1915 // https://beta.observablehq.com/@mbostock/lab-and-rgb
1916 var K = 18,
1917 Xn = 0.96422,
1918 Yn = 1,
1919 Zn = 0.82521,
1920 t0 = 4 / 29,
1921 t1 = 6 / 29,
1922 t2 = 3 * t1 * t1,
1923 t3 = t1 * t1 * t1;
1924
1925 function labConvert(o) {
1926 if (o instanceof Lab) return new Lab(o.l, o.a, o.b, o.opacity);
1927 if (o instanceof Hcl) {
1928 if (isNaN(o.h)) return new Lab(o.l, 0, 0, o.opacity);
1929 var h = o.h * deg2rad;
1930 return new Lab(o.l, Math.cos(h) * o.c, Math.sin(h) * o.c, o.opacity);
1931 }
1932 if (!(o instanceof Rgb)) o = rgbConvert(o);
1933 var r = rgb2lrgb(o.r),
1934 g = rgb2lrgb(o.g),
1935 b = rgb2lrgb(o.b),
1936 y = xyz2lab((0.2225045 * r + 0.7168786 * g + 0.0606169 * b) / Yn), x, z;
1937 if (r === g && g === b) x = z = y; else {
1938 x = xyz2lab((0.4360747 * r + 0.3850649 * g + 0.1430804 * b) / Xn);
1939 z = xyz2lab((0.0139322 * r + 0.0971045 * g + 0.7141733 * b) / Zn);
1940 }
1941 return new Lab(116 * y - 16, 500 * (x - y), 200 * (y - z), o.opacity);
1942 }
1943
1944 function lab(l, a, b, opacity) {
1945 return arguments.length === 1 ? labConvert(l) : new Lab(l, a, b, opacity == null ? 1 : opacity);
1946 }
1947
1948 function Lab(l, a, b, opacity) {
1949 this.l = +l;
1950 this.a = +a;
1951 this.b = +b;
1952 this.opacity = +opacity;
1953 }
1954
1955 define(Lab, lab, extend(Color, {
1956 brighter: function(k) {
1957 return new Lab(this.l + K * (k == null ? 1 : k), this.a, this.b, this.opacity);
1958 },
1959 darker: function(k) {
1960 return new Lab(this.l - K * (k == null ? 1 : k), this.a, this.b, this.opacity);
1961 },
1962 rgb: function() {
1963 var y = (this.l + 16) / 116,
1964 x = isNaN(this.a) ? y : y + this.a / 500,
1965 z = isNaN(this.b) ? y : y - this.b / 200;
1966 x = Xn * lab2xyz(x);
1967 y = Yn * lab2xyz(y);
1968 z = Zn * lab2xyz(z);
1969 return new Rgb(
1970 lrgb2rgb( 3.1338561 * x - 1.6168667 * y - 0.4906146 * z),
1971 lrgb2rgb(-0.9787684 * x + 1.9161415 * y + 0.0334540 * z),
1972 lrgb2rgb( 0.0719453 * x - 0.2289914 * y + 1.4052427 * z),
1973 this.opacity
1974 );
1975 }
1976 }));
1977
1978 function xyz2lab(t) {
1979 return t > t3 ? Math.pow(t, 1 / 3) : t / t2 + t0;
1980 }
1981
1982 function lab2xyz(t) {
1983 return t > t1 ? t * t * t : t2 * (t - t0);
1984 }
1985
1986 function lrgb2rgb(x) {
1987 return 255 * (x <= 0.0031308 ? 12.92 * x : 1.055 * Math.pow(x, 1 / 2.4) - 0.055);
1988 }
1989
1990 function rgb2lrgb(x) {
1991 return (x /= 255) <= 0.04045 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4);
1992 }
1993
1994 function hclConvert(o) {
1995 if (o instanceof Hcl) return new Hcl(o.h, o.c, o.l, o.opacity);
1996 if (!(o instanceof Lab)) o = labConvert(o);
1997 if (o.a === 0 && o.b === 0) return new Hcl(NaN, 0, o.l, o.opacity);
1998 var h = Math.atan2(o.b, o.a) * rad2deg;
1999 return new Hcl(h < 0 ? h + 360 : h, Math.sqrt(o.a * o.a + o.b * o.b), o.l, o.opacity);
2000 }
2001
2002 function hcl(h, c, l, opacity) {
2003 return arguments.length === 1 ? hclConvert(h) : new Hcl(h, c, l, opacity == null ? 1 : opacity);
2004 }
2005
2006 function Hcl(h, c, l, opacity) {
2007 this.h = +h;
2008 this.c = +c;
2009 this.l = +l;
2010 this.opacity = +opacity;
2011 }
2012
2013 define(Hcl, hcl, extend(Color, {
2014 brighter: function(k) {
2015 return new Hcl(this.h, this.c, this.l + K * (k == null ? 1 : k), this.opacity);
2016 },
2017 darker: function(k) {
2018 return new Hcl(this.h, this.c, this.l - K * (k == null ? 1 : k), this.opacity);
2019 },
2020 rgb: function() {
2021 return labConvert(this).rgb();
2022 }
2023 }));
2024
2025 var A = -0.14861,
2026 B = +1.78277,
2027 C = -0.29227,
2028 D = -0.90649,
2029 E = +1.97294,
2030 ED = E * D,
2031 EB = E * B,
2032 BC_DA = B * C - D * A;
2033
2034 function cubehelixConvert(o) {
2035 if (o instanceof Cubehelix) return new Cubehelix(o.h, o.s, o.l, o.opacity);
2036 if (!(o instanceof Rgb)) o = rgbConvert(o);
2037 var r = o.r / 255,
2038 g = o.g / 255,
2039 b = o.b / 255,
2040 l = (BC_DA * b + ED * r - EB * g) / (BC_DA + ED - EB),
2041 bl = b - l,
2042 k = (E * (g - l) - C * bl) / D,
2043 s = Math.sqrt(k * k + bl * bl) / (E * l * (1 - l)), // NaN if l=0 or l=1
2044 h = s ? Math.atan2(k, bl) * rad2deg - 120 : NaN;
2045 return new Cubehelix(h < 0 ? h + 360 : h, s, l, o.opacity);
2046 }
2047
2048 function cubehelix(h, s, l, opacity) {
2049 return arguments.length === 1 ? cubehelixConvert(h) : new Cubehelix(h, s, l, opacity == null ? 1 : opacity);
2050 }
2051
2052 function Cubehelix(h, s, l, opacity) {
2053 this.h = +h;
2054 this.s = +s;
2055 this.l = +l;
2056 this.opacity = +opacity;
2057 }
2058
2059 define(Cubehelix, cubehelix, extend(Color, {
2060 brighter: function(k) {
2061 k = k == null ? brighter : Math.pow(brighter, k);
2062 return new Cubehelix(this.h, this.s, this.l * k, this.opacity);
2063 },
2064 darker: function(k) {
2065 k = k == null ? darker : Math.pow(darker, k);
2066 return new Cubehelix(this.h, this.s, this.l * k, this.opacity);
2067 },
2068 rgb: function() {
2069 var h = isNaN(this.h) ? 0 : (this.h + 120) * deg2rad,
2070 l = +this.l,
2071 a = isNaN(this.s) ? 0 : this.s * l * (1 - l),
2072 cosh = Math.cos(h),
2073 sinh = Math.sin(h);
2074 return new Rgb(
2075 255 * (l + a * (A * cosh + B * sinh)),
2076 255 * (l + a * (C * cosh + D * sinh)),
2077 255 * (l + a * (E * cosh)),
2078 this.opacity
2079 );
2080 }
2081 }));
2082
2083 function constant$1(x) {
2084 return function() {
2085 return x;
2086 };
2087 }
2088
2089 function linear(a, d) {
2090 return function(t) {
2091 return a + t * d;
2092 };
2093 }
2094
2095 function exponential(a, b, y) {
2096 return a = Math.pow(a, y), b = Math.pow(b, y) - a, y = 1 / y, function(t) {
2097 return Math.pow(a + t * b, y);
2098 };
2099 }
2100
2101 function gamma(y) {
2102 return (y = +y) === 1 ? nogamma : function(a, b) {
2103 return b - a ? exponential(a, b, y) : constant$1(isNaN(a) ? b : a);
2104 };
2105 }
2106
2107 function nogamma(a, b) {
2108 var d = b - a;
2109 return d ? linear(a, d) : constant$1(isNaN(a) ? b : a);
2110 }
2111
2112 var rgb$1 = (function rgbGamma(y) {
2113 var color = gamma(y);
2114
2115 function rgb$1(start, end) {
2116 var r = color((start = rgb(start)).r, (end = rgb(end)).r),
2117 g = color(start.g, end.g),
2118 b = color(start.b, end.b),
2119 opacity = nogamma(start.opacity, end.opacity);
2120 return function(t) {
2121 start.r = r(t);
2122 start.g = g(t);
2123 start.b = b(t);
2124 start.opacity = opacity(t);
2125 return start + "";
2126 };
2127 }
2128
2129 rgb$1.gamma = rgbGamma;
2130
2131 return rgb$1;
2132 })(1);
2133
2134 function array(a, b) {
2135 var nb = b ? b.length : 0,
2136 na = a ? Math.min(nb, a.length) : 0,
2137 x = new Array(na),
2138 c = new Array(nb),
2139 i;
2140
2141 for (i = 0; i < na; ++i) x[i] = interpolateValue(a[i], b[i]);
2142 for (; i < nb; ++i) c[i] = b[i];
2143
2144 return function(t) {
2145 for (i = 0; i < na; ++i) c[i] = x[i](t);
2146 return c;
2147 };
2148 }
2149
2150 function date(a, b) {
2151 var d = new Date;
2152 return a = +a, b -= a, function(t) {
2153 return d.setTime(a + b * t), d;
2154 };
2155 }
2156
2157 function reinterpolate(a, b) {
2158 return a = +a, b -= a, function(t) {
2159 return a + b * t;
2160 };
2161 }
2162
2163 function object(a, b) {
2164 var i = {},
2165 c = {},
2166 k;
2167
2168 if (a === null || typeof a !== "object") a = {};
2169 if (b === null || typeof b !== "object") b = {};
2170
2171 for (k in b) {
2172 if (k in a) {
2173 i[k] = interpolateValue(a[k], b[k]);
2174 } else {
2175 c[k] = b[k];
2176 }
2177 }
2178
2179 return function(t) {
2180 for (k in i) c[k] = i[k](t);
2181 return c;
2182 };
2183 }
2184
2185 var reA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,
2186 reB = new RegExp(reA.source, "g");
2187
2188 function zero(b) {
2189 return function() {
2190 return b;
2191 };
2192 }
2193
2194 function one(b) {
2195 return function(t) {
2196 return b(t) + "";
2197 };
2198 }
2199
2200 function string(a, b) {
2201 var bi = reA.lastIndex = reB.lastIndex = 0, // scan index for next number in b
2202 am, // current match in a
2203 bm, // current match in b
2204 bs, // string preceding current number in b, if any
2205 i = -1, // index in s
2206 s = [], // string constants and placeholders
2207 q = []; // number interpolators
2208
2209 // Coerce inputs to strings.
2210 a = a + "", b = b + "";
2211
2212 // Interpolate pairs of numbers in a & b.
2213 while ((am = reA.exec(a))
2214 && (bm = reB.exec(b))) {
2215 if ((bs = bm.index) > bi) { // a string precedes the next number in b
2216 bs = b.slice(bi, bs);
2217 if (s[i]) s[i] += bs; // coalesce with previous string
2218 else s[++i] = bs;
2219 }
2220 if ((am = am[0]) === (bm = bm[0])) { // numbers in a & b match
2221 if (s[i]) s[i] += bm; // coalesce with previous string
2222 else s[++i] = bm;
2223 } else { // interpolate non-matching numbers
2224 s[++i] = null;
2225 q.push({i: i, x: reinterpolate(am, bm)});
2226 }
2227 bi = reB.lastIndex;
2228 }
2229
2230 // Add remains of b.
2231 if (bi < b.length) {
2232 bs = b.slice(bi);
2233 if (s[i]) s[i] += bs; // coalesce with previous string
2234 else s[++i] = bs;
2235 }
2236
2237 // Special optimization for only a single match.
2238 // Otherwise, interpolate each of the numbers and rejoin the string.
2239 return s.length < 2 ? (q[0]
2240 ? one(q[0].x)
2241 : zero(b))
2242 : (b = q.length, function(t) {
2243 for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t);
2244 return s.join("");
2245 });
2246 }
2247
2248 function interpolateValue(a, b) {
2249 var t = typeof b, c;
2250 return b == null || t === "boolean" ? constant$1(b)
2251 : (t === "number" ? reinterpolate
2252 : t === "string" ? ((c = color(b)) ? (b = c, rgb$1) : string)
2253 : b instanceof color ? rgb$1
2254 : b instanceof Date ? date
2255 : Array.isArray(b) ? array
2256 : typeof b.valueOf !== "function" && typeof b.toString !== "function" || isNaN(b) ? object
2257 : reinterpolate)(a, b);
2258 }
2259
2260 function interpolateRound(a, b) {
2261 return a = +a, b -= a, function(t) {
2262 return Math.round(a + b * t);
2263 };
2264 }
2265
2266 var array$1 = Array.prototype;
2267
2268 var map = array$1.map;
2269 var slice$1 = array$1.slice;
2270
2271 function constant$2(x) {
2272 return function() {
2273 return x;
2274 };
2275 }
2276
2277 function number$1(x) {
2278 return +x;
2279 }
2280
2281 var unit = [0, 1];
2282
2283 function deinterpolateLinear(a, b) {
2284 return (b -= (a = +a))
2285 ? function(x) { return (x - a) / b; }
2286 : constant$2(b);
2287 }
2288
2289 function deinterpolateClamp(deinterpolate) {
2290 return function(a, b) {
2291 var d = deinterpolate(a = +a, b = +b);
2292 return function(x) { return x <= a ? 0 : x >= b ? 1 : d(x); };
2293 };
2294 }
2295
2296 function reinterpolateClamp(reinterpolate) {
2297 return function(a, b) {
2298 var r = reinterpolate(a = +a, b = +b);
2299 return function(t) { return t <= 0 ? a : t >= 1 ? b : r(t); };
2300 };
2301 }
2302
2303 function bimap(domain, range, deinterpolate, reinterpolate) {
2304 var d0 = domain[0], d1 = domain[1], r0 = range[0], r1 = range[1];
2305 if (d1 < d0) d0 = deinterpolate(d1, d0), r0 = reinterpolate(r1, r0);
2306 else d0 = deinterpolate(d0, d1), r0 = reinterpolate(r0, r1);
2307 return function(x) { return r0(d0(x)); };
2308 }
2309
2310 function polymap(domain, range, deinterpolate, reinterpolate) {
2311 var j = Math.min(domain.length, range.length) - 1,
2312 d = new Array(j),
2313 r = new Array(j),
2314 i = -1;
2315
2316 // Reverse descending domains.
2317 if (domain[j] < domain[0]) {
2318 domain = domain.slice().reverse();
2319 range = range.slice().reverse();
2320 }
2321
2322 while (++i < j) {
2323 d[i] = deinterpolate(domain[i], domain[i + 1]);
2324 r[i] = reinterpolate(range[i], range[i + 1]);
2325 }
2326
2327 return function(x) {
2328 var i = bisectRight(domain, x, 1, j) - 1;
2329 return r[i](d[i](x));
2330 };
2331 }
2332
2333 function copy(source, target) {
2334 return target
2335 .domain(source.domain())
2336 .range(source.range())
2337 .interpolate(source.interpolate())
2338 .clamp(source.clamp());
2339 }
2340
2341 // deinterpolate(a, b)(x) takes a domain value x in [a,b] and returns the corresponding parameter t in [0,1].
2342 // reinterpolate(a, b)(t) takes a parameter t in [0,1] and returns the corresponding domain value x in [a,b].
2343 function continuous(deinterpolate, reinterpolate) {
2344 var domain = unit,
2345 range = unit,
2346 interpolate = interpolateValue,
2347 clamp = false,
2348 piecewise,
2349 output,
2350 input;
2351
2352 function rescale() {
2353 piecewise = Math.min(domain.length, range.length) > 2 ? polymap : bimap;
2354 output = input = null;
2355 return scale;
2356 }
2357
2358 function scale(x) {
2359 return (output || (output = piecewise(domain, range, clamp ? deinterpolateClamp(deinterpolate) : deinterpolate, interpolate)))(+x);
2360 }
2361
2362 scale.invert = function(y) {
2363 return (input || (input = piecewise(range, domain, deinterpolateLinear, clamp ? reinterpolateClamp(reinterpolate) : reinterpolate)))(+y);
2364 };
2365
2366 scale.domain = function(_) {
2367 return arguments.length ? (domain = map.call(_, number$1), rescale()) : domain.slice();
2368 };
2369
2370 scale.range = function(_) {
2371 return arguments.length ? (range = slice$1.call(_), rescale()) : range.slice();
2372 };
2373
2374 scale.rangeRound = function(_) {
2375 return range = slice$1.call(_), interpolate = interpolateRound, rescale();
2376 };
2377
2378 scale.clamp = function(_) {
2379 return arguments.length ? (clamp = !!_, rescale()) : clamp;
2380 };
2381
2382 scale.interpolate = function(_) {
2383 return arguments.length ? (interpolate = _, rescale()) : interpolate;
2384 };
2385
2386 return rescale();
2387 }
2388
2389 // Computes the decimal coefficient and exponent of the specified number x with
2390 // significant digits p, where x is positive and p is in [1, 21] or undefined.
2391 // For example, formatDecimal(1.23) returns ["123", 0].
2392 function formatDecimal(x, p) {
2393 if ((i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf("e")) < 0) return null; // NaN, ±Infinity
2394 var i, coefficient = x.slice(0, i);
2395
2396 // The string returned by toExponential either has the form \d\.\d+e[-+]\d+
2397 // (e.g., 1.2e+3) or the form \de[-+]\d+ (e.g., 1e+3).
2398 return [
2399 coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient,
2400 +x.slice(i + 1)
2401 ];
2402 }
2403
2404 function exponent(x) {
2405 return x = formatDecimal(Math.abs(x)), x ? x[1] : NaN;
2406 }
2407
2408 function formatGroup(grouping, thousands) {
2409 return function(value, width) {
2410 var i = value.length,
2411 t = [],
2412 j = 0,
2413 g = grouping[0],
2414 length = 0;
2415
2416 while (i > 0 && g > 0) {
2417 if (length + g + 1 > width) g = Math.max(1, width - length);
2418 t.push(value.substring(i -= g, i + g));
2419 if ((length += g + 1) > width) break;
2420 g = grouping[j = (j + 1) % grouping.length];
2421 }
2422
2423 return t.reverse().join(thousands);
2424 };
2425 }
2426
2427 function formatNumerals(numerals) {
2428 return function(value) {
2429 return value.replace(/[0-9]/g, function(i) {
2430 return numerals[+i];
2431 });
2432 };
2433 }
2434
2435 // [[fill]align][sign][symbol][0][width][,][.precision][~][type]
2436 var re = /^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;
2437
2438 function formatSpecifier(specifier) {
2439 return new FormatSpecifier(specifier);
2440 }
2441
2442 formatSpecifier.prototype = FormatSpecifier.prototype; // instanceof
2443
2444 function FormatSpecifier(specifier) {
2445 if (!(match = re.exec(specifier))) throw new Error("invalid format: " + specifier);
2446 var match;
2447 this.fill = match[1] || " ";
2448 this.align = match[2] || ">";
2449 this.sign = match[3] || "-";
2450 this.symbol = match[4] || "";
2451 this.zero = !!match[5];
2452 this.width = match[6] && +match[6];
2453 this.comma = !!match[7];
2454 this.precision = match[8] && +match[8].slice(1);
2455 this.trim = !!match[9];
2456 this.type = match[10] || "";
2457 }
2458
2459 FormatSpecifier.prototype.toString = function() {
2460 return this.fill
2461 + this.align
2462 + this.sign
2463 + this.symbol
2464 + (this.zero ? "0" : "")
2465 + (this.width == null ? "" : Math.max(1, this.width | 0))
2466 + (this.comma ? "," : "")
2467 + (this.precision == null ? "" : "." + Math.max(0, this.precision | 0))
2468 + (this.trim ? "~" : "")
2469 + this.type;
2470 };
2471
2472 // Trims insignificant zeros, e.g., replaces 1.2000k with 1.2k.
2473 function formatTrim(s) {
2474 out: for (var n = s.length, i = 1, i0 = -1, i1; i < n; ++i) {
2475 switch (s[i]) {
2476 case ".": i0 = i1 = i; break;
2477 case "0": if (i0 === 0) i0 = i; i1 = i; break;
2478 default: if (i0 > 0) { if (!+s[i]) break out; i0 = 0; } break;
2479 }
2480 }
2481 return i0 > 0 ? s.slice(0, i0) + s.slice(i1 + 1) : s;
2482 }
2483
2484 var prefixExponent;
2485
2486 function formatPrefixAuto(x, p) {
2487 var d = formatDecimal(x, p);
2488 if (!d) return x + "";
2489 var coefficient = d[0],
2490 exponent = d[1],
2491 i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1,
2492 n = coefficient.length;
2493 return i === n ? coefficient
2494 : i > n ? coefficient + new Array(i - n + 1).join("0")
2495 : i > 0 ? coefficient.slice(0, i) + "." + coefficient.slice(i)
2496 : "0." + new Array(1 - i).join("0") + formatDecimal(x, Math.max(0, p + i - 1))[0]; // less than 1y!
2497 }
2498
2499 function formatRounded(x, p) {
2500 var d = formatDecimal(x, p);
2501 if (!d) return x + "";
2502 var coefficient = d[0],
2503 exponent = d[1];
2504 return exponent < 0 ? "0." + new Array(-exponent).join("0") + coefficient
2505 : coefficient.length > exponent + 1 ? coefficient.slice(0, exponent + 1) + "." + coefficient.slice(exponent + 1)
2506 : coefficient + new Array(exponent - coefficient.length + 2).join("0");
2507 }
2508
2509 var formatTypes = {
2510 "%": function(x, p) { return (x * 100).toFixed(p); },
2511 "b": function(x) { return Math.round(x).toString(2); },
2512 "c": function(x) { return x + ""; },
2513 "d": function(x) { return Math.round(x).toString(10); },
2514 "e": function(x, p) { return x.toExponential(p); },
2515 "f": function(x, p) { return x.toFixed(p); },
2516 "g": function(x, p) { return x.toPrecision(p); },
2517 "o": function(x) { return Math.round(x).toString(8); },
2518 "p": function(x, p) { return formatRounded(x * 100, p); },
2519 "r": formatRounded,
2520 "s": formatPrefixAuto,
2521 "X": function(x) { return Math.round(x).toString(16).toUpperCase(); },
2522 "x": function(x) { return Math.round(x).toString(16); }
2523 };
2524
2525 function identity$1(x) {
2526 return x;
2527 }
2528
2529 var prefixes = ["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"];
2530
2531 function formatLocale(locale) {
2532 var group = locale.grouping && locale.thousands ? formatGroup(locale.grouping, locale.thousands) : identity$1,
2533 currency = locale.currency,
2534 decimal = locale.decimal,
2535 numerals = locale.numerals ? formatNumerals(locale.numerals) : identity$1,
2536 percent = locale.percent || "%";
2537
2538 function newFormat(specifier) {
2539 specifier = formatSpecifier(specifier);
2540
2541 var fill = specifier.fill,
2542 align = specifier.align,
2543 sign = specifier.sign,
2544 symbol = specifier.symbol,
2545 zero = specifier.zero,
2546 width = specifier.width,
2547 comma = specifier.comma,
2548 precision = specifier.precision,
2549 trim = specifier.trim,
2550 type = specifier.type;
2551
2552 // The "n" type is an alias for ",g".
2553 if (type === "n") comma = true, type = "g";
2554
2555 // The "" type, and any invalid type, is an alias for ".12~g".
2556 else if (!formatTypes[type]) precision == null && (precision = 12), trim = true, type = "g";
2557
2558 // If zero fill is specified, padding goes after sign and before digits.
2559 if (zero || (fill === "0" && align === "=")) zero = true, fill = "0", align = "=";
2560
2561 // Compute the prefix and suffix.
2562 // For SI-prefix, the suffix is lazily computed.
2563 var prefix = symbol === "$" ? currency[0] : symbol === "#" && /[boxX]/.test(type) ? "0" + type.toLowerCase() : "",
2564 suffix = symbol === "$" ? currency[1] : /[%p]/.test(type) ? percent : "";
2565
2566 // What format function should we use?
2567 // Is this an integer type?
2568 // Can this type generate exponential notation?
2569 var formatType = formatTypes[type],
2570 maybeSuffix = /[defgprs%]/.test(type);
2571
2572 // Set the default precision if not specified,
2573 // or clamp the specified precision to the supported range.
2574 // For significant precision, it must be in [1, 21].
2575 // For fixed precision, it must be in [0, 20].
2576 precision = precision == null ? 6
2577 : /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision))
2578 : Math.max(0, Math.min(20, precision));
2579
2580 function format(value) {
2581 var valuePrefix = prefix,
2582 valueSuffix = suffix,
2583 i, n, c;
2584
2585 if (type === "c") {
2586 valueSuffix = formatType(value) + valueSuffix;
2587 value = "";
2588 } else {
2589 value = +value;
2590
2591 // Perform the initial formatting.
2592 var valueNegative = value < 0;
2593 value = formatType(Math.abs(value), precision);
2594
2595 // Trim insignificant zeros.
2596 if (trim) value = formatTrim(value);
2597
2598 // If a negative value rounds to zero during formatting, treat as positive.
2599 if (valueNegative && +value === 0) valueNegative = false;
2600
2601 // Compute the prefix and suffix.
2602 valuePrefix = (valueNegative ? (sign === "(" ? sign : "-") : sign === "-" || sign === "(" ? "" : sign) + valuePrefix;
2603 valueSuffix = (type === "s" ? prefixes[8 + prefixExponent / 3] : "") + valueSuffix + (valueNegative && sign === "(" ? ")" : "");
2604
2605 // Break the formatted value into the integer “value” part that can be
2606 // grouped, and fractional or exponential “suffix” part that is not.
2607 if (maybeSuffix) {
2608 i = -1, n = value.length;
2609 while (++i < n) {
2610 if (c = value.charCodeAt(i), 48 > c || c > 57) {
2611 valueSuffix = (c === 46 ? decimal + value.slice(i + 1) : value.slice(i)) + valueSuffix;
2612 value = value.slice(0, i);
2613 break;
2614 }
2615 }
2616 }
2617 }
2618
2619 // If the fill character is not "0", grouping is applied before padding.
2620 if (comma && !zero) value = group(value, Infinity);
2621
2622 // Compute the padding.
2623 var length = valuePrefix.length + value.length + valueSuffix.length,
2624 padding = length < width ? new Array(width - length + 1).join(fill) : "";
2625
2626 // If the fill character is "0", grouping is applied after padding.
2627 if (comma && zero) value = group(padding + value, padding.length ? width - valueSuffix.length : Infinity), padding = "";
2628
2629 // Reconstruct the final output based on the desired alignment.
2630 switch (align) {
2631 case "<": value = valuePrefix + value + valueSuffix + padding; break;
2632 case "=": value = valuePrefix + padding + value + valueSuffix; break;
2633 case "^": value = padding.slice(0, length = padding.length >> 1) + valuePrefix + value + valueSuffix + padding.slice(length); break;
2634 default: value = padding + valuePrefix + value + valueSuffix; break;
2635 }
2636
2637 return numerals(value);
2638 }
2639
2640 format.toString = function() {
2641 return specifier + "";
2642 };
2643
2644 return format;
2645 }
2646
2647 function formatPrefix(specifier, value) {
2648 var f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier)),
2649 e = Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3,
2650 k = Math.pow(10, -e),
2651 prefix = prefixes[8 + e / 3];
2652 return function(value) {
2653 return f(k * value) + prefix;
2654 };
2655 }
2656
2657 return {
2658 format: newFormat,
2659 formatPrefix: formatPrefix
2660 };
2661 }
2662
2663 var locale;
2664 var format;
2665 var formatPrefix;
2666
2667 defaultLocale({
2668 decimal: ".",
2669 thousands: ",",
2670 grouping: [3],
2671 currency: ["$", ""]
2672 });
2673
2674 function defaultLocale(definition) {
2675 locale = formatLocale(definition);
2676 format = locale.format;
2677 formatPrefix = locale.formatPrefix;
2678 return locale;
2679 }
2680
2681 function precisionFixed(step) {
2682 return Math.max(0, -exponent(Math.abs(step)));
2683 }
2684
2685 function precisionPrefix(step, value) {
2686 return Math.max(0, Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3 - exponent(Math.abs(step)));
2687 }
2688
2689 function precisionRound(step, max) {
2690 step = Math.abs(step), max = Math.abs(max) - step;
2691 return Math.max(0, exponent(max) - exponent(step)) + 1;
2692 }
2693
2694 function tickFormat(domain, count, specifier) {
2695 var start = domain[0],
2696 stop = domain[domain.length - 1],
2697 step = tickStep(start, stop, count == null ? 10 : count),
2698 precision;
2699 specifier = formatSpecifier(specifier == null ? ",f" : specifier);
2700 switch (specifier.type) {
2701 case "s": {
2702 var value = Math.max(Math.abs(start), Math.abs(stop));
2703 if (specifier.precision == null && !isNaN(precision = precisionPrefix(step, value))) specifier.precision = precision;
2704 return formatPrefix(specifier, value);
2705 }
2706 case "":
2707 case "e":
2708 case "g":
2709 case "p":
2710 case "r": {
2711 if (specifier.precision == null && !isNaN(precision = precisionRound(step, Math.max(Math.abs(start), Math.abs(stop))))) specifier.precision = precision - (specifier.type === "e");
2712 break;
2713 }
2714 case "f":
2715 case "%": {
2716 if (specifier.precision == null && !isNaN(precision = precisionFixed(step))) specifier.precision = precision - (specifier.type === "%") * 2;
2717 break;
2718 }
2719 }
2720 return format(specifier);
2721 }
2722
2723 function linearish(scale) {
2724 var domain = scale.domain;
2725
2726 scale.ticks = function(count) {
2727 var d = domain();
2728 return ticks(d[0], d[d.length - 1], count == null ? 10 : count);
2729 };
2730
2731 scale.tickFormat = function(count, specifier) {
2732 return tickFormat(domain(), count, specifier);
2733 };
2734
2735 scale.nice = function(count) {
2736 if (count == null) count = 10;
2737
2738 var d = domain(),
2739 i0 = 0,
2740 i1 = d.length - 1,
2741 start = d[i0],
2742 stop = d[i1],
2743 step;
2744
2745 if (stop < start) {
2746 step = start, start = stop, stop = step;
2747 step = i0, i0 = i1, i1 = step;
2748 }
2749
2750 step = tickIncrement(start, stop, count);
2751
2752 if (step > 0) {
2753 start = Math.floor(start / step) * step;
2754 stop = Math.ceil(stop / step) * step;
2755 step = tickIncrement(start, stop, count);
2756 } else if (step < 0) {
2757 start = Math.ceil(start * step) / step;
2758 stop = Math.floor(stop * step) / step;
2759 step = tickIncrement(start, stop, count);
2760 }
2761
2762 if (step > 0) {
2763 d[i0] = Math.floor(start / step) * step;
2764 d[i1] = Math.ceil(stop / step) * step;
2765 domain(d);
2766 } else if (step < 0) {
2767 d[i0] = Math.ceil(start * step) / step;
2768 d[i1] = Math.floor(stop * step) / step;
2769 domain(d);
2770 }
2771
2772 return scale;
2773 };
2774
2775 return scale;
2776 }
2777
2778 function linear$1() {
2779 var scale = continuous(deinterpolateLinear, reinterpolate);
2780
2781 scale.copy = function() {
2782 return copy(scale, linear$1());
2783 };
2784
2785 return linearish(scale);
2786 }
2787
2788 var VERSION = "1.3.2";
2789
2790 function Slider(selector) {
2791 this.container = select(selector);
2792
2793 this._width = null;
2794 this._height = null;
2795
2796 this._handleRadius = 15;
2797 this._channelHeight = 5;
2798 this._channelRadius = null;
2799
2800 this._handleFill = "black";
2801 this._channelFill = "#eee";
2802
2803 this._margin = { top: null, left: null, right: null };
2804
2805 this._domain = [0, 1];
2806 this._value = null;
2807 this._snap = false;
2808
2809 this._scale = null;
2810 this._axis = false;
2811 this._ticks = null;
2812 this._tickFormat = null;
2813 this._tickSize = null;
2814
2815 this._label = null;
2816 this._labelSize = 18;
2817
2818 this._startLabel = null;
2819 this._startLabelBelow = false;
2820
2821 this._endLabel = null;
2822 this._endLabelBelow = false;
2823
2824 this._startEndLabelSize = 16;
2825
2826 this.handlers = { "change": [] };
2827 }
2828
2829 // Create accessor methods for all the _parameters defined by the constructor
2830 function accessor(k) {
2831 if (k.length > 0 && k.charAt(0) == "_") {
2832 Slider.prototype[k.substr(1)] = function(v) {
2833 if (typeof v == "undefined") return this[k];
2834 this[k] = v;
2835 return this;
2836 };
2837 }
2838 }
2839 var s = new Slider();
2840 for (var k in s) {
2841 accessor(k);
2842 }
2843
2844 // Special accessor function for margin
2845 Slider.prototype.margin = function Slider_margin(options) {
2846 if (!options) return this._margin;
2847 for (k in options) {
2848 if (k in this._margin) this._margin[k] = options[k];
2849 else throw "Slider.margin: unrecognised option " + k;
2850 }
2851 return this;
2852 };
2853
2854 // Attach event handlers
2855 Slider.prototype.on = function Slider_on(event, handler) {
2856 if (!(event in this.handlers)) throw "Slider.on: No such event: " + event;
2857 this.handlers[event].push(handler);
2858 return this;
2859 };
2860
2861 // Fire event
2862 Slider.prototype.fire = function Slider_fire(event, d) {
2863 if (!(event in this.handlers)) throw "Slider.fire: No such event: " + event;
2864 var handlers = this.handlers[event];
2865 for (var i = 0; i < handlers.length; i++) {
2866 handlers[i].call(this, d);
2867 }
2868 return this;
2869 };
2870
2871 // Binary search
2872 function closestValue(sorted_list, value, a, b) {
2873 if (typeof a === "undefined") a = 0;
2874 if (typeof b === "undefined") b = sorted_list.length;
2875
2876 if (b-a == 0) return value;
2877 if (b-a == 1) return sorted_list[a];
2878 if (b-a == 2) {
2879 var d1 = Math.abs(sorted_list[a] - value),
2880 d2 = Math.abs(sorted_list[a+1] - value);
2881 if (d1 <= d2) return sorted_list[a];
2882 else return sorted_list[a+1];
2883 }
2884
2885 var mid = a + Math.floor((b-a) / 2),
2886 mid_v = sorted_list[mid],
2887 pre = mid - 1,
2888 pre_v = sorted_list[pre];
2889 if (pre_v <= value && value <= mid_v) {
2890 return (Math.abs(pre_v - value) <= Math.abs(mid_v - value)) ? pre_v : mid_v;
2891 }
2892 if (mid_v <= value) return closestValue(sorted_list, value, mid, b);
2893 else return closestValue(sorted_list, value, a, mid);
2894 }
2895
2896 function snapTo(specification, value) {
2897 if (typeof specification == "boolean") {
2898 return specification ? Math.round(value) : value;
2899 }
2900 // Otherwise assume “specification” is a sorted array
2901 return closestValue(specification, value);
2902 }
2903
2904 // Draw or update the slider
2905 Slider.prototype.draw = function Slider_draw() {
2906 var that = this;
2907
2908 var cw = this._width,
2909 ch = this._height;
2910
2911 var container_node = this.container.node();
2912
2913 // If the width and height have not been specified, use
2914 // the client size of the container element.
2915 if (!cw) {
2916 var r = container_node.getBoundingClientRect();
2917 // If there isn’t a bounding client rect, e.g. because
2918 // the container is display: none;, then do nothing.
2919 if (!r || r.width == 0) return this;
2920
2921 cw = r.width;
2922 ch = r.height;
2923 }
2924
2925 var channel_r = this._channelRadius == null ? this._channelHeight/2 : this._channelRadius,
2926 left_margin = (this._margin.left == null ? Math.max(this._handleRadius, channel_r) : this._margin.left),
2927 right_margin = (this._margin.right == null ? Math.max(this._handleRadius, channel_r) : this._margin.right),
2928 top_margin = this._margin.top == null ? Math.max(this._handleRadius, this._channelHeight/2) : this._margin.top,
2929 w = cw - left_margin - right_margin, // Inner width of the slider channel (excluding endcaps)
2930 channel_w = w + 2*channel_r, // Width of the slider channel (including endcaps)
2931 label_h = this._labelSize * 1.5;
2932
2933 if (this._label != null && this._margin.top == null) top_margin += label_h;
2934
2935 var slider;
2936 if (container_node.namespaceURI == "http://www.w3.org/2000/svg") {
2937 slider = this.container;
2938 }
2939 else {
2940 slider = this.container.selectAll("svg").data([{ width: cw, height: ch }]);
2941 slider.exit().remove();
2942 slider = slider.enter().append("svg").merge(slider);
2943 slider.attr("width", function(d) { return d.width; })
2944 .attr("height", function(d) { return d.height; });
2945 }
2946
2947 var g = slider.selectAll("g.slider-container").data([{left: left_margin, top: top_margin, id: this._id}]);
2948 g.exit().remove();
2949 g = g.enter().append("g").attr("class", "slider-container").merge(g);
2950 g.attr("transform", function(d) {
2951 return "translate(" + d.left + "," + d.top + ")";
2952 })
2953 .attr("id", function(d) { return d.id; });
2954
2955 this.scale = (this._scale ? this._scale() : linear$1()).domain(this._domain).range([0, w]);
2956
2957 if (this._value == null || this._value < this._domain[0]) this._value = this._domain[0];
2958 else if (this._value > this._domain[1]) this._value = this._domain[1];
2959
2960 if (this._snap) this._value = snapTo(this._snap, this._value);
2961
2962 var axes_data = [];
2963 if (this._axis) {
2964 var axis;
2965 if (typeof this._axis != "boolean") {
2966 axis = this._axis(this.scale);
2967 }
2968 else {
2969 axis = axisBottom().scale(this.scale).tickPadding(6);
2970 }
2971
2972 if (this._ticks) axis.ticks(this._ticks);
2973 if (this._tickFormat) axis.tickFormat(this._tickFormat);
2974 if (this._tickSize) axis.tickSize(this._tickSize);
2975 else axis.tickSize(Math.max(5, this._handleRadius - this._channelHeight - 2));
2976 axes_data.push(axis);
2977 }
2978
2979 var axes = g.selectAll(".slider-axis");
2980 var axes_enter = axes.data(axes_data).enter();
2981 axes_enter.append("g").attr("class", "slider-axis")
2982 .attr("transform", "translate(" + 0 + "," + this._channelHeight/2 + ")")
2983 .each(function(axis) { axis(select(this)); });
2984 axes_enter.select(".domain").attr("fill", "none");
2985 axes_enter.selectAll(".tick line").attr("stroke", "black");
2986 axes_enter.exit().remove();
2987
2988 var channel, handle;
2989 channel = g.selectAll(".slider-channel")
2990 .data([{ width: channel_w, height: this._channelHeight, channel_r: channel_r }]);
2991 channel.exit().remove();
2992
2993 channel = channel.enter().append("rect").attr("class", "slider-channel")
2994 .attr("cursor", "pointer")
2995 .on("click", function() {
2996 var slider_x = Math.max(0, Math.min(w, d3_mouse(this)[0]));
2997 that._value = that.scale.invert(slider_x);
2998 if (that._snap) that._value = snapTo(that._snap, that._value);
2999 handle.attr("cx", that.scale(that._value));
3000 that.fire("change", that._value);
3001 })
3002 .merge(channel);
3003
3004 channel.attr("width", function(d) { return d.width; })
3005 .attr("fill", this._channelFill)
3006 .attr("height", function(d) { return d.height; })
3007 .attr("y", function(d) { return -d.height/2; })
3008 .attr("x", function(d) { return -d.channel_r; })
3009 .attr("rx", function(d) { return d.channel_r; });
3010
3011 var drag_dx_origin, drag_x_origin;
3012 function handleMousedown(event) {
3013 document.addEventListener("mouseup", handleMouseup, false);
3014 document.addEventListener("mousemove", handleMousemove, false);
3015 drag_dx_origin = event.clientX;
3016 drag_x_origin = that.scale(that._value);
3017 }
3018
3019 function handleMouseup() {
3020 document.removeEventListener("mouseup", handleMouseup, false);
3021 document.removeEventListener("mousemove", handleMousemove, false);
3022 }
3023
3024 function handleMousemove(event) {
3025 drag(event.clientX - drag_dx_origin);
3026 }
3027
3028 function handleTouchstart(event) {
3029 if (event.touches.length != 1) return;
3030 document.addEventListener("touchend", handleTouchend, false);
3031 document.addEventListener("touchmove", handleTouchmove, false);
3032 drag_dx_origin = event.touches[0].clientX;
3033 drag_x_origin = that.scale(that._value);
3034 }
3035
3036 function handleTouchend() {
3037 document.removeEventListener("touchend", handleTouchend, false);
3038 document.removeEventListener("touchmove", handleTouchmove, false);
3039 }
3040
3041 function handleTouchmove(event) {
3042 if (event.touches.length != 1) return;
3043 drag(event.touches[0].clientX - drag_dx_origin);
3044 }
3045
3046 function drag(dx) {
3047 var new_x = drag_x_origin + dx;
3048 var slider_x = Math.max(0, Math.min(w, new_x));
3049 var new_value = that.scale.invert(slider_x);
3050 if (that._snap) new_value = snapTo(that._snap, new_value);
3051 handle.attr("cx", that.scale(new_value));
3052 if (new_value != that._value) {
3053 that._value = new_value;
3054 that.fire("change", that._value);
3055 }
3056 }
3057
3058 handle = g.selectAll(".slider-handle").data([{ v: this._value, x: this.scale(this._value) }]);
3059 handle = handle.enter().append("circle").attr("class", "slider-handle")
3060 .attr("cursor", "col-resize")
3061 .merge(handle);
3062
3063 handle.attr("cx", function(d) { return d.x; })
3064 .attr("r", this._handleRadius)
3065 .attr("fill", this._handleFill)
3066 .on("mousedown", function() {
3067 event$1.preventDefault();
3068 handleMousedown(event$1);
3069 })
3070 .on("touchstart", function() {
3071 event$1.preventDefault();
3072 handleTouchstart(event$1);
3073 });
3074
3075 var label_data = [];
3076 if (this._label) {
3077 label_data.push({
3078 label: this._label, x: w/2, y: -label_h, font_size: this._labelSize
3079 });
3080 }
3081 var label = g.selectAll(".slider-label").data(label_data);
3082 label.exit().remove();
3083 label = label.enter()
3084 .append("text").attr("class", "slider-label")
3085 .attr("text-anchor", "middle")
3086 .attr("cursor", "default")
3087 .merge(label);
3088
3089 label
3090 .text(function(d) { return d.label; })
3091 .attr("x", function(d) { return d.x; })
3092 .attr("y", function(d) { return d.y; })
3093 .attr("font-size", this._labelSize);
3094
3095 var end_label_data = [];
3096 if (this._startLabel) {
3097 end_label_data.push({
3098 label: this._startLabel,
3099 x: this._startLabelBelow ? 0 : -(channel_r + 5 + Math.max(0, this._handleRadius - channel_r)),
3100 y: this._startLabelBelow ? (channel_r + 15) : this._startEndLabelSize/1.75 - channel_r/2,
3101 anchor: this._startLabelBelow ? "middle" : "end",
3102 font_size: this._startEndLabelSize
3103 });
3104 }
3105 if (this._endLabel) {
3106 end_label_data.push({
3107 label: this._endLabel,
3108 x: this._endLabelBelow ? w : w + (channel_r + Math.max(0, this._handleRadius - channel_r) + 5),
3109 y: this._startLabelBelow ? (channel_r + 15) : this._startEndLabelSize/1.75 - channel_r/2,
3110 anchor: this._endLabelBelow ? "middle" : "start",
3111 font_size: this._startEndLabelSize
3112 });
3113 }
3114
3115 var end_labels = g.selectAll(".slider-end-labels").data(end_label_data);
3116 end_labels.exit().remove();
3117 end_labels = end_labels.enter().append("text").attr("class", "slider-end-labels")
3118 .attr("pointer-events", "none")
3119 .merge(end_labels);
3120 end_labels
3121 .text(function(d) { return d.label; })
3122 .attr("font-size", function(d) { return d.font_size; })
3123 .attr("x", function(d) { return d.x; })
3124 .attr("y", function(d) { return d.y; })
3125 .attr("text-anchor", function(d) { return d.anchor; });
3126
3127 return this;
3128 };
3129
3130 Slider.prototype.update = Slider.prototype.draw;
3131
3132 function Flourish_slider(selector) {
3133 return new Slider(selector);
3134 }
3135 Flourish_slider.version = VERSION;
3136
3137 function createPlayButton(colour) {
3138 var start_string = '<svg width="25px" height="30px" viewBox="0 0 25 30"> <polygon fill="';
3139 var end_string = '" stroke="none" points="25 15 0 30 0 0"></polygon> </svg>';
3140 return start_string + colour + end_string;
3141 }
3142
3143 function createPauseButton(colour) {
3144 var start_string = '<svg width="26px" height="30px" viewBox="0 0 26 30"> <g stroke="none" stroke-width="1" fill="';
3145 var end_string = '"><rect x="2" y="2" width="9" height="26"></rect> <rect x="15" y="2" width="9" height="26"></rect> </g> </svg>';
3146 return start_string + colour + end_string;
3147 }
3148
3149 function createSlider(control_obj, state, container) {
3150 var slider_obj = {};
3151 var slider_holder = select(container).append("div").attr("class", "fl-control fl-control-slider animatable");
3152 var slider_play_button = slider_holder.append("div").attr("class", "slider-play");
3153 var slider_div = slider_holder.append("div").attr("class", "fl-controls-slider");
3154
3155 var play_string, pause_string, handle_color;
3156 var sorted_options, timesteps, is_playing;
3157 var timer_id = null;
3158
3159 var clearTimer = function() {
3160 clearTimeout(timer_id);
3161 timer_id = null;
3162 };
3163
3164 var sliderChangeFunction = function(i) {
3165 var d = sorted_options[i];
3166 if (d.options_index === control_obj.index()) return;
3167 control_obj.index(d.options_index);
3168 control_obj.trigger("change");
3169 };
3170
3171 var slider = Flourish_slider(slider_div.node())
3172 .snap(true)
3173 .on("change", function(i) {
3174 var is_playing = timer_id !== null;
3175 if (is_playing) clearTimer();
3176 sliderChangeFunction(i);
3177 if (is_playing) setNextTick();
3178 });
3179
3180
3181 var stopSliderPlayer = function() {
3182 clearTimer();
3183 slider_holder.classed("playing", false);
3184 slider_play_button.html(play_string);
3185 control_obj._isPlaying_(false);
3186 is_playing = false;
3187 };
3188
3189
3190 var setNextTick = function() {
3191 var current_index = control_obj.getSortedIndex();
3192 var current_delay = timesteps[current_index];
3193 var final_index = control_obj.n_options - 1;
3194 var next_index = current_index < final_index ? current_index + 1 : 0;
3195
3196 timer_id = setTimeout(function() {
3197 sliderChangeFunction(next_index);
3198 if (state.slider_loop || next_index < final_index) setNextTick();
3199 else stopSliderPlayer();
3200 }, current_delay);
3201 };
3202
3203
3204 var startSliderPlayer = function() {
3205 slider_holder.classed("playing", true);
3206 slider_play_button.html(pause_string);
3207 setNextTick();
3208 control_obj._isPlaying_(true);
3209 is_playing = true;
3210 };
3211
3212
3213 slider_play_button.on("click", function() {
3214 if (timer_id === null) startSliderPlayer();
3215 else stopSliderPlayer();
3216 });
3217
3218
3219 var setWidths = function() {
3220 var handle_radius = Math.round(remToPx(state.slider_handle_height) / 2);
3221 slider_holder.style("width", Math.round(remToPx(state.slider_width)) + "px");
3222 slider_play_button
3223 .style("height", handle_radius * 2 + "px")
3224 .style("width", handle_radius * 2 + "px")
3225 .style("display", state.slider_play_button ? null : "none");
3226 var holder_width = slider_holder.node().getBoundingClientRect().width;
3227 var button_width = slider_play_button.node().getBoundingClientRect().width;
3228 slider_div
3229 .style("width", Math.max((holder_width - button_width), 1) + "px")
3230 .style("height", handle_radius * 2 + "px");
3231
3232 slider
3233 .handleRadius(handle_radius)
3234 .margin({
3235 left: handle_radius + 5,
3236 right: handle_radius + remToPx(state.slider_margin),
3237 top: handle_radius
3238 });
3239 };
3240
3241
3242 var setHandles = function() {
3243 if (state.slider_play_button) {
3244 slider_holder.classed("animatable", true);
3245 }
3246 else {
3247 stopSliderPlayer();
3248 slider_holder.classed("animatable", false);
3249 }
3250 if (handle_color !== state.slider_handle_color) {
3251 slider.update(); // Make sure slider-handle actually exists before changing its colour
3252 handle_color = state.slider_handle_color || "currentColor";
3253 slider_holder.select(".slider-handle").style("fill", handle_color);
3254 play_string = createPlayButton(handle_color);
3255 pause_string = createPauseButton(handle_color);
3256 slider_play_button.html(timer_id ? pause_string : play_string);
3257 }
3258 };
3259
3260
3261 var showControl = function() {
3262 slider_holder.style("display", "inline-block");
3263 setWidths();
3264 setHandles();
3265 return slider_obj;
3266 };
3267
3268 var hideControl = function() {
3269 stopSliderPlayer();
3270 slider_holder.style("display", "none");
3271 return slider_obj;
3272 };
3273
3274 slider_obj.show = showControl;
3275 slider_obj.hide = hideControl;
3276
3277
3278 slider_obj.update = function(_sorted_options) {
3279 sorted_options = _sorted_options;
3280 if (!control_obj.n_options || state.control_type !== "slider") {
3281 hideControl();
3282 return slider_obj;
3283 }
3284
3285 showControl();
3286
3287 var n_options = control_obj.n_options;
3288 var loop = state.slider_loop;
3289
3290 timesteps = sorted_options.map(function(d, i) {
3291 var dur_in_seconds = state.slider_step_time + (loop && i === (n_options -1) ? state.slider_restart_pause : 0);
3292 return dur_in_seconds * 1000;
3293 });
3294
3295 var sorted_index = control_obj.getSortedIndex();
3296 var d = sorted_options[sorted_index];
3297
3298 slider.domain([0, n_options - 1])
3299 .value(sorted_index)
3300 .endLabel(d.display)
3301 .channelHeight(Math.round(remToPx(state.slider_track_height)))
3302 .channelFill(state.slider_background_color)
3303 .update();
3304
3305 slider.container.select("svg").attr("fill", "currentColor");
3306
3307 slider_div.select("text.slider-end-labels")
3308 .style("fill", state.slider_font_color)
3309 .attr("y", "0")
3310 .attr("dy", "0.25em");
3311
3312
3313 if (control_obj._isPlaying_() && !is_playing) startSliderPlayer();
3314 else if (!control_obj._isPlaying_() && is_playing) stopSliderPlayer();
3315
3316 return slider_obj;
3317 };
3318
3319
3320 return slider_obj;
3321 }
3322
3323 var DEFAULTS = Object.freeze({
3324 control_type: "dropdown",
3325 control_title: "",
3326
3327 // Dropdown
3328 dropdown_width_mode: "auto",
3329 dropdown_width_fixed: 20,
3330
3331 // Buttons
3332 button_group: true,
3333 button_group_width_mode: "fixed",
3334 button_group_width_fixed: 20,
3335
3336 // Slider
3337 slider_width: 15,
3338 slider_handle_color: null,
3339 slider_font_color: null,
3340 slider_background_color: "#dddddd",
3341
3342 slider_handle_height: 1,
3343 slider_track_height: 0.2,
3344 slider_margin: 4.5,
3345
3346 slider_play_button: true,
3347 slider_step_time: 2,
3348 slider_loop: true,
3349 slider_restart_pause: 0,
3350
3351 // Data typing
3352 sort: false,
3353
3354 _index_: null,
3355 _is_playing_: false
3356 });
3357
3358 function defaultParser(x) {
3359 return x;
3360 }
3361
3362 function defaultFormatter(x) {
3363 return x == null ? "" : x.toString();
3364 }
3365
3366 function init(state) {
3367 var control_obj = {};
3368 var parser = defaultParser;
3369 var formatter = defaultFormatter;
3370 var options = [];
3371 var sorted_options = [];
3372 var changeHandlers = [];
3373 var container = document.createElement("div");
3374 container.setAttribute("class", "fl-controls-container");
3375 var container_title = select(container).append("div").attr("class", "fl-controls-title");
3376 var dropdown_obj = createDropdown(control_obj, state, container);
3377 var buttons_obj = createButtons(control_obj, state, container);
3378 var slider_obj = createSlider(control_obj, state, container);
3379
3380 for (var key in DEFAULTS) {
3381 if (state[key] === undefined) state[key] = DEFAULTS[key];
3382 }
3383
3384 var current_index = state._index_;
3385
3386 var checkValidIndex = function(i) {
3387 return options.length && i >= 0 && i < options.length;
3388 };
3389
3390 var updateControls = function(sorted_options) {
3391 container.style.display = (sorted_options.length > 1) ? null : "none";
3392 container.style.width = "";
3393 container_title.node().innerHTML = state.control_title;
3394 container_title.node().style.display = (state.control_title === "") ? "none" : "inline-block";
3395 slider_obj.update(sorted_options); // Do slider first in case we're stopping playing
3396 dropdown_obj.update(sorted_options);
3397 buttons_obj.update(sorted_options);
3398 };
3399
3400 control_obj.appendTo = function(parent_container, bounding_container) {
3401 injectCSS();
3402 select(parent_container).node().appendChild(container);
3403 dropdown_obj.appendedToDOM(bounding_container);
3404 return control_obj;
3405 };
3406
3407 var callOnChangeCallbacks = function() {
3408 var index = indexFunction();
3409 var value = options[index];
3410 changeHandlers.forEach(function(func) {
3411 func(value, index);
3412 });
3413 return control_obj;
3414 };
3415
3416 control_obj.remove = function() {
3417 if (container.parentElement) container.parentElement.removeChild(container);
3418 dropdown_obj.removedFromDOM();
3419
3420 return control_obj;
3421 };
3422
3423 control_obj.options = function(arr) {
3424 if (arr === undefined) return options.slice();
3425 if (!Array.isArray(arr)) return control_obj;
3426 options = arr.slice();
3427 var n = options.length;
3428 var i = indexFunction();
3429 if (!n) indexFunction(null);
3430 else if (i === null || i >= n) indexFunction(0);
3431 return control_obj;
3432 };
3433
3434 Object.defineProperty(control_obj, "n_options", { get: function() { return options.length; } });
3435
3436
3437 var indexFunction = function(i) {
3438 if (i === undefined) {
3439 if (!state._is_playing_) current_index = state._index_;
3440 return current_index;
3441 }
3442 if (i === null || checkValidIndex(i)) {
3443 current_index = i;
3444 if (!state._is_playing_) state._index_ = current_index;
3445 }
3446 else console.warn("Invalid index, ignoring update call");
3447 return control_obj;
3448 };
3449 control_obj.index = indexFunction;
3450
3451 control_obj.getSortedIndex = function() {
3452 var options_index = indexFunction();
3453 if (state.sort == "unsorted") return options_index;
3454 var sorted_index;
3455 sorted_options.some(function(d, i) {
3456 if (d.options_index === options_index) {
3457 sorted_index = i;
3458 return true;
3459 }
3460 });
3461 return sorted_index;
3462 };
3463
3464 control_obj.value = function(value) {
3465 if (value === undefined) return options[indexFunction()];
3466 var index = options.indexOf(value);
3467 if (index !== -1) indexFunction(index);
3468 return control_obj;
3469 };
3470
3471 control_obj.on = function(event, callback) {
3472 if (event === "change") changeHandlers.push(callback.bind(control_obj));
3473 return control_obj;
3474 };
3475
3476 control_obj.parser = function (value) {
3477 if (value === undefined) return parser;
3478 if (value === null) parser = defaultParser;
3479 else parser = value;
3480 return control_obj;
3481 };
3482
3483 control_obj.formatter = function (value) {
3484 if (value === undefined) return formatter;
3485 if (value === null) formatter = defaultFormatter;
3486 else formatter = value;
3487 return control_obj;
3488 };
3489
3490 control_obj.update = function() {
3491 getRemToPx();
3492 sorted_options = sortArray(options, state.sort, parser, formatter);
3493 updateControls(sorted_options);
3494 return control_obj;
3495 };
3496
3497 control_obj.trigger = function(event) {
3498 if (event === "change") callOnChangeCallbacks();
3499 return control_obj;
3500 };
3501
3502 control_obj.getNode = function() {
3503 return container;
3504 };
3505
3506 var isPlaying = function(is_playing) {
3507 if (is_playing === undefined) return state._is_playing_;
3508 state._is_playing_ = !!is_playing;
3509 if (!is_playing) indexFunction(current_index); // Force _index_ to match current index
3510 };
3511
3512 control_obj._isPlaying_ = isPlaying;
3513
3514 return control_obj;
3515 }
3516
3517 return init;
3518
3519})));