UNPKG

134 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", "0");
1961
1962 s.select(".fl-controls-container, .fl-controls-container *")
1963 .style("box-sizing", "border-box");
1964
1965 s.select(".slider-holder")
1966 .style("margin-bottom", "20px");
1967
1968 s.select(".fl-controls-slider, .slider-play")
1969 .style("pointer-events", "all")
1970 .style("display", "inline-block")
1971 .style("vertical-align", "middle");
1972
1973 s.select(".slider-play svg")
1974 .style("height", "100%")
1975 .style("width", "100%")
1976 .style("cursor", " pointer", "");
1977
1978 s.select(".slider-play:hover")
1979 .style("opacity", "0.6");
1980
1981 s.select(".fl-control-slider")
1982 .style("width", "100%")
1983 .style("bottom", "0");
1984
1985 s.select(".fl-control")
1986 .style("position", "relative");
1987
1988 s.select(".fl-control.hidden")
1989 .style("display", "none");
1990
1991 s.select(".fl-control .button")
1992 .style("display", "inline-block")
1993 .style("background", "#eee")
1994 .style("padding", "0.5em")
1995 .style("margin-right", "0.25em")
1996 .style("margin-bottom", "0.25em")
1997 .style("line-height", "1em");
1998
1999 s.select(".fl-control.grouped:not(.hidden)")
2000 .style("display", "table")
2001 .style("table-layout", "fixed")
2002 .select(".button")
2003 .style("display", "table-cell")
2004 .style("margin", "0")
2005 .style("text-align", "center");
2006
2007 s.select(".fl-control .button.selected")
2008 .style("background", "#ddd");
2009
2010 s.select(".fl-control-dropdown")
2011 .style("line-height", "1em")
2012 .select(".list")
2013 .style("display", "none")
2014 .style("position", "absolute")
2015 .style("background-color", "white")
2016 .style("z-index", "100")
2017 .style("border", "1px solid #eee")
2018 .select(".list-item")
2019 .style("cursor", "pointer")
2020 .style("padding", "0.5rem");
2021
2022 s.select(".fl-control-dropdown.open .list")
2023 .style("display", "block");
2024
2025 s.select(".fl-control-dropdown .main")
2026 .style("position", "relative");
2027
2028 s.select(".fl-control-dropdown .symbol")
2029 .style("float", "right")
2030 .select("div")
2031 .style("border-top-color", "#333333");
2032
2033 return s.print();
2034 }
2035
2036 var css_injected = false;
2037
2038 function injectCSS() {
2039 if (css_injected || typeof document === "undefined") return;
2040
2041 var css_string = createCssString();
2042
2043 var head = document.head || document.getElementsByTagName("head")[0];
2044 var style = document.createElement("style");
2045 style.type = "text/css";
2046 style.className = "flourish-controls";
2047 head.appendChild(style);
2048 if (style.styleSheet) {
2049 style.styleSheet.cssText = css_string;
2050 }
2051 else {
2052 style.appendChild(document.createTextNode(css_string));
2053 }
2054 css_injected = true;
2055 }
2056
2057 var getTextWidth = (function() {
2058 var context = document.createElement("canvas").getContext("2d");
2059 return function(text, font) {
2060 context.font = font || "10px sans-serif";
2061 var metrics = context.measureText(text);
2062 return metrics.width;
2063 };
2064 })();
2065
2066 var remToPx;
2067
2068 function getRemToPx() {
2069 var font_size = parseFloat(getComputedStyle(document.documentElement).fontSize) || 16;
2070 remToPx = function (rem) { return rem * font_size; };
2071 }
2072
2073 var MIN_HEIGHT = 90;
2074 function createDropdown(control_obj, state, container) {
2075 var dropdown_obj = {};
2076 var bounding_container = document.body;
2077
2078 // Add dropdown elements to container
2079 var dropdown = select(container).append("div").attr("class", "fl-control fl-control-dropdown");
2080 var dropdown_node = dropdown.node();
2081 var dropdown_main = dropdown.append("div").attr("class", "main");
2082 var dropdown_current = dropdown_main.append("span").attr("class", "current");
2083 dropdown_main.append("span").attr("class", "symbol").style("width", "10px")
2084 .append("div")
2085 .style("border-left", "5px solid transparent")
2086 .style("border-right", "5px solid transparent")
2087 .style("border-bottom", "5px solid transparent")
2088 .style("border-top-style", "solid")
2089 .style("border-top-width", "5px")
2090 .style("top", "50%")
2091 .style("position", "absolute")
2092 .style("margin-top", "-2.5px");
2093 var dropdown_list = dropdown.append("div").attr("class", "list");
2094
2095 var showDropdownList = function() {
2096 dropdown.classed("open", true);
2097 dropdown_list.style("top", "100%");
2098 dropdown_list.style("bottom", null);
2099 dropdown_list.style("display", null);
2100 dropdown_list.style("overflow", "auto");
2101 var bounding_bb = bounding_container.getBoundingClientRect();
2102 var list_bb = dropdown_list.node().getBoundingClientRect();
2103 var overspill = list_bb.bottom - bounding_bb.bottom;
2104 if (overspill > 0) {
2105 var new_height = bounding_bb.bottom - list_bb.top - 30;
2106 if (new_height > MIN_HEIGHT) dropdown_list.style("max-height", new_height + "px");
2107 else dropdown_list
2108 .style("top", "auto")
2109 .style("bottom", "100%");
2110 }
2111 if (list_bb.right > window.innerWidth) {
2112 dropdown_list.style("right", 0);
2113 }
2114 };
2115
2116 var hideDropdownList = function() {
2117 dropdown.classed("open", false);
2118 dropdown_list.style("right", null);
2119 dropdown_list.style("max-height", null);
2120 dropdown_list.style("display", "none");
2121 };
2122
2123 var toggleDropdownList = function() {
2124 if (dropdown.classed("open")) hideDropdownList();
2125 else showDropdownList();
2126 };
2127
2128 dropdown_main.on("click", function() { toggleDropdownList(); });
2129
2130 var clickHandler = function() {
2131 if (!dropdown.classed("open")) return; // If already closed, nothing to close
2132 var el = event.target;
2133 var parent = el.parentElement;
2134 while (parent) {
2135 if (el === dropdown_node) return; // We've clicked the dropdown, don't close it here
2136 el = parent;
2137 parent = el.parentElement;
2138 }
2139 hideDropdownList(); // Clicked somewhere else, hide the dropdown
2140 };
2141
2142 var showControl = function(longest_text_width) {
2143 var dropdown_width = "100%";
2144 if (state.dropdown_width_mode == "auto") {
2145 dropdown_width = Math.min(longest_text_width + 40, remToPx(20)) + "px";
2146 }
2147 else if (state.dropdown_width_mode == "fixed") {
2148 dropdown_width = remToPx(state.dropdown_width_fixed) + "px";
2149 }
2150 container.style.width = state.dropdown_width_mode == "full" ? dropdown_width : "";
2151 dropdown.style("width", dropdown_width).style("display", state.dropdown_width_mode !== "full" ? "inline-table" : null);
2152 dropdown.select(".main").style("width", dropdown_width);
2153 };
2154
2155 var hideControl = function() {
2156 hideDropdownList();
2157 dropdown.style("display", "none");
2158 };
2159
2160
2161 dropdown_obj.appendedToDOM = function(_bounding_container) {
2162 if (_bounding_container) bounding_container = _bounding_container;
2163 document.querySelector("body").addEventListener("click", clickHandler, false);
2164 return dropdown_obj;
2165 };
2166
2167
2168 dropdown_obj.removedFromDOM = function() {
2169 document.querySelector("body").removeEventListener("click", clickHandler);
2170 return dropdown_obj;
2171 };
2172
2173 dropdown_obj.show = showControl;
2174 dropdown_obj.hide = hideControl;
2175
2176
2177 dropdown_obj.update = function(sorted_options) {
2178 dropdown_list.text("");
2179 var dropdown_font_size = window.getComputedStyle(dropdown.node()).fontSize;
2180 if (!control_obj.n_options || state.control_type !== "dropdown") {
2181 hideControl();
2182 return dropdown_obj;
2183 }
2184
2185 var longest_text = "";
2186 dropdown_list.text("")
2187 .selectAll(".list-item")
2188 .data(sorted_options)
2189 .enter()
2190 .append("div")
2191 .attr("class", "list-item")
2192 .text(function(d) {
2193 if (d.display.length > longest_text.length) longest_text = d.display;
2194 return d.display;
2195 })
2196 .on("click", function(d) {
2197 hideDropdownList();
2198 var i = d.options_index;
2199 if (i === control_obj.index()) return;
2200 control_obj.index(i);
2201 dropdown_current.text(d.display).attr("title", d.display);
2202 control_obj.trigger("change");
2203 });
2204 var longest_text_width = getTextWidth(longest_text, dropdown_font_size + " sans-serif");
2205 var sorted_index = control_obj.getSortedIndex();
2206 var value = sorted_options[sorted_index].display;
2207 dropdown_current.text(value).attr("title", value);
2208
2209 showControl(longest_text_width);
2210
2211 return dropdown_obj;
2212 };
2213
2214
2215 return dropdown_obj;
2216 }
2217
2218 function createButtons(control_obj, state, container) {
2219 var button_obj = {};
2220 var button_container = select(container).append("div").attr("class", "fl-control fl-control-buttons");
2221
2222 var showControl = function() {
2223 button_container.classed("hidden", false);
2224 };
2225
2226 var hideControl = function() {
2227 button_container.classed("hidden", true);
2228 };
2229
2230 button_obj.show = showControl;
2231 button_obj.hide = hideControl;
2232
2233 button_obj.update = function(sorted_options) {
2234 button_container.text("");
2235 if (!control_obj.n_options || state.control_type !== "buttons") {
2236 hideControl();
2237 return button_obj;
2238 }
2239
2240 var index = control_obj.index();
2241
2242 button_container.classed("grouped", state.button_group);
2243 button_container.classed("fixed-width", state.button_group_width_mode == "fixed" || state.button_group_width_mode == "full");
2244 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);
2245
2246 var buttons = button_container.selectAll(".button")
2247 .data(sorted_options)
2248 .enter()
2249 .append("div");
2250
2251 buttons.attr("class", "button")
2252 .style("cursor", "pointer")
2253 .classed("selected", function(d) { return d.options_index === index; })
2254 .on("click", function(d) {
2255 var i = d.options_index;
2256 if (i === control_obj.index()) return;
2257 control_obj.index(i);
2258 buttons.classed("selected", function(d) { return d.options_index === i; });
2259 control_obj.trigger("change");
2260 })
2261 .append("span")
2262 .text(function(d) { return d.display; });
2263
2264 showControl();
2265 };
2266
2267 return button_obj;
2268 }
2269
2270 var slice = Array.prototype.slice;
2271
2272 function identity(x) {
2273 return x;
2274 }
2275
2276 var top = 1,
2277 right = 2,
2278 bottom = 3,
2279 left = 4,
2280 epsilon = 1e-6;
2281
2282 function translateX(x) {
2283 return "translate(" + (x + 0.5) + ",0)";
2284 }
2285
2286 function translateY(y) {
2287 return "translate(0," + (y + 0.5) + ")";
2288 }
2289
2290 function number(scale) {
2291 return function(d) {
2292 return +scale(d);
2293 };
2294 }
2295
2296 function center(scale) {
2297 var offset = Math.max(0, scale.bandwidth() - 1) / 2; // Adjust for 0.5px offset.
2298 if (scale.round()) offset = Math.round(offset);
2299 return function(d) {
2300 return +scale(d) + offset;
2301 };
2302 }
2303
2304 function entering() {
2305 return !this.__axis;
2306 }
2307
2308 function axis(orient, scale) {
2309 var tickArguments = [],
2310 tickValues = null,
2311 tickFormat = null,
2312 tickSizeInner = 6,
2313 tickSizeOuter = 6,
2314 tickPadding = 3,
2315 k = orient === top || orient === left ? -1 : 1,
2316 x = orient === left || orient === right ? "x" : "y",
2317 transform = orient === top || orient === bottom ? translateX : translateY;
2318
2319 function axis(context) {
2320 var values = tickValues == null ? (scale.ticks ? scale.ticks.apply(scale, tickArguments) : scale.domain()) : tickValues,
2321 format = tickFormat == null ? (scale.tickFormat ? scale.tickFormat.apply(scale, tickArguments) : identity) : tickFormat,
2322 spacing = Math.max(tickSizeInner, 0) + tickPadding,
2323 range = scale.range(),
2324 range0 = +range[0] + 0.5,
2325 range1 = +range[range.length - 1] + 0.5,
2326 position = (scale.bandwidth ? center : number)(scale.copy()),
2327 selection = context.selection ? context.selection() : context,
2328 path = selection.selectAll(".domain").data([null]),
2329 tick = selection.selectAll(".tick").data(values, scale).order(),
2330 tickExit = tick.exit(),
2331 tickEnter = tick.enter().append("g").attr("class", "tick"),
2332 line = tick.select("line"),
2333 text = tick.select("text");
2334
2335 path = path.merge(path.enter().insert("path", ".tick")
2336 .attr("class", "domain")
2337 .attr("stroke", "currentColor"));
2338
2339 tick = tick.merge(tickEnter);
2340
2341 line = line.merge(tickEnter.append("line")
2342 .attr("stroke", "currentColor")
2343 .attr(x + "2", k * tickSizeInner));
2344
2345 text = text.merge(tickEnter.append("text")
2346 .attr("fill", "currentColor")
2347 .attr(x, k * spacing)
2348 .attr("dy", orient === top ? "0em" : orient === bottom ? "0.71em" : "0.32em"));
2349
2350 if (context !== selection) {
2351 path = path.transition(context);
2352 tick = tick.transition(context);
2353 line = line.transition(context);
2354 text = text.transition(context);
2355
2356 tickExit = tickExit.transition(context)
2357 .attr("opacity", epsilon)
2358 .attr("transform", function(d) { return isFinite(d = position(d)) ? transform(d) : this.getAttribute("transform"); });
2359
2360 tickEnter
2361 .attr("opacity", epsilon)
2362 .attr("transform", function(d) { var p = this.parentNode.__axis; return transform(p && isFinite(p = p(d)) ? p : position(d)); });
2363 }
2364
2365 tickExit.remove();
2366
2367 path
2368 .attr("d", orient === left || orient == right
2369 ? (tickSizeOuter ? "M" + k * tickSizeOuter + "," + range0 + "H0.5V" + range1 + "H" + k * tickSizeOuter : "M0.5," + range0 + "V" + range1)
2370 : (tickSizeOuter ? "M" + range0 + "," + k * tickSizeOuter + "V0.5H" + range1 + "V" + k * tickSizeOuter : "M" + range0 + ",0.5H" + range1));
2371
2372 tick
2373 .attr("opacity", 1)
2374 .attr("transform", function(d) { return transform(position(d)); });
2375
2376 line
2377 .attr(x + "2", k * tickSizeInner);
2378
2379 text
2380 .attr(x, k * spacing)
2381 .text(format);
2382
2383 selection.filter(entering)
2384 .attr("fill", "none")
2385 .attr("font-size", 10)
2386 .attr("font-family", "sans-serif")
2387 .attr("text-anchor", orient === right ? "start" : orient === left ? "end" : "middle");
2388
2389 selection
2390 .each(function() { this.__axis = position; });
2391 }
2392
2393 axis.scale = function(_) {
2394 return arguments.length ? (scale = _, axis) : scale;
2395 };
2396
2397 axis.ticks = function() {
2398 return tickArguments = slice.call(arguments), axis;
2399 };
2400
2401 axis.tickArguments = function(_) {
2402 return arguments.length ? (tickArguments = _ == null ? [] : slice.call(_), axis) : tickArguments.slice();
2403 };
2404
2405 axis.tickValues = function(_) {
2406 return arguments.length ? (tickValues = _ == null ? null : slice.call(_), axis) : tickValues && tickValues.slice();
2407 };
2408
2409 axis.tickFormat = function(_) {
2410 return arguments.length ? (tickFormat = _, axis) : tickFormat;
2411 };
2412
2413 axis.tickSize = function(_) {
2414 return arguments.length ? (tickSizeInner = tickSizeOuter = +_, axis) : tickSizeInner;
2415 };
2416
2417 axis.tickSizeInner = function(_) {
2418 return arguments.length ? (tickSizeInner = +_, axis) : tickSizeInner;
2419 };
2420
2421 axis.tickSizeOuter = function(_) {
2422 return arguments.length ? (tickSizeOuter = +_, axis) : tickSizeOuter;
2423 };
2424
2425 axis.tickPadding = function(_) {
2426 return arguments.length ? (tickPadding = +_, axis) : tickPadding;
2427 };
2428
2429 return axis;
2430 }
2431
2432 function axisBottom(scale) {
2433 return axis(bottom, scale);
2434 }
2435
2436 function ascending$2(a, b) {
2437 return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
2438 }
2439
2440 function bisector(compare) {
2441 if (compare.length === 1) compare = ascendingComparator(compare);
2442 return {
2443 left: function(a, x, lo, hi) {
2444 if (lo == null) lo = 0;
2445 if (hi == null) hi = a.length;
2446 while (lo < hi) {
2447 var mid = lo + hi >>> 1;
2448 if (compare(a[mid], x) < 0) lo = mid + 1;
2449 else hi = mid;
2450 }
2451 return lo;
2452 },
2453 right: function(a, x, lo, hi) {
2454 if (lo == null) lo = 0;
2455 if (hi == null) hi = a.length;
2456 while (lo < hi) {
2457 var mid = lo + hi >>> 1;
2458 if (compare(a[mid], x) > 0) hi = mid;
2459 else lo = mid + 1;
2460 }
2461 return lo;
2462 }
2463 };
2464 }
2465
2466 function ascendingComparator(f) {
2467 return function(d, x) {
2468 return ascending$2(f(d), x);
2469 };
2470 }
2471
2472 var ascendingBisect = bisector(ascending$2);
2473 var bisectRight = ascendingBisect.right;
2474
2475 var e10 = Math.sqrt(50),
2476 e5 = Math.sqrt(10),
2477 e2 = Math.sqrt(2);
2478
2479 function ticks(start, stop, count) {
2480 var reverse,
2481 i = -1,
2482 n,
2483 ticks,
2484 step;
2485
2486 stop = +stop, start = +start, count = +count;
2487 if (start === stop && count > 0) return [start];
2488 if (reverse = stop < start) n = start, start = stop, stop = n;
2489 if ((step = tickIncrement(start, stop, count)) === 0 || !isFinite(step)) return [];
2490
2491 if (step > 0) {
2492 start = Math.ceil(start / step);
2493 stop = Math.floor(stop / step);
2494 ticks = new Array(n = Math.ceil(stop - start + 1));
2495 while (++i < n) ticks[i] = (start + i) * step;
2496 } else {
2497 start = Math.floor(start * step);
2498 stop = Math.ceil(stop * step);
2499 ticks = new Array(n = Math.ceil(start - stop + 1));
2500 while (++i < n) ticks[i] = (start - i) / step;
2501 }
2502
2503 if (reverse) ticks.reverse();
2504
2505 return ticks;
2506 }
2507
2508 function tickIncrement(start, stop, count) {
2509 var step = (stop - start) / Math.max(0, count),
2510 power = Math.floor(Math.log(step) / Math.LN10),
2511 error = step / Math.pow(10, power);
2512 return power >= 0
2513 ? (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1) * Math.pow(10, power)
2514 : -Math.pow(10, -power) / (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1);
2515 }
2516
2517 function tickStep(start, stop, count) {
2518 var step0 = Math.abs(stop - start) / Math.max(0, count),
2519 step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)),
2520 error = step0 / step1;
2521 if (error >= e10) step1 *= 10;
2522 else if (error >= e5) step1 *= 5;
2523 else if (error >= e2) step1 *= 2;
2524 return stop < start ? -step1 : step1;
2525 }
2526
2527 function define(constructor, factory, prototype) {
2528 constructor.prototype = factory.prototype = prototype;
2529 prototype.constructor = constructor;
2530 }
2531
2532 function extend(parent, definition) {
2533 var prototype = Object.create(parent.prototype);
2534 for (var key in definition) prototype[key] = definition[key];
2535 return prototype;
2536 }
2537
2538 function Color() {}
2539
2540 var darker = 0.7;
2541 var brighter = 1 / darker;
2542
2543 var reI = "\\s*([+-]?\\d+)\\s*",
2544 reN = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*",
2545 reP = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*",
2546 reHex3 = /^#([0-9a-f]{3})$/,
2547 reHex6 = /^#([0-9a-f]{6})$/,
2548 reRgbInteger = new RegExp("^rgb\\(" + [reI, reI, reI] + "\\)$"),
2549 reRgbPercent = new RegExp("^rgb\\(" + [reP, reP, reP] + "\\)$"),
2550 reRgbaInteger = new RegExp("^rgba\\(" + [reI, reI, reI, reN] + "\\)$"),
2551 reRgbaPercent = new RegExp("^rgba\\(" + [reP, reP, reP, reN] + "\\)$"),
2552 reHslPercent = new RegExp("^hsl\\(" + [reN, reP, reP] + "\\)$"),
2553 reHslaPercent = new RegExp("^hsla\\(" + [reN, reP, reP, reN] + "\\)$");
2554
2555 var named = {
2556 aliceblue: 0xf0f8ff,
2557 antiquewhite: 0xfaebd7,
2558 aqua: 0x00ffff,
2559 aquamarine: 0x7fffd4,
2560 azure: 0xf0ffff,
2561 beige: 0xf5f5dc,
2562 bisque: 0xffe4c4,
2563 black: 0x000000,
2564 blanchedalmond: 0xffebcd,
2565 blue: 0x0000ff,
2566 blueviolet: 0x8a2be2,
2567 brown: 0xa52a2a,
2568 burlywood: 0xdeb887,
2569 cadetblue: 0x5f9ea0,
2570 chartreuse: 0x7fff00,
2571 chocolate: 0xd2691e,
2572 coral: 0xff7f50,
2573 cornflowerblue: 0x6495ed,
2574 cornsilk: 0xfff8dc,
2575 crimson: 0xdc143c,
2576 cyan: 0x00ffff,
2577 darkblue: 0x00008b,
2578 darkcyan: 0x008b8b,
2579 darkgoldenrod: 0xb8860b,
2580 darkgray: 0xa9a9a9,
2581 darkgreen: 0x006400,
2582 darkgrey: 0xa9a9a9,
2583 darkkhaki: 0xbdb76b,
2584 darkmagenta: 0x8b008b,
2585 darkolivegreen: 0x556b2f,
2586 darkorange: 0xff8c00,
2587 darkorchid: 0x9932cc,
2588 darkred: 0x8b0000,
2589 darksalmon: 0xe9967a,
2590 darkseagreen: 0x8fbc8f,
2591 darkslateblue: 0x483d8b,
2592 darkslategray: 0x2f4f4f,
2593 darkslategrey: 0x2f4f4f,
2594 darkturquoise: 0x00ced1,
2595 darkviolet: 0x9400d3,
2596 deeppink: 0xff1493,
2597 deepskyblue: 0x00bfff,
2598 dimgray: 0x696969,
2599 dimgrey: 0x696969,
2600 dodgerblue: 0x1e90ff,
2601 firebrick: 0xb22222,
2602 floralwhite: 0xfffaf0,
2603 forestgreen: 0x228b22,
2604 fuchsia: 0xff00ff,
2605 gainsboro: 0xdcdcdc,
2606 ghostwhite: 0xf8f8ff,
2607 gold: 0xffd700,
2608 goldenrod: 0xdaa520,
2609 gray: 0x808080,
2610 green: 0x008000,
2611 greenyellow: 0xadff2f,
2612 grey: 0x808080,
2613 honeydew: 0xf0fff0,
2614 hotpink: 0xff69b4,
2615 indianred: 0xcd5c5c,
2616 indigo: 0x4b0082,
2617 ivory: 0xfffff0,
2618 khaki: 0xf0e68c,
2619 lavender: 0xe6e6fa,
2620 lavenderblush: 0xfff0f5,
2621 lawngreen: 0x7cfc00,
2622 lemonchiffon: 0xfffacd,
2623 lightblue: 0xadd8e6,
2624 lightcoral: 0xf08080,
2625 lightcyan: 0xe0ffff,
2626 lightgoldenrodyellow: 0xfafad2,
2627 lightgray: 0xd3d3d3,
2628 lightgreen: 0x90ee90,
2629 lightgrey: 0xd3d3d3,
2630 lightpink: 0xffb6c1,
2631 lightsalmon: 0xffa07a,
2632 lightseagreen: 0x20b2aa,
2633 lightskyblue: 0x87cefa,
2634 lightslategray: 0x778899,
2635 lightslategrey: 0x778899,
2636 lightsteelblue: 0xb0c4de,
2637 lightyellow: 0xffffe0,
2638 lime: 0x00ff00,
2639 limegreen: 0x32cd32,
2640 linen: 0xfaf0e6,
2641 magenta: 0xff00ff,
2642 maroon: 0x800000,
2643 mediumaquamarine: 0x66cdaa,
2644 mediumblue: 0x0000cd,
2645 mediumorchid: 0xba55d3,
2646 mediumpurple: 0x9370db,
2647 mediumseagreen: 0x3cb371,
2648 mediumslateblue: 0x7b68ee,
2649 mediumspringgreen: 0x00fa9a,
2650 mediumturquoise: 0x48d1cc,
2651 mediumvioletred: 0xc71585,
2652 midnightblue: 0x191970,
2653 mintcream: 0xf5fffa,
2654 mistyrose: 0xffe4e1,
2655 moccasin: 0xffe4b5,
2656 navajowhite: 0xffdead,
2657 navy: 0x000080,
2658 oldlace: 0xfdf5e6,
2659 olive: 0x808000,
2660 olivedrab: 0x6b8e23,
2661 orange: 0xffa500,
2662 orangered: 0xff4500,
2663 orchid: 0xda70d6,
2664 palegoldenrod: 0xeee8aa,
2665 palegreen: 0x98fb98,
2666 paleturquoise: 0xafeeee,
2667 palevioletred: 0xdb7093,
2668 papayawhip: 0xffefd5,
2669 peachpuff: 0xffdab9,
2670 peru: 0xcd853f,
2671 pink: 0xffc0cb,
2672 plum: 0xdda0dd,
2673 powderblue: 0xb0e0e6,
2674 purple: 0x800080,
2675 rebeccapurple: 0x663399,
2676 red: 0xff0000,
2677 rosybrown: 0xbc8f8f,
2678 royalblue: 0x4169e1,
2679 saddlebrown: 0x8b4513,
2680 salmon: 0xfa8072,
2681 sandybrown: 0xf4a460,
2682 seagreen: 0x2e8b57,
2683 seashell: 0xfff5ee,
2684 sienna: 0xa0522d,
2685 silver: 0xc0c0c0,
2686 skyblue: 0x87ceeb,
2687 slateblue: 0x6a5acd,
2688 slategray: 0x708090,
2689 slategrey: 0x708090,
2690 snow: 0xfffafa,
2691 springgreen: 0x00ff7f,
2692 steelblue: 0x4682b4,
2693 tan: 0xd2b48c,
2694 teal: 0x008080,
2695 thistle: 0xd8bfd8,
2696 tomato: 0xff6347,
2697 turquoise: 0x40e0d0,
2698 violet: 0xee82ee,
2699 wheat: 0xf5deb3,
2700 white: 0xffffff,
2701 whitesmoke: 0xf5f5f5,
2702 yellow: 0xffff00,
2703 yellowgreen: 0x9acd32
2704 };
2705
2706 define(Color, color, {
2707 displayable: function() {
2708 return this.rgb().displayable();
2709 },
2710 hex: function() {
2711 return this.rgb().hex();
2712 },
2713 toString: function() {
2714 return this.rgb() + "";
2715 }
2716 });
2717
2718 function color(format) {
2719 var m;
2720 format = (format + "").trim().toLowerCase();
2721 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
2722 : (m = reHex6.exec(format)) ? rgbn(parseInt(m[1], 16)) // #ff0000
2723 : (m = reRgbInteger.exec(format)) ? new Rgb(m[1], m[2], m[3], 1) // rgb(255, 0, 0)
2724 : (m = reRgbPercent.exec(format)) ? new Rgb(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, 1) // rgb(100%, 0%, 0%)
2725 : (m = reRgbaInteger.exec(format)) ? rgba(m[1], m[2], m[3], m[4]) // rgba(255, 0, 0, 1)
2726 : (m = reRgbaPercent.exec(format)) ? rgba(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, m[4]) // rgb(100%, 0%, 0%, 1)
2727 : (m = reHslPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, 1) // hsl(120, 50%, 50%)
2728 : (m = reHslaPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) // hsla(120, 50%, 50%, 1)
2729 : named.hasOwnProperty(format) ? rgbn(named[format])
2730 : format === "transparent" ? new Rgb(NaN, NaN, NaN, 0)
2731 : null;
2732 }
2733
2734 function rgbn(n) {
2735 return new Rgb(n >> 16 & 0xff, n >> 8 & 0xff, n & 0xff, 1);
2736 }
2737
2738 function rgba(r, g, b, a) {
2739 if (a <= 0) r = g = b = NaN;
2740 return new Rgb(r, g, b, a);
2741 }
2742
2743 function rgbConvert(o) {
2744 if (!(o instanceof Color)) o = color(o);
2745 if (!o) return new Rgb;
2746 o = o.rgb();
2747 return new Rgb(o.r, o.g, o.b, o.opacity);
2748 }
2749
2750 function rgb(r, g, b, opacity) {
2751 return arguments.length === 1 ? rgbConvert(r) : new Rgb(r, g, b, opacity == null ? 1 : opacity);
2752 }
2753
2754 function Rgb(r, g, b, opacity) {
2755 this.r = +r;
2756 this.g = +g;
2757 this.b = +b;
2758 this.opacity = +opacity;
2759 }
2760
2761 define(Rgb, rgb, extend(Color, {
2762 brighter: function(k) {
2763 k = k == null ? brighter : Math.pow(brighter, k);
2764 return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
2765 },
2766 darker: function(k) {
2767 k = k == null ? darker : Math.pow(darker, k);
2768 return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
2769 },
2770 rgb: function() {
2771 return this;
2772 },
2773 displayable: function() {
2774 return (0 <= this.r && this.r <= 255)
2775 && (0 <= this.g && this.g <= 255)
2776 && (0 <= this.b && this.b <= 255)
2777 && (0 <= this.opacity && this.opacity <= 1);
2778 },
2779 hex: function() {
2780 return "#" + hex(this.r) + hex(this.g) + hex(this.b);
2781 },
2782 toString: function() {
2783 var a = this.opacity; a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));
2784 return (a === 1 ? "rgb(" : "rgba(")
2785 + Math.max(0, Math.min(255, Math.round(this.r) || 0)) + ", "
2786 + Math.max(0, Math.min(255, Math.round(this.g) || 0)) + ", "
2787 + Math.max(0, Math.min(255, Math.round(this.b) || 0))
2788 + (a === 1 ? ")" : ", " + a + ")");
2789 }
2790 }));
2791
2792 function hex(value) {
2793 value = Math.max(0, Math.min(255, Math.round(value) || 0));
2794 return (value < 16 ? "0" : "") + value.toString(16);
2795 }
2796
2797 function hsla(h, s, l, a) {
2798 if (a <= 0) h = s = l = NaN;
2799 else if (l <= 0 || l >= 1) h = s = NaN;
2800 else if (s <= 0) h = NaN;
2801 return new Hsl(h, s, l, a);
2802 }
2803
2804 function hslConvert(o) {
2805 if (o instanceof Hsl) return new Hsl(o.h, o.s, o.l, o.opacity);
2806 if (!(o instanceof Color)) o = color(o);
2807 if (!o) return new Hsl;
2808 if (o instanceof Hsl) return o;
2809 o = o.rgb();
2810 var r = o.r / 255,
2811 g = o.g / 255,
2812 b = o.b / 255,
2813 min = Math.min(r, g, b),
2814 max = Math.max(r, g, b),
2815 h = NaN,
2816 s = max - min,
2817 l = (max + min) / 2;
2818 if (s) {
2819 if (r === max) h = (g - b) / s + (g < b) * 6;
2820 else if (g === max) h = (b - r) / s + 2;
2821 else h = (r - g) / s + 4;
2822 s /= l < 0.5 ? max + min : 2 - max - min;
2823 h *= 60;
2824 } else {
2825 s = l > 0 && l < 1 ? 0 : h;
2826 }
2827 return new Hsl(h, s, l, o.opacity);
2828 }
2829
2830 function hsl(h, s, l, opacity) {
2831 return arguments.length === 1 ? hslConvert(h) : new Hsl(h, s, l, opacity == null ? 1 : opacity);
2832 }
2833
2834 function Hsl(h, s, l, opacity) {
2835 this.h = +h;
2836 this.s = +s;
2837 this.l = +l;
2838 this.opacity = +opacity;
2839 }
2840
2841 define(Hsl, hsl, extend(Color, {
2842 brighter: function(k) {
2843 k = k == null ? brighter : Math.pow(brighter, k);
2844 return new Hsl(this.h, this.s, this.l * k, this.opacity);
2845 },
2846 darker: function(k) {
2847 k = k == null ? darker : Math.pow(darker, k);
2848 return new Hsl(this.h, this.s, this.l * k, this.opacity);
2849 },
2850 rgb: function() {
2851 var h = this.h % 360 + (this.h < 0) * 360,
2852 s = isNaN(h) || isNaN(this.s) ? 0 : this.s,
2853 l = this.l,
2854 m2 = l + (l < 0.5 ? l : 1 - l) * s,
2855 m1 = 2 * l - m2;
2856 return new Rgb(
2857 hsl2rgb(h >= 240 ? h - 240 : h + 120, m1, m2),
2858 hsl2rgb(h, m1, m2),
2859 hsl2rgb(h < 120 ? h + 240 : h - 120, m1, m2),
2860 this.opacity
2861 );
2862 },
2863 displayable: function() {
2864 return (0 <= this.s && this.s <= 1 || isNaN(this.s))
2865 && (0 <= this.l && this.l <= 1)
2866 && (0 <= this.opacity && this.opacity <= 1);
2867 }
2868 }));
2869
2870 /* From FvD 13.37, CSS Color Module Level 3 */
2871 function hsl2rgb(h, m1, m2) {
2872 return (h < 60 ? m1 + (m2 - m1) * h / 60
2873 : h < 180 ? m2
2874 : h < 240 ? m1 + (m2 - m1) * (240 - h) / 60
2875 : m1) * 255;
2876 }
2877
2878 var deg2rad = Math.PI / 180;
2879 var rad2deg = 180 / Math.PI;
2880
2881 // https://beta.observablehq.com/@mbostock/lab-and-rgb
2882 var K = 18,
2883 Xn = 0.96422,
2884 Yn = 1,
2885 Zn = 0.82521,
2886 t0$1 = 4 / 29,
2887 t1$1 = 6 / 29,
2888 t2 = 3 * t1$1 * t1$1,
2889 t3 = t1$1 * t1$1 * t1$1;
2890
2891 function labConvert(o) {
2892 if (o instanceof Lab) return new Lab(o.l, o.a, o.b, o.opacity);
2893 if (o instanceof Hcl) {
2894 if (isNaN(o.h)) return new Lab(o.l, 0, 0, o.opacity);
2895 var h = o.h * deg2rad;
2896 return new Lab(o.l, Math.cos(h) * o.c, Math.sin(h) * o.c, o.opacity);
2897 }
2898 if (!(o instanceof Rgb)) o = rgbConvert(o);
2899 var r = rgb2lrgb(o.r),
2900 g = rgb2lrgb(o.g),
2901 b = rgb2lrgb(o.b),
2902 y = xyz2lab((0.2225045 * r + 0.7168786 * g + 0.0606169 * b) / Yn), x, z;
2903 if (r === g && g === b) x = z = y; else {
2904 x = xyz2lab((0.4360747 * r + 0.3850649 * g + 0.1430804 * b) / Xn);
2905 z = xyz2lab((0.0139322 * r + 0.0971045 * g + 0.7141733 * b) / Zn);
2906 }
2907 return new Lab(116 * y - 16, 500 * (x - y), 200 * (y - z), o.opacity);
2908 }
2909
2910 function lab(l, a, b, opacity) {
2911 return arguments.length === 1 ? labConvert(l) : new Lab(l, a, b, opacity == null ? 1 : opacity);
2912 }
2913
2914 function Lab(l, a, b, opacity) {
2915 this.l = +l;
2916 this.a = +a;
2917 this.b = +b;
2918 this.opacity = +opacity;
2919 }
2920
2921 define(Lab, lab, extend(Color, {
2922 brighter: function(k) {
2923 return new Lab(this.l + K * (k == null ? 1 : k), this.a, this.b, this.opacity);
2924 },
2925 darker: function(k) {
2926 return new Lab(this.l - K * (k == null ? 1 : k), this.a, this.b, this.opacity);
2927 },
2928 rgb: function() {
2929 var y = (this.l + 16) / 116,
2930 x = isNaN(this.a) ? y : y + this.a / 500,
2931 z = isNaN(this.b) ? y : y - this.b / 200;
2932 x = Xn * lab2xyz(x);
2933 y = Yn * lab2xyz(y);
2934 z = Zn * lab2xyz(z);
2935 return new Rgb(
2936 lrgb2rgb( 3.1338561 * x - 1.6168667 * y - 0.4906146 * z),
2937 lrgb2rgb(-0.9787684 * x + 1.9161415 * y + 0.0334540 * z),
2938 lrgb2rgb( 0.0719453 * x - 0.2289914 * y + 1.4052427 * z),
2939 this.opacity
2940 );
2941 }
2942 }));
2943
2944 function xyz2lab(t) {
2945 return t > t3 ? Math.pow(t, 1 / 3) : t / t2 + t0$1;
2946 }
2947
2948 function lab2xyz(t) {
2949 return t > t1$1 ? t * t * t : t2 * (t - t0$1);
2950 }
2951
2952 function lrgb2rgb(x) {
2953 return 255 * (x <= 0.0031308 ? 12.92 * x : 1.055 * Math.pow(x, 1 / 2.4) - 0.055);
2954 }
2955
2956 function rgb2lrgb(x) {
2957 return (x /= 255) <= 0.04045 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4);
2958 }
2959
2960 function hclConvert(o) {
2961 if (o instanceof Hcl) return new Hcl(o.h, o.c, o.l, o.opacity);
2962 if (!(o instanceof Lab)) o = labConvert(o);
2963 if (o.a === 0 && o.b === 0) return new Hcl(NaN, 0, o.l, o.opacity);
2964 var h = Math.atan2(o.b, o.a) * rad2deg;
2965 return new Hcl(h < 0 ? h + 360 : h, Math.sqrt(o.a * o.a + o.b * o.b), o.l, o.opacity);
2966 }
2967
2968 function hcl(h, c, l, opacity) {
2969 return arguments.length === 1 ? hclConvert(h) : new Hcl(h, c, l, opacity == null ? 1 : opacity);
2970 }
2971
2972 function Hcl(h, c, l, opacity) {
2973 this.h = +h;
2974 this.c = +c;
2975 this.l = +l;
2976 this.opacity = +opacity;
2977 }
2978
2979 define(Hcl, hcl, extend(Color, {
2980 brighter: function(k) {
2981 return new Hcl(this.h, this.c, this.l + K * (k == null ? 1 : k), this.opacity);
2982 },
2983 darker: function(k) {
2984 return new Hcl(this.h, this.c, this.l - K * (k == null ? 1 : k), this.opacity);
2985 },
2986 rgb: function() {
2987 return labConvert(this).rgb();
2988 }
2989 }));
2990
2991 var A = -0.14861,
2992 B = +1.78277,
2993 C = -0.29227,
2994 D = -0.90649,
2995 E = +1.97294,
2996 ED = E * D,
2997 EB = E * B,
2998 BC_DA = B * C - D * A;
2999
3000 function cubehelixConvert(o) {
3001 if (o instanceof Cubehelix) return new Cubehelix(o.h, o.s, o.l, o.opacity);
3002 if (!(o instanceof Rgb)) o = rgbConvert(o);
3003 var r = o.r / 255,
3004 g = o.g / 255,
3005 b = o.b / 255,
3006 l = (BC_DA * b + ED * r - EB * g) / (BC_DA + ED - EB),
3007 bl = b - l,
3008 k = (E * (g - l) - C * bl) / D,
3009 s = Math.sqrt(k * k + bl * bl) / (E * l * (1 - l)), // NaN if l=0 or l=1
3010 h = s ? Math.atan2(k, bl) * rad2deg - 120 : NaN;
3011 return new Cubehelix(h < 0 ? h + 360 : h, s, l, o.opacity);
3012 }
3013
3014 function cubehelix(h, s, l, opacity) {
3015 return arguments.length === 1 ? cubehelixConvert(h) : new Cubehelix(h, s, l, opacity == null ? 1 : opacity);
3016 }
3017
3018 function Cubehelix(h, s, l, opacity) {
3019 this.h = +h;
3020 this.s = +s;
3021 this.l = +l;
3022 this.opacity = +opacity;
3023 }
3024
3025 define(Cubehelix, cubehelix, extend(Color, {
3026 brighter: function(k) {
3027 k = k == null ? brighter : Math.pow(brighter, k);
3028 return new Cubehelix(this.h, this.s, this.l * k, this.opacity);
3029 },
3030 darker: function(k) {
3031 k = k == null ? darker : Math.pow(darker, k);
3032 return new Cubehelix(this.h, this.s, this.l * k, this.opacity);
3033 },
3034 rgb: function() {
3035 var h = isNaN(this.h) ? 0 : (this.h + 120) * deg2rad,
3036 l = +this.l,
3037 a = isNaN(this.s) ? 0 : this.s * l * (1 - l),
3038 cosh = Math.cos(h),
3039 sinh = Math.sin(h);
3040 return new Rgb(
3041 255 * (l + a * (A * cosh + B * sinh)),
3042 255 * (l + a * (C * cosh + D * sinh)),
3043 255 * (l + a * (E * cosh)),
3044 this.opacity
3045 );
3046 }
3047 }));
3048
3049 function constant$1(x) {
3050 return function() {
3051 return x;
3052 };
3053 }
3054
3055 function linear(a, d) {
3056 return function(t) {
3057 return a + t * d;
3058 };
3059 }
3060
3061 function exponential(a, b, y) {
3062 return a = Math.pow(a, y), b = Math.pow(b, y) - a, y = 1 / y, function(t) {
3063 return Math.pow(a + t * b, y);
3064 };
3065 }
3066
3067 function gamma(y) {
3068 return (y = +y) === 1 ? nogamma : function(a, b) {
3069 return b - a ? exponential(a, b, y) : constant$1(isNaN(a) ? b : a);
3070 };
3071 }
3072
3073 function nogamma(a, b) {
3074 var d = b - a;
3075 return d ? linear(a, d) : constant$1(isNaN(a) ? b : a);
3076 }
3077
3078 var rgb$1 = (function rgbGamma(y) {
3079 var color = gamma(y);
3080
3081 function rgb$1(start, end) {
3082 var r = color((start = rgb(start)).r, (end = rgb(end)).r),
3083 g = color(start.g, end.g),
3084 b = color(start.b, end.b),
3085 opacity = nogamma(start.opacity, end.opacity);
3086 return function(t) {
3087 start.r = r(t);
3088 start.g = g(t);
3089 start.b = b(t);
3090 start.opacity = opacity(t);
3091 return start + "";
3092 };
3093 }
3094
3095 rgb$1.gamma = rgbGamma;
3096
3097 return rgb$1;
3098 })(1);
3099
3100 function array(a, b) {
3101 var nb = b ? b.length : 0,
3102 na = a ? Math.min(nb, a.length) : 0,
3103 x = new Array(na),
3104 c = new Array(nb),
3105 i;
3106
3107 for (i = 0; i < na; ++i) x[i] = interpolateValue(a[i], b[i]);
3108 for (; i < nb; ++i) c[i] = b[i];
3109
3110 return function(t) {
3111 for (i = 0; i < na; ++i) c[i] = x[i](t);
3112 return c;
3113 };
3114 }
3115
3116 function date(a, b) {
3117 var d = new Date;
3118 return a = +a, b -= a, function(t) {
3119 return d.setTime(a + b * t), d;
3120 };
3121 }
3122
3123 function reinterpolate(a, b) {
3124 return a = +a, b -= a, function(t) {
3125 return a + b * t;
3126 };
3127 }
3128
3129 function object(a, b) {
3130 var i = {},
3131 c = {},
3132 k;
3133
3134 if (a === null || typeof a !== "object") a = {};
3135 if (b === null || typeof b !== "object") b = {};
3136
3137 for (k in b) {
3138 if (k in a) {
3139 i[k] = interpolateValue(a[k], b[k]);
3140 } else {
3141 c[k] = b[k];
3142 }
3143 }
3144
3145 return function(t) {
3146 for (k in i) c[k] = i[k](t);
3147 return c;
3148 };
3149 }
3150
3151 var reA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,
3152 reB = new RegExp(reA.source, "g");
3153
3154 function zero(b) {
3155 return function() {
3156 return b;
3157 };
3158 }
3159
3160 function one(b) {
3161 return function(t) {
3162 return b(t) + "";
3163 };
3164 }
3165
3166 function string(a, b) {
3167 var bi = reA.lastIndex = reB.lastIndex = 0, // scan index for next number in b
3168 am, // current match in a
3169 bm, // current match in b
3170 bs, // string preceding current number in b, if any
3171 i = -1, // index in s
3172 s = [], // string constants and placeholders
3173 q = []; // number interpolators
3174
3175 // Coerce inputs to strings.
3176 a = a + "", b = b + "";
3177
3178 // Interpolate pairs of numbers in a & b.
3179 while ((am = reA.exec(a))
3180 && (bm = reB.exec(b))) {
3181 if ((bs = bm.index) > bi) { // a string precedes the next number in b
3182 bs = b.slice(bi, bs);
3183 if (s[i]) s[i] += bs; // coalesce with previous string
3184 else s[++i] = bs;
3185 }
3186 if ((am = am[0]) === (bm = bm[0])) { // numbers in a & b match
3187 if (s[i]) s[i] += bm; // coalesce with previous string
3188 else s[++i] = bm;
3189 } else { // interpolate non-matching numbers
3190 s[++i] = null;
3191 q.push({i: i, x: reinterpolate(am, bm)});
3192 }
3193 bi = reB.lastIndex;
3194 }
3195
3196 // Add remains of b.
3197 if (bi < b.length) {
3198 bs = b.slice(bi);
3199 if (s[i]) s[i] += bs; // coalesce with previous string
3200 else s[++i] = bs;
3201 }
3202
3203 // Special optimization for only a single match.
3204 // Otherwise, interpolate each of the numbers and rejoin the string.
3205 return s.length < 2 ? (q[0]
3206 ? one(q[0].x)
3207 : zero(b))
3208 : (b = q.length, function(t) {
3209 for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t);
3210 return s.join("");
3211 });
3212 }
3213
3214 function interpolateValue(a, b) {
3215 var t = typeof b, c;
3216 return b == null || t === "boolean" ? constant$1(b)
3217 : (t === "number" ? reinterpolate
3218 : t === "string" ? ((c = color(b)) ? (b = c, rgb$1) : string)
3219 : b instanceof color ? rgb$1
3220 : b instanceof Date ? date
3221 : Array.isArray(b) ? array
3222 : typeof b.valueOf !== "function" && typeof b.toString !== "function" || isNaN(b) ? object
3223 : reinterpolate)(a, b);
3224 }
3225
3226 function interpolateRound(a, b) {
3227 return a = +a, b -= a, function(t) {
3228 return Math.round(a + b * t);
3229 };
3230 }
3231
3232 var array$1 = Array.prototype;
3233
3234 var map = array$1.map;
3235 var slice$1 = array$1.slice;
3236
3237 function constant$2(x) {
3238 return function() {
3239 return x;
3240 };
3241 }
3242
3243 function number$1(x) {
3244 return +x;
3245 }
3246
3247 var unit = [0, 1];
3248
3249 function deinterpolateLinear(a, b) {
3250 return (b -= (a = +a))
3251 ? function(x) { return (x - a) / b; }
3252 : constant$2(b);
3253 }
3254
3255 function deinterpolateClamp(deinterpolate) {
3256 return function(a, b) {
3257 var d = deinterpolate(a = +a, b = +b);
3258 return function(x) { return x <= a ? 0 : x >= b ? 1 : d(x); };
3259 };
3260 }
3261
3262 function reinterpolateClamp(reinterpolate) {
3263 return function(a, b) {
3264 var r = reinterpolate(a = +a, b = +b);
3265 return function(t) { return t <= 0 ? a : t >= 1 ? b : r(t); };
3266 };
3267 }
3268
3269 function bimap(domain, range, deinterpolate, reinterpolate) {
3270 var d0 = domain[0], d1 = domain[1], r0 = range[0], r1 = range[1];
3271 if (d1 < d0) d0 = deinterpolate(d1, d0), r0 = reinterpolate(r1, r0);
3272 else d0 = deinterpolate(d0, d1), r0 = reinterpolate(r0, r1);
3273 return function(x) { return r0(d0(x)); };
3274 }
3275
3276 function polymap(domain, range, deinterpolate, reinterpolate) {
3277 var j = Math.min(domain.length, range.length) - 1,
3278 d = new Array(j),
3279 r = new Array(j),
3280 i = -1;
3281
3282 // Reverse descending domains.
3283 if (domain[j] < domain[0]) {
3284 domain = domain.slice().reverse();
3285 range = range.slice().reverse();
3286 }
3287
3288 while (++i < j) {
3289 d[i] = deinterpolate(domain[i], domain[i + 1]);
3290 r[i] = reinterpolate(range[i], range[i + 1]);
3291 }
3292
3293 return function(x) {
3294 var i = bisectRight(domain, x, 1, j) - 1;
3295 return r[i](d[i](x));
3296 };
3297 }
3298
3299 function copy(source, target) {
3300 return target
3301 .domain(source.domain())
3302 .range(source.range())
3303 .interpolate(source.interpolate())
3304 .clamp(source.clamp());
3305 }
3306
3307 // deinterpolate(a, b)(x) takes a domain value x in [a,b] and returns the corresponding parameter t in [0,1].
3308 // reinterpolate(a, b)(t) takes a parameter t in [0,1] and returns the corresponding domain value x in [a,b].
3309 function continuous(deinterpolate, reinterpolate) {
3310 var domain = unit,
3311 range = unit,
3312 interpolate = interpolateValue,
3313 clamp = false,
3314 piecewise,
3315 output,
3316 input;
3317
3318 function rescale() {
3319 piecewise = Math.min(domain.length, range.length) > 2 ? polymap : bimap;
3320 output = input = null;
3321 return scale;
3322 }
3323
3324 function scale(x) {
3325 return (output || (output = piecewise(domain, range, clamp ? deinterpolateClamp(deinterpolate) : deinterpolate, interpolate)))(+x);
3326 }
3327
3328 scale.invert = function(y) {
3329 return (input || (input = piecewise(range, domain, deinterpolateLinear, clamp ? reinterpolateClamp(reinterpolate) : reinterpolate)))(+y);
3330 };
3331
3332 scale.domain = function(_) {
3333 return arguments.length ? (domain = map.call(_, number$1), rescale()) : domain.slice();
3334 };
3335
3336 scale.range = function(_) {
3337 return arguments.length ? (range = slice$1.call(_), rescale()) : range.slice();
3338 };
3339
3340 scale.rangeRound = function(_) {
3341 return range = slice$1.call(_), interpolate = interpolateRound, rescale();
3342 };
3343
3344 scale.clamp = function(_) {
3345 return arguments.length ? (clamp = !!_, rescale()) : clamp;
3346 };
3347
3348 scale.interpolate = function(_) {
3349 return arguments.length ? (interpolate = _, rescale()) : interpolate;
3350 };
3351
3352 return rescale();
3353 }
3354
3355 // Computes the decimal coefficient and exponent of the specified number x with
3356 // significant digits p, where x is positive and p is in [1, 21] or undefined.
3357 // For example, formatDecimal(1.23) returns ["123", 0].
3358 function formatDecimal(x, p) {
3359 if ((i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf("e")) < 0) return null; // NaN, ±Infinity
3360 var i, coefficient = x.slice(0, i);
3361
3362 // The string returned by toExponential either has the form \d\.\d+e[-+]\d+
3363 // (e.g., 1.2e+3) or the form \de[-+]\d+ (e.g., 1e+3).
3364 return [
3365 coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient,
3366 +x.slice(i + 1)
3367 ];
3368 }
3369
3370 function exponent(x) {
3371 return x = formatDecimal(Math.abs(x)), x ? x[1] : NaN;
3372 }
3373
3374 function formatGroup(grouping, thousands) {
3375 return function(value, width) {
3376 var i = value.length,
3377 t = [],
3378 j = 0,
3379 g = grouping[0],
3380 length = 0;
3381
3382 while (i > 0 && g > 0) {
3383 if (length + g + 1 > width) g = Math.max(1, width - length);
3384 t.push(value.substring(i -= g, i + g));
3385 if ((length += g + 1) > width) break;
3386 g = grouping[j = (j + 1) % grouping.length];
3387 }
3388
3389 return t.reverse().join(thousands);
3390 };
3391 }
3392
3393 function formatNumerals(numerals) {
3394 return function(value) {
3395 return value.replace(/[0-9]/g, function(i) {
3396 return numerals[+i];
3397 });
3398 };
3399 }
3400
3401 // [[fill]align][sign][symbol][0][width][,][.precision][~][type]
3402 var re = /^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;
3403
3404 function formatSpecifier(specifier) {
3405 return new FormatSpecifier(specifier);
3406 }
3407
3408 formatSpecifier.prototype = FormatSpecifier.prototype; // instanceof
3409
3410 function FormatSpecifier(specifier) {
3411 if (!(match = re.exec(specifier))) throw new Error("invalid format: " + specifier);
3412 var match;
3413 this.fill = match[1] || " ";
3414 this.align = match[2] || ">";
3415 this.sign = match[3] || "-";
3416 this.symbol = match[4] || "";
3417 this.zero = !!match[5];
3418 this.width = match[6] && +match[6];
3419 this.comma = !!match[7];
3420 this.precision = match[8] && +match[8].slice(1);
3421 this.trim = !!match[9];
3422 this.type = match[10] || "";
3423 }
3424
3425 FormatSpecifier.prototype.toString = function() {
3426 return this.fill
3427 + this.align
3428 + this.sign
3429 + this.symbol
3430 + (this.zero ? "0" : "")
3431 + (this.width == null ? "" : Math.max(1, this.width | 0))
3432 + (this.comma ? "," : "")
3433 + (this.precision == null ? "" : "." + Math.max(0, this.precision | 0))
3434 + (this.trim ? "~" : "")
3435 + this.type;
3436 };
3437
3438 // Trims insignificant zeros, e.g., replaces 1.2000k with 1.2k.
3439 function formatTrim(s) {
3440 out: for (var n = s.length, i = 1, i0 = -1, i1; i < n; ++i) {
3441 switch (s[i]) {
3442 case ".": i0 = i1 = i; break;
3443 case "0": if (i0 === 0) i0 = i; i1 = i; break;
3444 default: if (i0 > 0) { if (!+s[i]) break out; i0 = 0; } break;
3445 }
3446 }
3447 return i0 > 0 ? s.slice(0, i0) + s.slice(i1 + 1) : s;
3448 }
3449
3450 var prefixExponent;
3451
3452 function formatPrefixAuto(x, p) {
3453 var d = formatDecimal(x, p);
3454 if (!d) return x + "";
3455 var coefficient = d[0],
3456 exponent = d[1],
3457 i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1,
3458 n = coefficient.length;
3459 return i === n ? coefficient
3460 : i > n ? coefficient + new Array(i - n + 1).join("0")
3461 : i > 0 ? coefficient.slice(0, i) + "." + coefficient.slice(i)
3462 : "0." + new Array(1 - i).join("0") + formatDecimal(x, Math.max(0, p + i - 1))[0]; // less than 1y!
3463 }
3464
3465 function formatRounded(x, p) {
3466 var d = formatDecimal(x, p);
3467 if (!d) return x + "";
3468 var coefficient = d[0],
3469 exponent = d[1];
3470 return exponent < 0 ? "0." + new Array(-exponent).join("0") + coefficient
3471 : coefficient.length > exponent + 1 ? coefficient.slice(0, exponent + 1) + "." + coefficient.slice(exponent + 1)
3472 : coefficient + new Array(exponent - coefficient.length + 2).join("0");
3473 }
3474
3475 var formatTypes = {
3476 "%": function(x, p) { return (x * 100).toFixed(p); },
3477 "b": function(x) { return Math.round(x).toString(2); },
3478 "c": function(x) { return x + ""; },
3479 "d": function(x) { return Math.round(x).toString(10); },
3480 "e": function(x, p) { return x.toExponential(p); },
3481 "f": function(x, p) { return x.toFixed(p); },
3482 "g": function(x, p) { return x.toPrecision(p); },
3483 "o": function(x) { return Math.round(x).toString(8); },
3484 "p": function(x, p) { return formatRounded(x * 100, p); },
3485 "r": formatRounded,
3486 "s": formatPrefixAuto,
3487 "X": function(x) { return Math.round(x).toString(16).toUpperCase(); },
3488 "x": function(x) { return Math.round(x).toString(16); }
3489 };
3490
3491 function identity$1(x) {
3492 return x;
3493 }
3494
3495 var prefixes = ["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"];
3496
3497 function formatLocale$1(locale) {
3498 var group = locale.grouping && locale.thousands ? formatGroup(locale.grouping, locale.thousands) : identity$1,
3499 currency = locale.currency,
3500 decimal = locale.decimal,
3501 numerals = locale.numerals ? formatNumerals(locale.numerals) : identity$1,
3502 percent = locale.percent || "%";
3503
3504 function newFormat(specifier) {
3505 specifier = formatSpecifier(specifier);
3506
3507 var fill = specifier.fill,
3508 align = specifier.align,
3509 sign = specifier.sign,
3510 symbol = specifier.symbol,
3511 zero = specifier.zero,
3512 width = specifier.width,
3513 comma = specifier.comma,
3514 precision = specifier.precision,
3515 trim = specifier.trim,
3516 type = specifier.type;
3517
3518 // The "n" type is an alias for ",g".
3519 if (type === "n") comma = true, type = "g";
3520
3521 // The "" type, and any invalid type, is an alias for ".12~g".
3522 else if (!formatTypes[type]) precision == null && (precision = 12), trim = true, type = "g";
3523
3524 // If zero fill is specified, padding goes after sign and before digits.
3525 if (zero || (fill === "0" && align === "=")) zero = true, fill = "0", align = "=";
3526
3527 // Compute the prefix and suffix.
3528 // For SI-prefix, the suffix is lazily computed.
3529 var prefix = symbol === "$" ? currency[0] : symbol === "#" && /[boxX]/.test(type) ? "0" + type.toLowerCase() : "",
3530 suffix = symbol === "$" ? currency[1] : /[%p]/.test(type) ? percent : "";
3531
3532 // What format function should we use?
3533 // Is this an integer type?
3534 // Can this type generate exponential notation?
3535 var formatType = formatTypes[type],
3536 maybeSuffix = /[defgprs%]/.test(type);
3537
3538 // Set the default precision if not specified,
3539 // or clamp the specified precision to the supported range.
3540 // For significant precision, it must be in [1, 21].
3541 // For fixed precision, it must be in [0, 20].
3542 precision = precision == null ? 6
3543 : /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision))
3544 : Math.max(0, Math.min(20, precision));
3545
3546 function format(value) {
3547 var valuePrefix = prefix,
3548 valueSuffix = suffix,
3549 i, n, c;
3550
3551 if (type === "c") {
3552 valueSuffix = formatType(value) + valueSuffix;
3553 value = "";
3554 } else {
3555 value = +value;
3556
3557 // Perform the initial formatting.
3558 var valueNegative = value < 0;
3559 value = formatType(Math.abs(value), precision);
3560
3561 // Trim insignificant zeros.
3562 if (trim) value = formatTrim(value);
3563
3564 // If a negative value rounds to zero during formatting, treat as positive.
3565 if (valueNegative && +value === 0) valueNegative = false;
3566
3567 // Compute the prefix and suffix.
3568 valuePrefix = (valueNegative ? (sign === "(" ? sign : "-") : sign === "-" || sign === "(" ? "" : sign) + valuePrefix;
3569 valueSuffix = (type === "s" ? prefixes[8 + prefixExponent / 3] : "") + valueSuffix + (valueNegative && sign === "(" ? ")" : "");
3570
3571 // Break the formatted value into the integer “value” part that can be
3572 // grouped, and fractional or exponential “suffix” part that is not.
3573 if (maybeSuffix) {
3574 i = -1, n = value.length;
3575 while (++i < n) {
3576 if (c = value.charCodeAt(i), 48 > c || c > 57) {
3577 valueSuffix = (c === 46 ? decimal + value.slice(i + 1) : value.slice(i)) + valueSuffix;
3578 value = value.slice(0, i);
3579 break;
3580 }
3581 }
3582 }
3583 }
3584
3585 // If the fill character is not "0", grouping is applied before padding.
3586 if (comma && !zero) value = group(value, Infinity);
3587
3588 // Compute the padding.
3589 var length = valuePrefix.length + value.length + valueSuffix.length,
3590 padding = length < width ? new Array(width - length + 1).join(fill) : "";
3591
3592 // If the fill character is "0", grouping is applied after padding.
3593 if (comma && zero) value = group(padding + value, padding.length ? width - valueSuffix.length : Infinity), padding = "";
3594
3595 // Reconstruct the final output based on the desired alignment.
3596 switch (align) {
3597 case "<": value = valuePrefix + value + valueSuffix + padding; break;
3598 case "=": value = valuePrefix + padding + value + valueSuffix; break;
3599 case "^": value = padding.slice(0, length = padding.length >> 1) + valuePrefix + value + valueSuffix + padding.slice(length); break;
3600 default: value = padding + valuePrefix + value + valueSuffix; break;
3601 }
3602
3603 return numerals(value);
3604 }
3605
3606 format.toString = function() {
3607 return specifier + "";
3608 };
3609
3610 return format;
3611 }
3612
3613 function formatPrefix(specifier, value) {
3614 var f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier)),
3615 e = Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3,
3616 k = Math.pow(10, -e),
3617 prefix = prefixes[8 + e / 3];
3618 return function(value) {
3619 return f(k * value) + prefix;
3620 };
3621 }
3622
3623 return {
3624 format: newFormat,
3625 formatPrefix: formatPrefix
3626 };
3627 }
3628
3629 var locale$1;
3630 var format;
3631 var formatPrefix;
3632
3633 defaultLocale$1({
3634 decimal: ".",
3635 thousands: ",",
3636 grouping: [3],
3637 currency: ["$", ""]
3638 });
3639
3640 function defaultLocale$1(definition) {
3641 locale$1 = formatLocale$1(definition);
3642 format = locale$1.format;
3643 formatPrefix = locale$1.formatPrefix;
3644 return locale$1;
3645 }
3646
3647 function precisionFixed(step) {
3648 return Math.max(0, -exponent(Math.abs(step)));
3649 }
3650
3651 function precisionPrefix(step, value) {
3652 return Math.max(0, Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3 - exponent(Math.abs(step)));
3653 }
3654
3655 function precisionRound(step, max) {
3656 step = Math.abs(step), max = Math.abs(max) - step;
3657 return Math.max(0, exponent(max) - exponent(step)) + 1;
3658 }
3659
3660 function tickFormat(domain, count, specifier) {
3661 var start = domain[0],
3662 stop = domain[domain.length - 1],
3663 step = tickStep(start, stop, count == null ? 10 : count),
3664 precision;
3665 specifier = formatSpecifier(specifier == null ? ",f" : specifier);
3666 switch (specifier.type) {
3667 case "s": {
3668 var value = Math.max(Math.abs(start), Math.abs(stop));
3669 if (specifier.precision == null && !isNaN(precision = precisionPrefix(step, value))) specifier.precision = precision;
3670 return formatPrefix(specifier, value);
3671 }
3672 case "":
3673 case "e":
3674 case "g":
3675 case "p":
3676 case "r": {
3677 if (specifier.precision == null && !isNaN(precision = precisionRound(step, Math.max(Math.abs(start), Math.abs(stop))))) specifier.precision = precision - (specifier.type === "e");
3678 break;
3679 }
3680 case "f":
3681 case "%": {
3682 if (specifier.precision == null && !isNaN(precision = precisionFixed(step))) specifier.precision = precision - (specifier.type === "%") * 2;
3683 break;
3684 }
3685 }
3686 return format(specifier);
3687 }
3688
3689 function linearish(scale) {
3690 var domain = scale.domain;
3691
3692 scale.ticks = function(count) {
3693 var d = domain();
3694 return ticks(d[0], d[d.length - 1], count == null ? 10 : count);
3695 };
3696
3697 scale.tickFormat = function(count, specifier) {
3698 return tickFormat(domain(), count, specifier);
3699 };
3700
3701 scale.nice = function(count) {
3702 if (count == null) count = 10;
3703
3704 var d = domain(),
3705 i0 = 0,
3706 i1 = d.length - 1,
3707 start = d[i0],
3708 stop = d[i1],
3709 step;
3710
3711 if (stop < start) {
3712 step = start, start = stop, stop = step;
3713 step = i0, i0 = i1, i1 = step;
3714 }
3715
3716 step = tickIncrement(start, stop, count);
3717
3718 if (step > 0) {
3719 start = Math.floor(start / step) * step;
3720 stop = Math.ceil(stop / step) * step;
3721 step = tickIncrement(start, stop, count);
3722 } else if (step < 0) {
3723 start = Math.ceil(start * step) / step;
3724 stop = Math.floor(stop * step) / step;
3725 step = tickIncrement(start, stop, count);
3726 }
3727
3728 if (step > 0) {
3729 d[i0] = Math.floor(start / step) * step;
3730 d[i1] = Math.ceil(stop / step) * step;
3731 domain(d);
3732 } else if (step < 0) {
3733 d[i0] = Math.ceil(start * step) / step;
3734 d[i1] = Math.floor(stop * step) / step;
3735 domain(d);
3736 }
3737
3738 return scale;
3739 };
3740
3741 return scale;
3742 }
3743
3744 function linear$1() {
3745 var scale = continuous(deinterpolateLinear, reinterpolate);
3746
3747 scale.copy = function() {
3748 return copy(scale, linear$1());
3749 };
3750
3751 return linearish(scale);
3752 }
3753
3754 var VERSION = "1.3.2";
3755
3756 function Slider(selector) {
3757 this.container = select(selector);
3758
3759 this._width = null;
3760 this._height = null;
3761
3762 this._handleRadius = 15;
3763 this._channelHeight = 5;
3764 this._channelRadius = null;
3765
3766 this._handleFill = "black";
3767 this._channelFill = "#eee";
3768
3769 this._margin = { top: null, left: null, right: null };
3770
3771 this._domain = [0, 1];
3772 this._value = null;
3773 this._snap = false;
3774
3775 this._scale = null;
3776 this._axis = false;
3777 this._ticks = null;
3778 this._tickFormat = null;
3779 this._tickSize = null;
3780
3781 this._label = null;
3782 this._labelSize = 18;
3783
3784 this._startLabel = null;
3785 this._startLabelBelow = false;
3786
3787 this._endLabel = null;
3788 this._endLabelBelow = false;
3789
3790 this._startEndLabelSize = 16;
3791
3792 this.handlers = { "change": [] };
3793 }
3794
3795 // Create accessor methods for all the _parameters defined by the constructor
3796 function accessor(k) {
3797 if (k.length > 0 && k.charAt(0) == "_") {
3798 Slider.prototype[k.substr(1)] = function(v) {
3799 if (typeof v == "undefined") return this[k];
3800 this[k] = v;
3801 return this;
3802 };
3803 }
3804 }
3805 var s = new Slider();
3806 for (var k in s) {
3807 accessor(k);
3808 }
3809
3810 // Special accessor function for margin
3811 Slider.prototype.margin = function Slider_margin(options) {
3812 if (!options) return this._margin;
3813 for (k in options) {
3814 if (k in this._margin) this._margin[k] = options[k];
3815 else throw "Slider.margin: unrecognised option " + k;
3816 }
3817 return this;
3818 };
3819
3820 // Attach event handlers
3821 Slider.prototype.on = function Slider_on(event, handler) {
3822 if (!(event in this.handlers)) throw "Slider.on: No such event: " + event;
3823 this.handlers[event].push(handler);
3824 return this;
3825 };
3826
3827 // Fire event
3828 Slider.prototype.fire = function Slider_fire(event, d) {
3829 if (!(event in this.handlers)) throw "Slider.fire: No such event: " + event;
3830 var handlers = this.handlers[event];
3831 for (var i = 0; i < handlers.length; i++) {
3832 handlers[i].call(this, d);
3833 }
3834 return this;
3835 };
3836
3837 // Binary search
3838 function closestValue(sorted_list, value, a, b) {
3839 if (typeof a === "undefined") a = 0;
3840 if (typeof b === "undefined") b = sorted_list.length;
3841
3842 if (b-a == 0) return value;
3843 if (b-a == 1) return sorted_list[a];
3844 if (b-a == 2) {
3845 var d1 = Math.abs(sorted_list[a] - value),
3846 d2 = Math.abs(sorted_list[a+1] - value);
3847 if (d1 <= d2) return sorted_list[a];
3848 else return sorted_list[a+1];
3849 }
3850
3851 var mid = a + Math.floor((b-a) / 2),
3852 mid_v = sorted_list[mid],
3853 pre = mid - 1,
3854 pre_v = sorted_list[pre];
3855 if (pre_v <= value && value <= mid_v) {
3856 return (Math.abs(pre_v - value) <= Math.abs(mid_v - value)) ? pre_v : mid_v;
3857 }
3858 if (mid_v <= value) return closestValue(sorted_list, value, mid, b);
3859 else return closestValue(sorted_list, value, a, mid);
3860 }
3861
3862 function snapTo(specification, value) {
3863 if (typeof specification == "boolean") {
3864 return specification ? Math.round(value) : value;
3865 }
3866 // Otherwise assume “specification” is a sorted array
3867 return closestValue(specification, value);
3868 }
3869
3870 // Draw or update the slider
3871 Slider.prototype.draw = function Slider_draw() {
3872 var that = this;
3873
3874 var cw = this._width,
3875 ch = this._height;
3876
3877 var container_node = this.container.node();
3878
3879 // If the width and height have not been specified, use
3880 // the client size of the container element.
3881 if (!cw) {
3882 var r = container_node.getBoundingClientRect();
3883 // If there isn’t a bounding client rect, e.g. because
3884 // the container is display: none;, then do nothing.
3885 if (!r || r.width == 0) return this;
3886
3887 cw = r.width;
3888 ch = r.height;
3889 }
3890
3891 var channel_r = this._channelRadius == null ? this._channelHeight/2 : this._channelRadius,
3892 left_margin = (this._margin.left == null ? Math.max(this._handleRadius, channel_r) : this._margin.left),
3893 right_margin = (this._margin.right == null ? Math.max(this._handleRadius, channel_r) : this._margin.right),
3894 top_margin = this._margin.top == null ? Math.max(this._handleRadius, this._channelHeight/2) : this._margin.top,
3895 w = cw - left_margin - right_margin, // Inner width of the slider channel (excluding endcaps)
3896 channel_w = w + 2*channel_r, // Width of the slider channel (including endcaps)
3897 label_h = this._labelSize * 1.5;
3898
3899 if (this._label != null && this._margin.top == null) top_margin += label_h;
3900
3901 var slider;
3902 if (container_node.namespaceURI == "http://www.w3.org/2000/svg") {
3903 slider = this.container;
3904 }
3905 else {
3906 slider = this.container.selectAll("svg").data([{ width: cw, height: ch }]);
3907 slider.exit().remove();
3908 slider = slider.enter().append("svg").merge(slider);
3909 slider.attr("width", function(d) { return d.width; })
3910 .attr("height", function(d) { return d.height; });
3911 }
3912
3913 var g = slider.selectAll("g.slider-container").data([{left: left_margin, top: top_margin, id: this._id}]);
3914 g.exit().remove();
3915 g = g.enter().append("g").attr("class", "slider-container").merge(g);
3916 g.attr("transform", function(d) {
3917 return "translate(" + d.left + "," + d.top + ")";
3918 })
3919 .attr("id", function(d) { return d.id; });
3920
3921 this.scale = (this._scale ? this._scale() : linear$1()).domain(this._domain).range([0, w]);
3922
3923 if (this._value == null || this._value < this._domain[0]) this._value = this._domain[0];
3924 else if (this._value > this._domain[1]) this._value = this._domain[1];
3925
3926 if (this._snap) this._value = snapTo(this._snap, this._value);
3927
3928 var axes_data = [];
3929 if (this._axis) {
3930 var axis;
3931 if (typeof this._axis != "boolean") {
3932 axis = this._axis(this.scale);
3933 }
3934 else {
3935 axis = axisBottom().scale(this.scale).tickPadding(6);
3936 }
3937
3938 if (this._ticks) axis.ticks(this._ticks);
3939 if (this._tickFormat) axis.tickFormat(this._tickFormat);
3940 if (this._tickSize) axis.tickSize(this._tickSize);
3941 else axis.tickSize(Math.max(5, this._handleRadius - this._channelHeight - 2));
3942 axes_data.push(axis);
3943 }
3944
3945 var axes = g.selectAll(".slider-axis");
3946 var axes_enter = axes.data(axes_data).enter();
3947 axes_enter.append("g").attr("class", "slider-axis")
3948 .attr("transform", "translate(" + 0 + "," + this._channelHeight/2 + ")")
3949 .each(function(axis) { axis(select(this)); });
3950 axes_enter.select(".domain").attr("fill", "none");
3951 axes_enter.selectAll(".tick line").attr("stroke", "black");
3952 axes_enter.exit().remove();
3953
3954 var channel, handle;
3955 channel = g.selectAll(".slider-channel")
3956 .data([{ width: channel_w, height: this._channelHeight, channel_r: channel_r }]);
3957 channel.exit().remove();
3958
3959 channel = channel.enter().append("rect").attr("class", "slider-channel")
3960 .attr("cursor", "pointer")
3961 .on("click", function() {
3962 var slider_x = Math.max(0, Math.min(w, d3_mouse(this)[0]));
3963 that._value = that.scale.invert(slider_x);
3964 if (that._snap) that._value = snapTo(that._snap, that._value);
3965 handle.attr("cx", that.scale(that._value));
3966 that.fire("change", that._value);
3967 })
3968 .merge(channel);
3969
3970 channel.attr("width", function(d) { return d.width; })
3971 .attr("fill", this._channelFill)
3972 .attr("height", function(d) { return d.height; })
3973 .attr("y", function(d) { return -d.height/2; })
3974 .attr("x", function(d) { return -d.channel_r; })
3975 .attr("rx", function(d) { return d.channel_r; });
3976
3977 var drag_dx_origin, drag_x_origin;
3978 function handleMousedown(event) {
3979 document.addEventListener("mouseup", handleMouseup, false);
3980 document.addEventListener("mousemove", handleMousemove, false);
3981 drag_dx_origin = event.clientX;
3982 drag_x_origin = that.scale(that._value);
3983 }
3984
3985 function handleMouseup() {
3986 document.removeEventListener("mouseup", handleMouseup, false);
3987 document.removeEventListener("mousemove", handleMousemove, false);
3988 }
3989
3990 function handleMousemove(event) {
3991 drag(event.clientX - drag_dx_origin);
3992 }
3993
3994 function handleTouchstart(event) {
3995 if (event.touches.length != 1) return;
3996 document.addEventListener("touchend", handleTouchend, false);
3997 document.addEventListener("touchmove", handleTouchmove, false);
3998 drag_dx_origin = event.touches[0].clientX;
3999 drag_x_origin = that.scale(that._value);
4000 }
4001
4002 function handleTouchend() {
4003 document.removeEventListener("touchend", handleTouchend, false);
4004 document.removeEventListener("touchmove", handleTouchmove, false);
4005 }
4006
4007 function handleTouchmove(event) {
4008 if (event.touches.length != 1) return;
4009 drag(event.touches[0].clientX - drag_dx_origin);
4010 }
4011
4012 function drag(dx) {
4013 var new_x = drag_x_origin + dx;
4014 var slider_x = Math.max(0, Math.min(w, new_x));
4015 var new_value = that.scale.invert(slider_x);
4016 if (that._snap) new_value = snapTo(that._snap, new_value);
4017 handle.attr("cx", that.scale(new_value));
4018 if (new_value != that._value) {
4019 that._value = new_value;
4020 that.fire("change", that._value);
4021 }
4022 }
4023
4024 handle = g.selectAll(".slider-handle").data([{ v: this._value, x: this.scale(this._value) }]);
4025 handle = handle.enter().append("circle").attr("class", "slider-handle")
4026 .attr("cursor", "col-resize")
4027 .merge(handle);
4028
4029 handle.attr("cx", function(d) { return d.x; })
4030 .attr("r", this._handleRadius)
4031 .attr("fill", this._handleFill)
4032 .on("mousedown", function() {
4033 event$1.preventDefault();
4034 handleMousedown(event$1);
4035 })
4036 .on("touchstart", function() {
4037 event$1.preventDefault();
4038 handleTouchstart(event$1);
4039 });
4040
4041 var label_data = [];
4042 if (this._label) {
4043 label_data.push({
4044 label: this._label, x: w/2, y: -label_h, font_size: this._labelSize
4045 });
4046 }
4047 var label = g.selectAll(".slider-label").data(label_data);
4048 label.exit().remove();
4049 label = label.enter()
4050 .append("text").attr("class", "slider-label")
4051 .attr("text-anchor", "middle")
4052 .attr("cursor", "default")
4053 .merge(label);
4054
4055 label
4056 .text(function(d) { return d.label; })
4057 .attr("x", function(d) { return d.x; })
4058 .attr("y", function(d) { return d.y; })
4059 .attr("font-size", this._labelSize);
4060
4061 var end_label_data = [];
4062 if (this._startLabel) {
4063 end_label_data.push({
4064 label: this._startLabel,
4065 x: this._startLabelBelow ? 0 : -(channel_r + 5 + Math.max(0, this._handleRadius - channel_r)),
4066 y: this._startLabelBelow ? (channel_r + 15) : this._startEndLabelSize/1.75 - channel_r/2,
4067 anchor: this._startLabelBelow ? "middle" : "end",
4068 font_size: this._startEndLabelSize
4069 });
4070 }
4071 if (this._endLabel) {
4072 end_label_data.push({
4073 label: this._endLabel,
4074 x: this._endLabelBelow ? w : w + (channel_r + Math.max(0, this._handleRadius - channel_r) + 5),
4075 y: this._startLabelBelow ? (channel_r + 15) : this._startEndLabelSize/1.75 - channel_r/2,
4076 anchor: this._endLabelBelow ? "middle" : "start",
4077 font_size: this._startEndLabelSize
4078 });
4079 }
4080
4081 var end_labels = g.selectAll(".slider-end-labels").data(end_label_data);
4082 end_labels.exit().remove();
4083 end_labels = end_labels.enter().append("text").attr("class", "slider-end-labels")
4084 .attr("pointer-events", "none")
4085 .merge(end_labels);
4086 end_labels
4087 .text(function(d) { return d.label; })
4088 .attr("font-size", function(d) { return d.font_size; })
4089 .attr("x", function(d) { return d.x; })
4090 .attr("y", function(d) { return d.y; })
4091 .attr("text-anchor", function(d) { return d.anchor; });
4092
4093 return this;
4094 };
4095
4096 Slider.prototype.update = Slider.prototype.draw;
4097
4098 function Flourish_slider(selector) {
4099 return new Slider(selector);
4100 }
4101 Flourish_slider.version = VERSION;
4102
4103 function createPlayButton(colour) {
4104 var start_string = '<svg width="25px" height="30px" viewBox="0 0 25 30"> <polygon fill="';
4105 var end_string = '" stroke="none" points="25 15 0 30 0 0"></polygon> </svg>';
4106 return start_string + colour + end_string;
4107 }
4108
4109 function createPauseButton(colour) {
4110 var start_string = '<svg width="26px" height="30px" viewBox="0 0 26 30"> <g stroke="none" stroke-width="1" fill="';
4111 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>';
4112 return start_string + colour + end_string;
4113 }
4114
4115 function createSlider(control_obj, state, container) {
4116 var slider_obj = {};
4117 var slider_holder = select(container).append("div").attr("class", "fl-control fl-control-slider animatable");
4118 var slider_play_button = slider_holder.append("div").attr("class", "slider-play");
4119 var slider_div = slider_holder.append("div").attr("class", "fl-controls-slider");
4120
4121 var play_string, pause_string, handle_color;
4122 var sorted_options, timesteps, slider_label, is_playing;
4123 var timer_id = null;
4124
4125 var clearTimer = function() {
4126 clearTimeout(timer_id);
4127 timer_id = null;
4128 };
4129
4130 var sliderChangeFunction = function(i) {
4131 var d = sorted_options[i];
4132 if (d.options_index === control_obj.index()) return;
4133 control_obj.index(d.options_index);
4134 control_obj.trigger("change");
4135 };
4136
4137 var slider = Flourish_slider(slider_div.node())
4138 .snap(true)
4139 .on("change", function(i) {
4140 var is_playing = timer_id !== null;
4141 if (is_playing) clearTimer();
4142 sliderChangeFunction(i);
4143 if (is_playing) setNextTick();
4144 });
4145
4146
4147 var stopSliderPlayer = function() {
4148 clearTimer();
4149 slider_holder.classed("playing", false);
4150 slider_play_button.html(play_string);
4151 control_obj._isPlaying_(false);
4152 is_playing = false;
4153 };
4154
4155
4156 var setNextTick = function() {
4157 var current_index = control_obj.getSortedIndex();
4158 var current_delay = timesteps[current_index];
4159 var final_index = control_obj.n_options - 1;
4160 var next_index = current_index < final_index ? current_index + 1 : 0;
4161
4162 timer_id = setTimeout(function() {
4163 sliderChangeFunction(next_index);
4164 if (state.slider_loop || next_index < final_index) setNextTick();
4165 else stopSliderPlayer();
4166 }, current_delay);
4167 };
4168
4169
4170 var startSliderPlayer = function() {
4171 slider_holder.classed("playing", true);
4172 slider_play_button.html(pause_string);
4173 setNextTick();
4174 control_obj._isPlaying_(true);
4175 is_playing = true;
4176 };
4177
4178
4179 slider_play_button.on("click", function() {
4180 if (timer_id === null) startSliderPlayer();
4181 else stopSliderPlayer();
4182 });
4183
4184
4185 var setWidths = function() {
4186 var handle_radius = Math.round(remToPx(state.slider_handle_height) / 2);
4187 slider_holder.style("width", Math.round(remToPx(state.slider_width)) + "px");
4188 slider_play_button
4189 .style("height", handle_radius * 2 + "px")
4190 .style("width", handle_radius * 2 + "px")
4191 .style("display", state.slider_play_button ? null : "none");
4192 var holder_width = slider_holder.node().getBoundingClientRect().width;
4193 var button_width = slider_play_button.node().getBoundingClientRect().width;
4194 slider_div
4195 .style("width", Math.max((holder_width - button_width), 1) + "px")
4196 .style("height", handle_radius * 2 + "px");
4197
4198 slider
4199 .handleRadius(handle_radius)
4200 .margin({
4201 left: handle_radius + 5,
4202 right: handle_radius + remToPx(state.slider_margin),
4203 top: handle_radius
4204 });
4205 };
4206
4207
4208 var setHandles = function() {
4209 if (state.slider_play_button) {
4210 slider_holder.classed("animatable", true);
4211 }
4212 else {
4213 stopSliderPlayer();
4214 slider_holder.classed("animatable", false);
4215 }
4216 if (handle_color !== state.slider_handle_color) {
4217 slider.update(); // Make sure slider-handle actually exists before changing its colour
4218 handle_color = state.slider_handle_color || "currentColor";
4219 slider_holder.select(".slider-handle").style("fill", handle_color);
4220 play_string = createPlayButton(handle_color);
4221 pause_string = createPauseButton(handle_color);
4222 slider_play_button.html(timer_id ? pause_string : play_string);
4223 }
4224 };
4225
4226
4227 var showControl = function() {
4228 slider_holder.style("display", "inline-block");
4229 setWidths();
4230 setHandles();
4231 return slider_obj;
4232 };
4233
4234 var hideControl = function() {
4235 stopSliderPlayer();
4236 slider_holder.style("display", "none");
4237 return slider_obj;
4238 };
4239
4240 slider_obj.show = showControl;
4241 slider_obj.hide = hideControl;
4242
4243
4244 slider_obj.update = function(_sorted_options) {
4245 sorted_options = _sorted_options;
4246 if (!control_obj.n_options || state.control_type !== "slider") {
4247 hideControl();
4248 return slider_obj;
4249 }
4250
4251 showControl();
4252
4253 var n_options = control_obj.n_options;
4254 var loop = state.slider_loop;
4255
4256 timesteps = sorted_options.map(function(d, i) {
4257 var dur_in_seconds = state.slider_step_time + (loop && i === (n_options -1) ? state.slider_restart_pause : 0);
4258 return dur_in_seconds * 1000;
4259 });
4260
4261 var sorted_index = control_obj.getSortedIndex();
4262 var d = sorted_options[sorted_index];
4263
4264 slider.domain([0, n_options - 1])
4265 .value(sorted_index)
4266 .endLabel(d.display)
4267 .channelHeight(Math.round(remToPx(state.slider_track_height)))
4268 .channelFill(state.slider_background_color)
4269 .update();
4270
4271 slider.container.select("svg").attr("fill", "currentColor");
4272
4273 slider_label = slider_label || slider_div.select("text.slider-end-labels");
4274 slider_label
4275 .style("fill", state.slider_font_color)
4276 .attr("y", "0")
4277 .attr("dy", "0.25em");
4278
4279
4280 if (control_obj._isPlaying_() && !is_playing) startSliderPlayer();
4281 else if (!control_obj._isPlaying_() && is_playing) stopSliderPlayer();
4282
4283 return slider_obj;
4284 };
4285
4286
4287 return slider_obj;
4288 }
4289
4290 var DEFAULTS = Object.freeze({
4291 control_type: "dropdown",
4292
4293 // Dropdown
4294 dropdown_width_mode: "auto",
4295 dropdown_width_fixed: 20,
4296
4297 // Buttons
4298 button_group: true,
4299 button_group_width_mode: "fixed",
4300 button_group_width_fixed: 20,
4301
4302 // Slider
4303 slider_width: 15,
4304 slider_handle_color: null,
4305 slider_font_color: null,
4306 slider_background_color: "#dddddd",
4307
4308 slider_handle_height: 1,
4309 slider_track_height: 0.2,
4310 slider_margin: 4.5,
4311
4312 slider_play_button: true,
4313 slider_step_time: 2,
4314 slider_loop: true,
4315 slider_restart_pause: 0,
4316
4317 // Data typing
4318 sort: "unsorted",
4319 sort_temporal_format: "%Y",
4320
4321 _index_: null,
4322 _is_playing_: false
4323 });
4324
4325
4326 function init(state, getParser, getFormatter) {
4327 var control_obj = {};
4328 getParser = getParser || getDefaultParser;
4329 getFormatter = getFormatter || getDefaultFormatter;
4330 var options = [];
4331 var sorted_options = [];
4332 var changeHandlers = [];
4333 var container = document.createElement("div");
4334 container.setAttribute("class", "fl-controls-container");
4335 var dropdown_obj = createDropdown(control_obj, state, container);
4336 var buttons_obj = createButtons(control_obj, state, container);
4337 var slider_obj = createSlider(control_obj, state, container);
4338
4339 for (var key in DEFAULTS) {
4340 if (state[key] === undefined) state[key] = DEFAULTS[key];
4341 }
4342
4343 var current_index = state._index_;
4344
4345 var checkValidIndex = function(i) {
4346 return options.length && i >= 0 && i < options.length;
4347 };
4348
4349 var updateControls = function(sorted_options) {
4350 container.style.display = (sorted_options.length > 1) ? null : "none";
4351 container.style.width = "";
4352 slider_obj.update(sorted_options); // Do slider first in case we're stopping playing
4353 dropdown_obj.update(sorted_options);
4354 buttons_obj.update(sorted_options);
4355 };
4356
4357 control_obj.appendTo = function(parent_container, bounding_container) {
4358 injectCSS();
4359 select(parent_container).node().appendChild(container);
4360 dropdown_obj.appendedToDOM(bounding_container);
4361 return control_obj;
4362 };
4363
4364 var callOnChangeCallbacks = function() {
4365 var index = indexFunction();
4366 var value = options[index];
4367 changeHandlers.forEach(function(func) {
4368 func(value, index);
4369 });
4370 return control_obj;
4371 };
4372
4373 control_obj.remove = function() {
4374 if (container.parentElement) container.parentElement.removeChild(container);
4375 dropdown_obj.removedFromDOM();
4376
4377 return control_obj;
4378 };
4379
4380 control_obj.options = function(arr) {
4381 if (arr === undefined) return options.slice();
4382 if (!Array.isArray(arr)) return control_obj;
4383 options = arr.slice();
4384 var n = options.length;
4385 var i = indexFunction();
4386 if (!n) indexFunction(null);
4387 else if (i === null || i >= n) indexFunction(0);
4388 return control_obj;
4389 };
4390
4391 Object.defineProperty(control_obj, "n_options", { get: function() { return options.length; } });
4392
4393
4394 var indexFunction = function(i) {
4395 if (i === undefined) {
4396 if (!state._is_playing_) current_index = state._index_;
4397 return current_index;
4398 }
4399 if (i === null || checkValidIndex(i)) {
4400 current_index = i;
4401 if (!state._is_playing_) state._index_ = current_index;
4402 }
4403 else console.warn("Invalid index, ignoring update call");
4404 return control_obj;
4405 };
4406 control_obj.index = indexFunction;
4407
4408 control_obj.getSortedIndex = function() {
4409 var options_index = indexFunction();
4410 if (state.sort == "unsorted") return options_index;
4411 var sorted_index;
4412 sorted_options.some(function(d, i) {
4413 if (d.options_index === options_index) {
4414 sorted_index = i;
4415 return true;
4416 }
4417 });
4418 return sorted_index;
4419 };
4420
4421 control_obj.value = function(value) {
4422 if (value === undefined) return options[indexFunction()];
4423 var index = options.indexOf(value);
4424 if (index !== -1) indexFunction(index);
4425 return control_obj;
4426 };
4427
4428 control_obj.on = function(event, callback) {
4429 if (event === "change") changeHandlers.push(callback.bind(control_obj));
4430 return control_obj;
4431 };
4432
4433 control_obj.update = function() {
4434 getRemToPx();
4435 sorted_options = sortArray(options, state, getParser(), getFormatter());
4436 updateControls(sorted_options);
4437 return control_obj;
4438 };
4439
4440 control_obj.trigger = function(event) {
4441 if (event === "change") callOnChangeCallbacks();
4442 return control_obj;
4443 };
4444
4445 var isPlaying = function(is_playing) {
4446 if (is_playing === undefined) return state._is_playing_;
4447 state._is_playing_ = !!is_playing;
4448 if (!is_playing) indexFunction(current_index); // Force _index_ to match current index
4449 };
4450
4451 control_obj._isPlaying_ = isPlaying;
4452
4453 return control_obj;
4454 }
4455
4456 return init;
4457
4458})));