UNPKG

135 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 getDefaultParser() {
895 return parseFloat;
896 }
897
898 function getDefaultFormatter() {
899 return function(x) { return x.toString(); };
900 }
901
902 var t0 = new Date,
903 t1 = new Date;
904
905 function newInterval(floori, offseti, count, field) {
906
907 function interval(date) {
908 return floori(date = new Date(+date)), date;
909 }
910
911 interval.floor = interval;
912
913 interval.ceil = function(date) {
914 return floori(date = new Date(date - 1)), offseti(date, 1), floori(date), date;
915 };
916
917 interval.round = function(date) {
918 var d0 = interval(date),
919 d1 = interval.ceil(date);
920 return date - d0 < d1 - date ? d0 : d1;
921 };
922
923 interval.offset = function(date, step) {
924 return offseti(date = new Date(+date), step == null ? 1 : Math.floor(step)), date;
925 };
926
927 interval.range = function(start, stop, step) {
928 var range = [], previous;
929 start = interval.ceil(start);
930 step = step == null ? 1 : Math.floor(step);
931 if (!(start < stop) || !(step > 0)) return range; // also handles Invalid Date
932 do range.push(previous = new Date(+start)), offseti(start, step), floori(start);
933 while (previous < start && start < stop);
934 return range;
935 };
936
937 interval.filter = function(test) {
938 return newInterval(function(date) {
939 if (date >= date) while (floori(date), !test(date)) date.setTime(date - 1);
940 }, function(date, step) {
941 if (date >= date) {
942 if (step < 0) while (++step <= 0) {
943 while (offseti(date, -1), !test(date)) {} // eslint-disable-line no-empty
944 } else while (--step >= 0) {
945 while (offseti(date, +1), !test(date)) {} // eslint-disable-line no-empty
946 }
947 }
948 });
949 };
950
951 if (count) {
952 interval.count = function(start, end) {
953 t0.setTime(+start), t1.setTime(+end);
954 floori(t0), floori(t1);
955 return Math.floor(count(t0, t1));
956 };
957
958 interval.every = function(step) {
959 step = Math.floor(step);
960 return !isFinite(step) || !(step > 0) ? null
961 : !(step > 1) ? interval
962 : interval.filter(field
963 ? function(d) { return field(d) % step === 0; }
964 : function(d) { return interval.count(0, d) % step === 0; });
965 };
966 }
967
968 return interval;
969 }
970
971 var millisecond = newInterval(function() {
972 // noop
973 }, function(date, step) {
974 date.setTime(+date + step);
975 }, function(start, end) {
976 return end - start;
977 });
978
979 // An optimized implementation for this simple case.
980 millisecond.every = function(k) {
981 k = Math.floor(k);
982 if (!isFinite(k) || !(k > 0)) return null;
983 if (!(k > 1)) return millisecond;
984 return newInterval(function(date) {
985 date.setTime(Math.floor(date / k) * k);
986 }, function(date, step) {
987 date.setTime(+date + step * k);
988 }, function(start, end) {
989 return (end - start) / k;
990 });
991 };
992
993 var durationSecond = 1e3;
994 var durationMinute = 6e4;
995 var durationHour = 36e5;
996 var durationDay = 864e5;
997 var durationWeek = 6048e5;
998
999 var second = newInterval(function(date) {
1000 date.setTime(date - date.getMilliseconds());
1001 }, function(date, step) {
1002 date.setTime(+date + step * durationSecond);
1003 }, function(start, end) {
1004 return (end - start) / durationSecond;
1005 }, function(date) {
1006 return date.getUTCSeconds();
1007 });
1008
1009 var minute = newInterval(function(date) {
1010 date.setTime(date - date.getMilliseconds() - date.getSeconds() * durationSecond);
1011 }, function(date, step) {
1012 date.setTime(+date + step * durationMinute);
1013 }, function(start, end) {
1014 return (end - start) / durationMinute;
1015 }, function(date) {
1016 return date.getMinutes();
1017 });
1018
1019 var hour = newInterval(function(date) {
1020 date.setTime(date - date.getMilliseconds() - date.getSeconds() * durationSecond - date.getMinutes() * durationMinute);
1021 }, function(date, step) {
1022 date.setTime(+date + step * durationHour);
1023 }, function(start, end) {
1024 return (end - start) / durationHour;
1025 }, function(date) {
1026 return date.getHours();
1027 });
1028
1029 var day = newInterval(function(date) {
1030 date.setHours(0, 0, 0, 0);
1031 }, function(date, step) {
1032 date.setDate(date.getDate() + step);
1033 }, function(start, end) {
1034 return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute) / durationDay;
1035 }, function(date) {
1036 return date.getDate() - 1;
1037 });
1038
1039 function weekday(i) {
1040 return newInterval(function(date) {
1041 date.setDate(date.getDate() - (date.getDay() + 7 - i) % 7);
1042 date.setHours(0, 0, 0, 0);
1043 }, function(date, step) {
1044 date.setDate(date.getDate() + step * 7);
1045 }, function(start, end) {
1046 return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute) / durationWeek;
1047 });
1048 }
1049
1050 var sunday = weekday(0);
1051 var monday = weekday(1);
1052 var tuesday = weekday(2);
1053 var wednesday = weekday(3);
1054 var thursday = weekday(4);
1055 var friday = weekday(5);
1056 var saturday = weekday(6);
1057
1058 var month = newInterval(function(date) {
1059 date.setDate(1);
1060 date.setHours(0, 0, 0, 0);
1061 }, function(date, step) {
1062 date.setMonth(date.getMonth() + step);
1063 }, function(start, end) {
1064 return end.getMonth() - start.getMonth() + (end.getFullYear() - start.getFullYear()) * 12;
1065 }, function(date) {
1066 return date.getMonth();
1067 });
1068
1069 var year = newInterval(function(date) {
1070 date.setMonth(0, 1);
1071 date.setHours(0, 0, 0, 0);
1072 }, function(date, step) {
1073 date.setFullYear(date.getFullYear() + step);
1074 }, function(start, end) {
1075 return end.getFullYear() - start.getFullYear();
1076 }, function(date) {
1077 return date.getFullYear();
1078 });
1079
1080 // An optimized implementation for this simple case.
1081 year.every = function(k) {
1082 return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : newInterval(function(date) {
1083 date.setFullYear(Math.floor(date.getFullYear() / k) * k);
1084 date.setMonth(0, 1);
1085 date.setHours(0, 0, 0, 0);
1086 }, function(date, step) {
1087 date.setFullYear(date.getFullYear() + step * k);
1088 });
1089 };
1090
1091 var utcMinute = newInterval(function(date) {
1092 date.setUTCSeconds(0, 0);
1093 }, function(date, step) {
1094 date.setTime(+date + step * durationMinute);
1095 }, function(start, end) {
1096 return (end - start) / durationMinute;
1097 }, function(date) {
1098 return date.getUTCMinutes();
1099 });
1100
1101 var utcHour = newInterval(function(date) {
1102 date.setUTCMinutes(0, 0, 0);
1103 }, function(date, step) {
1104 date.setTime(+date + step * durationHour);
1105 }, function(start, end) {
1106 return (end - start) / durationHour;
1107 }, function(date) {
1108 return date.getUTCHours();
1109 });
1110
1111 var utcDay = newInterval(function(date) {
1112 date.setUTCHours(0, 0, 0, 0);
1113 }, function(date, step) {
1114 date.setUTCDate(date.getUTCDate() + step);
1115 }, function(start, end) {
1116 return (end - start) / durationDay;
1117 }, function(date) {
1118 return date.getUTCDate() - 1;
1119 });
1120
1121 function utcWeekday(i) {
1122 return newInterval(function(date) {
1123 date.setUTCDate(date.getUTCDate() - (date.getUTCDay() + 7 - i) % 7);
1124 date.setUTCHours(0, 0, 0, 0);
1125 }, function(date, step) {
1126 date.setUTCDate(date.getUTCDate() + step * 7);
1127 }, function(start, end) {
1128 return (end - start) / durationWeek;
1129 });
1130 }
1131
1132 var utcSunday = utcWeekday(0);
1133 var utcMonday = utcWeekday(1);
1134 var utcTuesday = utcWeekday(2);
1135 var utcWednesday = utcWeekday(3);
1136 var utcThursday = utcWeekday(4);
1137 var utcFriday = utcWeekday(5);
1138 var utcSaturday = utcWeekday(6);
1139
1140 var utcMonth = newInterval(function(date) {
1141 date.setUTCDate(1);
1142 date.setUTCHours(0, 0, 0, 0);
1143 }, function(date, step) {
1144 date.setUTCMonth(date.getUTCMonth() + step);
1145 }, function(start, end) {
1146 return end.getUTCMonth() - start.getUTCMonth() + (end.getUTCFullYear() - start.getUTCFullYear()) * 12;
1147 }, function(date) {
1148 return date.getUTCMonth();
1149 });
1150
1151 var utcYear = newInterval(function(date) {
1152 date.setUTCMonth(0, 1);
1153 date.setUTCHours(0, 0, 0, 0);
1154 }, function(date, step) {
1155 date.setUTCFullYear(date.getUTCFullYear() + step);
1156 }, function(start, end) {
1157 return end.getUTCFullYear() - start.getUTCFullYear();
1158 }, function(date) {
1159 return date.getUTCFullYear();
1160 });
1161
1162 // An optimized implementation for this simple case.
1163 utcYear.every = function(k) {
1164 return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : newInterval(function(date) {
1165 date.setUTCFullYear(Math.floor(date.getUTCFullYear() / k) * k);
1166 date.setUTCMonth(0, 1);
1167 date.setUTCHours(0, 0, 0, 0);
1168 }, function(date, step) {
1169 date.setUTCFullYear(date.getUTCFullYear() + step * k);
1170 });
1171 };
1172
1173 function localDate(d) {
1174 if (0 <= d.y && d.y < 100) {
1175 var date = new Date(-1, d.m, d.d, d.H, d.M, d.S, d.L);
1176 date.setFullYear(d.y);
1177 return date;
1178 }
1179 return new Date(d.y, d.m, d.d, d.H, d.M, d.S, d.L);
1180 }
1181
1182 function utcDate(d) {
1183 if (0 <= d.y && d.y < 100) {
1184 var date = new Date(Date.UTC(-1, d.m, d.d, d.H, d.M, d.S, d.L));
1185 date.setUTCFullYear(d.y);
1186 return date;
1187 }
1188 return new Date(Date.UTC(d.y, d.m, d.d, d.H, d.M, d.S, d.L));
1189 }
1190
1191 function newDate(y, m, d) {
1192 return {y: y, m: m, d: d, H: 0, M: 0, S: 0, L: 0};
1193 }
1194
1195 function formatLocale(locale) {
1196 var locale_dateTime = locale.dateTime,
1197 locale_date = locale.date,
1198 locale_time = locale.time,
1199 locale_periods = locale.periods,
1200 locale_weekdays = locale.days,
1201 locale_shortWeekdays = locale.shortDays,
1202 locale_months = locale.months,
1203 locale_shortMonths = locale.shortMonths;
1204
1205 var periodRe = formatRe(locale_periods),
1206 periodLookup = formatLookup(locale_periods),
1207 weekdayRe = formatRe(locale_weekdays),
1208 weekdayLookup = formatLookup(locale_weekdays),
1209 shortWeekdayRe = formatRe(locale_shortWeekdays),
1210 shortWeekdayLookup = formatLookup(locale_shortWeekdays),
1211 monthRe = formatRe(locale_months),
1212 monthLookup = formatLookup(locale_months),
1213 shortMonthRe = formatRe(locale_shortMonths),
1214 shortMonthLookup = formatLookup(locale_shortMonths);
1215
1216 var formats = {
1217 "a": formatShortWeekday,
1218 "A": formatWeekday,
1219 "b": formatShortMonth,
1220 "B": formatMonth,
1221 "c": null,
1222 "d": formatDayOfMonth,
1223 "e": formatDayOfMonth,
1224 "f": formatMicroseconds,
1225 "H": formatHour24,
1226 "I": formatHour12,
1227 "j": formatDayOfYear,
1228 "L": formatMilliseconds,
1229 "m": formatMonthNumber,
1230 "M": formatMinutes,
1231 "p": formatPeriod,
1232 "q": formatQuarter,
1233 "Q": formatUnixTimestamp,
1234 "s": formatUnixTimestampSeconds,
1235 "S": formatSeconds,
1236 "u": formatWeekdayNumberMonday,
1237 "U": formatWeekNumberSunday,
1238 "V": formatWeekNumberISO,
1239 "w": formatWeekdayNumberSunday,
1240 "W": formatWeekNumberMonday,
1241 "x": null,
1242 "X": null,
1243 "y": formatYear,
1244 "Y": formatFullYear,
1245 "Z": formatZone,
1246 "%": formatLiteralPercent
1247 };
1248
1249 var utcFormats = {
1250 "a": formatUTCShortWeekday,
1251 "A": formatUTCWeekday,
1252 "b": formatUTCShortMonth,
1253 "B": formatUTCMonth,
1254 "c": null,
1255 "d": formatUTCDayOfMonth,
1256 "e": formatUTCDayOfMonth,
1257 "f": formatUTCMicroseconds,
1258 "H": formatUTCHour24,
1259 "I": formatUTCHour12,
1260 "j": formatUTCDayOfYear,
1261 "L": formatUTCMilliseconds,
1262 "m": formatUTCMonthNumber,
1263 "M": formatUTCMinutes,
1264 "p": formatUTCPeriod,
1265 "q": formatUTCQuarter,
1266 "Q": formatUnixTimestamp,
1267 "s": formatUnixTimestampSeconds,
1268 "S": formatUTCSeconds,
1269 "u": formatUTCWeekdayNumberMonday,
1270 "U": formatUTCWeekNumberSunday,
1271 "V": formatUTCWeekNumberISO,
1272 "w": formatUTCWeekdayNumberSunday,
1273 "W": formatUTCWeekNumberMonday,
1274 "x": null,
1275 "X": null,
1276 "y": formatUTCYear,
1277 "Y": formatUTCFullYear,
1278 "Z": formatUTCZone,
1279 "%": formatLiteralPercent
1280 };
1281
1282 var parses = {
1283 "a": parseShortWeekday,
1284 "A": parseWeekday,
1285 "b": parseShortMonth,
1286 "B": parseMonth,
1287 "c": parseLocaleDateTime,
1288 "d": parseDayOfMonth,
1289 "e": parseDayOfMonth,
1290 "f": parseMicroseconds,
1291 "H": parseHour24,
1292 "I": parseHour24,
1293 "j": parseDayOfYear,
1294 "L": parseMilliseconds,
1295 "m": parseMonthNumber,
1296 "M": parseMinutes,
1297 "p": parsePeriod,
1298 "q": parseQuarter,
1299 "Q": parseUnixTimestamp,
1300 "s": parseUnixTimestampSeconds,
1301 "S": parseSeconds,
1302 "u": parseWeekdayNumberMonday,
1303 "U": parseWeekNumberSunday,
1304 "V": parseWeekNumberISO,
1305 "w": parseWeekdayNumberSunday,
1306 "W": parseWeekNumberMonday,
1307 "x": parseLocaleDate,
1308 "X": parseLocaleTime,
1309 "y": parseYear,
1310 "Y": parseFullYear,
1311 "Z": parseZone,
1312 "%": parseLiteralPercent
1313 };
1314
1315 // These recursive directive definitions must be deferred.
1316 formats.x = newFormat(locale_date, formats);
1317 formats.X = newFormat(locale_time, formats);
1318 formats.c = newFormat(locale_dateTime, formats);
1319 utcFormats.x = newFormat(locale_date, utcFormats);
1320 utcFormats.X = newFormat(locale_time, utcFormats);
1321 utcFormats.c = newFormat(locale_dateTime, utcFormats);
1322
1323 function newFormat(specifier, formats) {
1324 return function(date) {
1325 var string = [],
1326 i = -1,
1327 j = 0,
1328 n = specifier.length,
1329 c,
1330 pad,
1331 format;
1332
1333 if (!(date instanceof Date)) date = new Date(+date);
1334
1335 while (++i < n) {
1336 if (specifier.charCodeAt(i) === 37) {
1337 string.push(specifier.slice(j, i));
1338 if ((pad = pads[c = specifier.charAt(++i)]) != null) c = specifier.charAt(++i);
1339 else pad = c === "e" ? " " : "0";
1340 if (format = formats[c]) c = format(date, pad);
1341 string.push(c);
1342 j = i + 1;
1343 }
1344 }
1345
1346 string.push(specifier.slice(j, i));
1347 return string.join("");
1348 };
1349 }
1350
1351 function newParse(specifier, Z) {
1352 return function(string) {
1353 var d = newDate(1900, undefined, 1),
1354 i = parseSpecifier(d, specifier, string += "", 0),
1355 week, day$1;
1356 if (i != string.length) return null;
1357
1358 // If a UNIX timestamp is specified, return it.
1359 if ("Q" in d) return new Date(d.Q);
1360 if ("s" in d) return new Date(d.s * 1000 + ("L" in d ? d.L : 0));
1361
1362 // If this is utcParse, never use the local timezone.
1363 if (Z && !("Z" in d)) d.Z = 0;
1364
1365 // The am-pm flag is 0 for AM, and 1 for PM.
1366 if ("p" in d) d.H = d.H % 12 + d.p * 12;
1367
1368 // If the month was not specified, inherit from the quarter.
1369 if (d.m === undefined) d.m = "q" in d ? d.q : 0;
1370
1371 // Convert day-of-week and week-of-year to day-of-year.
1372 if ("V" in d) {
1373 if (d.V < 1 || d.V > 53) return null;
1374 if (!("w" in d)) d.w = 1;
1375 if ("Z" in d) {
1376 week = utcDate(newDate(d.y, 0, 1)), day$1 = week.getUTCDay();
1377 week = day$1 > 4 || day$1 === 0 ? utcMonday.ceil(week) : utcMonday(week);
1378 week = utcDay.offset(week, (d.V - 1) * 7);
1379 d.y = week.getUTCFullYear();
1380 d.m = week.getUTCMonth();
1381 d.d = week.getUTCDate() + (d.w + 6) % 7;
1382 } else {
1383 week = localDate(newDate(d.y, 0, 1)), day$1 = week.getDay();
1384 week = day$1 > 4 || day$1 === 0 ? monday.ceil(week) : monday(week);
1385 week = day.offset(week, (d.V - 1) * 7);
1386 d.y = week.getFullYear();
1387 d.m = week.getMonth();
1388 d.d = week.getDate() + (d.w + 6) % 7;
1389 }
1390 } else if ("W" in d || "U" in d) {
1391 if (!("w" in d)) d.w = "u" in d ? d.u % 7 : "W" in d ? 1 : 0;
1392 day$1 = "Z" in d ? utcDate(newDate(d.y, 0, 1)).getUTCDay() : localDate(newDate(d.y, 0, 1)).getDay();
1393 d.m = 0;
1394 d.d = "W" in d ? (d.w + 6) % 7 + d.W * 7 - (day$1 + 5) % 7 : d.w + d.U * 7 - (day$1 + 6) % 7;
1395 }
1396
1397 // If a time zone is specified, all fields are interpreted as UTC and then
1398 // offset according to the specified time zone.
1399 if ("Z" in d) {
1400 d.H += d.Z / 100 | 0;
1401 d.M += d.Z % 100;
1402 return utcDate(d);
1403 }
1404
1405 // Otherwise, all fields are in local time.
1406 return localDate(d);
1407 };
1408 }
1409
1410 function parseSpecifier(d, specifier, string, j) {
1411 var i = 0,
1412 n = specifier.length,
1413 m = string.length,
1414 c,
1415 parse;
1416
1417 while (i < n) {
1418 if (j >= m) return -1;
1419 c = specifier.charCodeAt(i++);
1420 if (c === 37) {
1421 c = specifier.charAt(i++);
1422 parse = parses[c in pads ? specifier.charAt(i++) : c];
1423 if (!parse || ((j = parse(d, string, j)) < 0)) return -1;
1424 } else if (c != string.charCodeAt(j++)) {
1425 return -1;
1426 }
1427 }
1428
1429 return j;
1430 }
1431
1432 function parsePeriod(d, string, i) {
1433 var n = periodRe.exec(string.slice(i));
1434 return n ? (d.p = periodLookup[n[0].toLowerCase()], i + n[0].length) : -1;
1435 }
1436
1437 function parseShortWeekday(d, string, i) {
1438 var n = shortWeekdayRe.exec(string.slice(i));
1439 return n ? (d.w = shortWeekdayLookup[n[0].toLowerCase()], i + n[0].length) : -1;
1440 }
1441
1442 function parseWeekday(d, string, i) {
1443 var n = weekdayRe.exec(string.slice(i));
1444 return n ? (d.w = weekdayLookup[n[0].toLowerCase()], i + n[0].length) : -1;
1445 }
1446
1447 function parseShortMonth(d, string, i) {
1448 var n = shortMonthRe.exec(string.slice(i));
1449 return n ? (d.m = shortMonthLookup[n[0].toLowerCase()], i + n[0].length) : -1;
1450 }
1451
1452 function parseMonth(d, string, i) {
1453 var n = monthRe.exec(string.slice(i));
1454 return n ? (d.m = monthLookup[n[0].toLowerCase()], i + n[0].length) : -1;
1455 }
1456
1457 function parseLocaleDateTime(d, string, i) {
1458 return parseSpecifier(d, locale_dateTime, string, i);
1459 }
1460
1461 function parseLocaleDate(d, string, i) {
1462 return parseSpecifier(d, locale_date, string, i);
1463 }
1464
1465 function parseLocaleTime(d, string, i) {
1466 return parseSpecifier(d, locale_time, string, i);
1467 }
1468
1469 function formatShortWeekday(d) {
1470 return locale_shortWeekdays[d.getDay()];
1471 }
1472
1473 function formatWeekday(d) {
1474 return locale_weekdays[d.getDay()];
1475 }
1476
1477 function formatShortMonth(d) {
1478 return locale_shortMonths[d.getMonth()];
1479 }
1480
1481 function formatMonth(d) {
1482 return locale_months[d.getMonth()];
1483 }
1484
1485 function formatPeriod(d) {
1486 return locale_periods[+(d.getHours() >= 12)];
1487 }
1488
1489 function formatQuarter(d) {
1490 return 1 + ~~(d.getMonth() / 3);
1491 }
1492
1493 function formatUTCShortWeekday(d) {
1494 return locale_shortWeekdays[d.getUTCDay()];
1495 }
1496
1497 function formatUTCWeekday(d) {
1498 return locale_weekdays[d.getUTCDay()];
1499 }
1500
1501 function formatUTCShortMonth(d) {
1502 return locale_shortMonths[d.getUTCMonth()];
1503 }
1504
1505 function formatUTCMonth(d) {
1506 return locale_months[d.getUTCMonth()];
1507 }
1508
1509 function formatUTCPeriod(d) {
1510 return locale_periods[+(d.getUTCHours() >= 12)];
1511 }
1512
1513 function formatUTCQuarter(d) {
1514 return 1 + ~~(d.getUTCMonth() / 3);
1515 }
1516
1517 return {
1518 format: function(specifier) {
1519 var f = newFormat(specifier += "", formats);
1520 f.toString = function() { return specifier; };
1521 return f;
1522 },
1523 parse: function(specifier) {
1524 var p = newParse(specifier += "", false);
1525 p.toString = function() { return specifier; };
1526 return p;
1527 },
1528 utcFormat: function(specifier) {
1529 var f = newFormat(specifier += "", utcFormats);
1530 f.toString = function() { return specifier; };
1531 return f;
1532 },
1533 utcParse: function(specifier) {
1534 var p = newParse(specifier += "", true);
1535 p.toString = function() { return specifier; };
1536 return p;
1537 }
1538 };
1539 }
1540
1541 var pads = {"-": "", "_": " ", "0": "0"},
1542 numberRe = /^\s*\d+/, // note: ignores next directive
1543 percentRe = /^%/,
1544 requoteRe = /[\\^$*+?|[\]().{}]/g;
1545
1546 function pad(value, fill, width) {
1547 var sign = value < 0 ? "-" : "",
1548 string = (sign ? -value : value) + "",
1549 length = string.length;
1550 return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string);
1551 }
1552
1553 function requote(s) {
1554 return s.replace(requoteRe, "\\$&");
1555 }
1556
1557 function formatRe(names) {
1558 return new RegExp("^(?:" + names.map(requote).join("|") + ")", "i");
1559 }
1560
1561 function formatLookup(names) {
1562 var map = {}, i = -1, n = names.length;
1563 while (++i < n) map[names[i].toLowerCase()] = i;
1564 return map;
1565 }
1566
1567 function parseWeekdayNumberSunday(d, string, i) {
1568 var n = numberRe.exec(string.slice(i, i + 1));
1569 return n ? (d.w = +n[0], i + n[0].length) : -1;
1570 }
1571
1572 function parseWeekdayNumberMonday(d, string, i) {
1573 var n = numberRe.exec(string.slice(i, i + 1));
1574 return n ? (d.u = +n[0], i + n[0].length) : -1;
1575 }
1576
1577 function parseWeekNumberSunday(d, string, i) {
1578 var n = numberRe.exec(string.slice(i, i + 2));
1579 return n ? (d.U = +n[0], i + n[0].length) : -1;
1580 }
1581
1582 function parseWeekNumberISO(d, string, i) {
1583 var n = numberRe.exec(string.slice(i, i + 2));
1584 return n ? (d.V = +n[0], i + n[0].length) : -1;
1585 }
1586
1587 function parseWeekNumberMonday(d, string, i) {
1588 var n = numberRe.exec(string.slice(i, i + 2));
1589 return n ? (d.W = +n[0], i + n[0].length) : -1;
1590 }
1591
1592 function parseFullYear(d, string, i) {
1593 var n = numberRe.exec(string.slice(i, i + 4));
1594 return n ? (d.y = +n[0], i + n[0].length) : -1;
1595 }
1596
1597 function parseYear(d, string, i) {
1598 var n = numberRe.exec(string.slice(i, i + 2));
1599 return n ? (d.y = +n[0] + (+n[0] > 68 ? 1900 : 2000), i + n[0].length) : -1;
1600 }
1601
1602 function parseZone(d, string, i) {
1603 var n = /^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(string.slice(i, i + 6));
1604 return n ? (d.Z = n[1] ? 0 : -(n[2] + (n[3] || "00")), i + n[0].length) : -1;
1605 }
1606
1607 function parseQuarter(d, string, i) {
1608 var n = numberRe.exec(string.slice(i, i + 1));
1609 return n ? (d.q = n[0] * 3 - 3, i + n[0].length) : -1;
1610 }
1611
1612 function parseMonthNumber(d, string, i) {
1613 var n = numberRe.exec(string.slice(i, i + 2));
1614 return n ? (d.m = n[0] - 1, i + n[0].length) : -1;
1615 }
1616
1617 function parseDayOfMonth(d, string, i) {
1618 var n = numberRe.exec(string.slice(i, i + 2));
1619 return n ? (d.d = +n[0], i + n[0].length) : -1;
1620 }
1621
1622 function parseDayOfYear(d, string, i) {
1623 var n = numberRe.exec(string.slice(i, i + 3));
1624 return n ? (d.m = 0, d.d = +n[0], i + n[0].length) : -1;
1625 }
1626
1627 function parseHour24(d, string, i) {
1628 var n = numberRe.exec(string.slice(i, i + 2));
1629 return n ? (d.H = +n[0], i + n[0].length) : -1;
1630 }
1631
1632 function parseMinutes(d, string, i) {
1633 var n = numberRe.exec(string.slice(i, i + 2));
1634 return n ? (d.M = +n[0], i + n[0].length) : -1;
1635 }
1636
1637 function parseSeconds(d, string, i) {
1638 var n = numberRe.exec(string.slice(i, i + 2));
1639 return n ? (d.S = +n[0], i + n[0].length) : -1;
1640 }
1641
1642 function parseMilliseconds(d, string, i) {
1643 var n = numberRe.exec(string.slice(i, i + 3));
1644 return n ? (d.L = +n[0], i + n[0].length) : -1;
1645 }
1646
1647 function parseMicroseconds(d, string, i) {
1648 var n = numberRe.exec(string.slice(i, i + 6));
1649 return n ? (d.L = Math.floor(n[0] / 1000), i + n[0].length) : -1;
1650 }
1651
1652 function parseLiteralPercent(d, string, i) {
1653 var n = percentRe.exec(string.slice(i, i + 1));
1654 return n ? i + n[0].length : -1;
1655 }
1656
1657 function parseUnixTimestamp(d, string, i) {
1658 var n = numberRe.exec(string.slice(i));
1659 return n ? (d.Q = +n[0], i + n[0].length) : -1;
1660 }
1661
1662 function parseUnixTimestampSeconds(d, string, i) {
1663 var n = numberRe.exec(string.slice(i));
1664 return n ? (d.s = +n[0], i + n[0].length) : -1;
1665 }
1666
1667 function formatDayOfMonth(d, p) {
1668 return pad(d.getDate(), p, 2);
1669 }
1670
1671 function formatHour24(d, p) {
1672 return pad(d.getHours(), p, 2);
1673 }
1674
1675 function formatHour12(d, p) {
1676 return pad(d.getHours() % 12 || 12, p, 2);
1677 }
1678
1679 function formatDayOfYear(d, p) {
1680 return pad(1 + day.count(year(d), d), p, 3);
1681 }
1682
1683 function formatMilliseconds(d, p) {
1684 return pad(d.getMilliseconds(), p, 3);
1685 }
1686
1687 function formatMicroseconds(d, p) {
1688 return formatMilliseconds(d, p) + "000";
1689 }
1690
1691 function formatMonthNumber(d, p) {
1692 return pad(d.getMonth() + 1, p, 2);
1693 }
1694
1695 function formatMinutes(d, p) {
1696 return pad(d.getMinutes(), p, 2);
1697 }
1698
1699 function formatSeconds(d, p) {
1700 return pad(d.getSeconds(), p, 2);
1701 }
1702
1703 function formatWeekdayNumberMonday(d) {
1704 var day = d.getDay();
1705 return day === 0 ? 7 : day;
1706 }
1707
1708 function formatWeekNumberSunday(d, p) {
1709 return pad(sunday.count(year(d) - 1, d), p, 2);
1710 }
1711
1712 function formatWeekNumberISO(d, p) {
1713 var day = d.getDay();
1714 d = (day >= 4 || day === 0) ? thursday(d) : thursday.ceil(d);
1715 return pad(thursday.count(year(d), d) + (year(d).getDay() === 4), p, 2);
1716 }
1717
1718 function formatWeekdayNumberSunday(d) {
1719 return d.getDay();
1720 }
1721
1722 function formatWeekNumberMonday(d, p) {
1723 return pad(monday.count(year(d) - 1, d), p, 2);
1724 }
1725
1726 function formatYear(d, p) {
1727 return pad(d.getFullYear() % 100, p, 2);
1728 }
1729
1730 function formatFullYear(d, p) {
1731 return pad(d.getFullYear() % 10000, p, 4);
1732 }
1733
1734 function formatZone(d) {
1735 var z = d.getTimezoneOffset();
1736 return (z > 0 ? "-" : (z *= -1, "+"))
1737 + pad(z / 60 | 0, "0", 2)
1738 + pad(z % 60, "0", 2);
1739 }
1740
1741 function formatUTCDayOfMonth(d, p) {
1742 return pad(d.getUTCDate(), p, 2);
1743 }
1744
1745 function formatUTCHour24(d, p) {
1746 return pad(d.getUTCHours(), p, 2);
1747 }
1748
1749 function formatUTCHour12(d, p) {
1750 return pad(d.getUTCHours() % 12 || 12, p, 2);
1751 }
1752
1753 function formatUTCDayOfYear(d, p) {
1754 return pad(1 + utcDay.count(utcYear(d), d), p, 3);
1755 }
1756
1757 function formatUTCMilliseconds(d, p) {
1758 return pad(d.getUTCMilliseconds(), p, 3);
1759 }
1760
1761 function formatUTCMicroseconds(d, p) {
1762 return formatUTCMilliseconds(d, p) + "000";
1763 }
1764
1765 function formatUTCMonthNumber(d, p) {
1766 return pad(d.getUTCMonth() + 1, p, 2);
1767 }
1768
1769 function formatUTCMinutes(d, p) {
1770 return pad(d.getUTCMinutes(), p, 2);
1771 }
1772
1773 function formatUTCSeconds(d, p) {
1774 return pad(d.getUTCSeconds(), p, 2);
1775 }
1776
1777 function formatUTCWeekdayNumberMonday(d) {
1778 var dow = d.getUTCDay();
1779 return dow === 0 ? 7 : dow;
1780 }
1781
1782 function formatUTCWeekNumberSunday(d, p) {
1783 return pad(utcSunday.count(utcYear(d) - 1, d), p, 2);
1784 }
1785
1786 function formatUTCWeekNumberISO(d, p) {
1787 var day = d.getUTCDay();
1788 d = (day >= 4 || day === 0) ? utcThursday(d) : utcThursday.ceil(d);
1789 return pad(utcThursday.count(utcYear(d), d) + (utcYear(d).getUTCDay() === 4), p, 2);
1790 }
1791
1792 function formatUTCWeekdayNumberSunday(d) {
1793 return d.getUTCDay();
1794 }
1795
1796 function formatUTCWeekNumberMonday(d, p) {
1797 return pad(utcMonday.count(utcYear(d) - 1, d), p, 2);
1798 }
1799
1800 function formatUTCYear(d, p) {
1801 return pad(d.getUTCFullYear() % 100, p, 2);
1802 }
1803
1804 function formatUTCFullYear(d, p) {
1805 return pad(d.getUTCFullYear() % 10000, p, 4);
1806 }
1807
1808 function formatUTCZone() {
1809 return "+0000";
1810 }
1811
1812 function formatLiteralPercent() {
1813 return "%";
1814 }
1815
1816 function formatUnixTimestamp(d) {
1817 return +d;
1818 }
1819
1820 function formatUnixTimestampSeconds(d) {
1821 return Math.floor(+d / 1000);
1822 }
1823
1824 var locale;
1825 var timeFormat;
1826 var timeParse;
1827 var utcFormat;
1828 var utcParse;
1829
1830 defaultLocale({
1831 dateTime: "%x, %X",
1832 date: "%-m/%-d/%Y",
1833 time: "%-I:%M:%S %p",
1834 periods: ["AM", "PM"],
1835 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
1836 shortDays: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
1837 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
1838 shortMonths: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
1839 });
1840
1841 function defaultLocale(definition) {
1842 locale = formatLocale(definition);
1843 timeFormat = locale.format;
1844 timeParse = locale.parse;
1845 utcFormat = locale.utcFormat;
1846 utcParse = locale.utcParse;
1847 return locale;
1848 }
1849
1850 var isoSpecifier = "%Y-%m-%dT%H:%M:%S.%LZ";
1851
1852 function formatIsoNative(date) {
1853 return date.toISOString();
1854 }
1855
1856 var formatIso = Date.prototype.toISOString
1857 ? formatIsoNative
1858 : utcFormat(isoSpecifier);
1859
1860 function parseIsoNative(string) {
1861 var date = new Date(string);
1862 return isNaN(date) ? null : date;
1863 }
1864
1865 var parseIso = +new Date("2000-01-01T00:00:00.000Z")
1866 ? parseIsoNative
1867 : utcParse(isoSpecifier);
1868
1869 function normalizeLetters(str) {
1870 return str.toLowerCase();
1871 }
1872
1873 function ascending$1(a, b) {
1874 return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
1875 }
1876
1877 /* function descending(a, b) {
1878 return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
1879 } */
1880
1881
1882 function sortArray(uarr, state, numericParser, numericFormatter) {
1883 var is_numeric = state.sort === "numeric";
1884 var is_temporal = state.sort === "temporal";
1885 var parser = normalizeLetters;
1886 if (is_numeric) parser = numericParser;
1887 else if (is_temporal) parser = timeParse(state.sort_temporal_format);
1888
1889 var sarr = uarr.map(function(d, i) {
1890 var parsed_value = parser(d);
1891 return {
1892 value: d,
1893 options_index: i,
1894 parsed: parsed_value,
1895 display: is_numeric && !isNaN(parsed_value) ? numericFormatter(parsed_value) : d
1896 };
1897 });
1898
1899 if (state.sort == "unsorted") return sarr;
1900 return sarr.sort(function(a, b) { return ascending$1(a.parsed, b.parsed); });
1901 }
1902
1903 function Stylesheet() {
1904 this.declarations = [];
1905 return this;
1906 }
1907
1908 Stylesheet.prototype.select = function(selector) {
1909 if (!selector) return this;
1910 var declaration = new Declaration(selector, this);
1911 declaration.parent = this;
1912 this.addDeclaration(declaration);
1913
1914 return declaration;
1915 };
1916
1917 Stylesheet.prototype.addDeclaration = function(declaration) {
1918 this.declarations.push(declaration);
1919 return this;
1920 };
1921
1922 Stylesheet.prototype.print = function() {
1923 var text = "";
1924 this.declarations.forEach(function(declaration) {
1925 text += declaration.selector + " {\n";
1926 declaration.styles.forEach(function(style) {
1927 text += "\t" + style[0] + ": " + style[1] + ";\n";
1928 });
1929 text += "}\n\n";
1930 });
1931 return text;
1932 };
1933
1934 Stylesheet.prototype.clear = function() {
1935 this.declarations = [];
1936 return this;
1937 };
1938
1939 function Declaration(selector) {
1940 this.selector = selector;
1941 this.styles = [];
1942 return this;
1943 }
1944
1945 Declaration.prototype.style = function(property, _value) {
1946 var value = typeof value_ == "function" ? _value() : _value;
1947 if (value !== "" && value !== null && value !== undefined) this.styles.push([property, value]);
1948 return this;
1949 };
1950
1951 Declaration.prototype.select = function(selector) {
1952 return this.parent.select(this.selector + " " + selector);
1953 };
1954
1955 function createCssString() {
1956 var s = new Stylesheet();
1957
1958 s.select(".fl-controls-container")
1959 .style("display", "inline-block")
1960 .style("line-height", "1em");
1961
1962 s.select(".fl-controls-title")
1963 .style("display", "inline-block")
1964 .style("margin", "0px 0.5em 0px 0px");
1965
1966 s.select(".fl-controls-container, .fl-controls-container *")
1967 .style("box-sizing", "border-box");
1968
1969 s.select(".slider-holder")
1970 .style("margin-bottom", "20px");
1971
1972 s.select(".fl-controls-slider, .slider-play")
1973 .style("pointer-events", "all")
1974 .style("display", "inline-block")
1975 .style("vertical-align", "middle");
1976
1977 s.select(".slider-play svg")
1978 .style("height", "100%")
1979 .style("width", "100%")
1980 .style("cursor", " pointer", "");
1981
1982 s.select(".slider-play:hover")
1983 .style("opacity", "0.6");
1984
1985 s.select(".fl-control-slider")
1986 .style("width", "100%")
1987 .style("bottom", "0");
1988
1989 s.select(".fl-control")
1990 .style("position", "relative");
1991
1992 s.select(".fl-control.hidden")
1993 .style("display", "none");
1994
1995 s.select(".fl-control .button")
1996 .style("display", "inline-block")
1997 .style("background", "#eee")
1998 .style("padding", "0.5em")
1999 .style("margin-right", "0.25em")
2000 .style("margin-bottom", "0.25em")
2001 .style("line-height", "1em");
2002
2003 s.select(".fl-control.grouped:not(.hidden)")
2004 .style("display", "inline-table")
2005 .style("table-layout", "fixed")
2006 .select(".button")
2007 .style("display", "table-cell")
2008 .style("margin", "0")
2009 .style("text-align", "center");
2010
2011 s.select(".fl-control .button.selected")
2012 .style("background", "#ddd");
2013
2014 s.select(".fl-control-dropdown")
2015 .style("line-height", "1em")
2016 .select(".list")
2017 .style("display", "none")
2018 .style("position", "absolute")
2019 .style("background-color", "white")
2020 .style("z-index", "100")
2021 .style("border", "1px solid #eee")
2022 .select(".list-item")
2023 .style("text-align", "start")
2024 .style("cursor", "pointer")
2025 .style("padding", "0.5rem");
2026
2027 s.select(".fl-control-dropdown.open .list")
2028 .style("display", "block");
2029
2030 s.select(".fl-control-dropdown .main")
2031 .style("position", "relative")
2032 .style("text-align", "start")
2033 .style("display", "flex")
2034 .style("justify-content", "space-between");
2035
2036 s.select(".fl-control-dropdown .symbol")
2037 .style("float", "right")
2038 .select("div")
2039 .style("border-top-color", "#333333");
2040
2041 return s.print();
2042 }
2043
2044 var css_injected = false;
2045
2046 function injectCSS() {
2047 if (css_injected || typeof document === "undefined") return;
2048
2049 var css_string = createCssString();
2050
2051 var head = document.head || document.getElementsByTagName("head")[0];
2052 var style = document.createElement("style");
2053 style.type = "text/css";
2054 style.className = "flourish-controls";
2055 head.appendChild(style);
2056 if (style.styleSheet) {
2057 style.styleSheet.cssText = css_string;
2058 }
2059 else {
2060 style.appendChild(document.createTextNode(css_string));
2061 }
2062 css_injected = true;
2063 }
2064
2065 var getTextWidth = (function() {
2066 var context = document.createElement("canvas").getContext("2d");
2067 return function(text, font) {
2068 context.font = font || "10px sans-serif";
2069 var metrics = context.measureText(text);
2070 return metrics.width;
2071 };
2072 })();
2073
2074 var remToPx;
2075
2076 function getRemToPx() {
2077 var font_size = parseFloat(getComputedStyle(document.documentElement).fontSize) || 16;
2078 remToPx = function (rem) { return rem * font_size; };
2079 }
2080
2081 var MIN_HEIGHT = 90;
2082 function createDropdown(control_obj, state, container) {
2083 var dropdown_obj = {};
2084 var bounding_container = document.body;
2085
2086 // Add dropdown elements to container
2087 var dropdown = select(container).append("div").attr("class", "fl-control fl-control-dropdown");
2088 var dropdown_node = dropdown.node();
2089 var dropdown_main = dropdown.append("div").attr("class", "main");
2090 var dropdown_current = dropdown_main.append("span").attr("class", "current");
2091 dropdown_main.append("span").attr("class", "symbol").style("width", "10px")
2092 .append("div")
2093 .style("border-left", "5px solid transparent")
2094 .style("border-right", "5px solid transparent")
2095 .style("border-bottom", "5px solid transparent")
2096 .style("border-top-style", "solid")
2097 .style("border-top-width", "5px")
2098 .style("top", "50%")
2099 .style("position", "absolute")
2100 .style("margin-top", "-2.5px");
2101 var dropdown_list = dropdown.append("div").attr("class", "list");
2102
2103 var showDropdownList = function() {
2104 dropdown.classed("open", true);
2105 dropdown_list.style("top", "100%");
2106 dropdown_list.style("bottom", null);
2107 dropdown_list.style("display", null);
2108 dropdown_list.style("overflow", "auto");
2109 var bounding_bb = bounding_container.getBoundingClientRect();
2110 var list_bb = dropdown_list.node().getBoundingClientRect();
2111 var overspill = list_bb.bottom - bounding_bb.bottom;
2112 if (overspill > 0) {
2113 var new_height = bounding_bb.bottom - list_bb.top - 30;
2114 if (new_height > MIN_HEIGHT) dropdown_list.style("max-height", new_height + "px");
2115 else dropdown_list
2116 .style("top", "auto")
2117 .style("bottom", "100%");
2118 }
2119 if (list_bb.right > window.innerWidth) {
2120 dropdown_list.style("right", 0);
2121 }
2122 };
2123
2124 var hideDropdownList = function() {
2125 dropdown.classed("open", false);
2126 dropdown_list.style("right", null);
2127 dropdown_list.style("max-height", null);
2128 dropdown_list.style("display", "none");
2129 };
2130
2131 var toggleDropdownList = function() {
2132 if (dropdown.classed("open")) hideDropdownList();
2133 else showDropdownList();
2134 };
2135
2136 dropdown_main.on("click", function() { toggleDropdownList(); });
2137
2138 var clickHandler = function() {
2139 if (!dropdown.classed("open")) return; // If already closed, nothing to close
2140 var el = event.target;
2141 var parent = el.parentElement;
2142 while (parent) {
2143 if (el === dropdown_node) return; // We've clicked the dropdown, don't close it here
2144 el = parent;
2145 parent = el.parentElement;
2146 }
2147 hideDropdownList(); // Clicked somewhere else, hide the dropdown
2148 };
2149
2150 var showControl = function(longest_text_width) {
2151 var dropdown_width = "100%";
2152 if (state.dropdown_width_mode == "auto") {
2153 dropdown_width = Math.min(longest_text_width + 40, remToPx(20)) + "px";
2154 }
2155 else if (state.dropdown_width_mode == "fixed") {
2156 dropdown_width = remToPx(state.dropdown_width_fixed) + "px";
2157 }
2158 container.style.width = state.dropdown_width_mode == "full" ? dropdown_width : "";
2159 dropdown.style("width", dropdown_width).style("display", state.dropdown_width_mode !== "full" ? "inline-table" : null);
2160 dropdown.select(".main").style("width", dropdown_width);
2161 };
2162
2163 var hideControl = function() {
2164 hideDropdownList();
2165 dropdown.style("display", "none");
2166 };
2167
2168
2169 dropdown_obj.appendedToDOM = function(_bounding_container) {
2170 if (_bounding_container) bounding_container = _bounding_container;
2171 document.querySelector("body").addEventListener("click", clickHandler, false);
2172 return dropdown_obj;
2173 };
2174
2175
2176 dropdown_obj.removedFromDOM = function() {
2177 document.querySelector("body").removeEventListener("click", clickHandler);
2178 return dropdown_obj;
2179 };
2180
2181 dropdown_obj.show = showControl;
2182 dropdown_obj.hide = hideControl;
2183
2184
2185 dropdown_obj.update = function(sorted_options) {
2186 dropdown_list.text("");
2187 var dropdown_font_size = window.getComputedStyle(dropdown.node()).fontSize;
2188 if (!control_obj.n_options || state.control_type !== "dropdown") {
2189 hideControl();
2190 return dropdown_obj;
2191 }
2192
2193 var longest_text = "";
2194 dropdown_list.text("")
2195 .selectAll(".list-item")
2196 .data(sorted_options)
2197 .enter()
2198 .append("div")
2199 .attr("class", "list-item")
2200 .text(function(d) {
2201 if (d.display.length > longest_text.length) longest_text = d.display;
2202 return d.display;
2203 })
2204 .on("click", function(d) {
2205 hideDropdownList();
2206 var i = d.options_index;
2207 if (i === control_obj.index()) return;
2208 control_obj.index(i);
2209 dropdown_current.text(d.display).attr("title", d.display);
2210 control_obj.trigger("change");
2211 });
2212 var longest_text_width = getTextWidth(longest_text, dropdown_font_size + " sans-serif");
2213 var sorted_index = control_obj.getSortedIndex();
2214 var value = sorted_options[sorted_index].display;
2215 dropdown_current.text(value).attr("title", value);
2216
2217 showControl(longest_text_width);
2218
2219 return dropdown_obj;
2220 };
2221
2222
2223 return dropdown_obj;
2224 }
2225
2226 function createButtons(control_obj, state, container) {
2227 var button_obj = {};
2228 var button_container = select(container).append("div").attr("class", "fl-control fl-control-buttons");
2229
2230 var showControl = function() {
2231 button_container.classed("hidden", false);
2232 };
2233
2234 var hideControl = function() {
2235 button_container.classed("hidden", true);
2236 };
2237
2238 button_obj.show = showControl;
2239 button_obj.hide = hideControl;
2240
2241 button_obj.update = function(sorted_options) {
2242 if (!control_obj.n_options || state.control_type !== "buttons") {
2243 hideControl();
2244 return button_obj;
2245 }
2246
2247 var index = control_obj.index();
2248
2249 button_container.classed("grouped", state.button_group);
2250 button_container.classed("fixed-width", state.button_group_width_mode == "fixed" || state.button_group_width_mode == "full");
2251 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);
2252
2253 var clickHandler = function(d) {
2254 var i = d.options_index;
2255 if (i === control_obj.index()) return;
2256 control_obj.index(i);
2257 control_obj.trigger("change");
2258 };
2259
2260 var buttons = button_container.selectAll(".button")
2261 .data(sorted_options, function(d) { return d.display; });
2262
2263 var buttons_enter = buttons.enter()
2264 .append("div")
2265 .attr("class", "button")
2266 .style("cursor", "pointer")
2267 .attr("role", "button")
2268 .attr("tabindex", 0)
2269 .on("click", function(d) { clickHandler(d); })
2270 .on("keyup", function(d) {
2271 if (event$1.code === "Space" || event$1.code === "Enter") {
2272 event$1.preventDefault();
2273 clickHandler(d);
2274 }
2275 });
2276
2277 buttons_enter.append("span")
2278 .text(function(d) { return d.display; });
2279
2280 showControl();
2281
2282 buttons.merge(buttons_enter)
2283 .classed("selected", function(d) { return d.options_index === index; })
2284 .attr("aria-pressed", function(d) { return d.options_index === index; });
2285
2286 buttons.exit().remove();
2287
2288 showControl();
2289 };
2290
2291 return button_obj;
2292 }
2293
2294 var slice = Array.prototype.slice;
2295
2296 function identity(x) {
2297 return x;
2298 }
2299
2300 var top = 1,
2301 right = 2,
2302 bottom = 3,
2303 left = 4,
2304 epsilon = 1e-6;
2305
2306 function translateX(x) {
2307 return "translate(" + (x + 0.5) + ",0)";
2308 }
2309
2310 function translateY(y) {
2311 return "translate(0," + (y + 0.5) + ")";
2312 }
2313
2314 function number(scale) {
2315 return function(d) {
2316 return +scale(d);
2317 };
2318 }
2319
2320 function center(scale) {
2321 var offset = Math.max(0, scale.bandwidth() - 1) / 2; // Adjust for 0.5px offset.
2322 if (scale.round()) offset = Math.round(offset);
2323 return function(d) {
2324 return +scale(d) + offset;
2325 };
2326 }
2327
2328 function entering() {
2329 return !this.__axis;
2330 }
2331
2332 function axis(orient, scale) {
2333 var tickArguments = [],
2334 tickValues = null,
2335 tickFormat = null,
2336 tickSizeInner = 6,
2337 tickSizeOuter = 6,
2338 tickPadding = 3,
2339 k = orient === top || orient === left ? -1 : 1,
2340 x = orient === left || orient === right ? "x" : "y",
2341 transform = orient === top || orient === bottom ? translateX : translateY;
2342
2343 function axis(context) {
2344 var values = tickValues == null ? (scale.ticks ? scale.ticks.apply(scale, tickArguments) : scale.domain()) : tickValues,
2345 format = tickFormat == null ? (scale.tickFormat ? scale.tickFormat.apply(scale, tickArguments) : identity) : tickFormat,
2346 spacing = Math.max(tickSizeInner, 0) + tickPadding,
2347 range = scale.range(),
2348 range0 = +range[0] + 0.5,
2349 range1 = +range[range.length - 1] + 0.5,
2350 position = (scale.bandwidth ? center : number)(scale.copy()),
2351 selection = context.selection ? context.selection() : context,
2352 path = selection.selectAll(".domain").data([null]),
2353 tick = selection.selectAll(".tick").data(values, scale).order(),
2354 tickExit = tick.exit(),
2355 tickEnter = tick.enter().append("g").attr("class", "tick"),
2356 line = tick.select("line"),
2357 text = tick.select("text");
2358
2359 path = path.merge(path.enter().insert("path", ".tick")
2360 .attr("class", "domain")
2361 .attr("stroke", "currentColor"));
2362
2363 tick = tick.merge(tickEnter);
2364
2365 line = line.merge(tickEnter.append("line")
2366 .attr("stroke", "currentColor")
2367 .attr(x + "2", k * tickSizeInner));
2368
2369 text = text.merge(tickEnter.append("text")
2370 .attr("fill", "currentColor")
2371 .attr(x, k * spacing)
2372 .attr("dy", orient === top ? "0em" : orient === bottom ? "0.71em" : "0.32em"));
2373
2374 if (context !== selection) {
2375 path = path.transition(context);
2376 tick = tick.transition(context);
2377 line = line.transition(context);
2378 text = text.transition(context);
2379
2380 tickExit = tickExit.transition(context)
2381 .attr("opacity", epsilon)
2382 .attr("transform", function(d) { return isFinite(d = position(d)) ? transform(d) : this.getAttribute("transform"); });
2383
2384 tickEnter
2385 .attr("opacity", epsilon)
2386 .attr("transform", function(d) { var p = this.parentNode.__axis; return transform(p && isFinite(p = p(d)) ? p : position(d)); });
2387 }
2388
2389 tickExit.remove();
2390
2391 path
2392 .attr("d", orient === left || orient == right
2393 ? (tickSizeOuter ? "M" + k * tickSizeOuter + "," + range0 + "H0.5V" + range1 + "H" + k * tickSizeOuter : "M0.5," + range0 + "V" + range1)
2394 : (tickSizeOuter ? "M" + range0 + "," + k * tickSizeOuter + "V0.5H" + range1 + "V" + k * tickSizeOuter : "M" + range0 + ",0.5H" + range1));
2395
2396 tick
2397 .attr("opacity", 1)
2398 .attr("transform", function(d) { return transform(position(d)); });
2399
2400 line
2401 .attr(x + "2", k * tickSizeInner);
2402
2403 text
2404 .attr(x, k * spacing)
2405 .text(format);
2406
2407 selection.filter(entering)
2408 .attr("fill", "none")
2409 .attr("font-size", 10)
2410 .attr("font-family", "sans-serif")
2411 .attr("text-anchor", orient === right ? "start" : orient === left ? "end" : "middle");
2412
2413 selection
2414 .each(function() { this.__axis = position; });
2415 }
2416
2417 axis.scale = function(_) {
2418 return arguments.length ? (scale = _, axis) : scale;
2419 };
2420
2421 axis.ticks = function() {
2422 return tickArguments = slice.call(arguments), axis;
2423 };
2424
2425 axis.tickArguments = function(_) {
2426 return arguments.length ? (tickArguments = _ == null ? [] : slice.call(_), axis) : tickArguments.slice();
2427 };
2428
2429 axis.tickValues = function(_) {
2430 return arguments.length ? (tickValues = _ == null ? null : slice.call(_), axis) : tickValues && tickValues.slice();
2431 };
2432
2433 axis.tickFormat = function(_) {
2434 return arguments.length ? (tickFormat = _, axis) : tickFormat;
2435 };
2436
2437 axis.tickSize = function(_) {
2438 return arguments.length ? (tickSizeInner = tickSizeOuter = +_, axis) : tickSizeInner;
2439 };
2440
2441 axis.tickSizeInner = function(_) {
2442 return arguments.length ? (tickSizeInner = +_, axis) : tickSizeInner;
2443 };
2444
2445 axis.tickSizeOuter = function(_) {
2446 return arguments.length ? (tickSizeOuter = +_, axis) : tickSizeOuter;
2447 };
2448
2449 axis.tickPadding = function(_) {
2450 return arguments.length ? (tickPadding = +_, axis) : tickPadding;
2451 };
2452
2453 return axis;
2454 }
2455
2456 function axisBottom(scale) {
2457 return axis(bottom, scale);
2458 }
2459
2460 function ascending$2(a, b) {
2461 return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
2462 }
2463
2464 function bisector(compare) {
2465 if (compare.length === 1) compare = ascendingComparator(compare);
2466 return {
2467 left: function(a, x, lo, hi) {
2468 if (lo == null) lo = 0;
2469 if (hi == null) hi = a.length;
2470 while (lo < hi) {
2471 var mid = lo + hi >>> 1;
2472 if (compare(a[mid], x) < 0) lo = mid + 1;
2473 else hi = mid;
2474 }
2475 return lo;
2476 },
2477 right: function(a, x, lo, hi) {
2478 if (lo == null) lo = 0;
2479 if (hi == null) hi = a.length;
2480 while (lo < hi) {
2481 var mid = lo + hi >>> 1;
2482 if (compare(a[mid], x) > 0) hi = mid;
2483 else lo = mid + 1;
2484 }
2485 return lo;
2486 }
2487 };
2488 }
2489
2490 function ascendingComparator(f) {
2491 return function(d, x) {
2492 return ascending$2(f(d), x);
2493 };
2494 }
2495
2496 var ascendingBisect = bisector(ascending$2);
2497 var bisectRight = ascendingBisect.right;
2498
2499 var e10 = Math.sqrt(50),
2500 e5 = Math.sqrt(10),
2501 e2 = Math.sqrt(2);
2502
2503 function ticks(start, stop, count) {
2504 var reverse,
2505 i = -1,
2506 n,
2507 ticks,
2508 step;
2509
2510 stop = +stop, start = +start, count = +count;
2511 if (start === stop && count > 0) return [start];
2512 if (reverse = stop < start) n = start, start = stop, stop = n;
2513 if ((step = tickIncrement(start, stop, count)) === 0 || !isFinite(step)) return [];
2514
2515 if (step > 0) {
2516 start = Math.ceil(start / step);
2517 stop = Math.floor(stop / step);
2518 ticks = new Array(n = Math.ceil(stop - start + 1));
2519 while (++i < n) ticks[i] = (start + i) * step;
2520 } else {
2521 start = Math.floor(start * step);
2522 stop = Math.ceil(stop * step);
2523 ticks = new Array(n = Math.ceil(start - stop + 1));
2524 while (++i < n) ticks[i] = (start - i) / step;
2525 }
2526
2527 if (reverse) ticks.reverse();
2528
2529 return ticks;
2530 }
2531
2532 function tickIncrement(start, stop, count) {
2533 var step = (stop - start) / Math.max(0, count),
2534 power = Math.floor(Math.log(step) / Math.LN10),
2535 error = step / Math.pow(10, power);
2536 return power >= 0
2537 ? (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1) * Math.pow(10, power)
2538 : -Math.pow(10, -power) / (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1);
2539 }
2540
2541 function tickStep(start, stop, count) {
2542 var step0 = Math.abs(stop - start) / Math.max(0, count),
2543 step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)),
2544 error = step0 / step1;
2545 if (error >= e10) step1 *= 10;
2546 else if (error >= e5) step1 *= 5;
2547 else if (error >= e2) step1 *= 2;
2548 return stop < start ? -step1 : step1;
2549 }
2550
2551 function define(constructor, factory, prototype) {
2552 constructor.prototype = factory.prototype = prototype;
2553 prototype.constructor = constructor;
2554 }
2555
2556 function extend(parent, definition) {
2557 var prototype = Object.create(parent.prototype);
2558 for (var key in definition) prototype[key] = definition[key];
2559 return prototype;
2560 }
2561
2562 function Color() {}
2563
2564 var darker = 0.7;
2565 var brighter = 1 / darker;
2566
2567 var reI = "\\s*([+-]?\\d+)\\s*",
2568 reN = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*",
2569 reP = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*",
2570 reHex3 = /^#([0-9a-f]{3})$/,
2571 reHex6 = /^#([0-9a-f]{6})$/,
2572 reRgbInteger = new RegExp("^rgb\\(" + [reI, reI, reI] + "\\)$"),
2573 reRgbPercent = new RegExp("^rgb\\(" + [reP, reP, reP] + "\\)$"),
2574 reRgbaInteger = new RegExp("^rgba\\(" + [reI, reI, reI, reN] + "\\)$"),
2575 reRgbaPercent = new RegExp("^rgba\\(" + [reP, reP, reP, reN] + "\\)$"),
2576 reHslPercent = new RegExp("^hsl\\(" + [reN, reP, reP] + "\\)$"),
2577 reHslaPercent = new RegExp("^hsla\\(" + [reN, reP, reP, reN] + "\\)$");
2578
2579 var named = {
2580 aliceblue: 0xf0f8ff,
2581 antiquewhite: 0xfaebd7,
2582 aqua: 0x00ffff,
2583 aquamarine: 0x7fffd4,
2584 azure: 0xf0ffff,
2585 beige: 0xf5f5dc,
2586 bisque: 0xffe4c4,
2587 black: 0x000000,
2588 blanchedalmond: 0xffebcd,
2589 blue: 0x0000ff,
2590 blueviolet: 0x8a2be2,
2591 brown: 0xa52a2a,
2592 burlywood: 0xdeb887,
2593 cadetblue: 0x5f9ea0,
2594 chartreuse: 0x7fff00,
2595 chocolate: 0xd2691e,
2596 coral: 0xff7f50,
2597 cornflowerblue: 0x6495ed,
2598 cornsilk: 0xfff8dc,
2599 crimson: 0xdc143c,
2600 cyan: 0x00ffff,
2601 darkblue: 0x00008b,
2602 darkcyan: 0x008b8b,
2603 darkgoldenrod: 0xb8860b,
2604 darkgray: 0xa9a9a9,
2605 darkgreen: 0x006400,
2606 darkgrey: 0xa9a9a9,
2607 darkkhaki: 0xbdb76b,
2608 darkmagenta: 0x8b008b,
2609 darkolivegreen: 0x556b2f,
2610 darkorange: 0xff8c00,
2611 darkorchid: 0x9932cc,
2612 darkred: 0x8b0000,
2613 darksalmon: 0xe9967a,
2614 darkseagreen: 0x8fbc8f,
2615 darkslateblue: 0x483d8b,
2616 darkslategray: 0x2f4f4f,
2617 darkslategrey: 0x2f4f4f,
2618 darkturquoise: 0x00ced1,
2619 darkviolet: 0x9400d3,
2620 deeppink: 0xff1493,
2621 deepskyblue: 0x00bfff,
2622 dimgray: 0x696969,
2623 dimgrey: 0x696969,
2624 dodgerblue: 0x1e90ff,
2625 firebrick: 0xb22222,
2626 floralwhite: 0xfffaf0,
2627 forestgreen: 0x228b22,
2628 fuchsia: 0xff00ff,
2629 gainsboro: 0xdcdcdc,
2630 ghostwhite: 0xf8f8ff,
2631 gold: 0xffd700,
2632 goldenrod: 0xdaa520,
2633 gray: 0x808080,
2634 green: 0x008000,
2635 greenyellow: 0xadff2f,
2636 grey: 0x808080,
2637 honeydew: 0xf0fff0,
2638 hotpink: 0xff69b4,
2639 indianred: 0xcd5c5c,
2640 indigo: 0x4b0082,
2641 ivory: 0xfffff0,
2642 khaki: 0xf0e68c,
2643 lavender: 0xe6e6fa,
2644 lavenderblush: 0xfff0f5,
2645 lawngreen: 0x7cfc00,
2646 lemonchiffon: 0xfffacd,
2647 lightblue: 0xadd8e6,
2648 lightcoral: 0xf08080,
2649 lightcyan: 0xe0ffff,
2650 lightgoldenrodyellow: 0xfafad2,
2651 lightgray: 0xd3d3d3,
2652 lightgreen: 0x90ee90,
2653 lightgrey: 0xd3d3d3,
2654 lightpink: 0xffb6c1,
2655 lightsalmon: 0xffa07a,
2656 lightseagreen: 0x20b2aa,
2657 lightskyblue: 0x87cefa,
2658 lightslategray: 0x778899,
2659 lightslategrey: 0x778899,
2660 lightsteelblue: 0xb0c4de,
2661 lightyellow: 0xffffe0,
2662 lime: 0x00ff00,
2663 limegreen: 0x32cd32,
2664 linen: 0xfaf0e6,
2665 magenta: 0xff00ff,
2666 maroon: 0x800000,
2667 mediumaquamarine: 0x66cdaa,
2668 mediumblue: 0x0000cd,
2669 mediumorchid: 0xba55d3,
2670 mediumpurple: 0x9370db,
2671 mediumseagreen: 0x3cb371,
2672 mediumslateblue: 0x7b68ee,
2673 mediumspringgreen: 0x00fa9a,
2674 mediumturquoise: 0x48d1cc,
2675 mediumvioletred: 0xc71585,
2676 midnightblue: 0x191970,
2677 mintcream: 0xf5fffa,
2678 mistyrose: 0xffe4e1,
2679 moccasin: 0xffe4b5,
2680 navajowhite: 0xffdead,
2681 navy: 0x000080,
2682 oldlace: 0xfdf5e6,
2683 olive: 0x808000,
2684 olivedrab: 0x6b8e23,
2685 orange: 0xffa500,
2686 orangered: 0xff4500,
2687 orchid: 0xda70d6,
2688 palegoldenrod: 0xeee8aa,
2689 palegreen: 0x98fb98,
2690 paleturquoise: 0xafeeee,
2691 palevioletred: 0xdb7093,
2692 papayawhip: 0xffefd5,
2693 peachpuff: 0xffdab9,
2694 peru: 0xcd853f,
2695 pink: 0xffc0cb,
2696 plum: 0xdda0dd,
2697 powderblue: 0xb0e0e6,
2698 purple: 0x800080,
2699 rebeccapurple: 0x663399,
2700 red: 0xff0000,
2701 rosybrown: 0xbc8f8f,
2702 royalblue: 0x4169e1,
2703 saddlebrown: 0x8b4513,
2704 salmon: 0xfa8072,
2705 sandybrown: 0xf4a460,
2706 seagreen: 0x2e8b57,
2707 seashell: 0xfff5ee,
2708 sienna: 0xa0522d,
2709 silver: 0xc0c0c0,
2710 skyblue: 0x87ceeb,
2711 slateblue: 0x6a5acd,
2712 slategray: 0x708090,
2713 slategrey: 0x708090,
2714 snow: 0xfffafa,
2715 springgreen: 0x00ff7f,
2716 steelblue: 0x4682b4,
2717 tan: 0xd2b48c,
2718 teal: 0x008080,
2719 thistle: 0xd8bfd8,
2720 tomato: 0xff6347,
2721 turquoise: 0x40e0d0,
2722 violet: 0xee82ee,
2723 wheat: 0xf5deb3,
2724 white: 0xffffff,
2725 whitesmoke: 0xf5f5f5,
2726 yellow: 0xffff00,
2727 yellowgreen: 0x9acd32
2728 };
2729
2730 define(Color, color, {
2731 displayable: function() {
2732 return this.rgb().displayable();
2733 },
2734 hex: function() {
2735 return this.rgb().hex();
2736 },
2737 toString: function() {
2738 return this.rgb() + "";
2739 }
2740 });
2741
2742 function color(format) {
2743 var m;
2744 format = (format + "").trim().toLowerCase();
2745 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
2746 : (m = reHex6.exec(format)) ? rgbn(parseInt(m[1], 16)) // #ff0000
2747 : (m = reRgbInteger.exec(format)) ? new Rgb(m[1], m[2], m[3], 1) // rgb(255, 0, 0)
2748 : (m = reRgbPercent.exec(format)) ? new Rgb(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, 1) // rgb(100%, 0%, 0%)
2749 : (m = reRgbaInteger.exec(format)) ? rgba(m[1], m[2], m[3], m[4]) // rgba(255, 0, 0, 1)
2750 : (m = reRgbaPercent.exec(format)) ? rgba(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, m[4]) // rgb(100%, 0%, 0%, 1)
2751 : (m = reHslPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, 1) // hsl(120, 50%, 50%)
2752 : (m = reHslaPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) // hsla(120, 50%, 50%, 1)
2753 : named.hasOwnProperty(format) ? rgbn(named[format])
2754 : format === "transparent" ? new Rgb(NaN, NaN, NaN, 0)
2755 : null;
2756 }
2757
2758 function rgbn(n) {
2759 return new Rgb(n >> 16 & 0xff, n >> 8 & 0xff, n & 0xff, 1);
2760 }
2761
2762 function rgba(r, g, b, a) {
2763 if (a <= 0) r = g = b = NaN;
2764 return new Rgb(r, g, b, a);
2765 }
2766
2767 function rgbConvert(o) {
2768 if (!(o instanceof Color)) o = color(o);
2769 if (!o) return new Rgb;
2770 o = o.rgb();
2771 return new Rgb(o.r, o.g, o.b, o.opacity);
2772 }
2773
2774 function rgb(r, g, b, opacity) {
2775 return arguments.length === 1 ? rgbConvert(r) : new Rgb(r, g, b, opacity == null ? 1 : opacity);
2776 }
2777
2778 function Rgb(r, g, b, opacity) {
2779 this.r = +r;
2780 this.g = +g;
2781 this.b = +b;
2782 this.opacity = +opacity;
2783 }
2784
2785 define(Rgb, rgb, extend(Color, {
2786 brighter: function(k) {
2787 k = k == null ? brighter : Math.pow(brighter, k);
2788 return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
2789 },
2790 darker: function(k) {
2791 k = k == null ? darker : Math.pow(darker, k);
2792 return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
2793 },
2794 rgb: function() {
2795 return this;
2796 },
2797 displayable: function() {
2798 return (0 <= this.r && this.r <= 255)
2799 && (0 <= this.g && this.g <= 255)
2800 && (0 <= this.b && this.b <= 255)
2801 && (0 <= this.opacity && this.opacity <= 1);
2802 },
2803 hex: function() {
2804 return "#" + hex(this.r) + hex(this.g) + hex(this.b);
2805 },
2806 toString: function() {
2807 var a = this.opacity; a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));
2808 return (a === 1 ? "rgb(" : "rgba(")
2809 + Math.max(0, Math.min(255, Math.round(this.r) || 0)) + ", "
2810 + Math.max(0, Math.min(255, Math.round(this.g) || 0)) + ", "
2811 + Math.max(0, Math.min(255, Math.round(this.b) || 0))
2812 + (a === 1 ? ")" : ", " + a + ")");
2813 }
2814 }));
2815
2816 function hex(value) {
2817 value = Math.max(0, Math.min(255, Math.round(value) || 0));
2818 return (value < 16 ? "0" : "") + value.toString(16);
2819 }
2820
2821 function hsla(h, s, l, a) {
2822 if (a <= 0) h = s = l = NaN;
2823 else if (l <= 0 || l >= 1) h = s = NaN;
2824 else if (s <= 0) h = NaN;
2825 return new Hsl(h, s, l, a);
2826 }
2827
2828 function hslConvert(o) {
2829 if (o instanceof Hsl) return new Hsl(o.h, o.s, o.l, o.opacity);
2830 if (!(o instanceof Color)) o = color(o);
2831 if (!o) return new Hsl;
2832 if (o instanceof Hsl) return o;
2833 o = o.rgb();
2834 var r = o.r / 255,
2835 g = o.g / 255,
2836 b = o.b / 255,
2837 min = Math.min(r, g, b),
2838 max = Math.max(r, g, b),
2839 h = NaN,
2840 s = max - min,
2841 l = (max + min) / 2;
2842 if (s) {
2843 if (r === max) h = (g - b) / s + (g < b) * 6;
2844 else if (g === max) h = (b - r) / s + 2;
2845 else h = (r - g) / s + 4;
2846 s /= l < 0.5 ? max + min : 2 - max - min;
2847 h *= 60;
2848 } else {
2849 s = l > 0 && l < 1 ? 0 : h;
2850 }
2851 return new Hsl(h, s, l, o.opacity);
2852 }
2853
2854 function hsl(h, s, l, opacity) {
2855 return arguments.length === 1 ? hslConvert(h) : new Hsl(h, s, l, opacity == null ? 1 : opacity);
2856 }
2857
2858 function Hsl(h, s, l, opacity) {
2859 this.h = +h;
2860 this.s = +s;
2861 this.l = +l;
2862 this.opacity = +opacity;
2863 }
2864
2865 define(Hsl, hsl, extend(Color, {
2866 brighter: function(k) {
2867 k = k == null ? brighter : Math.pow(brighter, k);
2868 return new Hsl(this.h, this.s, this.l * k, this.opacity);
2869 },
2870 darker: function(k) {
2871 k = k == null ? darker : Math.pow(darker, k);
2872 return new Hsl(this.h, this.s, this.l * k, this.opacity);
2873 },
2874 rgb: function() {
2875 var h = this.h % 360 + (this.h < 0) * 360,
2876 s = isNaN(h) || isNaN(this.s) ? 0 : this.s,
2877 l = this.l,
2878 m2 = l + (l < 0.5 ? l : 1 - l) * s,
2879 m1 = 2 * l - m2;
2880 return new Rgb(
2881 hsl2rgb(h >= 240 ? h - 240 : h + 120, m1, m2),
2882 hsl2rgb(h, m1, m2),
2883 hsl2rgb(h < 120 ? h + 240 : h - 120, m1, m2),
2884 this.opacity
2885 );
2886 },
2887 displayable: function() {
2888 return (0 <= this.s && this.s <= 1 || isNaN(this.s))
2889 && (0 <= this.l && this.l <= 1)
2890 && (0 <= this.opacity && this.opacity <= 1);
2891 }
2892 }));
2893
2894 /* From FvD 13.37, CSS Color Module Level 3 */
2895 function hsl2rgb(h, m1, m2) {
2896 return (h < 60 ? m1 + (m2 - m1) * h / 60
2897 : h < 180 ? m2
2898 : h < 240 ? m1 + (m2 - m1) * (240 - h) / 60
2899 : m1) * 255;
2900 }
2901
2902 var deg2rad = Math.PI / 180;
2903 var rad2deg = 180 / Math.PI;
2904
2905 // https://beta.observablehq.com/@mbostock/lab-and-rgb
2906 var K = 18,
2907 Xn = 0.96422,
2908 Yn = 1,
2909 Zn = 0.82521,
2910 t0$1 = 4 / 29,
2911 t1$1 = 6 / 29,
2912 t2 = 3 * t1$1 * t1$1,
2913 t3 = t1$1 * t1$1 * t1$1;
2914
2915 function labConvert(o) {
2916 if (o instanceof Lab) return new Lab(o.l, o.a, o.b, o.opacity);
2917 if (o instanceof Hcl) {
2918 if (isNaN(o.h)) return new Lab(o.l, 0, 0, o.opacity);
2919 var h = o.h * deg2rad;
2920 return new Lab(o.l, Math.cos(h) * o.c, Math.sin(h) * o.c, o.opacity);
2921 }
2922 if (!(o instanceof Rgb)) o = rgbConvert(o);
2923 var r = rgb2lrgb(o.r),
2924 g = rgb2lrgb(o.g),
2925 b = rgb2lrgb(o.b),
2926 y = xyz2lab((0.2225045 * r + 0.7168786 * g + 0.0606169 * b) / Yn), x, z;
2927 if (r === g && g === b) x = z = y; else {
2928 x = xyz2lab((0.4360747 * r + 0.3850649 * g + 0.1430804 * b) / Xn);
2929 z = xyz2lab((0.0139322 * r + 0.0971045 * g + 0.7141733 * b) / Zn);
2930 }
2931 return new Lab(116 * y - 16, 500 * (x - y), 200 * (y - z), o.opacity);
2932 }
2933
2934 function lab(l, a, b, opacity) {
2935 return arguments.length === 1 ? labConvert(l) : new Lab(l, a, b, opacity == null ? 1 : opacity);
2936 }
2937
2938 function Lab(l, a, b, opacity) {
2939 this.l = +l;
2940 this.a = +a;
2941 this.b = +b;
2942 this.opacity = +opacity;
2943 }
2944
2945 define(Lab, lab, extend(Color, {
2946 brighter: function(k) {
2947 return new Lab(this.l + K * (k == null ? 1 : k), this.a, this.b, this.opacity);
2948 },
2949 darker: function(k) {
2950 return new Lab(this.l - K * (k == null ? 1 : k), this.a, this.b, this.opacity);
2951 },
2952 rgb: function() {
2953 var y = (this.l + 16) / 116,
2954 x = isNaN(this.a) ? y : y + this.a / 500,
2955 z = isNaN(this.b) ? y : y - this.b / 200;
2956 x = Xn * lab2xyz(x);
2957 y = Yn * lab2xyz(y);
2958 z = Zn * lab2xyz(z);
2959 return new Rgb(
2960 lrgb2rgb( 3.1338561 * x - 1.6168667 * y - 0.4906146 * z),
2961 lrgb2rgb(-0.9787684 * x + 1.9161415 * y + 0.0334540 * z),
2962 lrgb2rgb( 0.0719453 * x - 0.2289914 * y + 1.4052427 * z),
2963 this.opacity
2964 );
2965 }
2966 }));
2967
2968 function xyz2lab(t) {
2969 return t > t3 ? Math.pow(t, 1 / 3) : t / t2 + t0$1;
2970 }
2971
2972 function lab2xyz(t) {
2973 return t > t1$1 ? t * t * t : t2 * (t - t0$1);
2974 }
2975
2976 function lrgb2rgb(x) {
2977 return 255 * (x <= 0.0031308 ? 12.92 * x : 1.055 * Math.pow(x, 1 / 2.4) - 0.055);
2978 }
2979
2980 function rgb2lrgb(x) {
2981 return (x /= 255) <= 0.04045 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4);
2982 }
2983
2984 function hclConvert(o) {
2985 if (o instanceof Hcl) return new Hcl(o.h, o.c, o.l, o.opacity);
2986 if (!(o instanceof Lab)) o = labConvert(o);
2987 if (o.a === 0 && o.b === 0) return new Hcl(NaN, 0, o.l, o.opacity);
2988 var h = Math.atan2(o.b, o.a) * rad2deg;
2989 return new Hcl(h < 0 ? h + 360 : h, Math.sqrt(o.a * o.a + o.b * o.b), o.l, o.opacity);
2990 }
2991
2992 function hcl(h, c, l, opacity) {
2993 return arguments.length === 1 ? hclConvert(h) : new Hcl(h, c, l, opacity == null ? 1 : opacity);
2994 }
2995
2996 function Hcl(h, c, l, opacity) {
2997 this.h = +h;
2998 this.c = +c;
2999 this.l = +l;
3000 this.opacity = +opacity;
3001 }
3002
3003 define(Hcl, hcl, extend(Color, {
3004 brighter: function(k) {
3005 return new Hcl(this.h, this.c, this.l + K * (k == null ? 1 : k), this.opacity);
3006 },
3007 darker: function(k) {
3008 return new Hcl(this.h, this.c, this.l - K * (k == null ? 1 : k), this.opacity);
3009 },
3010 rgb: function() {
3011 return labConvert(this).rgb();
3012 }
3013 }));
3014
3015 var A = -0.14861,
3016 B = +1.78277,
3017 C = -0.29227,
3018 D = -0.90649,
3019 E = +1.97294,
3020 ED = E * D,
3021 EB = E * B,
3022 BC_DA = B * C - D * A;
3023
3024 function cubehelixConvert(o) {
3025 if (o instanceof Cubehelix) return new Cubehelix(o.h, o.s, o.l, o.opacity);
3026 if (!(o instanceof Rgb)) o = rgbConvert(o);
3027 var r = o.r / 255,
3028 g = o.g / 255,
3029 b = o.b / 255,
3030 l = (BC_DA * b + ED * r - EB * g) / (BC_DA + ED - EB),
3031 bl = b - l,
3032 k = (E * (g - l) - C * bl) / D,
3033 s = Math.sqrt(k * k + bl * bl) / (E * l * (1 - l)), // NaN if l=0 or l=1
3034 h = s ? Math.atan2(k, bl) * rad2deg - 120 : NaN;
3035 return new Cubehelix(h < 0 ? h + 360 : h, s, l, o.opacity);
3036 }
3037
3038 function cubehelix(h, s, l, opacity) {
3039 return arguments.length === 1 ? cubehelixConvert(h) : new Cubehelix(h, s, l, opacity == null ? 1 : opacity);
3040 }
3041
3042 function Cubehelix(h, s, l, opacity) {
3043 this.h = +h;
3044 this.s = +s;
3045 this.l = +l;
3046 this.opacity = +opacity;
3047 }
3048
3049 define(Cubehelix, cubehelix, extend(Color, {
3050 brighter: function(k) {
3051 k = k == null ? brighter : Math.pow(brighter, k);
3052 return new Cubehelix(this.h, this.s, this.l * k, this.opacity);
3053 },
3054 darker: function(k) {
3055 k = k == null ? darker : Math.pow(darker, k);
3056 return new Cubehelix(this.h, this.s, this.l * k, this.opacity);
3057 },
3058 rgb: function() {
3059 var h = isNaN(this.h) ? 0 : (this.h + 120) * deg2rad,
3060 l = +this.l,
3061 a = isNaN(this.s) ? 0 : this.s * l * (1 - l),
3062 cosh = Math.cos(h),
3063 sinh = Math.sin(h);
3064 return new Rgb(
3065 255 * (l + a * (A * cosh + B * sinh)),
3066 255 * (l + a * (C * cosh + D * sinh)),
3067 255 * (l + a * (E * cosh)),
3068 this.opacity
3069 );
3070 }
3071 }));
3072
3073 function constant$1(x) {
3074 return function() {
3075 return x;
3076 };
3077 }
3078
3079 function linear(a, d) {
3080 return function(t) {
3081 return a + t * d;
3082 };
3083 }
3084
3085 function exponential(a, b, y) {
3086 return a = Math.pow(a, y), b = Math.pow(b, y) - a, y = 1 / y, function(t) {
3087 return Math.pow(a + t * b, y);
3088 };
3089 }
3090
3091 function gamma(y) {
3092 return (y = +y) === 1 ? nogamma : function(a, b) {
3093 return b - a ? exponential(a, b, y) : constant$1(isNaN(a) ? b : a);
3094 };
3095 }
3096
3097 function nogamma(a, b) {
3098 var d = b - a;
3099 return d ? linear(a, d) : constant$1(isNaN(a) ? b : a);
3100 }
3101
3102 var rgb$1 = (function rgbGamma(y) {
3103 var color = gamma(y);
3104
3105 function rgb$1(start, end) {
3106 var r = color((start = rgb(start)).r, (end = rgb(end)).r),
3107 g = color(start.g, end.g),
3108 b = color(start.b, end.b),
3109 opacity = nogamma(start.opacity, end.opacity);
3110 return function(t) {
3111 start.r = r(t);
3112 start.g = g(t);
3113 start.b = b(t);
3114 start.opacity = opacity(t);
3115 return start + "";
3116 };
3117 }
3118
3119 rgb$1.gamma = rgbGamma;
3120
3121 return rgb$1;
3122 })(1);
3123
3124 function array(a, b) {
3125 var nb = b ? b.length : 0,
3126 na = a ? Math.min(nb, a.length) : 0,
3127 x = new Array(na),
3128 c = new Array(nb),
3129 i;
3130
3131 for (i = 0; i < na; ++i) x[i] = interpolateValue(a[i], b[i]);
3132 for (; i < nb; ++i) c[i] = b[i];
3133
3134 return function(t) {
3135 for (i = 0; i < na; ++i) c[i] = x[i](t);
3136 return c;
3137 };
3138 }
3139
3140 function date(a, b) {
3141 var d = new Date;
3142 return a = +a, b -= a, function(t) {
3143 return d.setTime(a + b * t), d;
3144 };
3145 }
3146
3147 function reinterpolate(a, b) {
3148 return a = +a, b -= a, function(t) {
3149 return a + b * t;
3150 };
3151 }
3152
3153 function object(a, b) {
3154 var i = {},
3155 c = {},
3156 k;
3157
3158 if (a === null || typeof a !== "object") a = {};
3159 if (b === null || typeof b !== "object") b = {};
3160
3161 for (k in b) {
3162 if (k in a) {
3163 i[k] = interpolateValue(a[k], b[k]);
3164 } else {
3165 c[k] = b[k];
3166 }
3167 }
3168
3169 return function(t) {
3170 for (k in i) c[k] = i[k](t);
3171 return c;
3172 };
3173 }
3174
3175 var reA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,
3176 reB = new RegExp(reA.source, "g");
3177
3178 function zero(b) {
3179 return function() {
3180 return b;
3181 };
3182 }
3183
3184 function one(b) {
3185 return function(t) {
3186 return b(t) + "";
3187 };
3188 }
3189
3190 function string(a, b) {
3191 var bi = reA.lastIndex = reB.lastIndex = 0, // scan index for next number in b
3192 am, // current match in a
3193 bm, // current match in b
3194 bs, // string preceding current number in b, if any
3195 i = -1, // index in s
3196 s = [], // string constants and placeholders
3197 q = []; // number interpolators
3198
3199 // Coerce inputs to strings.
3200 a = a + "", b = b + "";
3201
3202 // Interpolate pairs of numbers in a & b.
3203 while ((am = reA.exec(a))
3204 && (bm = reB.exec(b))) {
3205 if ((bs = bm.index) > bi) { // a string precedes the next number in b
3206 bs = b.slice(bi, bs);
3207 if (s[i]) s[i] += bs; // coalesce with previous string
3208 else s[++i] = bs;
3209 }
3210 if ((am = am[0]) === (bm = bm[0])) { // numbers in a & b match
3211 if (s[i]) s[i] += bm; // coalesce with previous string
3212 else s[++i] = bm;
3213 } else { // interpolate non-matching numbers
3214 s[++i] = null;
3215 q.push({i: i, x: reinterpolate(am, bm)});
3216 }
3217 bi = reB.lastIndex;
3218 }
3219
3220 // Add remains of b.
3221 if (bi < b.length) {
3222 bs = b.slice(bi);
3223 if (s[i]) s[i] += bs; // coalesce with previous string
3224 else s[++i] = bs;
3225 }
3226
3227 // Special optimization for only a single match.
3228 // Otherwise, interpolate each of the numbers and rejoin the string.
3229 return s.length < 2 ? (q[0]
3230 ? one(q[0].x)
3231 : zero(b))
3232 : (b = q.length, function(t) {
3233 for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t);
3234 return s.join("");
3235 });
3236 }
3237
3238 function interpolateValue(a, b) {
3239 var t = typeof b, c;
3240 return b == null || t === "boolean" ? constant$1(b)
3241 : (t === "number" ? reinterpolate
3242 : t === "string" ? ((c = color(b)) ? (b = c, rgb$1) : string)
3243 : b instanceof color ? rgb$1
3244 : b instanceof Date ? date
3245 : Array.isArray(b) ? array
3246 : typeof b.valueOf !== "function" && typeof b.toString !== "function" || isNaN(b) ? object
3247 : reinterpolate)(a, b);
3248 }
3249
3250 function interpolateRound(a, b) {
3251 return a = +a, b -= a, function(t) {
3252 return Math.round(a + b * t);
3253 };
3254 }
3255
3256 var array$1 = Array.prototype;
3257
3258 var map = array$1.map;
3259 var slice$1 = array$1.slice;
3260
3261 function constant$2(x) {
3262 return function() {
3263 return x;
3264 };
3265 }
3266
3267 function number$1(x) {
3268 return +x;
3269 }
3270
3271 var unit = [0, 1];
3272
3273 function deinterpolateLinear(a, b) {
3274 return (b -= (a = +a))
3275 ? function(x) { return (x - a) / b; }
3276 : constant$2(b);
3277 }
3278
3279 function deinterpolateClamp(deinterpolate) {
3280 return function(a, b) {
3281 var d = deinterpolate(a = +a, b = +b);
3282 return function(x) { return x <= a ? 0 : x >= b ? 1 : d(x); };
3283 };
3284 }
3285
3286 function reinterpolateClamp(reinterpolate) {
3287 return function(a, b) {
3288 var r = reinterpolate(a = +a, b = +b);
3289 return function(t) { return t <= 0 ? a : t >= 1 ? b : r(t); };
3290 };
3291 }
3292
3293 function bimap(domain, range, deinterpolate, reinterpolate) {
3294 var d0 = domain[0], d1 = domain[1], r0 = range[0], r1 = range[1];
3295 if (d1 < d0) d0 = deinterpolate(d1, d0), r0 = reinterpolate(r1, r0);
3296 else d0 = deinterpolate(d0, d1), r0 = reinterpolate(r0, r1);
3297 return function(x) { return r0(d0(x)); };
3298 }
3299
3300 function polymap(domain, range, deinterpolate, reinterpolate) {
3301 var j = Math.min(domain.length, range.length) - 1,
3302 d = new Array(j),
3303 r = new Array(j),
3304 i = -1;
3305
3306 // Reverse descending domains.
3307 if (domain[j] < domain[0]) {
3308 domain = domain.slice().reverse();
3309 range = range.slice().reverse();
3310 }
3311
3312 while (++i < j) {
3313 d[i] = deinterpolate(domain[i], domain[i + 1]);
3314 r[i] = reinterpolate(range[i], range[i + 1]);
3315 }
3316
3317 return function(x) {
3318 var i = bisectRight(domain, x, 1, j) - 1;
3319 return r[i](d[i](x));
3320 };
3321 }
3322
3323 function copy(source, target) {
3324 return target
3325 .domain(source.domain())
3326 .range(source.range())
3327 .interpolate(source.interpolate())
3328 .clamp(source.clamp());
3329 }
3330
3331 // deinterpolate(a, b)(x) takes a domain value x in [a,b] and returns the corresponding parameter t in [0,1].
3332 // reinterpolate(a, b)(t) takes a parameter t in [0,1] and returns the corresponding domain value x in [a,b].
3333 function continuous(deinterpolate, reinterpolate) {
3334 var domain = unit,
3335 range = unit,
3336 interpolate = interpolateValue,
3337 clamp = false,
3338 piecewise,
3339 output,
3340 input;
3341
3342 function rescale() {
3343 piecewise = Math.min(domain.length, range.length) > 2 ? polymap : bimap;
3344 output = input = null;
3345 return scale;
3346 }
3347
3348 function scale(x) {
3349 return (output || (output = piecewise(domain, range, clamp ? deinterpolateClamp(deinterpolate) : deinterpolate, interpolate)))(+x);
3350 }
3351
3352 scale.invert = function(y) {
3353 return (input || (input = piecewise(range, domain, deinterpolateLinear, clamp ? reinterpolateClamp(reinterpolate) : reinterpolate)))(+y);
3354 };
3355
3356 scale.domain = function(_) {
3357 return arguments.length ? (domain = map.call(_, number$1), rescale()) : domain.slice();
3358 };
3359
3360 scale.range = function(_) {
3361 return arguments.length ? (range = slice$1.call(_), rescale()) : range.slice();
3362 };
3363
3364 scale.rangeRound = function(_) {
3365 return range = slice$1.call(_), interpolate = interpolateRound, rescale();
3366 };
3367
3368 scale.clamp = function(_) {
3369 return arguments.length ? (clamp = !!_, rescale()) : clamp;
3370 };
3371
3372 scale.interpolate = function(_) {
3373 return arguments.length ? (interpolate = _, rescale()) : interpolate;
3374 };
3375
3376 return rescale();
3377 }
3378
3379 // Computes the decimal coefficient and exponent of the specified number x with
3380 // significant digits p, where x is positive and p is in [1, 21] or undefined.
3381 // For example, formatDecimal(1.23) returns ["123", 0].
3382 function formatDecimal(x, p) {
3383 if ((i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf("e")) < 0) return null; // NaN, ±Infinity
3384 var i, coefficient = x.slice(0, i);
3385
3386 // The string returned by toExponential either has the form \d\.\d+e[-+]\d+
3387 // (e.g., 1.2e+3) or the form \de[-+]\d+ (e.g., 1e+3).
3388 return [
3389 coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient,
3390 +x.slice(i + 1)
3391 ];
3392 }
3393
3394 function exponent(x) {
3395 return x = formatDecimal(Math.abs(x)), x ? x[1] : NaN;
3396 }
3397
3398 function formatGroup(grouping, thousands) {
3399 return function(value, width) {
3400 var i = value.length,
3401 t = [],
3402 j = 0,
3403 g = grouping[0],
3404 length = 0;
3405
3406 while (i > 0 && g > 0) {
3407 if (length + g + 1 > width) g = Math.max(1, width - length);
3408 t.push(value.substring(i -= g, i + g));
3409 if ((length += g + 1) > width) break;
3410 g = grouping[j = (j + 1) % grouping.length];
3411 }
3412
3413 return t.reverse().join(thousands);
3414 };
3415 }
3416
3417 function formatNumerals(numerals) {
3418 return function(value) {
3419 return value.replace(/[0-9]/g, function(i) {
3420 return numerals[+i];
3421 });
3422 };
3423 }
3424
3425 // [[fill]align][sign][symbol][0][width][,][.precision][~][type]
3426 var re = /^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;
3427
3428 function formatSpecifier(specifier) {
3429 return new FormatSpecifier(specifier);
3430 }
3431
3432 formatSpecifier.prototype = FormatSpecifier.prototype; // instanceof
3433
3434 function FormatSpecifier(specifier) {
3435 if (!(match = re.exec(specifier))) throw new Error("invalid format: " + specifier);
3436 var match;
3437 this.fill = match[1] || " ";
3438 this.align = match[2] || ">";
3439 this.sign = match[3] || "-";
3440 this.symbol = match[4] || "";
3441 this.zero = !!match[5];
3442 this.width = match[6] && +match[6];
3443 this.comma = !!match[7];
3444 this.precision = match[8] && +match[8].slice(1);
3445 this.trim = !!match[9];
3446 this.type = match[10] || "";
3447 }
3448
3449 FormatSpecifier.prototype.toString = function() {
3450 return this.fill
3451 + this.align
3452 + this.sign
3453 + this.symbol
3454 + (this.zero ? "0" : "")
3455 + (this.width == null ? "" : Math.max(1, this.width | 0))
3456 + (this.comma ? "," : "")
3457 + (this.precision == null ? "" : "." + Math.max(0, this.precision | 0))
3458 + (this.trim ? "~" : "")
3459 + this.type;
3460 };
3461
3462 // Trims insignificant zeros, e.g., replaces 1.2000k with 1.2k.
3463 function formatTrim(s) {
3464 out: for (var n = s.length, i = 1, i0 = -1, i1; i < n; ++i) {
3465 switch (s[i]) {
3466 case ".": i0 = i1 = i; break;
3467 case "0": if (i0 === 0) i0 = i; i1 = i; break;
3468 default: if (i0 > 0) { if (!+s[i]) break out; i0 = 0; } break;
3469 }
3470 }
3471 return i0 > 0 ? s.slice(0, i0) + s.slice(i1 + 1) : s;
3472 }
3473
3474 var prefixExponent;
3475
3476 function formatPrefixAuto(x, p) {
3477 var d = formatDecimal(x, p);
3478 if (!d) return x + "";
3479 var coefficient = d[0],
3480 exponent = d[1],
3481 i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1,
3482 n = coefficient.length;
3483 return i === n ? coefficient
3484 : i > n ? coefficient + new Array(i - n + 1).join("0")
3485 : i > 0 ? coefficient.slice(0, i) + "." + coefficient.slice(i)
3486 : "0." + new Array(1 - i).join("0") + formatDecimal(x, Math.max(0, p + i - 1))[0]; // less than 1y!
3487 }
3488
3489 function formatRounded(x, p) {
3490 var d = formatDecimal(x, p);
3491 if (!d) return x + "";
3492 var coefficient = d[0],
3493 exponent = d[1];
3494 return exponent < 0 ? "0." + new Array(-exponent).join("0") + coefficient
3495 : coefficient.length > exponent + 1 ? coefficient.slice(0, exponent + 1) + "." + coefficient.slice(exponent + 1)
3496 : coefficient + new Array(exponent - coefficient.length + 2).join("0");
3497 }
3498
3499 var formatTypes = {
3500 "%": function(x, p) { return (x * 100).toFixed(p); },
3501 "b": function(x) { return Math.round(x).toString(2); },
3502 "c": function(x) { return x + ""; },
3503 "d": function(x) { return Math.round(x).toString(10); },
3504 "e": function(x, p) { return x.toExponential(p); },
3505 "f": function(x, p) { return x.toFixed(p); },
3506 "g": function(x, p) { return x.toPrecision(p); },
3507 "o": function(x) { return Math.round(x).toString(8); },
3508 "p": function(x, p) { return formatRounded(x * 100, p); },
3509 "r": formatRounded,
3510 "s": formatPrefixAuto,
3511 "X": function(x) { return Math.round(x).toString(16).toUpperCase(); },
3512 "x": function(x) { return Math.round(x).toString(16); }
3513 };
3514
3515 function identity$1(x) {
3516 return x;
3517 }
3518
3519 var prefixes = ["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"];
3520
3521 function formatLocale$1(locale) {
3522 var group = locale.grouping && locale.thousands ? formatGroup(locale.grouping, locale.thousands) : identity$1,
3523 currency = locale.currency,
3524 decimal = locale.decimal,
3525 numerals = locale.numerals ? formatNumerals(locale.numerals) : identity$1,
3526 percent = locale.percent || "%";
3527
3528 function newFormat(specifier) {
3529 specifier = formatSpecifier(specifier);
3530
3531 var fill = specifier.fill,
3532 align = specifier.align,
3533 sign = specifier.sign,
3534 symbol = specifier.symbol,
3535 zero = specifier.zero,
3536 width = specifier.width,
3537 comma = specifier.comma,
3538 precision = specifier.precision,
3539 trim = specifier.trim,
3540 type = specifier.type;
3541
3542 // The "n" type is an alias for ",g".
3543 if (type === "n") comma = true, type = "g";
3544
3545 // The "" type, and any invalid type, is an alias for ".12~g".
3546 else if (!formatTypes[type]) precision == null && (precision = 12), trim = true, type = "g";
3547
3548 // If zero fill is specified, padding goes after sign and before digits.
3549 if (zero || (fill === "0" && align === "=")) zero = true, fill = "0", align = "=";
3550
3551 // Compute the prefix and suffix.
3552 // For SI-prefix, the suffix is lazily computed.
3553 var prefix = symbol === "$" ? currency[0] : symbol === "#" && /[boxX]/.test(type) ? "0" + type.toLowerCase() : "",
3554 suffix = symbol === "$" ? currency[1] : /[%p]/.test(type) ? percent : "";
3555
3556 // What format function should we use?
3557 // Is this an integer type?
3558 // Can this type generate exponential notation?
3559 var formatType = formatTypes[type],
3560 maybeSuffix = /[defgprs%]/.test(type);
3561
3562 // Set the default precision if not specified,
3563 // or clamp the specified precision to the supported range.
3564 // For significant precision, it must be in [1, 21].
3565 // For fixed precision, it must be in [0, 20].
3566 precision = precision == null ? 6
3567 : /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision))
3568 : Math.max(0, Math.min(20, precision));
3569
3570 function format(value) {
3571 var valuePrefix = prefix,
3572 valueSuffix = suffix,
3573 i, n, c;
3574
3575 if (type === "c") {
3576 valueSuffix = formatType(value) + valueSuffix;
3577 value = "";
3578 } else {
3579 value = +value;
3580
3581 // Perform the initial formatting.
3582 var valueNegative = value < 0;
3583 value = formatType(Math.abs(value), precision);
3584
3585 // Trim insignificant zeros.
3586 if (trim) value = formatTrim(value);
3587
3588 // If a negative value rounds to zero during formatting, treat as positive.
3589 if (valueNegative && +value === 0) valueNegative = false;
3590
3591 // Compute the prefix and suffix.
3592 valuePrefix = (valueNegative ? (sign === "(" ? sign : "-") : sign === "-" || sign === "(" ? "" : sign) + valuePrefix;
3593 valueSuffix = (type === "s" ? prefixes[8 + prefixExponent / 3] : "") + valueSuffix + (valueNegative && sign === "(" ? ")" : "");
3594
3595 // Break the formatted value into the integer “value” part that can be
3596 // grouped, and fractional or exponential “suffix” part that is not.
3597 if (maybeSuffix) {
3598 i = -1, n = value.length;
3599 while (++i < n) {
3600 if (c = value.charCodeAt(i), 48 > c || c > 57) {
3601 valueSuffix = (c === 46 ? decimal + value.slice(i + 1) : value.slice(i)) + valueSuffix;
3602 value = value.slice(0, i);
3603 break;
3604 }
3605 }
3606 }
3607 }
3608
3609 // If the fill character is not "0", grouping is applied before padding.
3610 if (comma && !zero) value = group(value, Infinity);
3611
3612 // Compute the padding.
3613 var length = valuePrefix.length + value.length + valueSuffix.length,
3614 padding = length < width ? new Array(width - length + 1).join(fill) : "";
3615
3616 // If the fill character is "0", grouping is applied after padding.
3617 if (comma && zero) value = group(padding + value, padding.length ? width - valueSuffix.length : Infinity), padding = "";
3618
3619 // Reconstruct the final output based on the desired alignment.
3620 switch (align) {
3621 case "<": value = valuePrefix + value + valueSuffix + padding; break;
3622 case "=": value = valuePrefix + padding + value + valueSuffix; break;
3623 case "^": value = padding.slice(0, length = padding.length >> 1) + valuePrefix + value + valueSuffix + padding.slice(length); break;
3624 default: value = padding + valuePrefix + value + valueSuffix; break;
3625 }
3626
3627 return numerals(value);
3628 }
3629
3630 format.toString = function() {
3631 return specifier + "";
3632 };
3633
3634 return format;
3635 }
3636
3637 function formatPrefix(specifier, value) {
3638 var f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier)),
3639 e = Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3,
3640 k = Math.pow(10, -e),
3641 prefix = prefixes[8 + e / 3];
3642 return function(value) {
3643 return f(k * value) + prefix;
3644 };
3645 }
3646
3647 return {
3648 format: newFormat,
3649 formatPrefix: formatPrefix
3650 };
3651 }
3652
3653 var locale$1;
3654 var format;
3655 var formatPrefix;
3656
3657 defaultLocale$1({
3658 decimal: ".",
3659 thousands: ",",
3660 grouping: [3],
3661 currency: ["$", ""]
3662 });
3663
3664 function defaultLocale$1(definition) {
3665 locale$1 = formatLocale$1(definition);
3666 format = locale$1.format;
3667 formatPrefix = locale$1.formatPrefix;
3668 return locale$1;
3669 }
3670
3671 function precisionFixed(step) {
3672 return Math.max(0, -exponent(Math.abs(step)));
3673 }
3674
3675 function precisionPrefix(step, value) {
3676 return Math.max(0, Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3 - exponent(Math.abs(step)));
3677 }
3678
3679 function precisionRound(step, max) {
3680 step = Math.abs(step), max = Math.abs(max) - step;
3681 return Math.max(0, exponent(max) - exponent(step)) + 1;
3682 }
3683
3684 function tickFormat(domain, count, specifier) {
3685 var start = domain[0],
3686 stop = domain[domain.length - 1],
3687 step = tickStep(start, stop, count == null ? 10 : count),
3688 precision;
3689 specifier = formatSpecifier(specifier == null ? ",f" : specifier);
3690 switch (specifier.type) {
3691 case "s": {
3692 var value = Math.max(Math.abs(start), Math.abs(stop));
3693 if (specifier.precision == null && !isNaN(precision = precisionPrefix(step, value))) specifier.precision = precision;
3694 return formatPrefix(specifier, value);
3695 }
3696 case "":
3697 case "e":
3698 case "g":
3699 case "p":
3700 case "r": {
3701 if (specifier.precision == null && !isNaN(precision = precisionRound(step, Math.max(Math.abs(start), Math.abs(stop))))) specifier.precision = precision - (specifier.type === "e");
3702 break;
3703 }
3704 case "f":
3705 case "%": {
3706 if (specifier.precision == null && !isNaN(precision = precisionFixed(step))) specifier.precision = precision - (specifier.type === "%") * 2;
3707 break;
3708 }
3709 }
3710 return format(specifier);
3711 }
3712
3713 function linearish(scale) {
3714 var domain = scale.domain;
3715
3716 scale.ticks = function(count) {
3717 var d = domain();
3718 return ticks(d[0], d[d.length - 1], count == null ? 10 : count);
3719 };
3720
3721 scale.tickFormat = function(count, specifier) {
3722 return tickFormat(domain(), count, specifier);
3723 };
3724
3725 scale.nice = function(count) {
3726 if (count == null) count = 10;
3727
3728 var d = domain(),
3729 i0 = 0,
3730 i1 = d.length - 1,
3731 start = d[i0],
3732 stop = d[i1],
3733 step;
3734
3735 if (stop < start) {
3736 step = start, start = stop, stop = step;
3737 step = i0, i0 = i1, i1 = step;
3738 }
3739
3740 step = tickIncrement(start, stop, count);
3741
3742 if (step > 0) {
3743 start = Math.floor(start / step) * step;
3744 stop = Math.ceil(stop / step) * step;
3745 step = tickIncrement(start, stop, count);
3746 } else if (step < 0) {
3747 start = Math.ceil(start * step) / step;
3748 stop = Math.floor(stop * step) / step;
3749 step = tickIncrement(start, stop, count);
3750 }
3751
3752 if (step > 0) {
3753 d[i0] = Math.floor(start / step) * step;
3754 d[i1] = Math.ceil(stop / step) * step;
3755 domain(d);
3756 } else if (step < 0) {
3757 d[i0] = Math.ceil(start * step) / step;
3758 d[i1] = Math.floor(stop * step) / step;
3759 domain(d);
3760 }
3761
3762 return scale;
3763 };
3764
3765 return scale;
3766 }
3767
3768 function linear$1() {
3769 var scale = continuous(deinterpolateLinear, reinterpolate);
3770
3771 scale.copy = function() {
3772 return copy(scale, linear$1());
3773 };
3774
3775 return linearish(scale);
3776 }
3777
3778 var VERSION = "1.3.2";
3779
3780 function Slider(selector) {
3781 this.container = select(selector);
3782
3783 this._width = null;
3784 this._height = null;
3785
3786 this._handleRadius = 15;
3787 this._channelHeight = 5;
3788 this._channelRadius = null;
3789
3790 this._handleFill = "black";
3791 this._channelFill = "#eee";
3792
3793 this._margin = { top: null, left: null, right: null };
3794
3795 this._domain = [0, 1];
3796 this._value = null;
3797 this._snap = false;
3798
3799 this._scale = null;
3800 this._axis = false;
3801 this._ticks = null;
3802 this._tickFormat = null;
3803 this._tickSize = null;
3804
3805 this._label = null;
3806 this._labelSize = 18;
3807
3808 this._startLabel = null;
3809 this._startLabelBelow = false;
3810
3811 this._endLabel = null;
3812 this._endLabelBelow = false;
3813
3814 this._startEndLabelSize = 16;
3815
3816 this.handlers = { "change": [] };
3817 }
3818
3819 // Create accessor methods for all the _parameters defined by the constructor
3820 function accessor(k) {
3821 if (k.length > 0 && k.charAt(0) == "_") {
3822 Slider.prototype[k.substr(1)] = function(v) {
3823 if (typeof v == "undefined") return this[k];
3824 this[k] = v;
3825 return this;
3826 };
3827 }
3828 }
3829 var s = new Slider();
3830 for (var k in s) {
3831 accessor(k);
3832 }
3833
3834 // Special accessor function for margin
3835 Slider.prototype.margin = function Slider_margin(options) {
3836 if (!options) return this._margin;
3837 for (k in options) {
3838 if (k in this._margin) this._margin[k] = options[k];
3839 else throw "Slider.margin: unrecognised option " + k;
3840 }
3841 return this;
3842 };
3843
3844 // Attach event handlers
3845 Slider.prototype.on = function Slider_on(event, handler) {
3846 if (!(event in this.handlers)) throw "Slider.on: No such event: " + event;
3847 this.handlers[event].push(handler);
3848 return this;
3849 };
3850
3851 // Fire event
3852 Slider.prototype.fire = function Slider_fire(event, d) {
3853 if (!(event in this.handlers)) throw "Slider.fire: No such event: " + event;
3854 var handlers = this.handlers[event];
3855 for (var i = 0; i < handlers.length; i++) {
3856 handlers[i].call(this, d);
3857 }
3858 return this;
3859 };
3860
3861 // Binary search
3862 function closestValue(sorted_list, value, a, b) {
3863 if (typeof a === "undefined") a = 0;
3864 if (typeof b === "undefined") b = sorted_list.length;
3865
3866 if (b-a == 0) return value;
3867 if (b-a == 1) return sorted_list[a];
3868 if (b-a == 2) {
3869 var d1 = Math.abs(sorted_list[a] - value),
3870 d2 = Math.abs(sorted_list[a+1] - value);
3871 if (d1 <= d2) return sorted_list[a];
3872 else return sorted_list[a+1];
3873 }
3874
3875 var mid = a + Math.floor((b-a) / 2),
3876 mid_v = sorted_list[mid],
3877 pre = mid - 1,
3878 pre_v = sorted_list[pre];
3879 if (pre_v <= value && value <= mid_v) {
3880 return (Math.abs(pre_v - value) <= Math.abs(mid_v - value)) ? pre_v : mid_v;
3881 }
3882 if (mid_v <= value) return closestValue(sorted_list, value, mid, b);
3883 else return closestValue(sorted_list, value, a, mid);
3884 }
3885
3886 function snapTo(specification, value) {
3887 if (typeof specification == "boolean") {
3888 return specification ? Math.round(value) : value;
3889 }
3890 // Otherwise assume “specification” is a sorted array
3891 return closestValue(specification, value);
3892 }
3893
3894 // Draw or update the slider
3895 Slider.prototype.draw = function Slider_draw() {
3896 var that = this;
3897
3898 var cw = this._width,
3899 ch = this._height;
3900
3901 var container_node = this.container.node();
3902
3903 // If the width and height have not been specified, use
3904 // the client size of the container element.
3905 if (!cw) {
3906 var r = container_node.getBoundingClientRect();
3907 // If there isn’t a bounding client rect, e.g. because
3908 // the container is display: none;, then do nothing.
3909 if (!r || r.width == 0) return this;
3910
3911 cw = r.width;
3912 ch = r.height;
3913 }
3914
3915 var channel_r = this._channelRadius == null ? this._channelHeight/2 : this._channelRadius,
3916 left_margin = (this._margin.left == null ? Math.max(this._handleRadius, channel_r) : this._margin.left),
3917 right_margin = (this._margin.right == null ? Math.max(this._handleRadius, channel_r) : this._margin.right),
3918 top_margin = this._margin.top == null ? Math.max(this._handleRadius, this._channelHeight/2) : this._margin.top,
3919 w = cw - left_margin - right_margin, // Inner width of the slider channel (excluding endcaps)
3920 channel_w = w + 2*channel_r, // Width of the slider channel (including endcaps)
3921 label_h = this._labelSize * 1.5;
3922
3923 if (this._label != null && this._margin.top == null) top_margin += label_h;
3924
3925 var slider;
3926 if (container_node.namespaceURI == "http://www.w3.org/2000/svg") {
3927 slider = this.container;
3928 }
3929 else {
3930 slider = this.container.selectAll("svg").data([{ width: cw, height: ch }]);
3931 slider.exit().remove();
3932 slider = slider.enter().append("svg").merge(slider);
3933 slider.attr("width", function(d) { return d.width; })
3934 .attr("height", function(d) { return d.height; });
3935 }
3936
3937 var g = slider.selectAll("g.slider-container").data([{left: left_margin, top: top_margin, id: this._id}]);
3938 g.exit().remove();
3939 g = g.enter().append("g").attr("class", "slider-container").merge(g);
3940 g.attr("transform", function(d) {
3941 return "translate(" + d.left + "," + d.top + ")";
3942 })
3943 .attr("id", function(d) { return d.id; });
3944
3945 this.scale = (this._scale ? this._scale() : linear$1()).domain(this._domain).range([0, w]);
3946
3947 if (this._value == null || this._value < this._domain[0]) this._value = this._domain[0];
3948 else if (this._value > this._domain[1]) this._value = this._domain[1];
3949
3950 if (this._snap) this._value = snapTo(this._snap, this._value);
3951
3952 var axes_data = [];
3953 if (this._axis) {
3954 var axis;
3955 if (typeof this._axis != "boolean") {
3956 axis = this._axis(this.scale);
3957 }
3958 else {
3959 axis = axisBottom().scale(this.scale).tickPadding(6);
3960 }
3961
3962 if (this._ticks) axis.ticks(this._ticks);
3963 if (this._tickFormat) axis.tickFormat(this._tickFormat);
3964 if (this._tickSize) axis.tickSize(this._tickSize);
3965 else axis.tickSize(Math.max(5, this._handleRadius - this._channelHeight - 2));
3966 axes_data.push(axis);
3967 }
3968
3969 var axes = g.selectAll(".slider-axis");
3970 var axes_enter = axes.data(axes_data).enter();
3971 axes_enter.append("g").attr("class", "slider-axis")
3972 .attr("transform", "translate(" + 0 + "," + this._channelHeight/2 + ")")
3973 .each(function(axis) { axis(select(this)); });
3974 axes_enter.select(".domain").attr("fill", "none");
3975 axes_enter.selectAll(".tick line").attr("stroke", "black");
3976 axes_enter.exit().remove();
3977
3978 var channel, handle;
3979 channel = g.selectAll(".slider-channel")
3980 .data([{ width: channel_w, height: this._channelHeight, channel_r: channel_r }]);
3981 channel.exit().remove();
3982
3983 channel = channel.enter().append("rect").attr("class", "slider-channel")
3984 .attr("cursor", "pointer")
3985 .on("click", function() {
3986 var slider_x = Math.max(0, Math.min(w, d3_mouse(this)[0]));
3987 that._value = that.scale.invert(slider_x);
3988 if (that._snap) that._value = snapTo(that._snap, that._value);
3989 handle.attr("cx", that.scale(that._value));
3990 that.fire("change", that._value);
3991 })
3992 .merge(channel);
3993
3994 channel.attr("width", function(d) { return d.width; })
3995 .attr("fill", this._channelFill)
3996 .attr("height", function(d) { return d.height; })
3997 .attr("y", function(d) { return -d.height/2; })
3998 .attr("x", function(d) { return -d.channel_r; })
3999 .attr("rx", function(d) { return d.channel_r; });
4000
4001 var drag_dx_origin, drag_x_origin;
4002 function handleMousedown(event) {
4003 document.addEventListener("mouseup", handleMouseup, false);
4004 document.addEventListener("mousemove", handleMousemove, false);
4005 drag_dx_origin = event.clientX;
4006 drag_x_origin = that.scale(that._value);
4007 }
4008
4009 function handleMouseup() {
4010 document.removeEventListener("mouseup", handleMouseup, false);
4011 document.removeEventListener("mousemove", handleMousemove, false);
4012 }
4013
4014 function handleMousemove(event) {
4015 drag(event.clientX - drag_dx_origin);
4016 }
4017
4018 function handleTouchstart(event) {
4019 if (event.touches.length != 1) return;
4020 document.addEventListener("touchend", handleTouchend, false);
4021 document.addEventListener("touchmove", handleTouchmove, false);
4022 drag_dx_origin = event.touches[0].clientX;
4023 drag_x_origin = that.scale(that._value);
4024 }
4025
4026 function handleTouchend() {
4027 document.removeEventListener("touchend", handleTouchend, false);
4028 document.removeEventListener("touchmove", handleTouchmove, false);
4029 }
4030
4031 function handleTouchmove(event) {
4032 if (event.touches.length != 1) return;
4033 drag(event.touches[0].clientX - drag_dx_origin);
4034 }
4035
4036 function drag(dx) {
4037 var new_x = drag_x_origin + dx;
4038 var slider_x = Math.max(0, Math.min(w, new_x));
4039 var new_value = that.scale.invert(slider_x);
4040 if (that._snap) new_value = snapTo(that._snap, new_value);
4041 handle.attr("cx", that.scale(new_value));
4042 if (new_value != that._value) {
4043 that._value = new_value;
4044 that.fire("change", that._value);
4045 }
4046 }
4047
4048 handle = g.selectAll(".slider-handle").data([{ v: this._value, x: this.scale(this._value) }]);
4049 handle = handle.enter().append("circle").attr("class", "slider-handle")
4050 .attr("cursor", "col-resize")
4051 .merge(handle);
4052
4053 handle.attr("cx", function(d) { return d.x; })
4054 .attr("r", this._handleRadius)
4055 .attr("fill", this._handleFill)
4056 .on("mousedown", function() {
4057 event$1.preventDefault();
4058 handleMousedown(event$1);
4059 })
4060 .on("touchstart", function() {
4061 event$1.preventDefault();
4062 handleTouchstart(event$1);
4063 });
4064
4065 var label_data = [];
4066 if (this._label) {
4067 label_data.push({
4068 label: this._label, x: w/2, y: -label_h, font_size: this._labelSize
4069 });
4070 }
4071 var label = g.selectAll(".slider-label").data(label_data);
4072 label.exit().remove();
4073 label = label.enter()
4074 .append("text").attr("class", "slider-label")
4075 .attr("text-anchor", "middle")
4076 .attr("cursor", "default")
4077 .merge(label);
4078
4079 label
4080 .text(function(d) { return d.label; })
4081 .attr("x", function(d) { return d.x; })
4082 .attr("y", function(d) { return d.y; })
4083 .attr("font-size", this._labelSize);
4084
4085 var end_label_data = [];
4086 if (this._startLabel) {
4087 end_label_data.push({
4088 label: this._startLabel,
4089 x: this._startLabelBelow ? 0 : -(channel_r + 5 + Math.max(0, this._handleRadius - channel_r)),
4090 y: this._startLabelBelow ? (channel_r + 15) : this._startEndLabelSize/1.75 - channel_r/2,
4091 anchor: this._startLabelBelow ? "middle" : "end",
4092 font_size: this._startEndLabelSize
4093 });
4094 }
4095 if (this._endLabel) {
4096 end_label_data.push({
4097 label: this._endLabel,
4098 x: this._endLabelBelow ? w : w + (channel_r + Math.max(0, this._handleRadius - channel_r) + 5),
4099 y: this._startLabelBelow ? (channel_r + 15) : this._startEndLabelSize/1.75 - channel_r/2,
4100 anchor: this._endLabelBelow ? "middle" : "start",
4101 font_size: this._startEndLabelSize
4102 });
4103 }
4104
4105 var end_labels = g.selectAll(".slider-end-labels").data(end_label_data);
4106 end_labels.exit().remove();
4107 end_labels = end_labels.enter().append("text").attr("class", "slider-end-labels")
4108 .attr("pointer-events", "none")
4109 .merge(end_labels);
4110 end_labels
4111 .text(function(d) { return d.label; })
4112 .attr("font-size", function(d) { return d.font_size; })
4113 .attr("x", function(d) { return d.x; })
4114 .attr("y", function(d) { return d.y; })
4115 .attr("text-anchor", function(d) { return d.anchor; });
4116
4117 return this;
4118 };
4119
4120 Slider.prototype.update = Slider.prototype.draw;
4121
4122 function Flourish_slider(selector) {
4123 return new Slider(selector);
4124 }
4125 Flourish_slider.version = VERSION;
4126
4127 function createPlayButton(colour) {
4128 var start_string = '<svg width="25px" height="30px" viewBox="0 0 25 30"> <polygon fill="';
4129 var end_string = '" stroke="none" points="25 15 0 30 0 0"></polygon> </svg>';
4130 return start_string + colour + end_string;
4131 }
4132
4133 function createPauseButton(colour) {
4134 var start_string = '<svg width="26px" height="30px" viewBox="0 0 26 30"> <g stroke="none" stroke-width="1" fill="';
4135 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>';
4136 return start_string + colour + end_string;
4137 }
4138
4139 function createSlider(control_obj, state, container) {
4140 var slider_obj = {};
4141 var slider_holder = select(container).append("div").attr("class", "fl-control fl-control-slider animatable");
4142 var slider_play_button = slider_holder.append("div").attr("class", "slider-play");
4143 var slider_div = slider_holder.append("div").attr("class", "fl-controls-slider");
4144
4145 var play_string, pause_string, handle_color;
4146 var sorted_options, timesteps, slider_label, is_playing;
4147 var timer_id = null;
4148
4149 var clearTimer = function() {
4150 clearTimeout(timer_id);
4151 timer_id = null;
4152 };
4153
4154 var sliderChangeFunction = function(i) {
4155 var d = sorted_options[i];
4156 if (d.options_index === control_obj.index()) return;
4157 control_obj.index(d.options_index);
4158 control_obj.trigger("change");
4159 };
4160
4161 var slider = Flourish_slider(slider_div.node())
4162 .snap(true)
4163 .on("change", function(i) {
4164 var is_playing = timer_id !== null;
4165 if (is_playing) clearTimer();
4166 sliderChangeFunction(i);
4167 if (is_playing) setNextTick();
4168 });
4169
4170
4171 var stopSliderPlayer = function() {
4172 clearTimer();
4173 slider_holder.classed("playing", false);
4174 slider_play_button.html(play_string);
4175 control_obj._isPlaying_(false);
4176 is_playing = false;
4177 };
4178
4179
4180 var setNextTick = function() {
4181 var current_index = control_obj.getSortedIndex();
4182 var current_delay = timesteps[current_index];
4183 var final_index = control_obj.n_options - 1;
4184 var next_index = current_index < final_index ? current_index + 1 : 0;
4185
4186 timer_id = setTimeout(function() {
4187 sliderChangeFunction(next_index);
4188 if (state.slider_loop || next_index < final_index) setNextTick();
4189 else stopSliderPlayer();
4190 }, current_delay);
4191 };
4192
4193
4194 var startSliderPlayer = function() {
4195 slider_holder.classed("playing", true);
4196 slider_play_button.html(pause_string);
4197 setNextTick();
4198 control_obj._isPlaying_(true);
4199 is_playing = true;
4200 };
4201
4202
4203 slider_play_button.on("click", function() {
4204 if (timer_id === null) startSliderPlayer();
4205 else stopSliderPlayer();
4206 });
4207
4208
4209 var setWidths = function() {
4210 var handle_radius = Math.round(remToPx(state.slider_handle_height) / 2);
4211 slider_holder.style("width", Math.round(remToPx(state.slider_width)) + "px");
4212 slider_play_button
4213 .style("height", handle_radius * 2 + "px")
4214 .style("width", handle_radius * 2 + "px")
4215 .style("display", state.slider_play_button ? null : "none");
4216 var holder_width = slider_holder.node().getBoundingClientRect().width;
4217 var button_width = slider_play_button.node().getBoundingClientRect().width;
4218 slider_div
4219 .style("width", Math.max((holder_width - button_width), 1) + "px")
4220 .style("height", handle_radius * 2 + "px");
4221
4222 slider
4223 .handleRadius(handle_radius)
4224 .margin({
4225 left: handle_radius + 5,
4226 right: handle_radius + remToPx(state.slider_margin),
4227 top: handle_radius
4228 });
4229 };
4230
4231
4232 var setHandles = function() {
4233 if (state.slider_play_button) {
4234 slider_holder.classed("animatable", true);
4235 }
4236 else {
4237 stopSliderPlayer();
4238 slider_holder.classed("animatable", false);
4239 }
4240 if (handle_color !== state.slider_handle_color) {
4241 slider.update(); // Make sure slider-handle actually exists before changing its colour
4242 handle_color = state.slider_handle_color || "currentColor";
4243 slider_holder.select(".slider-handle").style("fill", handle_color);
4244 play_string = createPlayButton(handle_color);
4245 pause_string = createPauseButton(handle_color);
4246 slider_play_button.html(timer_id ? pause_string : play_string);
4247 }
4248 };
4249
4250
4251 var showControl = function() {
4252 slider_holder.style("display", "inline-block");
4253 setWidths();
4254 setHandles();
4255 return slider_obj;
4256 };
4257
4258 var hideControl = function() {
4259 stopSliderPlayer();
4260 slider_holder.style("display", "none");
4261 return slider_obj;
4262 };
4263
4264 slider_obj.show = showControl;
4265 slider_obj.hide = hideControl;
4266
4267
4268 slider_obj.update = function(_sorted_options) {
4269 sorted_options = _sorted_options;
4270 if (!control_obj.n_options || state.control_type !== "slider") {
4271 hideControl();
4272 return slider_obj;
4273 }
4274
4275 showControl();
4276
4277 var n_options = control_obj.n_options;
4278 var loop = state.slider_loop;
4279
4280 timesteps = sorted_options.map(function(d, i) {
4281 var dur_in_seconds = state.slider_step_time + (loop && i === (n_options -1) ? state.slider_restart_pause : 0);
4282 return dur_in_seconds * 1000;
4283 });
4284
4285 var sorted_index = control_obj.getSortedIndex();
4286 var d = sorted_options[sorted_index];
4287
4288 slider.domain([0, n_options - 1])
4289 .value(sorted_index)
4290 .endLabel(d.display)
4291 .channelHeight(Math.round(remToPx(state.slider_track_height)))
4292 .channelFill(state.slider_background_color)
4293 .update();
4294
4295 slider.container.select("svg").attr("fill", "currentColor");
4296
4297 slider_label = slider_label || slider_div.select("text.slider-end-labels");
4298 slider_label
4299 .style("fill", state.slider_font_color)
4300 .attr("y", "0")
4301 .attr("dy", "0.25em");
4302
4303
4304 if (control_obj._isPlaying_() && !is_playing) startSliderPlayer();
4305 else if (!control_obj._isPlaying_() && is_playing) stopSliderPlayer();
4306
4307 return slider_obj;
4308 };
4309
4310
4311 return slider_obj;
4312 }
4313
4314 var DEFAULTS = Object.freeze({
4315 control_type: "dropdown",
4316 control_title: "",
4317
4318 // Dropdown
4319 dropdown_width_mode: "auto",
4320 dropdown_width_fixed: 20,
4321
4322 // Buttons
4323 button_group: true,
4324 button_group_width_mode: "fixed",
4325 button_group_width_fixed: 20,
4326
4327 // Slider
4328 slider_width: 15,
4329 slider_handle_color: null,
4330 slider_font_color: null,
4331 slider_background_color: "#dddddd",
4332
4333 slider_handle_height: 1,
4334 slider_track_height: 0.2,
4335 slider_margin: 4.5,
4336
4337 slider_play_button: true,
4338 slider_step_time: 2,
4339 slider_loop: true,
4340 slider_restart_pause: 0,
4341
4342 // Data typing
4343 sort: "unsorted",
4344 sort_temporal_format: "%Y",
4345
4346 _index_: null,
4347 _is_playing_: false
4348 });
4349
4350
4351 function init(state, getParser, getFormatter) {
4352 var control_obj = {};
4353 getParser = getParser || getDefaultParser;
4354 getFormatter = getFormatter || getDefaultFormatter;
4355 var options = [];
4356 var sorted_options = [];
4357 var changeHandlers = [];
4358 var container = document.createElement("div");
4359 container.setAttribute("class", "fl-controls-container");
4360 var container_title = select(container).append("div").attr("class", "fl-controls-title");
4361 var dropdown_obj = createDropdown(control_obj, state, container);
4362 var buttons_obj = createButtons(control_obj, state, container);
4363 var slider_obj = createSlider(control_obj, state, container);
4364
4365 for (var key in DEFAULTS) {
4366 if (state[key] === undefined) state[key] = DEFAULTS[key];
4367 }
4368
4369 var current_index = state._index_;
4370
4371 var checkValidIndex = function(i) {
4372 return options.length && i >= 0 && i < options.length;
4373 };
4374
4375 var updateControls = function(sorted_options) {
4376 container.style.display = (sorted_options.length > 1) ? null : "none";
4377 container.style.width = "";
4378 container_title.node().innerHTML = state.control_title;
4379 container_title.node().style.display = (state.control_title === "") ? "none" : "inline-block";
4380 slider_obj.update(sorted_options); // Do slider first in case we're stopping playing
4381 dropdown_obj.update(sorted_options);
4382 buttons_obj.update(sorted_options);
4383 };
4384
4385 control_obj.appendTo = function(parent_container, bounding_container) {
4386 injectCSS();
4387 select(parent_container).node().appendChild(container);
4388 dropdown_obj.appendedToDOM(bounding_container);
4389 return control_obj;
4390 };
4391
4392 var callOnChangeCallbacks = function() {
4393 var index = indexFunction();
4394 var value = options[index];
4395 changeHandlers.forEach(function(func) {
4396 func(value, index);
4397 });
4398 return control_obj;
4399 };
4400
4401 control_obj.remove = function() {
4402 if (container.parentElement) container.parentElement.removeChild(container);
4403 dropdown_obj.removedFromDOM();
4404
4405 return control_obj;
4406 };
4407
4408 control_obj.options = function(arr) {
4409 if (arr === undefined) return options.slice();
4410 if (!Array.isArray(arr)) return control_obj;
4411 options = arr.slice();
4412 var n = options.length;
4413 var i = indexFunction();
4414 if (!n) indexFunction(null);
4415 else if (i === null || i >= n) indexFunction(0);
4416 return control_obj;
4417 };
4418
4419 Object.defineProperty(control_obj, "n_options", { get: function() { return options.length; } });
4420
4421
4422 var indexFunction = function(i) {
4423 if (i === undefined) {
4424 if (!state._is_playing_) current_index = state._index_;
4425 return current_index;
4426 }
4427 if (i === null || checkValidIndex(i)) {
4428 current_index = i;
4429 if (!state._is_playing_) state._index_ = current_index;
4430 }
4431 else console.warn("Invalid index, ignoring update call");
4432 return control_obj;
4433 };
4434 control_obj.index = indexFunction;
4435
4436 control_obj.getSortedIndex = function() {
4437 var options_index = indexFunction();
4438 if (state.sort == "unsorted") return options_index;
4439 var sorted_index;
4440 sorted_options.some(function(d, i) {
4441 if (d.options_index === options_index) {
4442 sorted_index = i;
4443 return true;
4444 }
4445 });
4446 return sorted_index;
4447 };
4448
4449 control_obj.value = function(value) {
4450 if (value === undefined) return options[indexFunction()];
4451 var index = options.indexOf(value);
4452 if (index !== -1) indexFunction(index);
4453 return control_obj;
4454 };
4455
4456 control_obj.on = function(event, callback) {
4457 if (event === "change") changeHandlers.push(callback.bind(control_obj));
4458 return control_obj;
4459 };
4460
4461 control_obj.update = function() {
4462 getRemToPx();
4463 sorted_options = sortArray(options, state, getParser(), getFormatter());
4464 updateControls(sorted_options);
4465 return control_obj;
4466 };
4467
4468 control_obj.trigger = function(event) {
4469 if (event === "change") callOnChangeCallbacks();
4470 return control_obj;
4471 };
4472
4473 control_obj.getNode = function() {
4474 return container;
4475 };
4476
4477 var isPlaying = function(is_playing) {
4478 if (is_playing === undefined) return state._is_playing_;
4479 state._is_playing_ = !!is_playing;
4480 if (!is_playing) indexFunction(current_index); // Force _index_ to match current index
4481 };
4482
4483 control_obj._isPlaying_ = isPlaying;
4484
4485 return control_obj;
4486 }
4487
4488 return init;
4489
4490})));