From 8d4fd7bdf10268afed8a998b72c7065b280560a1 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 10 Apr 2024 23:30:41 +0200 Subject: Adding debian version 3.8.1-1. Signed-off-by: Daniel Baumann --- debian/missing-sources/codejar-linenumbers.js | 98 + debian/missing-sources/codejar.js | 428 + debian/missing-sources/d3.js | 17769 ++++++++++++++++++++++++ debian/missing-sources/d3evolution.js | 562 + debian/missing-sources/d3pie.js | 2178 +++ debian/missing-sources/fontawesome.js | 2479 ++++ debian/missing-sources/footable.js | 7371 ++++++++++ debian/missing-sources/jquery.stickytabs.js | 61 + debian/missing-sources/nprogress.js | 476 + debian/missing-sources/prism.js | 1137 ++ debian/missing-sources/require.js | 2145 +++ debian/missing-sources/solid.js | 1128 ++ debian/missing-sources/visibility.js | 191 + 13 files changed, 36023 insertions(+) create mode 100644 debian/missing-sources/codejar-linenumbers.js create mode 100644 debian/missing-sources/codejar.js create mode 100644 debian/missing-sources/d3.js create mode 100644 debian/missing-sources/d3evolution.js create mode 100644 debian/missing-sources/d3pie.js create mode 100644 debian/missing-sources/fontawesome.js create mode 100644 debian/missing-sources/footable.js create mode 100644 debian/missing-sources/jquery.stickytabs.js create mode 100644 debian/missing-sources/nprogress.js create mode 100644 debian/missing-sources/prism.js create mode 100644 debian/missing-sources/require.js create mode 100644 debian/missing-sources/solid.js create mode 100644 debian/missing-sources/visibility.js (limited to 'debian/missing-sources') diff --git a/debian/missing-sources/codejar-linenumbers.js b/debian/missing-sources/codejar-linenumbers.js new file mode 100644 index 0000000..07b723b --- /dev/null +++ b/debian/missing-sources/codejar-linenumbers.js @@ -0,0 +1,98 @@ +// Source: https://github.com/julianpoemp/codejar-linenumbers/blob/main/src/codejar-linenumbers.ts +// This class was copied from https://github.com/antonmedv/codejar/blob/3.7.0/linenumbers.ts +// and applied with further improvements and fixes + +type Options = { + class: string + wrapClass: string + width: string + backgroundColor: string + color: string +} + +export function withLineNumbers( + highlight: (e: HTMLElement) => void, + options: Partial = {} +) { + const opts: Options = { + class: "codejar-linenumbers", + wrapClass: "codejar-wrap", + width: "35px", + backgroundColor: "rgba(128, 128, 128, 0.15)", + color: "", + ...options + } + + let lineNumbers: HTMLElement + return function (editor: HTMLElement) { + highlight(editor) + + if (!lineNumbers) { + lineNumbers = init(editor, opts) + editor.addEventListener("scroll", () => lineNumbers.style.top = `-${editor.scrollTop}px`); + } + + const code = editor.textContent || "" + const linesCount = code.replace(/\n$/g, "").split("\n").length + + let text = "" + for (let i = 0; i < linesCount; i++) { + text += `${i + 1}\n` + } + + lineNumbers.innerText = text + } +} + +function init(editor: HTMLElement, opts: Options): HTMLElement { + const css = getComputedStyle(editor); + + const wrap = document.createElement("div") + wrap.className = opts.wrapClass + wrap.style.position = "relative" + + const innerWrap = document.createElement("div"); + innerWrap.className = "codejar-linenumbers-inner-wrap"; + innerWrap.style.background = css.background; + innerWrap.style.marginTop = css.borderTopWidth; + innerWrap.style.marginBottom = css.borderBottomWidth; + innerWrap.style.marginLeft = css.borderLeftWidth; + innerWrap.style.borderTopLeftRadius = css.borderTopLeftRadius; + innerWrap.style.borderBottomLeftRadius = css.borderBottomLeftRadius; + + const gutter = document.createElement("div") + gutter.className = opts.class; + innerWrap.appendChild(gutter); + + wrap.appendChild(innerWrap) + + // Add own styles + gutter.style.width = opts.width + gutter.style.overflow = "hidden" + gutter.style.backgroundColor = opts.backgroundColor + + // Copy editor styles + gutter.style.fontFamily = css.fontFamily + gutter.style.fontSize = css.fontSize + gutter.style.lineHeight = css.lineHeight + gutter.style.paddingTop = `calc(${css.paddingTop})`; + gutter.style.paddingLeft = css.paddingLeft + gutter.style.borderTopLeftRadius = css.borderTopLeftRadius + gutter.style.borderBottomLeftRadius = css.borderBottomLeftRadius; + + // Add line numbers + const lineNumbers = document.createElement("div"); + lineNumbers.setAttribute("class", "codejar-linenumber"); + lineNumbers.style.color = opts.color || css.color; + lineNumbers.style.setProperty("mix-blend-mode", "unset"); + gutter.appendChild(lineNumbers) + + // Tweak editor styles + editor.style.paddingLeft = `calc(${opts.width} + ${gutter.style.paddingLeft} + 5px)` + editor.style.whiteSpace = "pre" + + // Swap editor with a wrap + editor.parentNode!.insertBefore(wrap, editor) + wrap.appendChild(editor) + return lineNumbers +} diff --git a/debian/missing-sources/codejar.js b/debian/missing-sources/codejar.js new file mode 100644 index 0000000..7ea7d44 --- /dev/null +++ b/debian/missing-sources/codejar.js @@ -0,0 +1,428 @@ +type Options = { + tab: string + indentOn: RegExp +} + +type HistoryRecord = { + html: string + pos: Position +} + +type Position = { + start: number + end: number + dir?: "->" | "<-" +} + +export type CodeJar = ReturnType + +export function CodeJar(editor: HTMLElement, highlight: (e: HTMLElement) => void, opt: Partial = {}) { + const options: Options = { + tab: "\t", + indentOn: /{$/, + ...opt + } + let listeners: [string, any][] = [] + let history: HistoryRecord[] = [] + let at = -1 + let focus = false + let callback: (code: string) => void | undefined + let prev: string // code content prior keydown event + let isFirefox = navigator.userAgent.toLowerCase().indexOf("firefox") > -1 + + editor.setAttribute("contentEditable", isFirefox ? "true" : "plaintext-only") + editor.setAttribute("spellcheck", "false") + editor.style.outline = "none" + editor.style.overflowWrap = "break-word" + editor.style.overflowY = "auto" + editor.style.resize = "vertical" + editor.style.whiteSpace = "pre-wrap" + + highlight(editor) + + const debounceHighlight = debounce(() => { + const pos = save() + highlight(editor) + restore(pos) + }, 30) + + let recording = false + const shouldRecord = (event: KeyboardEvent): boolean => { + return !isUndo(event) && !isRedo(event) + && event.key !== "Meta" + && event.key !== "Control" + && event.key !== "Alt" + && !event.key.startsWith("Arrow") + } + const debounceRecordHistory = debounce((event: KeyboardEvent) => { + if (shouldRecord(event)) { + recordHistory() + recording = false + } + }, 300) + + const on = (type: K, fn: (event: HTMLElementEventMap[K]) => void) => { + listeners.push([type, fn]) + editor.addEventListener(type, fn) + } + + on("keydown", event => { + if (event.defaultPrevented) return + + prev = toString() + handleNewLine(event) + handleTabCharacters(event) + handleSelfClosingCharacters(event) + handleUndoRedo(event) + if (shouldRecord(event) && !recording) { + recordHistory() + recording = true + } + }) + + on("keyup", event => { + if (event.defaultPrevented) return + if (event.isComposing) return + + if (prev !== toString()) debounceHighlight() + debounceRecordHistory(event) + if (callback) callback(toString()) + }) + + on("focus", _event => { + focus = true + }) + + on("blur", _event => { + focus = false + }) + + on("paste", event => { + recordHistory() + handlePaste(event) + recordHistory() + if (callback) callback(toString()) + }) + + function save(): Position { + const s = window.getSelection()! + const pos: Position = {start: 0, end: 0, dir: undefined} + + visit(editor, el => { + if (el === s.anchorNode && el === s.focusNode) { + pos.start += s.anchorOffset + pos.end += s.focusOffset + pos.dir = s.anchorOffset <= s.focusOffset ? "->" : "<-" + return "stop" + } + + if (el === s.anchorNode) { + pos.start += s.anchorOffset + if (!pos.dir) { + pos.dir = "->" + } else { + return "stop" + } + } else if (el === s.focusNode) { + pos.end += s.focusOffset + if (!pos.dir) { + pos.dir = "<-" + } else { + return "stop" + } + } + + if (el.nodeType === Node.TEXT_NODE) { + if (pos.dir != "->") pos.start += el.nodeValue!.length + if (pos.dir != "<-") pos.end += el.nodeValue!.length + } + }) + + return pos + } + + function restore(pos: Position) { + const s = window.getSelection()! + let startNode: Node | undefined, startOffset = 0 + let endNode: Node | undefined, endOffset = 0 + + if (!pos.dir) pos.dir = "->" + if (pos.start < 0) pos.start = 0 + if (pos.end < 0) pos.end = 0 + + // Flip start and end if the direction reversed + if (pos.dir == "<-") { + const {start, end} = pos + pos.start = end + pos.end = start + } + + let current = 0 + + visit(editor, el => { + if (el.nodeType !== Node.TEXT_NODE) return + + const len = (el.nodeValue || "").length + if (current + len >= pos.start) { + if (!startNode) { + startNode = el + startOffset = pos.start - current + } + if (current + len >= pos.end) { + endNode = el + endOffset = pos.end - current + return "stop" + } + } + current += len + }) + + // If everything deleted place cursor at editor + if (!startNode) startNode = editor + if (!endNode) endNode = editor + + // Flip back the selection + if (pos.dir == "<-") { + [startNode, startOffset, endNode, endOffset] = [endNode, endOffset, startNode, startOffset] + } + + s.setBaseAndExtent(startNode, startOffset, endNode, endOffset) + } + + function beforeCursor() { + const s = window.getSelection()! + const r0 = s.getRangeAt(0) + const r = document.createRange() + r.selectNodeContents(editor) + r.setEnd(r0.startContainer, r0.startOffset) + return r.toString() + } + + function afterCursor() { + const s = window.getSelection()! + const r0 = s.getRangeAt(0) + const r = document.createRange() + r.selectNodeContents(editor) + r.setStart(r0.endContainer, r0.endOffset) + return r.toString() + } + + function handleNewLine(event: KeyboardEvent) { + if (event.key === "Enter") { + const before = beforeCursor() + const after = afterCursor() + + let [padding] = findPadding(before) + let newLinePadding = padding + + // If last symbol is "{" ident new line + // Allow user defines indent rule + if (options.indentOn.test(before)) { + newLinePadding += options.tab + } + + if (isFirefox) { + preventDefault(event) + insert("\n" + newLinePadding) + } else { + // Normal browsers + if (newLinePadding.length > 0) { + preventDefault(event) + insert("\n" + newLinePadding) + } + } + + // Place adjacent "}" on next line + if (newLinePadding !== padding && after[0] === "}") { + const pos = save() + insert("\n" + padding) + restore(pos) + } + } + } + + function handleSelfClosingCharacters(event: KeyboardEvent) { + const open = `([{'"` + const close = `)]}'"` + const codeAfter = afterCursor() + if (close.includes(event.key) && codeAfter.substr(0, 1) === event.key) { + const pos = save() + preventDefault(event) + pos.start = ++pos.end + restore(pos) + } else if (open.includes(event.key)) { + const pos = save() + preventDefault(event) + const text = event.key + close[open.indexOf(event.key)] + insert(text) + pos.start = ++pos.end + restore(pos) + } + } + + function handleTabCharacters(event: KeyboardEvent) { + if (event.key === "Tab") { + preventDefault(event) + if (event.shiftKey) { + const before = beforeCursor() + let [padding, start,] = findPadding(before) + if (padding.length > 0) { + const pos = save() + // Remove full length tab or just remaining padding + const len = Math.min(options.tab.length, padding.length) + restore({start, end: start + len}) + document.execCommand("delete") + pos.start -= len + pos.end -= len + restore(pos) + } + } else { + insert(options.tab) + } + } + } + + function handleUndoRedo(event: KeyboardEvent) { + if (isUndo(event)) { + preventDefault(event) + at-- + const record = history[at] + if (record) { + editor.innerHTML = record.html + restore(record.pos) + } + if (at < 0) at = 0 + } + if (isRedo(event)) { + preventDefault(event) + at++ + const record = history[at] + if (record) { + editor.innerHTML = record.html + restore(record.pos) + } + if (at >= history.length) at-- + } + } + + function recordHistory() { + if (!focus) return + + const html = editor.innerHTML + const pos = save() + + const lastRecord = history[at] + if (lastRecord) { + if (lastRecord.html === html + && lastRecord.pos.start === pos.start + && lastRecord.pos.end === pos.end) return + } + + at++ + history[at] = {html, pos} + history.splice(at + 1) + + const maxHistory = 300 + if (at > maxHistory) { + at = maxHistory + history.splice(0, 1) + } + } + + function handlePaste(event: ClipboardEvent) { + preventDefault(event) + const text = ((event as any).originalEvent || event).clipboardData.getData("text/plain") + const pos = save() + insert(text) + highlight(editor) + restore({start: pos.end + text.length, end: pos.end + text.length}) + } + + + function visit(editor: HTMLElement, visitor: (el: Node) => "stop" | undefined) { + const queue: Node[] = [] + + if (editor.firstChild) queue.push(editor.firstChild) + + let el = queue.pop() + + while (el) { + if (visitor(el) === "stop") + break + + if (el.nextSibling) queue.push(el.nextSibling) + if (el.firstChild) queue.push(el.firstChild) + + el = queue.pop() + } + } + + function isCtrl(event: KeyboardEvent) { + return event.metaKey || event.ctrlKey + } + + function isUndo(event: KeyboardEvent) { + return isCtrl(event) && !event.shiftKey && event.code === "KeyZ" + } + + function isRedo(event: KeyboardEvent) { + return isCtrl(event) && event.shiftKey && event.code === "KeyZ" + } + + function insert(text: string) { + text = text + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'") + document.execCommand("insertHTML", false, text) + } + + function debounce(cb: any, wait: number) { + let timeout = 0 + return (...args: any) => { + clearTimeout(timeout) + timeout = window.setTimeout(() => cb(...args), wait) + } + } + + function findPadding(text: string): [string, number, number] { + // Find beginning of previous line. + let i = text.length - 1 + while (i >= 0 && text[i] !== "\n") i-- + i++ + // Find padding of the line. + let j = i + while (j < text.length && /[ \t]/.test(text[j])) j++ + return [text.substring(i, j) || "", i, j] + } + + function toString() { + return editor.textContent || "" + } + + function preventDefault(event: Event) { + event.preventDefault() + } + + return { + updateOptions(options: Partial) { + options = {...options, ...options} + }, + updateCode(code: string) { + editor.textContent = code + highlight(editor) + }, + onUpdate(cb: (code: string) => void) { + callback = cb + }, + toString, + destroy() { + for (let [type, fn] of listeners) { + editor.removeEventListener(type, fn) + } + }, + } +} + diff --git a/debian/missing-sources/d3.js b/debian/missing-sources/d3.js new file mode 100644 index 0000000..3b1b112 --- /dev/null +++ b/debian/missing-sources/d3.js @@ -0,0 +1,17769 @@ +// https://d3js.org Version 5.0.0. Copyright 2018 Mike Bostock. +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (factory((global.d3 = global.d3 || {}))); +}(this, (function (exports) { 'use strict'; + +var version = "5.0.0"; + +function ascending(a, b) { + return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; +} + +function bisector(compare) { + if (compare.length === 1) compare = ascendingComparator(compare); + return { + left: function(a, x, lo, hi) { + if (lo == null) lo = 0; + if (hi == null) hi = a.length; + while (lo < hi) { + var mid = lo + hi >>> 1; + if (compare(a[mid], x) < 0) lo = mid + 1; + else hi = mid; + } + return lo; + }, + right: function(a, x, lo, hi) { + if (lo == null) lo = 0; + if (hi == null) hi = a.length; + while (lo < hi) { + var mid = lo + hi >>> 1; + if (compare(a[mid], x) > 0) hi = mid; + else lo = mid + 1; + } + return lo; + } + }; +} + +function ascendingComparator(f) { + return function(d, x) { + return ascending(f(d), x); + }; +} + +var ascendingBisect = bisector(ascending); +var bisectRight = ascendingBisect.right; +var bisectLeft = ascendingBisect.left; + +function pairs(array, f) { + if (f == null) f = pair; + var i = 0, n = array.length - 1, p = array[0], pairs = new Array(n < 0 ? 0 : n); + while (i < n) pairs[i] = f(p, p = array[++i]); + return pairs; +} + +function pair(a, b) { + return [a, b]; +} + +function cross(values0, values1, reduce) { + var n0 = values0.length, + n1 = values1.length, + values = new Array(n0 * n1), + i0, + i1, + i, + value0; + + if (reduce == null) reduce = pair; + + for (i0 = i = 0; i0 < n0; ++i0) { + for (value0 = values0[i0], i1 = 0; i1 < n1; ++i1, ++i) { + values[i] = reduce(value0, values1[i1]); + } + } + + return values; +} + +function descending(a, b) { + return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN; +} + +function number(x) { + return x === null ? NaN : +x; +} + +function variance(values, valueof) { + var n = values.length, + m = 0, + i = -1, + mean = 0, + value, + delta, + sum = 0; + + if (valueof == null) { + while (++i < n) { + if (!isNaN(value = number(values[i]))) { + delta = value - mean; + mean += delta / ++m; + sum += delta * (value - mean); + } + } + } + + else { + while (++i < n) { + if (!isNaN(value = number(valueof(values[i], i, values)))) { + delta = value - mean; + mean += delta / ++m; + sum += delta * (value - mean); + } + } + } + + if (m > 1) return sum / (m - 1); +} + +function deviation(array, f) { + var v = variance(array, f); + return v ? Math.sqrt(v) : v; +} + +function extent(values, valueof) { + var n = values.length, + i = -1, + value, + min, + max; + + if (valueof == null) { + while (++i < n) { // Find the first comparable value. + if ((value = values[i]) != null && value >= value) { + min = max = value; + while (++i < n) { // Compare the remaining values. + if ((value = values[i]) != null) { + if (min > value) min = value; + if (max < value) max = value; + } + } + } + } + } + + else { + while (++i < n) { // Find the first comparable value. + if ((value = valueof(values[i], i, values)) != null && value >= value) { + min = max = value; + while (++i < n) { // Compare the remaining values. + if ((value = valueof(values[i], i, values)) != null) { + if (min > value) min = value; + if (max < value) max = value; + } + } + } + } + } + + return [min, max]; +} + +var array = Array.prototype; + +var slice = array.slice; +var map = array.map; + +function constant(x) { + return function() { + return x; + }; +} + +function identity(x) { + return x; +} + +function sequence(start, stop, step) { + start = +start, stop = +stop, step = (n = arguments.length) < 2 ? (stop = start, start = 0, 1) : n < 3 ? 1 : +step; + + var i = -1, + n = Math.max(0, Math.ceil((stop - start) / step)) | 0, + range = new Array(n); + + while (++i < n) { + range[i] = start + i * step; + } + + return range; +} + +var e10 = Math.sqrt(50), + e5 = Math.sqrt(10), + e2 = Math.sqrt(2); + +function ticks(start, stop, count) { + var reverse, + i = -1, + n, + ticks, + step; + + stop = +stop, start = +start, count = +count; + if (start === stop && count > 0) return [start]; + if (reverse = stop < start) n = start, start = stop, stop = n; + if ((step = tickIncrement(start, stop, count)) === 0 || !isFinite(step)) return []; + + if (step > 0) { + start = Math.ceil(start / step); + stop = Math.floor(stop / step); + ticks = new Array(n = Math.ceil(stop - start + 1)); + while (++i < n) ticks[i] = (start + i) * step; + } else { + start = Math.floor(start * step); + stop = Math.ceil(stop * step); + ticks = new Array(n = Math.ceil(start - stop + 1)); + while (++i < n) ticks[i] = (start - i) / step; + } + + if (reverse) ticks.reverse(); + + return ticks; +} + +function tickIncrement(start, stop, count) { + var step = (stop - start) / Math.max(0, count), + power = Math.floor(Math.log(step) / Math.LN10), + error = step / Math.pow(10, power); + return power >= 0 + ? (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1) * Math.pow(10, power) + : -Math.pow(10, -power) / (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1); +} + +function tickStep(start, stop, count) { + var step0 = Math.abs(stop - start) / Math.max(0, count), + step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)), + error = step0 / step1; + if (error >= e10) step1 *= 10; + else if (error >= e5) step1 *= 5; + else if (error >= e2) step1 *= 2; + return stop < start ? -step1 : step1; +} + +function thresholdSturges(values) { + return Math.ceil(Math.log(values.length) / Math.LN2) + 1; +} + +function histogram() { + var value = identity, + domain = extent, + threshold = thresholdSturges; + + function histogram(data) { + var i, + n = data.length, + x, + values = new Array(n); + + for (i = 0; i < n; ++i) { + values[i] = value(data[i], i, data); + } + + var xz = domain(values), + x0 = xz[0], + x1 = xz[1], + tz = threshold(values, x0, x1); + + // Convert number of thresholds into uniform thresholds. + if (!Array.isArray(tz)) { + tz = tickStep(x0, x1, tz); + tz = sequence(Math.ceil(x0 / tz) * tz, Math.floor(x1 / tz) * tz, tz); // exclusive + } + + // Remove any thresholds outside the domain. + var m = tz.length; + while (tz[0] <= x0) tz.shift(), --m; + while (tz[m - 1] > x1) tz.pop(), --m; + + var bins = new Array(m + 1), + bin; + + // Initialize bins. + for (i = 0; i <= m; ++i) { + bin = bins[i] = []; + bin.x0 = i > 0 ? tz[i - 1] : x0; + bin.x1 = i < m ? tz[i] : x1; + } + + // Assign data to bins by value, ignoring any outside the domain. + for (i = 0; i < n; ++i) { + x = values[i]; + if (x0 <= x && x <= x1) { + bins[bisectRight(tz, x, 0, m)].push(data[i]); + } + } + + return bins; + } + + histogram.value = function(_) { + return arguments.length ? (value = typeof _ === "function" ? _ : constant(_), histogram) : value; + }; + + histogram.domain = function(_) { + return arguments.length ? (domain = typeof _ === "function" ? _ : constant([_[0], _[1]]), histogram) : domain; + }; + + histogram.thresholds = function(_) { + return arguments.length ? (threshold = typeof _ === "function" ? _ : Array.isArray(_) ? constant(slice.call(_)) : constant(_), histogram) : threshold; + }; + + return histogram; +} + +function threshold(values, p, valueof) { + if (valueof == null) valueof = number; + if (!(n = values.length)) return; + if ((p = +p) <= 0 || n < 2) return +valueof(values[0], 0, values); + if (p >= 1) return +valueof(values[n - 1], n - 1, values); + var n, + i = (n - 1) * p, + i0 = Math.floor(i), + value0 = +valueof(values[i0], i0, values), + value1 = +valueof(values[i0 + 1], i0 + 1, values); + return value0 + (value1 - value0) * (i - i0); +} + +function freedmanDiaconis(values, min, max) { + values = map.call(values, number).sort(ascending); + return Math.ceil((max - min) / (2 * (threshold(values, 0.75) - threshold(values, 0.25)) * Math.pow(values.length, -1 / 3))); +} + +function scott(values, min, max) { + return Math.ceil((max - min) / (3.5 * deviation(values) * Math.pow(values.length, -1 / 3))); +} + +function max(values, valueof) { + var n = values.length, + i = -1, + value, + max; + + if (valueof == null) { + while (++i < n) { // Find the first comparable value. + if ((value = values[i]) != null && value >= value) { + max = value; + while (++i < n) { // Compare the remaining values. + if ((value = values[i]) != null && value > max) { + max = value; + } + } + } + } + } + + else { + while (++i < n) { // Find the first comparable value. + if ((value = valueof(values[i], i, values)) != null && value >= value) { + max = value; + while (++i < n) { // Compare the remaining values. + if ((value = valueof(values[i], i, values)) != null && value > max) { + max = value; + } + } + } + } + } + + return max; +} + +function mean(values, valueof) { + var n = values.length, + m = n, + i = -1, + value, + sum = 0; + + if (valueof == null) { + while (++i < n) { + if (!isNaN(value = number(values[i]))) sum += value; + else --m; + } + } + + else { + while (++i < n) { + if (!isNaN(value = number(valueof(values[i], i, values)))) sum += value; + else --m; + } + } + + if (m) return sum / m; +} + +function median(values, valueof) { + var n = values.length, + i = -1, + value, + numbers = []; + + if (valueof == null) { + while (++i < n) { + if (!isNaN(value = number(values[i]))) { + numbers.push(value); + } + } + } + + else { + while (++i < n) { + if (!isNaN(value = number(valueof(values[i], i, values)))) { + numbers.push(value); + } + } + } + + return threshold(numbers.sort(ascending), 0.5); +} + +function merge(arrays) { + var n = arrays.length, + m, + i = -1, + j = 0, + merged, + array; + + while (++i < n) j += arrays[i].length; + merged = new Array(j); + + while (--n >= 0) { + array = arrays[n]; + m = array.length; + while (--m >= 0) { + merged[--j] = array[m]; + } + } + + return merged; +} + +function min(values, valueof) { + var n = values.length, + i = -1, + value, + min; + + if (valueof == null) { + while (++i < n) { // Find the first comparable value. + if ((value = values[i]) != null && value >= value) { + min = value; + while (++i < n) { // Compare the remaining values. + if ((value = values[i]) != null && min > value) { + min = value; + } + } + } + } + } + + else { + while (++i < n) { // Find the first comparable value. + if ((value = valueof(values[i], i, values)) != null && value >= value) { + min = value; + while (++i < n) { // Compare the remaining values. + if ((value = valueof(values[i], i, values)) != null && min > value) { + min = value; + } + } + } + } + } + + return min; +} + +function permute(array, indexes) { + var i = indexes.length, permutes = new Array(i); + while (i--) permutes[i] = array[indexes[i]]; + return permutes; +} + +function scan(values, compare) { + if (!(n = values.length)) return; + var n, + i = 0, + j = 0, + xi, + xj = values[j]; + + if (compare == null) compare = ascending; + + while (++i < n) { + if (compare(xi = values[i], xj) < 0 || compare(xj, xj) !== 0) { + xj = xi, j = i; + } + } + + if (compare(xj, xj) === 0) return j; +} + +function shuffle(array, i0, i1) { + var m = (i1 == null ? array.length : i1) - (i0 = i0 == null ? 0 : +i0), + t, + i; + + while (m) { + i = Math.random() * m-- | 0; + t = array[m + i0]; + array[m + i0] = array[i + i0]; + array[i + i0] = t; + } + + return array; +} + +function sum(values, valueof) { + var n = values.length, + i = -1, + value, + sum = 0; + + if (valueof == null) { + while (++i < n) { + if (value = +values[i]) sum += value; // Note: zero and null are equivalent. + } + } + + else { + while (++i < n) { + if (value = +valueof(values[i], i, values)) sum += value; + } + } + + return sum; +} + +function transpose(matrix) { + if (!(n = matrix.length)) return []; + for (var i = -1, m = min(matrix, length), transpose = new Array(m); ++i < m;) { + for (var j = -1, n, row = transpose[i] = new Array(n); ++j < n;) { + row[j] = matrix[j][i]; + } + } + return transpose; +} + +function length(d) { + return d.length; +} + +function zip() { + return transpose(arguments); +} + +var slice$1 = Array.prototype.slice; + +function identity$1(x) { + return x; +} + +var top = 1, + right = 2, + bottom = 3, + left = 4, + epsilon = 1e-6; + +function translateX(x) { + return "translate(" + (x + 0.5) + ",0)"; +} + +function translateY(y) { + return "translate(0," + (y + 0.5) + ")"; +} + +function number$1(scale) { + return function(d) { + return +scale(d); + }; +} + +function center(scale) { + var offset = Math.max(0, scale.bandwidth() - 1) / 2; // Adjust for 0.5px offset. + if (scale.round()) offset = Math.round(offset); + return function(d) { + return +scale(d) + offset; + }; +} + +function entering() { + return !this.__axis; +} + +function axis(orient, scale) { + var tickArguments = [], + tickValues = null, + tickFormat = null, + tickSizeInner = 6, + tickSizeOuter = 6, + tickPadding = 3, + k = orient === top || orient === left ? -1 : 1, + x = orient === left || orient === right ? "x" : "y", + transform = orient === top || orient === bottom ? translateX : translateY; + + function axis(context) { + var values = tickValues == null ? (scale.ticks ? scale.ticks.apply(scale, tickArguments) : scale.domain()) : tickValues, + format = tickFormat == null ? (scale.tickFormat ? scale.tickFormat.apply(scale, tickArguments) : identity$1) : tickFormat, + spacing = Math.max(tickSizeInner, 0) + tickPadding, + range = scale.range(), + range0 = +range[0] + 0.5, + range1 = +range[range.length - 1] + 0.5, + position = (scale.bandwidth ? center : number$1)(scale.copy()), + selection = context.selection ? context.selection() : context, + path = selection.selectAll(".domain").data([null]), + tick = selection.selectAll(".tick").data(values, scale).order(), + tickExit = tick.exit(), + tickEnter = tick.enter().append("g").attr("class", "tick"), + line = tick.select("line"), + text = tick.select("text"); + + path = path.merge(path.enter().insert("path", ".tick") + .attr("class", "domain") + .attr("stroke", "#000")); + + tick = tick.merge(tickEnter); + + line = line.merge(tickEnter.append("line") + .attr("stroke", "#000") + .attr(x + "2", k * tickSizeInner)); + + text = text.merge(tickEnter.append("text") + .attr("fill", "#000") + .attr(x, k * spacing) + .attr("dy", orient === top ? "0em" : orient === bottom ? "0.71em" : "0.32em")); + + if (context !== selection) { + path = path.transition(context); + tick = tick.transition(context); + line = line.transition(context); + text = text.transition(context); + + tickExit = tickExit.transition(context) + .attr("opacity", epsilon) + .attr("transform", function(d) { return isFinite(d = position(d)) ? transform(d) : this.getAttribute("transform"); }); + + tickEnter + .attr("opacity", epsilon) + .attr("transform", function(d) { var p = this.parentNode.__axis; return transform(p && isFinite(p = p(d)) ? p : position(d)); }); + } + + tickExit.remove(); + + path + .attr("d", orient === left || orient == right + ? "M" + k * tickSizeOuter + "," + range0 + "H0.5V" + range1 + "H" + k * tickSizeOuter + : "M" + range0 + "," + k * tickSizeOuter + "V0.5H" + range1 + "V" + k * tickSizeOuter); + + tick + .attr("opacity", 1) + .attr("transform", function(d) { return transform(position(d)); }); + + line + .attr(x + "2", k * tickSizeInner); + + text + .attr(x, k * spacing) + .text(format); + + selection.filter(entering) + .attr("fill", "none") + .attr("font-size", 10) + .attr("font-family", "sans-serif") + .attr("text-anchor", orient === right ? "start" : orient === left ? "end" : "middle"); + + selection + .each(function() { this.__axis = position; }); + } + + axis.scale = function(_) { + return arguments.length ? (scale = _, axis) : scale; + }; + + axis.ticks = function() { + return tickArguments = slice$1.call(arguments), axis; + }; + + axis.tickArguments = function(_) { + return arguments.length ? (tickArguments = _ == null ? [] : slice$1.call(_), axis) : tickArguments.slice(); + }; + + axis.tickValues = function(_) { + return arguments.length ? (tickValues = _ == null ? null : slice$1.call(_), axis) : tickValues && tickValues.slice(); + }; + + axis.tickFormat = function(_) { + return arguments.length ? (tickFormat = _, axis) : tickFormat; + }; + + axis.tickSize = function(_) { + return arguments.length ? (tickSizeInner = tickSizeOuter = +_, axis) : tickSizeInner; + }; + + axis.tickSizeInner = function(_) { + return arguments.length ? (tickSizeInner = +_, axis) : tickSizeInner; + }; + + axis.tickSizeOuter = function(_) { + return arguments.length ? (tickSizeOuter = +_, axis) : tickSizeOuter; + }; + + axis.tickPadding = function(_) { + return arguments.length ? (tickPadding = +_, axis) : tickPadding; + }; + + return axis; +} + +function axisTop(scale) { + return axis(top, scale); +} + +function axisRight(scale) { + return axis(right, scale); +} + +function axisBottom(scale) { + return axis(bottom, scale); +} + +function axisLeft(scale) { + return axis(left, scale); +} + +var noop = {value: function() {}}; + +function dispatch() { + for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) { + if (!(t = arguments[i] + "") || (t in _)) throw new Error("illegal type: " + t); + _[t] = []; + } + return new Dispatch(_); +} + +function Dispatch(_) { + this._ = _; +} + +function parseTypenames(typenames, types) { + return typenames.trim().split(/^|\s+/).map(function(t) { + var name = "", i = t.indexOf("."); + if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i); + if (t && !types.hasOwnProperty(t)) throw new Error("unknown type: " + t); + return {type: t, name: name}; + }); +} + +Dispatch.prototype = dispatch.prototype = { + constructor: Dispatch, + on: function(typename, callback) { + var _ = this._, + T = parseTypenames(typename + "", _), + t, + i = -1, + n = T.length; + + // If no callback was specified, return the callback of the given type and name. + if (arguments.length < 2) { + while (++i < n) if ((t = (typename = T[i]).type) && (t = get(_[t], typename.name))) return t; + return; + } + + // If a type was specified, set the callback for the given type and name. + // Otherwise, if a null callback was specified, remove callbacks of the given name. + if (callback != null && typeof callback !== "function") throw new Error("invalid callback: " + callback); + while (++i < n) { + if (t = (typename = T[i]).type) _[t] = set(_[t], typename.name, callback); + else if (callback == null) for (t in _) _[t] = set(_[t], typename.name, null); + } + + return this; + }, + copy: function() { + var copy = {}, _ = this._; + for (var t in _) copy[t] = _[t].slice(); + return new Dispatch(copy); + }, + call: function(type, that) { + if ((n = arguments.length - 2) > 0) for (var args = new Array(n), i = 0, n, t; i < n; ++i) args[i] = arguments[i + 2]; + if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type); + for (t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args); + }, + apply: function(type, that, args) { + if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type); + for (var t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args); + } +}; + +function get(type, name) { + for (var i = 0, n = type.length, c; i < n; ++i) { + if ((c = type[i]).name === name) { + return c.value; + } + } +} + +function set(type, name, callback) { + for (var i = 0, n = type.length; i < n; ++i) { + if (type[i].name === name) { + type[i] = noop, type = type.slice(0, i).concat(type.slice(i + 1)); + break; + } + } + if (callback != null) type.push({name: name, value: callback}); + return type; +} + +var xhtml = "http://www.w3.org/1999/xhtml"; + +var namespaces = { + svg: "http://www.w3.org/2000/svg", + xhtml: xhtml, + xlink: "http://www.w3.org/1999/xlink", + xml: "http://www.w3.org/XML/1998/namespace", + xmlns: "http://www.w3.org/2000/xmlns/" +}; + +function namespace(name) { + var prefix = name += "", i = prefix.indexOf(":"); + if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") name = name.slice(i + 1); + return namespaces.hasOwnProperty(prefix) ? {space: namespaces[prefix], local: name} : name; +} + +function creatorInherit(name) { + return function() { + var document = this.ownerDocument, + uri = this.namespaceURI; + return uri === xhtml && document.documentElement.namespaceURI === xhtml + ? document.createElement(name) + : document.createElementNS(uri, name); + }; +} + +function creatorFixed(fullname) { + return function() { + return this.ownerDocument.createElementNS(fullname.space, fullname.local); + }; +} + +function creator(name) { + var fullname = namespace(name); + return (fullname.local + ? creatorFixed + : creatorInherit)(fullname); +} + +function none() {} + +function selector(selector) { + return selector == null ? none : function() { + return this.querySelector(selector); + }; +} + +function selection_select(select) { + if (typeof select !== "function") select = selector(select); + + for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) { + for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) { + if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) { + if ("__data__" in node) subnode.__data__ = node.__data__; + subgroup[i] = subnode; + } + } + } + + return new Selection(subgroups, this._parents); +} + +function empty() { + return []; +} + +function selectorAll(selector) { + return selector == null ? empty : function() { + return this.querySelectorAll(selector); + }; +} + +function selection_selectAll(select) { + if (typeof select !== "function") select = selectorAll(select); + + for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) { + for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { + if (node = group[i]) { + subgroups.push(select.call(node, node.__data__, i, group)); + parents.push(node); + } + } + } + + return new Selection(subgroups, parents); +} + +var matcher = function(selector) { + return function() { + return this.matches(selector); + }; +}; + +if (typeof document !== "undefined") { + var element = document.documentElement; + if (!element.matches) { + var vendorMatches = element.webkitMatchesSelector + || element.msMatchesSelector + || element.mozMatchesSelector + || element.oMatchesSelector; + matcher = function(selector) { + return function() { + return vendorMatches.call(this, selector); + }; + }; + } +} + +var matcher$1 = matcher; + +function selection_filter(match) { + if (typeof match !== "function") match = matcher$1(match); + + for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) { + for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) { + if ((node = group[i]) && match.call(node, node.__data__, i, group)) { + subgroup.push(node); + } + } + } + + return new Selection(subgroups, this._parents); +} + +function sparse(update) { + return new Array(update.length); +} + +function selection_enter() { + return new Selection(this._enter || this._groups.map(sparse), this._parents); +} + +function EnterNode(parent, datum) { + this.ownerDocument = parent.ownerDocument; + this.namespaceURI = parent.namespaceURI; + this._next = null; + this._parent = parent; + this.__data__ = datum; +} + +EnterNode.prototype = { + constructor: EnterNode, + appendChild: function(child) { return this._parent.insertBefore(child, this._next); }, + insertBefore: function(child, next) { return this._parent.insertBefore(child, next); }, + querySelector: function(selector) { return this._parent.querySelector(selector); }, + querySelectorAll: function(selector) { return this._parent.querySelectorAll(selector); } +}; + +function constant$1(x) { + return function() { + return x; + }; +} + +var keyPrefix = "$"; // Protect against keys like “__proto__”. + +function bindIndex(parent, group, enter, update, exit, data) { + var i = 0, + node, + groupLength = group.length, + dataLength = data.length; + + // Put any non-null nodes that fit into update. + // Put any null nodes into enter. + // Put any remaining data into enter. + for (; i < dataLength; ++i) { + if (node = group[i]) { + node.__data__ = data[i]; + update[i] = node; + } else { + enter[i] = new EnterNode(parent, data[i]); + } + } + + // Put any non-null nodes that don’t fit into exit. + for (; i < groupLength; ++i) { + if (node = group[i]) { + exit[i] = node; + } + } +} + +function bindKey(parent, group, enter, update, exit, data, key) { + var i, + node, + nodeByKeyValue = {}, + groupLength = group.length, + dataLength = data.length, + keyValues = new Array(groupLength), + keyValue; + + // Compute the key for each node. + // If multiple nodes have the same key, the duplicates are added to exit. + for (i = 0; i < groupLength; ++i) { + if (node = group[i]) { + keyValues[i] = keyValue = keyPrefix + key.call(node, node.__data__, i, group); + if (keyValue in nodeByKeyValue) { + exit[i] = node; + } else { + nodeByKeyValue[keyValue] = node; + } + } + } + + // Compute the key for each datum. + // If there a node associated with this key, join and add it to update. + // If there is not (or the key is a duplicate), add it to enter. + for (i = 0; i < dataLength; ++i) { + keyValue = keyPrefix + key.call(parent, data[i], i, data); + if (node = nodeByKeyValue[keyValue]) { + update[i] = node; + node.__data__ = data[i]; + nodeByKeyValue[keyValue] = null; + } else { + enter[i] = new EnterNode(parent, data[i]); + } + } + + // Add any remaining nodes that were not bound to data to exit. + for (i = 0; i < groupLength; ++i) { + if ((node = group[i]) && (nodeByKeyValue[keyValues[i]] === node)) { + exit[i] = node; + } + } +} + +function selection_data(value, key) { + if (!value) { + data = new Array(this.size()), j = -1; + this.each(function(d) { data[++j] = d; }); + return data; + } + + var bind = key ? bindKey : bindIndex, + parents = this._parents, + groups = this._groups; + + if (typeof value !== "function") value = constant$1(value); + + for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) { + var parent = parents[j], + group = groups[j], + groupLength = group.length, + data = value.call(parent, parent && parent.__data__, j, parents), + dataLength = data.length, + enterGroup = enter[j] = new Array(dataLength), + updateGroup = update[j] = new Array(dataLength), + exitGroup = exit[j] = new Array(groupLength); + + bind(parent, group, enterGroup, updateGroup, exitGroup, data, key); + + // Now connect the enter nodes to their following update node, such that + // appendChild can insert the materialized enter node before this node, + // rather than at the end of the parent node. + for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) { + if (previous = enterGroup[i0]) { + if (i0 >= i1) i1 = i0 + 1; + while (!(next = updateGroup[i1]) && ++i1 < dataLength); + previous._next = next || null; + } + } + } + + update = new Selection(update, parents); + update._enter = enter; + update._exit = exit; + return update; +} + +function selection_exit() { + return new Selection(this._exit || this._groups.map(sparse), this._parents); +} + +function selection_merge(selection$$1) { + + for (var groups0 = this._groups, groups1 = selection$$1._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) { + for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) { + if (node = group0[i] || group1[i]) { + merge[i] = node; + } + } + } + + for (; j < m0; ++j) { + merges[j] = groups0[j]; + } + + return new Selection(merges, this._parents); +} + +function selection_order() { + + for (var groups = this._groups, j = -1, m = groups.length; ++j < m;) { + for (var group = groups[j], i = group.length - 1, next = group[i], node; --i >= 0;) { + if (node = group[i]) { + if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next); + next = node; + } + } + } + + return this; +} + +function selection_sort(compare) { + if (!compare) compare = ascending$1; + + function compareNode(a, b) { + return a && b ? compare(a.__data__, b.__data__) : !a - !b; + } + + for (var groups = this._groups, m = groups.length, sortgroups = new Array(m), j = 0; j < m; ++j) { + for (var group = groups[j], n = group.length, sortgroup = sortgroups[j] = new Array(n), node, i = 0; i < n; ++i) { + if (node = group[i]) { + sortgroup[i] = node; + } + } + sortgroup.sort(compareNode); + } + + return new Selection(sortgroups, this._parents).order(); +} + +function ascending$1(a, b) { + return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; +} + +function selection_call() { + var callback = arguments[0]; + arguments[0] = this; + callback.apply(null, arguments); + return this; +} + +function selection_nodes() { + var nodes = new Array(this.size()), i = -1; + this.each(function() { nodes[++i] = this; }); + return nodes; +} + +function selection_node() { + + for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) { + for (var group = groups[j], i = 0, n = group.length; i < n; ++i) { + var node = group[i]; + if (node) return node; + } + } + + return null; +} + +function selection_size() { + var size = 0; + this.each(function() { ++size; }); + return size; +} + +function selection_empty() { + return !this.node(); +} + +function selection_each(callback) { + + for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) { + for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) { + if (node = group[i]) callback.call(node, node.__data__, i, group); + } + } + + return this; +} + +function attrRemove(name) { + return function() { + this.removeAttribute(name); + }; +} + +function attrRemoveNS(fullname) { + return function() { + this.removeAttributeNS(fullname.space, fullname.local); + }; +} + +function attrConstant(name, value) { + return function() { + this.setAttribute(name, value); + }; +} + +function attrConstantNS(fullname, value) { + return function() { + this.setAttributeNS(fullname.space, fullname.local, value); + }; +} + +function attrFunction(name, value) { + return function() { + var v = value.apply(this, arguments); + if (v == null) this.removeAttribute(name); + else this.setAttribute(name, v); + }; +} + +function attrFunctionNS(fullname, value) { + return function() { + var v = value.apply(this, arguments); + if (v == null) this.removeAttributeNS(fullname.space, fullname.local); + else this.setAttributeNS(fullname.space, fullname.local, v); + }; +} + +function selection_attr(name, value) { + var fullname = namespace(name); + + if (arguments.length < 2) { + var node = this.node(); + return fullname.local + ? node.getAttributeNS(fullname.space, fullname.local) + : node.getAttribute(fullname); + } + + return this.each((value == null + ? (fullname.local ? attrRemoveNS : attrRemove) : (typeof value === "function" + ? (fullname.local ? attrFunctionNS : attrFunction) + : (fullname.local ? attrConstantNS : attrConstant)))(fullname, value)); +} + +function defaultView(node) { + return (node.ownerDocument && node.ownerDocument.defaultView) // node is a Node + || (node.document && node) // node is a Window + || node.defaultView; // node is a Document +} + +function styleRemove(name) { + return function() { + this.style.removeProperty(name); + }; +} + +function styleConstant(name, value, priority) { + return function() { + this.style.setProperty(name, value, priority); + }; +} + +function styleFunction(name, value, priority) { + return function() { + var v = value.apply(this, arguments); + if (v == null) this.style.removeProperty(name); + else this.style.setProperty(name, v, priority); + }; +} + +function selection_style(name, value, priority) { + return arguments.length > 1 + ? this.each((value == null + ? styleRemove : typeof value === "function" + ? styleFunction + : styleConstant)(name, value, priority == null ? "" : priority)) + : styleValue(this.node(), name); +} + +function styleValue(node, name) { + return node.style.getPropertyValue(name) + || defaultView(node).getComputedStyle(node, null).getPropertyValue(name); +} + +function propertyRemove(name) { + return function() { + delete this[name]; + }; +} + +function propertyConstant(name, value) { + return function() { + this[name] = value; + }; +} + +function propertyFunction(name, value) { + return function() { + var v = value.apply(this, arguments); + if (v == null) delete this[name]; + else this[name] = v; + }; +} + +function selection_property(name, value) { + return arguments.length > 1 + ? this.each((value == null + ? propertyRemove : typeof value === "function" + ? propertyFunction + : propertyConstant)(name, value)) + : this.node()[name]; +} + +function classArray(string) { + return string.trim().split(/^|\s+/); +} + +function classList(node) { + return node.classList || new ClassList(node); +} + +function ClassList(node) { + this._node = node; + this._names = classArray(node.getAttribute("class") || ""); +} + +ClassList.prototype = { + add: function(name) { + var i = this._names.indexOf(name); + if (i < 0) { + this._names.push(name); + this._node.setAttribute("class", this._names.join(" ")); + } + }, + remove: function(name) { + var i = this._names.indexOf(name); + if (i >= 0) { + this._names.splice(i, 1); + this._node.setAttribute("class", this._names.join(" ")); + } + }, + contains: function(name) { + return this._names.indexOf(name) >= 0; + } +}; + +function classedAdd(node, names) { + var list = classList(node), i = -1, n = names.length; + while (++i < n) list.add(names[i]); +} + +function classedRemove(node, names) { + var list = classList(node), i = -1, n = names.length; + while (++i < n) list.remove(names[i]); +} + +function classedTrue(names) { + return function() { + classedAdd(this, names); + }; +} + +function classedFalse(names) { + return function() { + classedRemove(this, names); + }; +} + +function classedFunction(names, value) { + return function() { + (value.apply(this, arguments) ? classedAdd : classedRemove)(this, names); + }; +} + +function selection_classed(name, value) { + var names = classArray(name + ""); + + if (arguments.length < 2) { + var list = classList(this.node()), i = -1, n = names.length; + while (++i < n) if (!list.contains(names[i])) return false; + return true; + } + + return this.each((typeof value === "function" + ? classedFunction : value + ? classedTrue + : classedFalse)(names, value)); +} + +function textRemove() { + this.textContent = ""; +} + +function textConstant(value) { + return function() { + this.textContent = value; + }; +} + +function textFunction(value) { + return function() { + var v = value.apply(this, arguments); + this.textContent = v == null ? "" : v; + }; +} + +function selection_text(value) { + return arguments.length + ? this.each(value == null + ? textRemove : (typeof value === "function" + ? textFunction + : textConstant)(value)) + : this.node().textContent; +} + +function htmlRemove() { + this.innerHTML = ""; +} + +function htmlConstant(value) { + return function() { + this.innerHTML = value; + }; +} + +function htmlFunction(value) { + return function() { + var v = value.apply(this, arguments); + this.innerHTML = v == null ? "" : v; + }; +} + +function selection_html(value) { + return arguments.length + ? this.each(value == null + ? htmlRemove : (typeof value === "function" + ? htmlFunction + : htmlConstant)(value)) + : this.node().innerHTML; +} + +function raise() { + if (this.nextSibling) this.parentNode.appendChild(this); +} + +function selection_raise() { + return this.each(raise); +} + +function lower() { + if (this.previousSibling) this.parentNode.insertBefore(this, this.parentNode.firstChild); +} + +function selection_lower() { + return this.each(lower); +} + +function selection_append(name) { + var create = typeof name === "function" ? name : creator(name); + return this.select(function() { + return this.appendChild(create.apply(this, arguments)); + }); +} + +function constantNull() { + return null; +} + +function selection_insert(name, before) { + var create = typeof name === "function" ? name : creator(name), + select = before == null ? constantNull : typeof before === "function" ? before : selector(before); + return this.select(function() { + return this.insertBefore(create.apply(this, arguments), select.apply(this, arguments) || null); + }); +} + +function remove() { + var parent = this.parentNode; + if (parent) parent.removeChild(this); +} + +function selection_remove() { + return this.each(remove); +} + +function selection_cloneShallow() { + return this.parentNode.insertBefore(this.cloneNode(false), this.nextSibling); +} + +function selection_cloneDeep() { + return this.parentNode.insertBefore(this.cloneNode(true), this.nextSibling); +} + +function selection_clone(deep) { + return this.select(deep ? selection_cloneDeep : selection_cloneShallow); +} + +function selection_datum(value) { + return arguments.length + ? this.property("__data__", value) + : this.node().__data__; +} + +var filterEvents = {}; + +exports.event = null; + +if (typeof document !== "undefined") { + var element$1 = document.documentElement; + if (!("onmouseenter" in element$1)) { + filterEvents = {mouseenter: "mouseover", mouseleave: "mouseout"}; + } +} + +function filterContextListener(listener, index, group) { + listener = contextListener(listener, index, group); + return function(event) { + var related = event.relatedTarget; + if (!related || (related !== this && !(related.compareDocumentPosition(this) & 8))) { + listener.call(this, event); + } + }; +} + +function contextListener(listener, index, group) { + return function(event1) { + var event0 = exports.event; // Events can be reentrant (e.g., focus). + exports.event = event1; + try { + listener.call(this, this.__data__, index, group); + } finally { + exports.event = event0; + } + }; +} + +function parseTypenames$1(typenames) { + return typenames.trim().split(/^|\s+/).map(function(t) { + var name = "", i = t.indexOf("."); + if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i); + return {type: t, name: name}; + }); +} + +function onRemove(typename) { + return function() { + var on = this.__on; + if (!on) return; + for (var j = 0, i = -1, m = on.length, o; j < m; ++j) { + if (o = on[j], (!typename.type || o.type === typename.type) && o.name === typename.name) { + this.removeEventListener(o.type, o.listener, o.capture); + } else { + on[++i] = o; + } + } + if (++i) on.length = i; + else delete this.__on; + }; +} + +function onAdd(typename, value, capture) { + var wrap = filterEvents.hasOwnProperty(typename.type) ? filterContextListener : contextListener; + return function(d, i, group) { + var on = this.__on, o, listener = wrap(value, i, group); + if (on) for (var j = 0, m = on.length; j < m; ++j) { + if ((o = on[j]).type === typename.type && o.name === typename.name) { + this.removeEventListener(o.type, o.listener, o.capture); + this.addEventListener(o.type, o.listener = listener, o.capture = capture); + o.value = value; + return; + } + } + this.addEventListener(typename.type, listener, capture); + o = {type: typename.type, name: typename.name, value: value, listener: listener, capture: capture}; + if (!on) this.__on = [o]; + else on.push(o); + }; +} + +function selection_on(typename, value, capture) { + var typenames = parseTypenames$1(typename + ""), i, n = typenames.length, t; + + if (arguments.length < 2) { + var on = this.node().__on; + if (on) for (var j = 0, m = on.length, o; j < m; ++j) { + for (i = 0, o = on[j]; i < n; ++i) { + if ((t = typenames[i]).type === o.type && t.name === o.name) { + return o.value; + } + } + } + return; + } + + on = value ? onAdd : onRemove; + if (capture == null) capture = false; + for (i = 0; i < n; ++i) this.each(on(typenames[i], value, capture)); + return this; +} + +function customEvent(event1, listener, that, args) { + var event0 = exports.event; + event1.sourceEvent = exports.event; + exports.event = event1; + try { + return listener.apply(that, args); + } finally { + exports.event = event0; + } +} + +function dispatchEvent(node, type, params) { + var window = defaultView(node), + event = window.CustomEvent; + + if (typeof event === "function") { + event = new event(type, params); + } else { + event = window.document.createEvent("Event"); + if (params) event.initEvent(type, params.bubbles, params.cancelable), event.detail = params.detail; + else event.initEvent(type, false, false); + } + + node.dispatchEvent(event); +} + +function dispatchConstant(type, params) { + return function() { + return dispatchEvent(this, type, params); + }; +} + +function dispatchFunction(type, params) { + return function() { + return dispatchEvent(this, type, params.apply(this, arguments)); + }; +} + +function selection_dispatch(type, params) { + return this.each((typeof params === "function" + ? dispatchFunction + : dispatchConstant)(type, params)); +} + +var root = [null]; + +function Selection(groups, parents) { + this._groups = groups; + this._parents = parents; +} + +function selection() { + return new Selection([[document.documentElement]], root); +} + +Selection.prototype = selection.prototype = { + constructor: Selection, + select: selection_select, + selectAll: selection_selectAll, + filter: selection_filter, + data: selection_data, + enter: selection_enter, + exit: selection_exit, + merge: selection_merge, + order: selection_order, + sort: selection_sort, + call: selection_call, + nodes: selection_nodes, + node: selection_node, + size: selection_size, + empty: selection_empty, + each: selection_each, + attr: selection_attr, + style: selection_style, + property: selection_property, + classed: selection_classed, + text: selection_text, + html: selection_html, + raise: selection_raise, + lower: selection_lower, + append: selection_append, + insert: selection_insert, + remove: selection_remove, + clone: selection_clone, + datum: selection_datum, + on: selection_on, + dispatch: selection_dispatch +}; + +function select(selector) { + return typeof selector === "string" + ? new Selection([[document.querySelector(selector)]], [document.documentElement]) + : new Selection([[selector]], root); +} + +function create(name) { + return select(creator(name).call(document.documentElement)); +} + +var nextId = 0; + +function local() { + return new Local; +} + +function Local() { + this._ = "@" + (++nextId).toString(36); +} + +Local.prototype = local.prototype = { + constructor: Local, + get: function(node) { + var id = this._; + while (!(id in node)) if (!(node = node.parentNode)) return; + return node[id]; + }, + set: function(node, value) { + return node[this._] = value; + }, + remove: function(node) { + return this._ in node && delete node[this._]; + }, + toString: function() { + return this._; + } +}; + +function sourceEvent() { + var current = exports.event, source; + while (source = current.sourceEvent) current = source; + return current; +} + +function point(node, event) { + var svg = node.ownerSVGElement || node; + + if (svg.createSVGPoint) { + var point = svg.createSVGPoint(); + point.x = event.clientX, point.y = event.clientY; + point = point.matrixTransform(node.getScreenCTM().inverse()); + return [point.x, point.y]; + } + + var rect = node.getBoundingClientRect(); + return [event.clientX - rect.left - node.clientLeft, event.clientY - rect.top - node.clientTop]; +} + +function mouse(node) { + var event = sourceEvent(); + if (event.changedTouches) event = event.changedTouches[0]; + return point(node, event); +} + +function selectAll(selector) { + return typeof selector === "string" + ? new Selection([document.querySelectorAll(selector)], [document.documentElement]) + : new Selection([selector == null ? [] : selector], root); +} + +function touch(node, touches, identifier) { + if (arguments.length < 3) identifier = touches, touches = sourceEvent().changedTouches; + + for (var i = 0, n = touches ? touches.length : 0, touch; i < n; ++i) { + if ((touch = touches[i]).identifier === identifier) { + return point(node, touch); + } + } + + return null; +} + +function touches(node, touches) { + if (touches == null) touches = sourceEvent().touches; + + for (var i = 0, n = touches ? touches.length : 0, points = new Array(n); i < n; ++i) { + points[i] = point(node, touches[i]); + } + + return points; +} + +function nopropagation() { + exports.event.stopImmediatePropagation(); +} + +function noevent() { + exports.event.preventDefault(); + exports.event.stopImmediatePropagation(); +} + +function dragDisable(view) { + var root = view.document.documentElement, + selection$$1 = select(view).on("dragstart.drag", noevent, true); + if ("onselectstart" in root) { + selection$$1.on("selectstart.drag", noevent, true); + } else { + root.__noselect = root.style.MozUserSelect; + root.style.MozUserSelect = "none"; + } +} + +function yesdrag(view, noclick) { + var root = view.document.documentElement, + selection$$1 = select(view).on("dragstart.drag", null); + if (noclick) { + selection$$1.on("click.drag", noevent, true); + setTimeout(function() { selection$$1.on("click.drag", null); }, 0); + } + if ("onselectstart" in root) { + selection$$1.on("selectstart.drag", null); + } else { + root.style.MozUserSelect = root.__noselect; + delete root.__noselect; + } +} + +function constant$2(x) { + return function() { + return x; + }; +} + +function DragEvent(target, type, subject, id, active, x, y, dx, dy, dispatch) { + this.target = target; + this.type = type; + this.subject = subject; + this.identifier = id; + this.active = active; + this.x = x; + this.y = y; + this.dx = dx; + this.dy = dy; + this._ = dispatch; +} + +DragEvent.prototype.on = function() { + var value = this._.on.apply(this._, arguments); + return value === this._ ? this : value; +}; + +// Ignore right-click, since that should open the context menu. +function defaultFilter() { + return !exports.event.button; +} + +function defaultContainer() { + return this.parentNode; +} + +function defaultSubject(d) { + return d == null ? {x: exports.event.x, y: exports.event.y} : d; +} + +function defaultTouchable() { + return "ontouchstart" in this; +} + +function drag() { + var filter = defaultFilter, + container = defaultContainer, + subject = defaultSubject, + touchable = defaultTouchable, + gestures = {}, + listeners = dispatch("start", "drag", "end"), + active = 0, + mousedownx, + mousedowny, + mousemoving, + touchending, + clickDistance2 = 0; + + function drag(selection$$1) { + selection$$1 + .on("mousedown.drag", mousedowned) + .filter(touchable) + .on("touchstart.drag", touchstarted) + .on("touchmove.drag", touchmoved) + .on("touchend.drag touchcancel.drag", touchended) + .style("touch-action", "none") + .style("-webkit-tap-highlight-color", "rgba(0,0,0,0)"); + } + + function mousedowned() { + if (touchending || !filter.apply(this, arguments)) return; + var gesture = beforestart("mouse", container.apply(this, arguments), mouse, this, arguments); + if (!gesture) return; + select(exports.event.view).on("mousemove.drag", mousemoved, true).on("mouseup.drag", mouseupped, true); + dragDisable(exports.event.view); + nopropagation(); + mousemoving = false; + mousedownx = exports.event.clientX; + mousedowny = exports.event.clientY; + gesture("start"); + } + + function mousemoved() { + noevent(); + if (!mousemoving) { + var dx = exports.event.clientX - mousedownx, dy = exports.event.clientY - mousedowny; + mousemoving = dx * dx + dy * dy > clickDistance2; + } + gestures.mouse("drag"); + } + + function mouseupped() { + select(exports.event.view).on("mousemove.drag mouseup.drag", null); + yesdrag(exports.event.view, mousemoving); + noevent(); + gestures.mouse("end"); + } + + function touchstarted() { + if (!filter.apply(this, arguments)) return; + var touches$$1 = exports.event.changedTouches, + c = container.apply(this, arguments), + n = touches$$1.length, i, gesture; + + for (i = 0; i < n; ++i) { + if (gesture = beforestart(touches$$1[i].identifier, c, touch, this, arguments)) { + nopropagation(); + gesture("start"); + } + } + } + + function touchmoved() { + var touches$$1 = exports.event.changedTouches, + n = touches$$1.length, i, gesture; + + for (i = 0; i < n; ++i) { + if (gesture = gestures[touches$$1[i].identifier]) { + noevent(); + gesture("drag"); + } + } + } + + function touchended() { + var touches$$1 = exports.event.changedTouches, + n = touches$$1.length, i, gesture; + + if (touchending) clearTimeout(touchending); + touchending = setTimeout(function() { touchending = null; }, 500); // Ghost clicks are delayed! + for (i = 0; i < n; ++i) { + if (gesture = gestures[touches$$1[i].identifier]) { + nopropagation(); + gesture("end"); + } + } + } + + function beforestart(id, container, point$$1, that, args) { + var p = point$$1(container, id), s, dx, dy, + sublisteners = listeners.copy(); + + if (!customEvent(new DragEvent(drag, "beforestart", s, id, active, p[0], p[1], 0, 0, sublisteners), function() { + if ((exports.event.subject = s = subject.apply(that, args)) == null) return false; + dx = s.x - p[0] || 0; + dy = s.y - p[1] || 0; + return true; + })) return; + + return function gesture(type) { + var p0 = p, n; + switch (type) { + case "start": gestures[id] = gesture, n = active++; break; + case "end": delete gestures[id], --active; // nobreak + case "drag": p = point$$1(container, id), n = active; break; + } + customEvent(new DragEvent(drag, type, s, id, n, p[0] + dx, p[1] + dy, p[0] - p0[0], p[1] - p0[1], sublisteners), sublisteners.apply, sublisteners, [type, that, args]); + }; + } + + drag.filter = function(_) { + return arguments.length ? (filter = typeof _ === "function" ? _ : constant$2(!!_), drag) : filter; + }; + + drag.container = function(_) { + return arguments.length ? (container = typeof _ === "function" ? _ : constant$2(_), drag) : container; + }; + + drag.subject = function(_) { + return arguments.length ? (subject = typeof _ === "function" ? _ : constant$2(_), drag) : subject; + }; + + drag.touchable = function(_) { + return arguments.length ? (touchable = typeof _ === "function" ? _ : constant$2(!!_), drag) : touchable; + }; + + drag.on = function() { + var value = listeners.on.apply(listeners, arguments); + return value === listeners ? drag : value; + }; + + drag.clickDistance = function(_) { + return arguments.length ? (clickDistance2 = (_ = +_) * _, drag) : Math.sqrt(clickDistance2); + }; + + return drag; +} + +function define(constructor, factory, prototype) { + constructor.prototype = factory.prototype = prototype; + prototype.constructor = constructor; +} + +function extend(parent, definition) { + var prototype = Object.create(parent.prototype); + for (var key in definition) prototype[key] = definition[key]; + return prototype; +} + +function Color() {} + +var darker = 0.7; +var brighter = 1 / darker; + +var reI = "\\s*([+-]?\\d+)\\s*", + reN = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*", + reP = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*", + reHex3 = /^#([0-9a-f]{3})$/, + reHex6 = /^#([0-9a-f]{6})$/, + reRgbInteger = new RegExp("^rgb\\(" + [reI, reI, reI] + "\\)$"), + reRgbPercent = new RegExp("^rgb\\(" + [reP, reP, reP] + "\\)$"), + reRgbaInteger = new RegExp("^rgba\\(" + [reI, reI, reI, reN] + "\\)$"), + reRgbaPercent = new RegExp("^rgba\\(" + [reP, reP, reP, reN] + "\\)$"), + reHslPercent = new RegExp("^hsl\\(" + [reN, reP, reP] + "\\)$"), + reHslaPercent = new RegExp("^hsla\\(" + [reN, reP, reP, reN] + "\\)$"); + +var named = { + aliceblue: 0xf0f8ff, + antiquewhite: 0xfaebd7, + aqua: 0x00ffff, + aquamarine: 0x7fffd4, + azure: 0xf0ffff, + beige: 0xf5f5dc, + bisque: 0xffe4c4, + black: 0x000000, + blanchedalmond: 0xffebcd, + blue: 0x0000ff, + blueviolet: 0x8a2be2, + brown: 0xa52a2a, + burlywood: 0xdeb887, + cadetblue: 0x5f9ea0, + chartreuse: 0x7fff00, + chocolate: 0xd2691e, + coral: 0xff7f50, + cornflowerblue: 0x6495ed, + cornsilk: 0xfff8dc, + crimson: 0xdc143c, + cyan: 0x00ffff, + darkblue: 0x00008b, + darkcyan: 0x008b8b, + darkgoldenrod: 0xb8860b, + darkgray: 0xa9a9a9, + darkgreen: 0x006400, + darkgrey: 0xa9a9a9, + darkkhaki: 0xbdb76b, + darkmagenta: 0x8b008b, + darkolivegreen: 0x556b2f, + darkorange: 0xff8c00, + darkorchid: 0x9932cc, + darkred: 0x8b0000, + darksalmon: 0xe9967a, + darkseagreen: 0x8fbc8f, + darkslateblue: 0x483d8b, + darkslategray: 0x2f4f4f, + darkslategrey: 0x2f4f4f, + darkturquoise: 0x00ced1, + darkviolet: 0x9400d3, + deeppink: 0xff1493, + deepskyblue: 0x00bfff, + dimgray: 0x696969, + dimgrey: 0x696969, + dodgerblue: 0x1e90ff, + firebrick: 0xb22222, + floralwhite: 0xfffaf0, + forestgreen: 0x228b22, + fuchsia: 0xff00ff, + gainsboro: 0xdcdcdc, + ghostwhite: 0xf8f8ff, + gold: 0xffd700, + goldenrod: 0xdaa520, + gray: 0x808080, + green: 0x008000, + greenyellow: 0xadff2f, + grey: 0x808080, + honeydew: 0xf0fff0, + hotpink: 0xff69b4, + indianred: 0xcd5c5c, + indigo: 0x4b0082, + ivory: 0xfffff0, + khaki: 0xf0e68c, + lavender: 0xe6e6fa, + lavenderblush: 0xfff0f5, + lawngreen: 0x7cfc00, + lemonchiffon: 0xfffacd, + lightblue: 0xadd8e6, + lightcoral: 0xf08080, + lightcyan: 0xe0ffff, + lightgoldenrodyellow: 0xfafad2, + lightgray: 0xd3d3d3, + lightgreen: 0x90ee90, + lightgrey: 0xd3d3d3, + lightpink: 0xffb6c1, + lightsalmon: 0xffa07a, + lightseagreen: 0x20b2aa, + lightskyblue: 0x87cefa, + lightslategray: 0x778899, + lightslategrey: 0x778899, + lightsteelblue: 0xb0c4de, + lightyellow: 0xffffe0, + lime: 0x00ff00, + limegreen: 0x32cd32, + linen: 0xfaf0e6, + magenta: 0xff00ff, + maroon: 0x800000, + mediumaquamarine: 0x66cdaa, + mediumblue: 0x0000cd, + mediumorchid: 0xba55d3, + mediumpurple: 0x9370db, + mediumseagreen: 0x3cb371, + mediumslateblue: 0x7b68ee, + mediumspringgreen: 0x00fa9a, + mediumturquoise: 0x48d1cc, + mediumvioletred: 0xc71585, + midnightblue: 0x191970, + mintcream: 0xf5fffa, + mistyrose: 0xffe4e1, + moccasin: 0xffe4b5, + navajowhite: 0xffdead, + navy: 0x000080, + oldlace: 0xfdf5e6, + olive: 0x808000, + olivedrab: 0x6b8e23, + orange: 0xffa500, + orangered: 0xff4500, + orchid: 0xda70d6, + palegoldenrod: 0xeee8aa, + palegreen: 0x98fb98, + paleturquoise: 0xafeeee, + palevioletred: 0xdb7093, + papayawhip: 0xffefd5, + peachpuff: 0xffdab9, + peru: 0xcd853f, + pink: 0xffc0cb, + plum: 0xdda0dd, + powderblue: 0xb0e0e6, + purple: 0x800080, + rebeccapurple: 0x663399, + red: 0xff0000, + rosybrown: 0xbc8f8f, + royalblue: 0x4169e1, + saddlebrown: 0x8b4513, + salmon: 0xfa8072, + sandybrown: 0xf4a460, + seagreen: 0x2e8b57, + seashell: 0xfff5ee, + sienna: 0xa0522d, + silver: 0xc0c0c0, + skyblue: 0x87ceeb, + slateblue: 0x6a5acd, + slategray: 0x708090, + slategrey: 0x708090, + snow: 0xfffafa, + springgreen: 0x00ff7f, + steelblue: 0x4682b4, + tan: 0xd2b48c, + teal: 0x008080, + thistle: 0xd8bfd8, + tomato: 0xff6347, + turquoise: 0x40e0d0, + violet: 0xee82ee, + wheat: 0xf5deb3, + white: 0xffffff, + whitesmoke: 0xf5f5f5, + yellow: 0xffff00, + yellowgreen: 0x9acd32 +}; + +define(Color, color, { + displayable: function() { + return this.rgb().displayable(); + }, + toString: function() { + return this.rgb() + ""; + } +}); + +function color(format) { + var m; + format = (format + "").trim().toLowerCase(); + 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 + : (m = reHex6.exec(format)) ? rgbn(parseInt(m[1], 16)) // #ff0000 + : (m = reRgbInteger.exec(format)) ? new Rgb(m[1], m[2], m[3], 1) // rgb(255, 0, 0) + : (m = reRgbPercent.exec(format)) ? new Rgb(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, 1) // rgb(100%, 0%, 0%) + : (m = reRgbaInteger.exec(format)) ? rgba(m[1], m[2], m[3], m[4]) // rgba(255, 0, 0, 1) + : (m = reRgbaPercent.exec(format)) ? rgba(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, m[4]) // rgb(100%, 0%, 0%, 1) + : (m = reHslPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, 1) // hsl(120, 50%, 50%) + : (m = reHslaPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) // hsla(120, 50%, 50%, 1) + : named.hasOwnProperty(format) ? rgbn(named[format]) + : format === "transparent" ? new Rgb(NaN, NaN, NaN, 0) + : null; +} + +function rgbn(n) { + return new Rgb(n >> 16 & 0xff, n >> 8 & 0xff, n & 0xff, 1); +} + +function rgba(r, g, b, a) { + if (a <= 0) r = g = b = NaN; + return new Rgb(r, g, b, a); +} + +function rgbConvert(o) { + if (!(o instanceof Color)) o = color(o); + if (!o) return new Rgb; + o = o.rgb(); + return new Rgb(o.r, o.g, o.b, o.opacity); +} + +function rgb(r, g, b, opacity) { + return arguments.length === 1 ? rgbConvert(r) : new Rgb(r, g, b, opacity == null ? 1 : opacity); +} + +function Rgb(r, g, b, opacity) { + this.r = +r; + this.g = +g; + this.b = +b; + this.opacity = +opacity; +} + +define(Rgb, rgb, extend(Color, { + brighter: function(k) { + k = k == null ? brighter : Math.pow(brighter, k); + return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity); + }, + darker: function(k) { + k = k == null ? darker : Math.pow(darker, k); + return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity); + }, + rgb: function() { + return this; + }, + displayable: function() { + return (0 <= this.r && this.r <= 255) + && (0 <= this.g && this.g <= 255) + && (0 <= this.b && this.b <= 255) + && (0 <= this.opacity && this.opacity <= 1); + }, + toString: function() { + var a = this.opacity; a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a)); + return (a === 1 ? "rgb(" : "rgba(") + + Math.max(0, Math.min(255, Math.round(this.r) || 0)) + ", " + + Math.max(0, Math.min(255, Math.round(this.g) || 0)) + ", " + + Math.max(0, Math.min(255, Math.round(this.b) || 0)) + + (a === 1 ? ")" : ", " + a + ")"); + } +})); + +function hsla(h, s, l, a) { + if (a <= 0) h = s = l = NaN; + else if (l <= 0 || l >= 1) h = s = NaN; + else if (s <= 0) h = NaN; + return new Hsl(h, s, l, a); +} + +function hslConvert(o) { + if (o instanceof Hsl) return new Hsl(o.h, o.s, o.l, o.opacity); + if (!(o instanceof Color)) o = color(o); + if (!o) return new Hsl; + if (o instanceof Hsl) return o; + o = o.rgb(); + var r = o.r / 255, + g = o.g / 255, + b = o.b / 255, + min = Math.min(r, g, b), + max = Math.max(r, g, b), + h = NaN, + s = max - min, + l = (max + min) / 2; + if (s) { + if (r === max) h = (g - b) / s + (g < b) * 6; + else if (g === max) h = (b - r) / s + 2; + else h = (r - g) / s + 4; + s /= l < 0.5 ? max + min : 2 - max - min; + h *= 60; + } else { + s = l > 0 && l < 1 ? 0 : h; + } + return new Hsl(h, s, l, o.opacity); +} + +function hsl(h, s, l, opacity) { + return arguments.length === 1 ? hslConvert(h) : new Hsl(h, s, l, opacity == null ? 1 : opacity); +} + +function Hsl(h, s, l, opacity) { + this.h = +h; + this.s = +s; + this.l = +l; + this.opacity = +opacity; +} + +define(Hsl, hsl, extend(Color, { + brighter: function(k) { + k = k == null ? brighter : Math.pow(brighter, k); + return new Hsl(this.h, this.s, this.l * k, this.opacity); + }, + darker: function(k) { + k = k == null ? darker : Math.pow(darker, k); + return new Hsl(this.h, this.s, this.l * k, this.opacity); + }, + rgb: function() { + var h = this.h % 360 + (this.h < 0) * 360, + s = isNaN(h) || isNaN(this.s) ? 0 : this.s, + l = this.l, + m2 = l + (l < 0.5 ? l : 1 - l) * s, + m1 = 2 * l - m2; + return new Rgb( + hsl2rgb(h >= 240 ? h - 240 : h + 120, m1, m2), + hsl2rgb(h, m1, m2), + hsl2rgb(h < 120 ? h + 240 : h - 120, m1, m2), + this.opacity + ); + }, + displayable: function() { + return (0 <= this.s && this.s <= 1 || isNaN(this.s)) + && (0 <= this.l && this.l <= 1) + && (0 <= this.opacity && this.opacity <= 1); + } +})); + +/* From FvD 13.37, CSS Color Module Level 3 */ +function hsl2rgb(h, m1, m2) { + return (h < 60 ? m1 + (m2 - m1) * h / 60 + : h < 180 ? m2 + : h < 240 ? m1 + (m2 - m1) * (240 - h) / 60 + : m1) * 255; +} + +var deg2rad = Math.PI / 180; +var rad2deg = 180 / Math.PI; + +var Kn = 18, + Xn = 0.950470, // D65 standard referent + Yn = 1, + Zn = 1.088830, + t0 = 4 / 29, + t1 = 6 / 29, + t2 = 3 * t1 * t1, + t3 = t1 * t1 * t1; + +function labConvert(o) { + if (o instanceof Lab) return new Lab(o.l, o.a, o.b, o.opacity); + if (o instanceof Hcl) { + var h = o.h * deg2rad; + return new Lab(o.l, Math.cos(h) * o.c, Math.sin(h) * o.c, o.opacity); + } + if (!(o instanceof Rgb)) o = rgbConvert(o); + var b = rgb2xyz(o.r), + a = rgb2xyz(o.g), + l = rgb2xyz(o.b), + x = xyz2lab((0.4124564 * b + 0.3575761 * a + 0.1804375 * l) / Xn), + y = xyz2lab((0.2126729 * b + 0.7151522 * a + 0.0721750 * l) / Yn), + z = xyz2lab((0.0193339 * b + 0.1191920 * a + 0.9503041 * l) / Zn); + return new Lab(116 * y - 16, 500 * (x - y), 200 * (y - z), o.opacity); +} + +function lab(l, a, b, opacity) { + return arguments.length === 1 ? labConvert(l) : new Lab(l, a, b, opacity == null ? 1 : opacity); +} + +function Lab(l, a, b, opacity) { + this.l = +l; + this.a = +a; + this.b = +b; + this.opacity = +opacity; +} + +define(Lab, lab, extend(Color, { + brighter: function(k) { + return new Lab(this.l + Kn * (k == null ? 1 : k), this.a, this.b, this.opacity); + }, + darker: function(k) { + return new Lab(this.l - Kn * (k == null ? 1 : k), this.a, this.b, this.opacity); + }, + rgb: function() { + var y = (this.l + 16) / 116, + x = isNaN(this.a) ? y : y + this.a / 500, + z = isNaN(this.b) ? y : y - this.b / 200; + y = Yn * lab2xyz(y); + x = Xn * lab2xyz(x); + z = Zn * lab2xyz(z); + return new Rgb( + xyz2rgb( 3.2404542 * x - 1.5371385 * y - 0.4985314 * z), // D65 -> sRGB + xyz2rgb(-0.9692660 * x + 1.8760108 * y + 0.0415560 * z), + xyz2rgb( 0.0556434 * x - 0.2040259 * y + 1.0572252 * z), + this.opacity + ); + } +})); + +function xyz2lab(t) { + return t > t3 ? Math.pow(t, 1 / 3) : t / t2 + t0; +} + +function lab2xyz(t) { + return t > t1 ? t * t * t : t2 * (t - t0); +} + +function xyz2rgb(x) { + return 255 * (x <= 0.0031308 ? 12.92 * x : 1.055 * Math.pow(x, 1 / 2.4) - 0.055); +} + +function rgb2xyz(x) { + return (x /= 255) <= 0.04045 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4); +} + +function hclConvert(o) { + if (o instanceof Hcl) return new Hcl(o.h, o.c, o.l, o.opacity); + if (!(o instanceof Lab)) o = labConvert(o); + var h = Math.atan2(o.b, o.a) * rad2deg; + return new Hcl(h < 0 ? h + 360 : h, Math.sqrt(o.a * o.a + o.b * o.b), o.l, o.opacity); +} + +function hcl(h, c, l, opacity) { + return arguments.length === 1 ? hclConvert(h) : new Hcl(h, c, l, opacity == null ? 1 : opacity); +} + +function Hcl(h, c, l, opacity) { + this.h = +h; + this.c = +c; + this.l = +l; + this.opacity = +opacity; +} + +define(Hcl, hcl, extend(Color, { + brighter: function(k) { + return new Hcl(this.h, this.c, this.l + Kn * (k == null ? 1 : k), this.opacity); + }, + darker: function(k) { + return new Hcl(this.h, this.c, this.l - Kn * (k == null ? 1 : k), this.opacity); + }, + rgb: function() { + return labConvert(this).rgb(); + } +})); + +var A = -0.14861, + B = +1.78277, + C = -0.29227, + D = -0.90649, + E = +1.97294, + ED = E * D, + EB = E * B, + BC_DA = B * C - D * A; + +function cubehelixConvert(o) { + if (o instanceof Cubehelix) return new Cubehelix(o.h, o.s, o.l, o.opacity); + if (!(o instanceof Rgb)) o = rgbConvert(o); + var r = o.r / 255, + g = o.g / 255, + b = o.b / 255, + l = (BC_DA * b + ED * r - EB * g) / (BC_DA + ED - EB), + bl = b - l, + k = (E * (g - l) - C * bl) / D, + s = Math.sqrt(k * k + bl * bl) / (E * l * (1 - l)), // NaN if l=0 or l=1 + h = s ? Math.atan2(k, bl) * rad2deg - 120 : NaN; + return new Cubehelix(h < 0 ? h + 360 : h, s, l, o.opacity); +} + +function cubehelix(h, s, l, opacity) { + return arguments.length === 1 ? cubehelixConvert(h) : new Cubehelix(h, s, l, opacity == null ? 1 : opacity); +} + +function Cubehelix(h, s, l, opacity) { + this.h = +h; + this.s = +s; + this.l = +l; + this.opacity = +opacity; +} + +define(Cubehelix, cubehelix, extend(Color, { + brighter: function(k) { + k = k == null ? brighter : Math.pow(brighter, k); + return new Cubehelix(this.h, this.s, this.l * k, this.opacity); + }, + darker: function(k) { + k = k == null ? darker : Math.pow(darker, k); + return new Cubehelix(this.h, this.s, this.l * k, this.opacity); + }, + rgb: function() { + var h = isNaN(this.h) ? 0 : (this.h + 120) * deg2rad, + l = +this.l, + a = isNaN(this.s) ? 0 : this.s * l * (1 - l), + cosh = Math.cos(h), + sinh = Math.sin(h); + return new Rgb( + 255 * (l + a * (A * cosh + B * sinh)), + 255 * (l + a * (C * cosh + D * sinh)), + 255 * (l + a * (E * cosh)), + this.opacity + ); + } +})); + +function basis(t1, v0, v1, v2, v3) { + var t2 = t1 * t1, t3 = t2 * t1; + return ((1 - 3 * t1 + 3 * t2 - t3) * v0 + + (4 - 6 * t2 + 3 * t3) * v1 + + (1 + 3 * t1 + 3 * t2 - 3 * t3) * v2 + + t3 * v3) / 6; +} + +function basis$1(values) { + var n = values.length - 1; + return function(t) { + var i = t <= 0 ? (t = 0) : t >= 1 ? (t = 1, n - 1) : Math.floor(t * n), + v1 = values[i], + v2 = values[i + 1], + v0 = i > 0 ? values[i - 1] : 2 * v1 - v2, + v3 = i < n - 1 ? values[i + 2] : 2 * v2 - v1; + return basis((t - i / n) * n, v0, v1, v2, v3); + }; +} + +function basisClosed(values) { + var n = values.length; + return function(t) { + var i = Math.floor(((t %= 1) < 0 ? ++t : t) * n), + v0 = values[(i + n - 1) % n], + v1 = values[i % n], + v2 = values[(i + 1) % n], + v3 = values[(i + 2) % n]; + return basis((t - i / n) * n, v0, v1, v2, v3); + }; +} + +function constant$3(x) { + return function() { + return x; + }; +} + +function linear(a, d) { + return function(t) { + return a + t * d; + }; +} + +function exponential(a, b, y) { + return a = Math.pow(a, y), b = Math.pow(b, y) - a, y = 1 / y, function(t) { + return Math.pow(a + t * b, y); + }; +} + +function hue(a, b) { + var d = b - a; + return d ? linear(a, d > 180 || d < -180 ? d - 360 * Math.round(d / 360) : d) : constant$3(isNaN(a) ? b : a); +} + +function gamma(y) { + return (y = +y) === 1 ? nogamma : function(a, b) { + return b - a ? exponential(a, b, y) : constant$3(isNaN(a) ? b : a); + }; +} + +function nogamma(a, b) { + var d = b - a; + return d ? linear(a, d) : constant$3(isNaN(a) ? b : a); +} + +var interpolateRgb = (function rgbGamma(y) { + var color$$1 = gamma(y); + + function rgb$$1(start, end) { + var r = color$$1((start = rgb(start)).r, (end = rgb(end)).r), + g = color$$1(start.g, end.g), + b = color$$1(start.b, end.b), + opacity = nogamma(start.opacity, end.opacity); + return function(t) { + start.r = r(t); + start.g = g(t); + start.b = b(t); + start.opacity = opacity(t); + return start + ""; + }; + } + + rgb$$1.gamma = rgbGamma; + + return rgb$$1; +})(1); + +function rgbSpline(spline) { + return function(colors) { + var n = colors.length, + r = new Array(n), + g = new Array(n), + b = new Array(n), + i, color$$1; + for (i = 0; i < n; ++i) { + color$$1 = rgb(colors[i]); + r[i] = color$$1.r || 0; + g[i] = color$$1.g || 0; + b[i] = color$$1.b || 0; + } + r = spline(r); + g = spline(g); + b = spline(b); + color$$1.opacity = 1; + return function(t) { + color$$1.r = r(t); + color$$1.g = g(t); + color$$1.b = b(t); + return color$$1 + ""; + }; + }; +} + +var rgbBasis = rgbSpline(basis$1); +var rgbBasisClosed = rgbSpline(basisClosed); + +function array$1(a, b) { + var nb = b ? b.length : 0, + na = a ? Math.min(nb, a.length) : 0, + x = new Array(na), + c = new Array(nb), + i; + + for (i = 0; i < na; ++i) x[i] = interpolateValue(a[i], b[i]); + for (; i < nb; ++i) c[i] = b[i]; + + return function(t) { + for (i = 0; i < na; ++i) c[i] = x[i](t); + return c; + }; +} + +function date(a, b) { + var d = new Date; + return a = +a, b -= a, function(t) { + return d.setTime(a + b * t), d; + }; +} + +function reinterpolate(a, b) { + return a = +a, b -= a, function(t) { + return a + b * t; + }; +} + +function object(a, b) { + var i = {}, + c = {}, + k; + + if (a === null || typeof a !== "object") a = {}; + if (b === null || typeof b !== "object") b = {}; + + for (k in b) { + if (k in a) { + i[k] = interpolateValue(a[k], b[k]); + } else { + c[k] = b[k]; + } + } + + return function(t) { + for (k in i) c[k] = i[k](t); + return c; + }; +} + +var reA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g, + reB = new RegExp(reA.source, "g"); + +function zero(b) { + return function() { + return b; + }; +} + +function one(b) { + return function(t) { + return b(t) + ""; + }; +} + +function interpolateString(a, b) { + var bi = reA.lastIndex = reB.lastIndex = 0, // scan index for next number in b + am, // current match in a + bm, // current match in b + bs, // string preceding current number in b, if any + i = -1, // index in s + s = [], // string constants and placeholders + q = []; // number interpolators + + // Coerce inputs to strings. + a = a + "", b = b + ""; + + // Interpolate pairs of numbers in a & b. + while ((am = reA.exec(a)) + && (bm = reB.exec(b))) { + if ((bs = bm.index) > bi) { // a string precedes the next number in b + bs = b.slice(bi, bs); + if (s[i]) s[i] += bs; // coalesce with previous string + else s[++i] = bs; + } + if ((am = am[0]) === (bm = bm[0])) { // numbers in a & b match + if (s[i]) s[i] += bm; // coalesce with previous string + else s[++i] = bm; + } else { // interpolate non-matching numbers + s[++i] = null; + q.push({i: i, x: reinterpolate(am, bm)}); + } + bi = reB.lastIndex; + } + + // Add remains of b. + if (bi < b.length) { + bs = b.slice(bi); + if (s[i]) s[i] += bs; // coalesce with previous string + else s[++i] = bs; + } + + // Special optimization for only a single match. + // Otherwise, interpolate each of the numbers and rejoin the string. + return s.length < 2 ? (q[0] + ? one(q[0].x) + : zero(b)) + : (b = q.length, function(t) { + for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t); + return s.join(""); + }); +} + +function interpolateValue(a, b) { + var t = typeof b, c; + return b == null || t === "boolean" ? constant$3(b) + : (t === "number" ? reinterpolate + : t === "string" ? ((c = color(b)) ? (b = c, interpolateRgb) : interpolateString) + : b instanceof color ? interpolateRgb + : b instanceof Date ? date + : Array.isArray(b) ? array$1 + : typeof b.valueOf !== "function" && typeof b.toString !== "function" || isNaN(b) ? object + : reinterpolate)(a, b); +} + +function interpolateRound(a, b) { + return a = +a, b -= a, function(t) { + return Math.round(a + b * t); + }; +} + +var degrees = 180 / Math.PI; + +var identity$2 = { + translateX: 0, + translateY: 0, + rotate: 0, + skewX: 0, + scaleX: 1, + scaleY: 1 +}; + +function decompose(a, b, c, d, e, f) { + var scaleX, scaleY, skewX; + if (scaleX = Math.sqrt(a * a + b * b)) a /= scaleX, b /= scaleX; + if (skewX = a * c + b * d) c -= a * skewX, d -= b * skewX; + if (scaleY = Math.sqrt(c * c + d * d)) c /= scaleY, d /= scaleY, skewX /= scaleY; + if (a * d < b * c) a = -a, b = -b, skewX = -skewX, scaleX = -scaleX; + return { + translateX: e, + translateY: f, + rotate: Math.atan2(b, a) * degrees, + skewX: Math.atan(skewX) * degrees, + scaleX: scaleX, + scaleY: scaleY + }; +} + +var cssNode, + cssRoot, + cssView, + svgNode; + +function parseCss(value) { + if (value === "none") return identity$2; + if (!cssNode) cssNode = document.createElement("DIV"), cssRoot = document.documentElement, cssView = document.defaultView; + cssNode.style.transform = value; + value = cssView.getComputedStyle(cssRoot.appendChild(cssNode), null).getPropertyValue("transform"); + cssRoot.removeChild(cssNode); + value = value.slice(7, -1).split(","); + return decompose(+value[0], +value[1], +value[2], +value[3], +value[4], +value[5]); +} + +function parseSvg(value) { + if (value == null) return identity$2; + if (!svgNode) svgNode = document.createElementNS("http://www.w3.org/2000/svg", "g"); + svgNode.setAttribute("transform", value); + if (!(value = svgNode.transform.baseVal.consolidate())) return identity$2; + value = value.matrix; + return decompose(value.a, value.b, value.c, value.d, value.e, value.f); +} + +function interpolateTransform(parse, pxComma, pxParen, degParen) { + + function pop(s) { + return s.length ? s.pop() + " " : ""; + } + + function translate(xa, ya, xb, yb, s, q) { + if (xa !== xb || ya !== yb) { + var i = s.push("translate(", null, pxComma, null, pxParen); + q.push({i: i - 4, x: reinterpolate(xa, xb)}, {i: i - 2, x: reinterpolate(ya, yb)}); + } else if (xb || yb) { + s.push("translate(" + xb + pxComma + yb + pxParen); + } + } + + function rotate(a, b, s, q) { + if (a !== b) { + if (a - b > 180) b += 360; else if (b - a > 180) a += 360; // shortest path + q.push({i: s.push(pop(s) + "rotate(", null, degParen) - 2, x: reinterpolate(a, b)}); + } else if (b) { + s.push(pop(s) + "rotate(" + b + degParen); + } + } + + function skewX(a, b, s, q) { + if (a !== b) { + q.push({i: s.push(pop(s) + "skewX(", null, degParen) - 2, x: reinterpolate(a, b)}); + } else if (b) { + s.push(pop(s) + "skewX(" + b + degParen); + } + } + + function scale(xa, ya, xb, yb, s, q) { + if (xa !== xb || ya !== yb) { + var i = s.push(pop(s) + "scale(", null, ",", null, ")"); + q.push({i: i - 4, x: reinterpolate(xa, xb)}, {i: i - 2, x: reinterpolate(ya, yb)}); + } else if (xb !== 1 || yb !== 1) { + s.push(pop(s) + "scale(" + xb + "," + yb + ")"); + } + } + + return function(a, b) { + var s = [], // string constants and placeholders + q = []; // number interpolators + a = parse(a), b = parse(b); + translate(a.translateX, a.translateY, b.translateX, b.translateY, s, q); + rotate(a.rotate, b.rotate, s, q); + skewX(a.skewX, b.skewX, s, q); + scale(a.scaleX, a.scaleY, b.scaleX, b.scaleY, s, q); + a = b = null; // gc + return function(t) { + var i = -1, n = q.length, o; + while (++i < n) s[(o = q[i]).i] = o.x(t); + return s.join(""); + }; + }; +} + +var interpolateTransformCss = interpolateTransform(parseCss, "px, ", "px)", "deg)"); +var interpolateTransformSvg = interpolateTransform(parseSvg, ", ", ")", ")"); + +var rho = Math.SQRT2, + rho2 = 2, + rho4 = 4, + epsilon2 = 1e-12; + +function cosh(x) { + return ((x = Math.exp(x)) + 1 / x) / 2; +} + +function sinh(x) { + return ((x = Math.exp(x)) - 1 / x) / 2; +} + +function tanh(x) { + return ((x = Math.exp(2 * x)) - 1) / (x + 1); +} + +// p0 = [ux0, uy0, w0] +// p1 = [ux1, uy1, w1] +function interpolateZoom(p0, p1) { + var ux0 = p0[0], uy0 = p0[1], w0 = p0[2], + ux1 = p1[0], uy1 = p1[1], w1 = p1[2], + dx = ux1 - ux0, + dy = uy1 - uy0, + d2 = dx * dx + dy * dy, + i, + S; + + // Special case for u0 ≅ u1. + if (d2 < epsilon2) { + S = Math.log(w1 / w0) / rho; + i = function(t) { + return [ + ux0 + t * dx, + uy0 + t * dy, + w0 * Math.exp(rho * t * S) + ]; + }; + } + + // General case. + else { + var d1 = Math.sqrt(d2), + b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2 * w0 * rho2 * d1), + b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2 * w1 * rho2 * d1), + r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0), + r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1); + S = (r1 - r0) / rho; + i = function(t) { + var s = t * S, + coshr0 = cosh(r0), + u = w0 / (rho2 * d1) * (coshr0 * tanh(rho * s + r0) - sinh(r0)); + return [ + ux0 + u * dx, + uy0 + u * dy, + w0 * coshr0 / cosh(rho * s + r0) + ]; + }; + } + + i.duration = S * 1000; + + return i; +} + +function hsl$1(hue$$1) { + return function(start, end) { + var h = hue$$1((start = hsl(start)).h, (end = hsl(end)).h), + s = nogamma(start.s, end.s), + l = nogamma(start.l, end.l), + opacity = nogamma(start.opacity, end.opacity); + return function(t) { + start.h = h(t); + start.s = s(t); + start.l = l(t); + start.opacity = opacity(t); + return start + ""; + }; + } +} + +var hsl$2 = hsl$1(hue); +var hslLong = hsl$1(nogamma); + +function lab$1(start, end) { + var l = nogamma((start = lab(start)).l, (end = lab(end)).l), + a = nogamma(start.a, end.a), + b = nogamma(start.b, end.b), + opacity = nogamma(start.opacity, end.opacity); + return function(t) { + start.l = l(t); + start.a = a(t); + start.b = b(t); + start.opacity = opacity(t); + return start + ""; + }; +} + +function hcl$1(hue$$1) { + return function(start, end) { + var h = hue$$1((start = hcl(start)).h, (end = hcl(end)).h), + c = nogamma(start.c, end.c), + l = nogamma(start.l, end.l), + opacity = nogamma(start.opacity, end.opacity); + return function(t) { + start.h = h(t); + start.c = c(t); + start.l = l(t); + start.opacity = opacity(t); + return start + ""; + }; + } +} + +var hcl$2 = hcl$1(hue); +var hclLong = hcl$1(nogamma); + +function cubehelix$1(hue$$1) { + return (function cubehelixGamma(y) { + y = +y; + + function cubehelix$$1(start, end) { + var h = hue$$1((start = cubehelix(start)).h, (end = cubehelix(end)).h), + s = nogamma(start.s, end.s), + l = nogamma(start.l, end.l), + opacity = nogamma(start.opacity, end.opacity); + return function(t) { + start.h = h(t); + start.s = s(t); + start.l = l(Math.pow(t, y)); + start.opacity = opacity(t); + return start + ""; + }; + } + + cubehelix$$1.gamma = cubehelixGamma; + + return cubehelix$$1; + })(1); +} + +var cubehelix$2 = cubehelix$1(hue); +var cubehelixLong = cubehelix$1(nogamma); + +function quantize(interpolator, n) { + var samples = new Array(n); + for (var i = 0; i < n; ++i) samples[i] = interpolator(i / (n - 1)); + return samples; +} + +var frame = 0, // is an animation frame pending? + timeout = 0, // is a timeout pending? + interval = 0, // are any timers active? + pokeDelay = 1000, // how frequently we check for clock skew + taskHead, + taskTail, + clockLast = 0, + clockNow = 0, + clockSkew = 0, + clock = typeof performance === "object" && performance.now ? performance : Date, + setFrame = typeof window === "object" && window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : function(f) { setTimeout(f, 17); }; + +function now() { + return clockNow || (setFrame(clearNow), clockNow = clock.now() + clockSkew); +} + +function clearNow() { + clockNow = 0; +} + +function Timer() { + this._call = + this._time = + this._next = null; +} + +Timer.prototype = timer.prototype = { + constructor: Timer, + restart: function(callback, delay, time) { + if (typeof callback !== "function") throw new TypeError("callback is not a function"); + time = (time == null ? now() : +time) + (delay == null ? 0 : +delay); + if (!this._next && taskTail !== this) { + if (taskTail) taskTail._next = this; + else taskHead = this; + taskTail = this; + } + this._call = callback; + this._time = time; + sleep(); + }, + stop: function() { + if (this._call) { + this._call = null; + this._time = Infinity; + sleep(); + } + } +}; + +function timer(callback, delay, time) { + var t = new Timer; + t.restart(callback, delay, time); + return t; +} + +function timerFlush() { + now(); // Get the current time, if not already set. + ++frame; // Pretend we’ve set an alarm, if we haven’t already. + var t = taskHead, e; + while (t) { + if ((e = clockNow - t._time) >= 0) t._call.call(null, e); + t = t._next; + } + --frame; +} + +function wake() { + clockNow = (clockLast = clock.now()) + clockSkew; + frame = timeout = 0; + try { + timerFlush(); + } finally { + frame = 0; + nap(); + clockNow = 0; + } +} + +function poke() { + var now = clock.now(), delay = now - clockLast; + if (delay > pokeDelay) clockSkew -= delay, clockLast = now; +} + +function nap() { + var t0, t1 = taskHead, t2, time = Infinity; + while (t1) { + if (t1._call) { + if (time > t1._time) time = t1._time; + t0 = t1, t1 = t1._next; + } else { + t2 = t1._next, t1._next = null; + t1 = t0 ? t0._next = t2 : taskHead = t2; + } + } + taskTail = t0; + sleep(time); +} + +function sleep(time) { + if (frame) return; // Soonest alarm already set, or will be. + if (timeout) timeout = clearTimeout(timeout); + var delay = time - clockNow; // Strictly less than if we recomputed clockNow. + if (delay > 24) { + if (time < Infinity) timeout = setTimeout(wake, time - clock.now() - clockSkew); + if (interval) interval = clearInterval(interval); + } else { + if (!interval) clockLast = clock.now(), interval = setInterval(poke, pokeDelay); + frame = 1, setFrame(wake); + } +} + +function timeout$1(callback, delay, time) { + var t = new Timer; + delay = delay == null ? 0 : +delay; + t.restart(function(elapsed) { + t.stop(); + callback(elapsed + delay); + }, delay, time); + return t; +} + +function interval$1(callback, delay, time) { + var t = new Timer, total = delay; + if (delay == null) return t.restart(callback, delay, time), t; + delay = +delay, time = time == null ? now() : +time; + t.restart(function tick(elapsed) { + elapsed += total; + t.restart(tick, total += delay, time); + callback(elapsed); + }, delay, time); + return t; +} + +var emptyOn = dispatch("start", "end", "interrupt"); +var emptyTween = []; + +var CREATED = 0; +var SCHEDULED = 1; +var STARTING = 2; +var STARTED = 3; +var RUNNING = 4; +var ENDING = 5; +var ENDED = 6; + +function schedule(node, name, id, index, group, timing) { + var schedules = node.__transition; + if (!schedules) node.__transition = {}; + else if (id in schedules) return; + create$1(node, id, { + name: name, + index: index, // For context during callback. + group: group, // For context during callback. + on: emptyOn, + tween: emptyTween, + time: timing.time, + delay: timing.delay, + duration: timing.duration, + ease: timing.ease, + timer: null, + state: CREATED + }); +} + +function init(node, id) { + var schedule = get$1(node, id); + if (schedule.state > CREATED) throw new Error("too late; already scheduled"); + return schedule; +} + +function set$1(node, id) { + var schedule = get$1(node, id); + if (schedule.state > STARTING) throw new Error("too late; already started"); + return schedule; +} + +function get$1(node, id) { + var schedule = node.__transition; + if (!schedule || !(schedule = schedule[id])) throw new Error("transition not found"); + return schedule; +} + +function create$1(node, id, self) { + var schedules = node.__transition, + tween; + + // Initialize the self timer when the transition is created. + // Note the actual delay is not known until the first callback! + schedules[id] = self; + self.timer = timer(schedule, 0, self.time); + + function schedule(elapsed) { + self.state = SCHEDULED; + self.timer.restart(start, self.delay, self.time); + + // If the elapsed delay is less than our first sleep, start immediately. + if (self.delay <= elapsed) start(elapsed - self.delay); + } + + function start(elapsed) { + var i, j, n, o; + + // If the state is not SCHEDULED, then we previously errored on start. + if (self.state !== SCHEDULED) return stop(); + + for (i in schedules) { + o = schedules[i]; + if (o.name !== self.name) continue; + + // While this element already has a starting transition during this frame, + // defer starting an interrupting transition until that transition has a + // chance to tick (and possibly end); see d3/d3-transition#54! + if (o.state === STARTED) return timeout$1(start); + + // Interrupt the active transition, if any. + // Dispatch the interrupt event. + if (o.state === RUNNING) { + o.state = ENDED; + o.timer.stop(); + o.on.call("interrupt", node, node.__data__, o.index, o.group); + delete schedules[i]; + } + + // Cancel any pre-empted transitions. No interrupt event is dispatched + // because the cancelled transitions never started. Note that this also + // removes this transition from the pending list! + else if (+i < id) { + o.state = ENDED; + o.timer.stop(); + delete schedules[i]; + } + } + + // Defer the first tick to end of the current frame; see d3/d3#1576. + // Note the transition may be canceled after start and before the first tick! + // Note this must be scheduled before the start event; see d3/d3-transition#16! + // Assuming this is successful, subsequent callbacks go straight to tick. + timeout$1(function() { + if (self.state === STARTED) { + self.state = RUNNING; + self.timer.restart(tick, self.delay, self.time); + tick(elapsed); + } + }); + + // Dispatch the start event. + // Note this must be done before the tween are initialized. + self.state = STARTING; + self.on.call("start", node, node.__data__, self.index, self.group); + if (self.state !== STARTING) return; // interrupted + self.state = STARTED; + + // Initialize the tween, deleting null tween. + tween = new Array(n = self.tween.length); + for (i = 0, j = -1; i < n; ++i) { + if (o = self.tween[i].value.call(node, node.__data__, self.index, self.group)) { + tween[++j] = o; + } + } + tween.length = j + 1; + } + + function tick(elapsed) { + var t = elapsed < self.duration ? self.ease.call(null, elapsed / self.duration) : (self.timer.restart(stop), self.state = ENDING, 1), + i = -1, + n = tween.length; + + while (++i < n) { + tween[i].call(null, t); + } + + // Dispatch the end event. + if (self.state === ENDING) { + self.on.call("end", node, node.__data__, self.index, self.group); + stop(); + } + } + + function stop() { + self.state = ENDED; + self.timer.stop(); + delete schedules[id]; + for (var i in schedules) return; // eslint-disable-line no-unused-vars + delete node.__transition; + } +} + +function interrupt(node, name) { + var schedules = node.__transition, + schedule$$1, + active, + empty = true, + i; + + if (!schedules) return; + + name = name == null ? null : name + ""; + + for (i in schedules) { + if ((schedule$$1 = schedules[i]).name !== name) { empty = false; continue; } + active = schedule$$1.state > STARTING && schedule$$1.state < ENDING; + schedule$$1.state = ENDED; + schedule$$1.timer.stop(); + if (active) schedule$$1.on.call("interrupt", node, node.__data__, schedule$$1.index, schedule$$1.group); + delete schedules[i]; + } + + if (empty) delete node.__transition; +} + +function selection_interrupt(name) { + return this.each(function() { + interrupt(this, name); + }); +} + +function tweenRemove(id, name) { + var tween0, tween1; + return function() { + var schedule$$1 = set$1(this, id), + tween = schedule$$1.tween; + + // If this node shared tween with the previous node, + // just assign the updated shared tween and we’re done! + // Otherwise, copy-on-write. + if (tween !== tween0) { + tween1 = tween0 = tween; + for (var i = 0, n = tween1.length; i < n; ++i) { + if (tween1[i].name === name) { + tween1 = tween1.slice(); + tween1.splice(i, 1); + break; + } + } + } + + schedule$$1.tween = tween1; + }; +} + +function tweenFunction(id, name, value) { + var tween0, tween1; + if (typeof value !== "function") throw new Error; + return function() { + var schedule$$1 = set$1(this, id), + tween = schedule$$1.tween; + + // If this node shared tween with the previous node, + // just assign the updated shared tween and we’re done! + // Otherwise, copy-on-write. + if (tween !== tween0) { + tween1 = (tween0 = tween).slice(); + for (var t = {name: name, value: value}, i = 0, n = tween1.length; i < n; ++i) { + if (tween1[i].name === name) { + tween1[i] = t; + break; + } + } + if (i === n) tween1.push(t); + } + + schedule$$1.tween = tween1; + }; +} + +function transition_tween(name, value) { + var id = this._id; + + name += ""; + + if (arguments.length < 2) { + var tween = get$1(this.node(), id).tween; + for (var i = 0, n = tween.length, t; i < n; ++i) { + if ((t = tween[i]).name === name) { + return t.value; + } + } + return null; + } + + return this.each((value == null ? tweenRemove : tweenFunction)(id, name, value)); +} + +function tweenValue(transition, name, value) { + var id = transition._id; + + transition.each(function() { + var schedule$$1 = set$1(this, id); + (schedule$$1.value || (schedule$$1.value = {}))[name] = value.apply(this, arguments); + }); + + return function(node) { + return get$1(node, id).value[name]; + }; +} + +function interpolate(a, b) { + var c; + return (typeof b === "number" ? reinterpolate + : b instanceof color ? interpolateRgb + : (c = color(b)) ? (b = c, interpolateRgb) + : interpolateString)(a, b); +} + +function attrRemove$1(name) { + return function() { + this.removeAttribute(name); + }; +} + +function attrRemoveNS$1(fullname) { + return function() { + this.removeAttributeNS(fullname.space, fullname.local); + }; +} + +function attrConstant$1(name, interpolate$$1, value1) { + var value00, + interpolate0; + return function() { + var value0 = this.getAttribute(name); + return value0 === value1 ? null + : value0 === value00 ? interpolate0 + : interpolate0 = interpolate$$1(value00 = value0, value1); + }; +} + +function attrConstantNS$1(fullname, interpolate$$1, value1) { + var value00, + interpolate0; + return function() { + var value0 = this.getAttributeNS(fullname.space, fullname.local); + return value0 === value1 ? null + : value0 === value00 ? interpolate0 + : interpolate0 = interpolate$$1(value00 = value0, value1); + }; +} + +function attrFunction$1(name, interpolate$$1, value) { + var value00, + value10, + interpolate0; + return function() { + var value0, value1 = value(this); + if (value1 == null) return void this.removeAttribute(name); + value0 = this.getAttribute(name); + return value0 === value1 ? null + : value0 === value00 && value1 === value10 ? interpolate0 + : interpolate0 = interpolate$$1(value00 = value0, value10 = value1); + }; +} + +function attrFunctionNS$1(fullname, interpolate$$1, value) { + var value00, + value10, + interpolate0; + return function() { + var value0, value1 = value(this); + if (value1 == null) return void this.removeAttributeNS(fullname.space, fullname.local); + value0 = this.getAttributeNS(fullname.space, fullname.local); + return value0 === value1 ? null + : value0 === value00 && value1 === value10 ? interpolate0 + : interpolate0 = interpolate$$1(value00 = value0, value10 = value1); + }; +} + +function transition_attr(name, value) { + var fullname = namespace(name), i = fullname === "transform" ? interpolateTransformSvg : interpolate; + return this.attrTween(name, typeof value === "function" + ? (fullname.local ? attrFunctionNS$1 : attrFunction$1)(fullname, i, tweenValue(this, "attr." + name, value)) + : value == null ? (fullname.local ? attrRemoveNS$1 : attrRemove$1)(fullname) + : (fullname.local ? attrConstantNS$1 : attrConstant$1)(fullname, i, value + "")); +} + +function attrTweenNS(fullname, value) { + function tween() { + var node = this, i = value.apply(node, arguments); + return i && function(t) { + node.setAttributeNS(fullname.space, fullname.local, i(t)); + }; + } + tween._value = value; + return tween; +} + +function attrTween(name, value) { + function tween() { + var node = this, i = value.apply(node, arguments); + return i && function(t) { + node.setAttribute(name, i(t)); + }; + } + tween._value = value; + return tween; +} + +function transition_attrTween(name, value) { + var key = "attr." + name; + if (arguments.length < 2) return (key = this.tween(key)) && key._value; + if (value == null) return this.tween(key, null); + if (typeof value !== "function") throw new Error; + var fullname = namespace(name); + return this.tween(key, (fullname.local ? attrTweenNS : attrTween)(fullname, value)); +} + +function delayFunction(id, value) { + return function() { + init(this, id).delay = +value.apply(this, arguments); + }; +} + +function delayConstant(id, value) { + return value = +value, function() { + init(this, id).delay = value; + }; +} + +function transition_delay(value) { + var id = this._id; + + return arguments.length + ? this.each((typeof value === "function" + ? delayFunction + : delayConstant)(id, value)) + : get$1(this.node(), id).delay; +} + +function durationFunction(id, value) { + return function() { + set$1(this, id).duration = +value.apply(this, arguments); + }; +} + +function durationConstant(id, value) { + return value = +value, function() { + set$1(this, id).duration = value; + }; +} + +function transition_duration(value) { + var id = this._id; + + return arguments.length + ? this.each((typeof value === "function" + ? durationFunction + : durationConstant)(id, value)) + : get$1(this.node(), id).duration; +} + +function easeConstant(id, value) { + if (typeof value !== "function") throw new Error; + return function() { + set$1(this, id).ease = value; + }; +} + +function transition_ease(value) { + var id = this._id; + + return arguments.length + ? this.each(easeConstant(id, value)) + : get$1(this.node(), id).ease; +} + +function transition_filter(match) { + if (typeof match !== "function") match = matcher$1(match); + + for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) { + for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) { + if ((node = group[i]) && match.call(node, node.__data__, i, group)) { + subgroup.push(node); + } + } + } + + return new Transition(subgroups, this._parents, this._name, this._id); +} + +function transition_merge(transition$$1) { + if (transition$$1._id !== this._id) throw new Error; + + for (var groups0 = this._groups, groups1 = transition$$1._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) { + for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) { + if (node = group0[i] || group1[i]) { + merge[i] = node; + } + } + } + + for (; j < m0; ++j) { + merges[j] = groups0[j]; + } + + return new Transition(merges, this._parents, this._name, this._id); +} + +function start(name) { + return (name + "").trim().split(/^|\s+/).every(function(t) { + var i = t.indexOf("."); + if (i >= 0) t = t.slice(0, i); + return !t || t === "start"; + }); +} + +function onFunction(id, name, listener) { + var on0, on1, sit = start(name) ? init : set$1; + return function() { + var schedule$$1 = sit(this, id), + on = schedule$$1.on; + + // If this node shared a dispatch with the previous node, + // just assign the updated shared dispatch and we’re done! + // Otherwise, copy-on-write. + if (on !== on0) (on1 = (on0 = on).copy()).on(name, listener); + + schedule$$1.on = on1; + }; +} + +function transition_on(name, listener) { + var id = this._id; + + return arguments.length < 2 + ? get$1(this.node(), id).on.on(name) + : this.each(onFunction(id, name, listener)); +} + +function removeFunction(id) { + return function() { + var parent = this.parentNode; + for (var i in this.__transition) if (+i !== id) return; + if (parent) parent.removeChild(this); + }; +} + +function transition_remove() { + return this.on("end.remove", removeFunction(this._id)); +} + +function transition_select(select$$1) { + var name = this._name, + id = this._id; + + if (typeof select$$1 !== "function") select$$1 = selector(select$$1); + + for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) { + for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) { + if ((node = group[i]) && (subnode = select$$1.call(node, node.__data__, i, group))) { + if ("__data__" in node) subnode.__data__ = node.__data__; + subgroup[i] = subnode; + schedule(subgroup[i], name, id, i, subgroup, get$1(node, id)); + } + } + } + + return new Transition(subgroups, this._parents, name, id); +} + +function transition_selectAll(select$$1) { + var name = this._name, + id = this._id; + + if (typeof select$$1 !== "function") select$$1 = selectorAll(select$$1); + + for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) { + for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { + if (node = group[i]) { + for (var children = select$$1.call(node, node.__data__, i, group), child, inherit = get$1(node, id), k = 0, l = children.length; k < l; ++k) { + if (child = children[k]) { + schedule(child, name, id, k, children, inherit); + } + } + subgroups.push(children); + parents.push(node); + } + } + } + + return new Transition(subgroups, parents, name, id); +} + +var Selection$1 = selection.prototype.constructor; + +function transition_selection() { + return new Selection$1(this._groups, this._parents); +} + +function styleRemove$1(name, interpolate$$1) { + var value00, + value10, + interpolate0; + return function() { + var value0 = styleValue(this, name), + value1 = (this.style.removeProperty(name), styleValue(this, name)); + return value0 === value1 ? null + : value0 === value00 && value1 === value10 ? interpolate0 + : interpolate0 = interpolate$$1(value00 = value0, value10 = value1); + }; +} + +function styleRemoveEnd(name) { + return function() { + this.style.removeProperty(name); + }; +} + +function styleConstant$1(name, interpolate$$1, value1) { + var value00, + interpolate0; + return function() { + var value0 = styleValue(this, name); + return value0 === value1 ? null + : value0 === value00 ? interpolate0 + : interpolate0 = interpolate$$1(value00 = value0, value1); + }; +} + +function styleFunction$1(name, interpolate$$1, value) { + var value00, + value10, + interpolate0; + return function() { + var value0 = styleValue(this, name), + value1 = value(this); + if (value1 == null) value1 = (this.style.removeProperty(name), styleValue(this, name)); + return value0 === value1 ? null + : value0 === value00 && value1 === value10 ? interpolate0 + : interpolate0 = interpolate$$1(value00 = value0, value10 = value1); + }; +} + +function transition_style(name, value, priority) { + var i = (name += "") === "transform" ? interpolateTransformCss : interpolate; + return value == null ? this + .styleTween(name, styleRemove$1(name, i)) + .on("end.style." + name, styleRemoveEnd(name)) + : this.styleTween(name, typeof value === "function" + ? styleFunction$1(name, i, tweenValue(this, "style." + name, value)) + : styleConstant$1(name, i, value + ""), priority); +} + +function styleTween(name, value, priority) { + function tween() { + var node = this, i = value.apply(node, arguments); + return i && function(t) { + node.style.setProperty(name, i(t), priority); + }; + } + tween._value = value; + return tween; +} + +function transition_styleTween(name, value, priority) { + var key = "style." + (name += ""); + if (arguments.length < 2) return (key = this.tween(key)) && key._value; + if (value == null) return this.tween(key, null); + if (typeof value !== "function") throw new Error; + return this.tween(key, styleTween(name, value, priority == null ? "" : priority)); +} + +function textConstant$1(value) { + return function() { + this.textContent = value; + }; +} + +function textFunction$1(value) { + return function() { + var value1 = value(this); + this.textContent = value1 == null ? "" : value1; + }; +} + +function transition_text(value) { + return this.tween("text", typeof value === "function" + ? textFunction$1(tweenValue(this, "text", value)) + : textConstant$1(value == null ? "" : value + "")); +} + +function transition_transition() { + var name = this._name, + id0 = this._id, + id1 = newId(); + + for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) { + for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { + if (node = group[i]) { + var inherit = get$1(node, id0); + schedule(node, name, id1, i, group, { + time: inherit.time + inherit.delay + inherit.duration, + delay: 0, + duration: inherit.duration, + ease: inherit.ease + }); + } + } + } + + return new Transition(groups, this._parents, name, id1); +} + +var id = 0; + +function Transition(groups, parents, name, id) { + this._groups = groups; + this._parents = parents; + this._name = name; + this._id = id; +} + +function transition(name) { + return selection().transition(name); +} + +function newId() { + return ++id; +} + +var selection_prototype = selection.prototype; + +Transition.prototype = transition.prototype = { + constructor: Transition, + select: transition_select, + selectAll: transition_selectAll, + filter: transition_filter, + merge: transition_merge, + selection: transition_selection, + transition: transition_transition, + call: selection_prototype.call, + nodes: selection_prototype.nodes, + node: selection_prototype.node, + size: selection_prototype.size, + empty: selection_prototype.empty, + each: selection_prototype.each, + on: transition_on, + attr: transition_attr, + attrTween: transition_attrTween, + style: transition_style, + styleTween: transition_styleTween, + text: transition_text, + remove: transition_remove, + tween: transition_tween, + delay: transition_delay, + duration: transition_duration, + ease: transition_ease +}; + +function linear$1(t) { + return +t; +} + +function quadIn(t) { + return t * t; +} + +function quadOut(t) { + return t * (2 - t); +} + +function quadInOut(t) { + return ((t *= 2) <= 1 ? t * t : --t * (2 - t) + 1) / 2; +} + +function cubicIn(t) { + return t * t * t; +} + +function cubicOut(t) { + return --t * t * t + 1; +} + +function cubicInOut(t) { + return ((t *= 2) <= 1 ? t * t * t : (t -= 2) * t * t + 2) / 2; +} + +var exponent = 3; + +var polyIn = (function custom(e) { + e = +e; + + function polyIn(t) { + return Math.pow(t, e); + } + + polyIn.exponent = custom; + + return polyIn; +})(exponent); + +var polyOut = (function custom(e) { + e = +e; + + function polyOut(t) { + return 1 - Math.pow(1 - t, e); + } + + polyOut.exponent = custom; + + return polyOut; +})(exponent); + +var polyInOut = (function custom(e) { + e = +e; + + function polyInOut(t) { + return ((t *= 2) <= 1 ? Math.pow(t, e) : 2 - Math.pow(2 - t, e)) / 2; + } + + polyInOut.exponent = custom; + + return polyInOut; +})(exponent); + +var pi = Math.PI, + halfPi = pi / 2; + +function sinIn(t) { + return 1 - Math.cos(t * halfPi); +} + +function sinOut(t) { + return Math.sin(t * halfPi); +} + +function sinInOut(t) { + return (1 - Math.cos(pi * t)) / 2; +} + +function expIn(t) { + return Math.pow(2, 10 * t - 10); +} + +function expOut(t) { + return 1 - Math.pow(2, -10 * t); +} + +function expInOut(t) { + return ((t *= 2) <= 1 ? Math.pow(2, 10 * t - 10) : 2 - Math.pow(2, 10 - 10 * t)) / 2; +} + +function circleIn(t) { + return 1 - Math.sqrt(1 - t * t); +} + +function circleOut(t) { + return Math.sqrt(1 - --t * t); +} + +function circleInOut(t) { + return ((t *= 2) <= 1 ? 1 - Math.sqrt(1 - t * t) : Math.sqrt(1 - (t -= 2) * t) + 1) / 2; +} + +var b1 = 4 / 11, + b2 = 6 / 11, + b3 = 8 / 11, + b4 = 3 / 4, + b5 = 9 / 11, + b6 = 10 / 11, + b7 = 15 / 16, + b8 = 21 / 22, + b9 = 63 / 64, + b0 = 1 / b1 / b1; + +function bounceIn(t) { + return 1 - bounceOut(1 - t); +} + +function bounceOut(t) { + return (t = +t) < b1 ? b0 * t * t : t < b3 ? b0 * (t -= b2) * t + b4 : t < b6 ? b0 * (t -= b5) * t + b7 : b0 * (t -= b8) * t + b9; +} + +function bounceInOut(t) { + return ((t *= 2) <= 1 ? 1 - bounceOut(1 - t) : bounceOut(t - 1) + 1) / 2; +} + +var overshoot = 1.70158; + +var backIn = (function custom(s) { + s = +s; + + function backIn(t) { + return t * t * ((s + 1) * t - s); + } + + backIn.overshoot = custom; + + return backIn; +})(overshoot); + +var backOut = (function custom(s) { + s = +s; + + function backOut(t) { + return --t * t * ((s + 1) * t + s) + 1; + } + + backOut.overshoot = custom; + + return backOut; +})(overshoot); + +var backInOut = (function custom(s) { + s = +s; + + function backInOut(t) { + return ((t *= 2) < 1 ? t * t * ((s + 1) * t - s) : (t -= 2) * t * ((s + 1) * t + s) + 2) / 2; + } + + backInOut.overshoot = custom; + + return backInOut; +})(overshoot); + +var tau = 2 * Math.PI, + amplitude = 1, + period = 0.3; + +var elasticIn = (function custom(a, p) { + var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau); + + function elasticIn(t) { + return a * Math.pow(2, 10 * --t) * Math.sin((s - t) / p); + } + + elasticIn.amplitude = function(a) { return custom(a, p * tau); }; + elasticIn.period = function(p) { return custom(a, p); }; + + return elasticIn; +})(amplitude, period); + +var elasticOut = (function custom(a, p) { + var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau); + + function elasticOut(t) { + return 1 - a * Math.pow(2, -10 * (t = +t)) * Math.sin((t + s) / p); + } + + elasticOut.amplitude = function(a) { return custom(a, p * tau); }; + elasticOut.period = function(p) { return custom(a, p); }; + + return elasticOut; +})(amplitude, period); + +var elasticInOut = (function custom(a, p) { + var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau); + + function elasticInOut(t) { + return ((t = t * 2 - 1) < 0 + ? a * Math.pow(2, 10 * t) * Math.sin((s - t) / p) + : 2 - a * Math.pow(2, -10 * t) * Math.sin((s + t) / p)) / 2; + } + + elasticInOut.amplitude = function(a) { return custom(a, p * tau); }; + elasticInOut.period = function(p) { return custom(a, p); }; + + return elasticInOut; +})(amplitude, period); + +var defaultTiming = { + time: null, // Set on use. + delay: 0, + duration: 250, + ease: cubicInOut +}; + +function inherit(node, id) { + var timing; + while (!(timing = node.__transition) || !(timing = timing[id])) { + if (!(node = node.parentNode)) { + return defaultTiming.time = now(), defaultTiming; + } + } + return timing; +} + +function selection_transition(name) { + var id, + timing; + + if (name instanceof Transition) { + id = name._id, name = name._name; + } else { + id = newId(), (timing = defaultTiming).time = now(), name = name == null ? null : name + ""; + } + + for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) { + for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { + if (node = group[i]) { + schedule(node, name, id, i, group, timing || inherit(node, id)); + } + } + } + + return new Transition(groups, this._parents, name, id); +} + +selection.prototype.interrupt = selection_interrupt; +selection.prototype.transition = selection_transition; + +var root$1 = [null]; + +function active(node, name) { + var schedules = node.__transition, + schedule$$1, + i; + + if (schedules) { + name = name == null ? null : name + ""; + for (i in schedules) { + if ((schedule$$1 = schedules[i]).state > SCHEDULED && schedule$$1.name === name) { + return new Transition([[node]], root$1, name, +i); + } + } + } + + return null; +} + +function constant$4(x) { + return function() { + return x; + }; +} + +function BrushEvent(target, type, selection) { + this.target = target; + this.type = type; + this.selection = selection; +} + +function nopropagation$1() { + exports.event.stopImmediatePropagation(); +} + +function noevent$1() { + exports.event.preventDefault(); + exports.event.stopImmediatePropagation(); +} + +var MODE_DRAG = {name: "drag"}, + MODE_SPACE = {name: "space"}, + MODE_HANDLE = {name: "handle"}, + MODE_CENTER = {name: "center"}; + +var X = { + name: "x", + handles: ["e", "w"].map(type), + input: function(x, e) { return x && [[x[0], e[0][1]], [x[1], e[1][1]]]; }, + output: function(xy) { return xy && [xy[0][0], xy[1][0]]; } +}; + +var Y = { + name: "y", + handles: ["n", "s"].map(type), + input: function(y, e) { return y && [[e[0][0], y[0]], [e[1][0], y[1]]]; }, + output: function(xy) { return xy && [xy[0][1], xy[1][1]]; } +}; + +var XY = { + name: "xy", + handles: ["n", "e", "s", "w", "nw", "ne", "se", "sw"].map(type), + input: function(xy) { return xy; }, + output: function(xy) { return xy; } +}; + +var cursors = { + overlay: "crosshair", + selection: "move", + n: "ns-resize", + e: "ew-resize", + s: "ns-resize", + w: "ew-resize", + nw: "nwse-resize", + ne: "nesw-resize", + se: "nwse-resize", + sw: "nesw-resize" +}; + +var flipX = { + e: "w", + w: "e", + nw: "ne", + ne: "nw", + se: "sw", + sw: "se" +}; + +var flipY = { + n: "s", + s: "n", + nw: "sw", + ne: "se", + se: "ne", + sw: "nw" +}; + +var signsX = { + overlay: +1, + selection: +1, + n: null, + e: +1, + s: null, + w: -1, + nw: -1, + ne: +1, + se: +1, + sw: -1 +}; + +var signsY = { + overlay: +1, + selection: +1, + n: -1, + e: null, + s: +1, + w: null, + nw: -1, + ne: -1, + se: +1, + sw: +1 +}; + +function type(t) { + return {type: t}; +} + +// Ignore right-click, since that should open the context menu. +function defaultFilter$1() { + return !exports.event.button; +} + +function defaultExtent() { + var svg = this.ownerSVGElement || this; + return [[0, 0], [svg.width.baseVal.value, svg.height.baseVal.value]]; +} + +// Like d3.local, but with the name “__brush” rather than auto-generated. +function local$1(node) { + while (!node.__brush) if (!(node = node.parentNode)) return; + return node.__brush; +} + +function empty$1(extent) { + return extent[0][0] === extent[1][0] + || extent[0][1] === extent[1][1]; +} + +function brushSelection(node) { + var state = node.__brush; + return state ? state.dim.output(state.selection) : null; +} + +function brushX() { + return brush$1(X); +} + +function brushY() { + return brush$1(Y); +} + +function brush() { + return brush$1(XY); +} + +function brush$1(dim) { + var extent = defaultExtent, + filter = defaultFilter$1, + listeners = dispatch(brush, "start", "brush", "end"), + handleSize = 6, + touchending; + + function brush(group) { + var overlay = group + .property("__brush", initialize) + .selectAll(".overlay") + .data([type("overlay")]); + + overlay.enter().append("rect") + .attr("class", "overlay") + .attr("pointer-events", "all") + .attr("cursor", cursors.overlay) + .merge(overlay) + .each(function() { + var extent = local$1(this).extent; + select(this) + .attr("x", extent[0][0]) + .attr("y", extent[0][1]) + .attr("width", extent[1][0] - extent[0][0]) + .attr("height", extent[1][1] - extent[0][1]); + }); + + group.selectAll(".selection") + .data([type("selection")]) + .enter().append("rect") + .attr("class", "selection") + .attr("cursor", cursors.selection) + .attr("fill", "#777") + .attr("fill-opacity", 0.3) + .attr("stroke", "#fff") + .attr("shape-rendering", "crispEdges"); + + var handle = group.selectAll(".handle") + .data(dim.handles, function(d) { return d.type; }); + + handle.exit().remove(); + + handle.enter().append("rect") + .attr("class", function(d) { return "handle handle--" + d.type; }) + .attr("cursor", function(d) { return cursors[d.type]; }); + + group + .each(redraw) + .attr("fill", "none") + .attr("pointer-events", "all") + .style("-webkit-tap-highlight-color", "rgba(0,0,0,0)") + .on("mousedown.brush touchstart.brush", started); + } + + brush.move = function(group, selection$$1) { + if (group.selection) { + group + .on("start.brush", function() { emitter(this, arguments).beforestart().start(); }) + .on("interrupt.brush end.brush", function() { emitter(this, arguments).end(); }) + .tween("brush", function() { + var that = this, + state = that.__brush, + emit = emitter(that, arguments), + selection0 = state.selection, + selection1 = dim.input(typeof selection$$1 === "function" ? selection$$1.apply(this, arguments) : selection$$1, state.extent), + i = interpolateValue(selection0, selection1); + + function tween(t) { + state.selection = t === 1 && empty$1(selection1) ? null : i(t); + redraw.call(that); + emit.brush(); + } + + return selection0 && selection1 ? tween : tween(1); + }); + } else { + group + .each(function() { + var that = this, + args = arguments, + state = that.__brush, + selection1 = dim.input(typeof selection$$1 === "function" ? selection$$1.apply(that, args) : selection$$1, state.extent), + emit = emitter(that, args).beforestart(); + + interrupt(that); + state.selection = selection1 == null || empty$1(selection1) ? null : selection1; + redraw.call(that); + emit.start().brush().end(); + }); + } + }; + + function redraw() { + var group = select(this), + selection$$1 = local$1(this).selection; + + if (selection$$1) { + group.selectAll(".selection") + .style("display", null) + .attr("x", selection$$1[0][0]) + .attr("y", selection$$1[0][1]) + .attr("width", selection$$1[1][0] - selection$$1[0][0]) + .attr("height", selection$$1[1][1] - selection$$1[0][1]); + + group.selectAll(".handle") + .style("display", null) + .attr("x", function(d) { return d.type[d.type.length - 1] === "e" ? selection$$1[1][0] - handleSize / 2 : selection$$1[0][0] - handleSize / 2; }) + .attr("y", function(d) { return d.type[0] === "s" ? selection$$1[1][1] - handleSize / 2 : selection$$1[0][1] - handleSize / 2; }) + .attr("width", function(d) { return d.type === "n" || d.type === "s" ? selection$$1[1][0] - selection$$1[0][0] + handleSize : handleSize; }) + .attr("height", function(d) { return d.type === "e" || d.type === "w" ? selection$$1[1][1] - selection$$1[0][1] + handleSize : handleSize; }); + } + + else { + group.selectAll(".selection,.handle") + .style("display", "none") + .attr("x", null) + .attr("y", null) + .attr("width", null) + .attr("height", null); + } + } + + function emitter(that, args) { + return that.__brush.emitter || new Emitter(that, args); + } + + function Emitter(that, args) { + this.that = that; + this.args = args; + this.state = that.__brush; + this.active = 0; + } + + Emitter.prototype = { + beforestart: function() { + if (++this.active === 1) this.state.emitter = this, this.starting = true; + return this; + }, + start: function() { + if (this.starting) this.starting = false, this.emit("start"); + return this; + }, + brush: function() { + this.emit("brush"); + return this; + }, + end: function() { + if (--this.active === 0) delete this.state.emitter, this.emit("end"); + return this; + }, + emit: function(type) { + customEvent(new BrushEvent(brush, type, dim.output(this.state.selection)), listeners.apply, listeners, [type, this.that, this.args]); + } + }; + + function started() { + if (exports.event.touches) { if (exports.event.changedTouches.length < exports.event.touches.length) return noevent$1(); } + else if (touchending) return; + if (!filter.apply(this, arguments)) return; + + var that = this, + type = exports.event.target.__data__.type, + mode = (exports.event.metaKey ? type = "overlay" : type) === "selection" ? MODE_DRAG : (exports.event.altKey ? MODE_CENTER : MODE_HANDLE), + signX = dim === Y ? null : signsX[type], + signY = dim === X ? null : signsY[type], + state = local$1(that), + extent = state.extent, + selection$$1 = state.selection, + W = extent[0][0], w0, w1, + N = extent[0][1], n0, n1, + E = extent[1][0], e0, e1, + S = extent[1][1], s0, s1, + dx, + dy, + moving, + shifting = signX && signY && exports.event.shiftKey, + lockX, + lockY, + point0 = mouse(that), + point$$1 = point0, + emit = emitter(that, arguments).beforestart(); + + if (type === "overlay") { + state.selection = selection$$1 = [ + [w0 = dim === Y ? W : point0[0], n0 = dim === X ? N : point0[1]], + [e0 = dim === Y ? E : w0, s0 = dim === X ? S : n0] + ]; + } else { + w0 = selection$$1[0][0]; + n0 = selection$$1[0][1]; + e0 = selection$$1[1][0]; + s0 = selection$$1[1][1]; + } + + w1 = w0; + n1 = n0; + e1 = e0; + s1 = s0; + + var group = select(that) + .attr("pointer-events", "none"); + + var overlay = group.selectAll(".overlay") + .attr("cursor", cursors[type]); + + if (exports.event.touches) { + group + .on("touchmove.brush", moved, true) + .on("touchend.brush touchcancel.brush", ended, true); + } else { + var view = select(exports.event.view) + .on("keydown.brush", keydowned, true) + .on("keyup.brush", keyupped, true) + .on("mousemove.brush", moved, true) + .on("mouseup.brush", ended, true); + + dragDisable(exports.event.view); + } + + nopropagation$1(); + interrupt(that); + redraw.call(that); + emit.start(); + + function moved() { + var point1 = mouse(that); + if (shifting && !lockX && !lockY) { + if (Math.abs(point1[0] - point$$1[0]) > Math.abs(point1[1] - point$$1[1])) lockY = true; + else lockX = true; + } + point$$1 = point1; + moving = true; + noevent$1(); + move(); + } + + function move() { + var t; + + dx = point$$1[0] - point0[0]; + dy = point$$1[1] - point0[1]; + + switch (mode) { + case MODE_SPACE: + case MODE_DRAG: { + if (signX) dx = Math.max(W - w0, Math.min(E - e0, dx)), w1 = w0 + dx, e1 = e0 + dx; + if (signY) dy = Math.max(N - n0, Math.min(S - s0, dy)), n1 = n0 + dy, s1 = s0 + dy; + break; + } + case MODE_HANDLE: { + if (signX < 0) dx = Math.max(W - w0, Math.min(E - w0, dx)), w1 = w0 + dx, e1 = e0; + else if (signX > 0) dx = Math.max(W - e0, Math.min(E - e0, dx)), w1 = w0, e1 = e0 + dx; + if (signY < 0) dy = Math.max(N - n0, Math.min(S - n0, dy)), n1 = n0 + dy, s1 = s0; + else if (signY > 0) dy = Math.max(N - s0, Math.min(S - s0, dy)), n1 = n0, s1 = s0 + dy; + break; + } + case MODE_CENTER: { + if (signX) w1 = Math.max(W, Math.min(E, w0 - dx * signX)), e1 = Math.max(W, Math.min(E, e0 + dx * signX)); + if (signY) n1 = Math.max(N, Math.min(S, n0 - dy * signY)), s1 = Math.max(N, Math.min(S, s0 + dy * signY)); + break; + } + } + + if (e1 < w1) { + signX *= -1; + t = w0, w0 = e0, e0 = t; + t = w1, w1 = e1, e1 = t; + if (type in flipX) overlay.attr("cursor", cursors[type = flipX[type]]); + } + + if (s1 < n1) { + signY *= -1; + t = n0, n0 = s0, s0 = t; + t = n1, n1 = s1, s1 = t; + if (type in flipY) overlay.attr("cursor", cursors[type = flipY[type]]); + } + + if (state.selection) selection$$1 = state.selection; // May be set by brush.move! + if (lockX) w1 = selection$$1[0][0], e1 = selection$$1[1][0]; + if (lockY) n1 = selection$$1[0][1], s1 = selection$$1[1][1]; + + if (selection$$1[0][0] !== w1 + || selection$$1[0][1] !== n1 + || selection$$1[1][0] !== e1 + || selection$$1[1][1] !== s1) { + state.selection = [[w1, n1], [e1, s1]]; + redraw.call(that); + emit.brush(); + } + } + + function ended() { + nopropagation$1(); + if (exports.event.touches) { + if (exports.event.touches.length) return; + if (touchending) clearTimeout(touchending); + touchending = setTimeout(function() { touchending = null; }, 500); // Ghost clicks are delayed! + group.on("touchmove.brush touchend.brush touchcancel.brush", null); + } else { + yesdrag(exports.event.view, moving); + view.on("keydown.brush keyup.brush mousemove.brush mouseup.brush", null); + } + group.attr("pointer-events", "all"); + overlay.attr("cursor", cursors.overlay); + if (state.selection) selection$$1 = state.selection; // May be set by brush.move (on start)! + if (empty$1(selection$$1)) state.selection = null, redraw.call(that); + emit.end(); + } + + function keydowned() { + switch (exports.event.keyCode) { + case 16: { // SHIFT + shifting = signX && signY; + break; + } + case 18: { // ALT + if (mode === MODE_HANDLE) { + if (signX) e0 = e1 - dx * signX, w0 = w1 + dx * signX; + if (signY) s0 = s1 - dy * signY, n0 = n1 + dy * signY; + mode = MODE_CENTER; + move(); + } + break; + } + case 32: { // SPACE; takes priority over ALT + if (mode === MODE_HANDLE || mode === MODE_CENTER) { + if (signX < 0) e0 = e1 - dx; else if (signX > 0) w0 = w1 - dx; + if (signY < 0) s0 = s1 - dy; else if (signY > 0) n0 = n1 - dy; + mode = MODE_SPACE; + overlay.attr("cursor", cursors.selection); + move(); + } + break; + } + default: return; + } + noevent$1(); + } + + function keyupped() { + switch (exports.event.keyCode) { + case 16: { // SHIFT + if (shifting) { + lockX = lockY = shifting = false; + move(); + } + break; + } + case 18: { // ALT + if (mode === MODE_CENTER) { + if (signX < 0) e0 = e1; else if (signX > 0) w0 = w1; + if (signY < 0) s0 = s1; else if (signY > 0) n0 = n1; + mode = MODE_HANDLE; + move(); + } + break; + } + case 32: { // SPACE + if (mode === MODE_SPACE) { + if (exports.event.altKey) { + if (signX) e0 = e1 - dx * signX, w0 = w1 + dx * signX; + if (signY) s0 = s1 - dy * signY, n0 = n1 + dy * signY; + mode = MODE_CENTER; + } else { + if (signX < 0) e0 = e1; else if (signX > 0) w0 = w1; + if (signY < 0) s0 = s1; else if (signY > 0) n0 = n1; + mode = MODE_HANDLE; + } + overlay.attr("cursor", cursors[type]); + move(); + } + break; + } + default: return; + } + noevent$1(); + } + } + + function initialize() { + var state = this.__brush || {selection: null}; + state.extent = extent.apply(this, arguments); + state.dim = dim; + return state; + } + + brush.extent = function(_) { + return arguments.length ? (extent = typeof _ === "function" ? _ : constant$4([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), brush) : extent; + }; + + brush.filter = function(_) { + return arguments.length ? (filter = typeof _ === "function" ? _ : constant$4(!!_), brush) : filter; + }; + + brush.handleSize = function(_) { + return arguments.length ? (handleSize = +_, brush) : handleSize; + }; + + brush.on = function() { + var value = listeners.on.apply(listeners, arguments); + return value === listeners ? brush : value; + }; + + return brush; +} + +var cos = Math.cos; +var sin = Math.sin; +var pi$1 = Math.PI; +var halfPi$1 = pi$1 / 2; +var tau$1 = pi$1 * 2; +var max$1 = Math.max; + +function compareValue(compare) { + return function(a, b) { + return compare( + a.source.value + a.target.value, + b.source.value + b.target.value + ); + }; +} + +function chord() { + var padAngle = 0, + sortGroups = null, + sortSubgroups = null, + sortChords = null; + + function chord(matrix) { + var n = matrix.length, + groupSums = [], + groupIndex = sequence(n), + subgroupIndex = [], + chords = [], + groups = chords.groups = new Array(n), + subgroups = new Array(n * n), + k, + x, + x0, + dx, + i, + j; + + // Compute the sum. + k = 0, i = -1; while (++i < n) { + x = 0, j = -1; while (++j < n) { + x += matrix[i][j]; + } + groupSums.push(x); + subgroupIndex.push(sequence(n)); + k += x; + } + + // Sort groups… + if (sortGroups) groupIndex.sort(function(a, b) { + return sortGroups(groupSums[a], groupSums[b]); + }); + + // Sort subgroups… + if (sortSubgroups) subgroupIndex.forEach(function(d, i) { + d.sort(function(a, b) { + return sortSubgroups(matrix[i][a], matrix[i][b]); + }); + }); + + // Convert the sum to scaling factor for [0, 2pi]. + // TODO Allow start and end angle to be specified? + // TODO Allow padding to be specified as percentage? + k = max$1(0, tau$1 - padAngle * n) / k; + dx = k ? padAngle : tau$1 / n; + + // Compute the start and end angle for each group and subgroup. + // Note: Opera has a bug reordering object literal properties! + x = 0, i = -1; while (++i < n) { + x0 = x, j = -1; while (++j < n) { + var di = groupIndex[i], + dj = subgroupIndex[di][j], + v = matrix[di][dj], + a0 = x, + a1 = x += v * k; + subgroups[dj * n + di] = { + index: di, + subindex: dj, + startAngle: a0, + endAngle: a1, + value: v + }; + } + groups[di] = { + index: di, + startAngle: x0, + endAngle: x, + value: groupSums[di] + }; + x += dx; + } + + // Generate chords for each (non-empty) subgroup-subgroup link. + i = -1; while (++i < n) { + j = i - 1; while (++j < n) { + var source = subgroups[j * n + i], + target = subgroups[i * n + j]; + if (source.value || target.value) { + chords.push(source.value < target.value + ? {source: target, target: source} + : {source: source, target: target}); + } + } + } + + return sortChords ? chords.sort(sortChords) : chords; + } + + chord.padAngle = function(_) { + return arguments.length ? (padAngle = max$1(0, _), chord) : padAngle; + }; + + chord.sortGroups = function(_) { + return arguments.length ? (sortGroups = _, chord) : sortGroups; + }; + + chord.sortSubgroups = function(_) { + return arguments.length ? (sortSubgroups = _, chord) : sortSubgroups; + }; + + chord.sortChords = function(_) { + return arguments.length ? (_ == null ? sortChords = null : (sortChords = compareValue(_))._ = _, chord) : sortChords && sortChords._; + }; + + return chord; +} + +var slice$2 = Array.prototype.slice; + +function constant$5(x) { + return function() { + return x; + }; +} + +var pi$2 = Math.PI, + tau$2 = 2 * pi$2, + epsilon$1 = 1e-6, + tauEpsilon = tau$2 - epsilon$1; + +function Path() { + this._x0 = this._y0 = // start of current subpath + this._x1 = this._y1 = null; // end of current subpath + this._ = ""; +} + +function path() { + return new Path; +} + +Path.prototype = path.prototype = { + constructor: Path, + moveTo: function(x, y) { + this._ += "M" + (this._x0 = this._x1 = +x) + "," + (this._y0 = this._y1 = +y); + }, + closePath: function() { + if (this._x1 !== null) { + this._x1 = this._x0, this._y1 = this._y0; + this._ += "Z"; + } + }, + lineTo: function(x, y) { + this._ += "L" + (this._x1 = +x) + "," + (this._y1 = +y); + }, + quadraticCurveTo: function(x1, y1, x, y) { + this._ += "Q" + (+x1) + "," + (+y1) + "," + (this._x1 = +x) + "," + (this._y1 = +y); + }, + bezierCurveTo: function(x1, y1, x2, y2, x, y) { + this._ += "C" + (+x1) + "," + (+y1) + "," + (+x2) + "," + (+y2) + "," + (this._x1 = +x) + "," + (this._y1 = +y); + }, + arcTo: function(x1, y1, x2, y2, r) { + x1 = +x1, y1 = +y1, x2 = +x2, y2 = +y2, r = +r; + var x0 = this._x1, + y0 = this._y1, + x21 = x2 - x1, + y21 = y2 - y1, + x01 = x0 - x1, + y01 = y0 - y1, + l01_2 = x01 * x01 + y01 * y01; + + // Is the radius negative? Error. + if (r < 0) throw new Error("negative radius: " + r); + + // Is this path empty? Move to (x1,y1). + if (this._x1 === null) { + this._ += "M" + (this._x1 = x1) + "," + (this._y1 = y1); + } + + // Or, is (x1,y1) coincident with (x0,y0)? Do nothing. + else if (!(l01_2 > epsilon$1)) {} + + // Or, are (x0,y0), (x1,y1) and (x2,y2) collinear? + // Equivalently, is (x1,y1) coincident with (x2,y2)? + // Or, is the radius zero? Line to (x1,y1). + else if (!(Math.abs(y01 * x21 - y21 * x01) > epsilon$1) || !r) { + this._ += "L" + (this._x1 = x1) + "," + (this._y1 = y1); + } + + // Otherwise, draw an arc! + else { + var x20 = x2 - x0, + y20 = y2 - y0, + l21_2 = x21 * x21 + y21 * y21, + l20_2 = x20 * x20 + y20 * y20, + l21 = Math.sqrt(l21_2), + l01 = Math.sqrt(l01_2), + l = r * Math.tan((pi$2 - Math.acos((l21_2 + l01_2 - l20_2) / (2 * l21 * l01))) / 2), + t01 = l / l01, + t21 = l / l21; + + // If the start tangent is not coincident with (x0,y0), line to. + if (Math.abs(t01 - 1) > epsilon$1) { + this._ += "L" + (x1 + t01 * x01) + "," + (y1 + t01 * y01); + } + + this._ += "A" + r + "," + r + ",0,0," + (+(y01 * x20 > x01 * y20)) + "," + (this._x1 = x1 + t21 * x21) + "," + (this._y1 = y1 + t21 * y21); + } + }, + arc: function(x, y, r, a0, a1, ccw) { + x = +x, y = +y, r = +r; + var dx = r * Math.cos(a0), + dy = r * Math.sin(a0), + x0 = x + dx, + y0 = y + dy, + cw = 1 ^ ccw, + da = ccw ? a0 - a1 : a1 - a0; + + // Is the radius negative? Error. + if (r < 0) throw new Error("negative radius: " + r); + + // Is this path empty? Move to (x0,y0). + if (this._x1 === null) { + this._ += "M" + x0 + "," + y0; + } + + // Or, is (x0,y0) not coincident with the previous point? Line to (x0,y0). + else if (Math.abs(this._x1 - x0) > epsilon$1 || Math.abs(this._y1 - y0) > epsilon$1) { + this._ += "L" + x0 + "," + y0; + } + + // Is this arc empty? We’re done. + if (!r) return; + + // Does the angle go the wrong way? Flip the direction. + if (da < 0) da = da % tau$2 + tau$2; + + // Is this a complete circle? Draw two arcs to complete the circle. + if (da > tauEpsilon) { + this._ += "A" + r + "," + r + ",0,1," + cw + "," + (x - dx) + "," + (y - dy) + "A" + r + "," + r + ",0,1," + cw + "," + (this._x1 = x0) + "," + (this._y1 = y0); + } + + // Is this arc non-empty? Draw an arc! + else if (da > epsilon$1) { + this._ += "A" + r + "," + r + ",0," + (+(da >= pi$2)) + "," + cw + "," + (this._x1 = x + r * Math.cos(a1)) + "," + (this._y1 = y + r * Math.sin(a1)); + } + }, + rect: function(x, y, w, h) { + this._ += "M" + (this._x0 = this._x1 = +x) + "," + (this._y0 = this._y1 = +y) + "h" + (+w) + "v" + (+h) + "h" + (-w) + "Z"; + }, + toString: function() { + return this._; + } +}; + +function defaultSource(d) { + return d.source; +} + +function defaultTarget(d) { + return d.target; +} + +function defaultRadius(d) { + return d.radius; +} + +function defaultStartAngle(d) { + return d.startAngle; +} + +function defaultEndAngle(d) { + return d.endAngle; +} + +function ribbon() { + var source = defaultSource, + target = defaultTarget, + radius = defaultRadius, + startAngle = defaultStartAngle, + endAngle = defaultEndAngle, + context = null; + + function ribbon() { + var buffer, + argv = slice$2.call(arguments), + s = source.apply(this, argv), + t = target.apply(this, argv), + sr = +radius.apply(this, (argv[0] = s, argv)), + sa0 = startAngle.apply(this, argv) - halfPi$1, + sa1 = endAngle.apply(this, argv) - halfPi$1, + sx0 = sr * cos(sa0), + sy0 = sr * sin(sa0), + tr = +radius.apply(this, (argv[0] = t, argv)), + ta0 = startAngle.apply(this, argv) - halfPi$1, + ta1 = endAngle.apply(this, argv) - halfPi$1; + + if (!context) context = buffer = path(); + + context.moveTo(sx0, sy0); + context.arc(0, 0, sr, sa0, sa1); + if (sa0 !== ta0 || sa1 !== ta1) { // TODO sr !== tr? + context.quadraticCurveTo(0, 0, tr * cos(ta0), tr * sin(ta0)); + context.arc(0, 0, tr, ta0, ta1); + } + context.quadraticCurveTo(0, 0, sx0, sy0); + context.closePath(); + + if (buffer) return context = null, buffer + "" || null; + } + + ribbon.radius = function(_) { + return arguments.length ? (radius = typeof _ === "function" ? _ : constant$5(+_), ribbon) : radius; + }; + + ribbon.startAngle = function(_) { + return arguments.length ? (startAngle = typeof _ === "function" ? _ : constant$5(+_), ribbon) : startAngle; + }; + + ribbon.endAngle = function(_) { + return arguments.length ? (endAngle = typeof _ === "function" ? _ : constant$5(+_), ribbon) : endAngle; + }; + + ribbon.source = function(_) { + return arguments.length ? (source = _, ribbon) : source; + }; + + ribbon.target = function(_) { + return arguments.length ? (target = _, ribbon) : target; + }; + + ribbon.context = function(_) { + return arguments.length ? (context = _ == null ? null : _, ribbon) : context; + }; + + return ribbon; +} + +var prefix = "$"; + +function Map() {} + +Map.prototype = map$1.prototype = { + constructor: Map, + has: function(key) { + return (prefix + key) in this; + }, + get: function(key) { + return this[prefix + key]; + }, + set: function(key, value) { + this[prefix + key] = value; + return this; + }, + remove: function(key) { + var property = prefix + key; + return property in this && delete this[property]; + }, + clear: function() { + for (var property in this) if (property[0] === prefix) delete this[property]; + }, + keys: function() { + var keys = []; + for (var property in this) if (property[0] === prefix) keys.push(property.slice(1)); + return keys; + }, + values: function() { + var values = []; + for (var property in this) if (property[0] === prefix) values.push(this[property]); + return values; + }, + entries: function() { + var entries = []; + for (var property in this) if (property[0] === prefix) entries.push({key: property.slice(1), value: this[property]}); + return entries; + }, + size: function() { + var size = 0; + for (var property in this) if (property[0] === prefix) ++size; + return size; + }, + empty: function() { + for (var property in this) if (property[0] === prefix) return false; + return true; + }, + each: function(f) { + for (var property in this) if (property[0] === prefix) f(this[property], property.slice(1), this); + } +}; + +function map$1(object, f) { + var map = new Map; + + // Copy constructor. + if (object instanceof Map) object.each(function(value, key) { map.set(key, value); }); + + // Index array by numeric index or specified key function. + else if (Array.isArray(object)) { + var i = -1, + n = object.length, + o; + + if (f == null) while (++i < n) map.set(i, object[i]); + else while (++i < n) map.set(f(o = object[i], i, object), o); + } + + // Convert object to map. + else if (object) for (var key in object) map.set(key, object[key]); + + return map; +} + +function nest() { + var keys = [], + sortKeys = [], + sortValues, + rollup, + nest; + + function apply(array, depth, createResult, setResult) { + if (depth >= keys.length) { + if (sortValues != null) array.sort(sortValues); + return rollup != null ? rollup(array) : array; + } + + var i = -1, + n = array.length, + key = keys[depth++], + keyValue, + value, + valuesByKey = map$1(), + values, + result = createResult(); + + while (++i < n) { + if (values = valuesByKey.get(keyValue = key(value = array[i]) + "")) { + values.push(value); + } else { + valuesByKey.set(keyValue, [value]); + } + } + + valuesByKey.each(function(values, key) { + setResult(result, key, apply(values, depth, createResult, setResult)); + }); + + return result; + } + + function entries(map, depth) { + if (++depth > keys.length) return map; + var array, sortKey = sortKeys[depth - 1]; + if (rollup != null && depth >= keys.length) array = map.entries(); + else array = [], map.each(function(v, k) { array.push({key: k, values: entries(v, depth)}); }); + return sortKey != null ? array.sort(function(a, b) { return sortKey(a.key, b.key); }) : array; + } + + return nest = { + object: function(array) { return apply(array, 0, createObject, setObject); }, + map: function(array) { return apply(array, 0, createMap, setMap); }, + entries: function(array) { return entries(apply(array, 0, createMap, setMap), 0); }, + key: function(d) { keys.push(d); return nest; }, + sortKeys: function(order) { sortKeys[keys.length - 1] = order; return nest; }, + sortValues: function(order) { sortValues = order; return nest; }, + rollup: function(f) { rollup = f; return nest; } + }; +} + +function createObject() { + return {}; +} + +function setObject(object, key, value) { + object[key] = value; +} + +function createMap() { + return map$1(); +} + +function setMap(map, key, value) { + map.set(key, value); +} + +function Set() {} + +var proto = map$1.prototype; + +Set.prototype = set$2.prototype = { + constructor: Set, + has: proto.has, + add: function(value) { + value += ""; + this[prefix + value] = value; + return this; + }, + remove: proto.remove, + clear: proto.clear, + values: proto.keys, + size: proto.size, + empty: proto.empty, + each: proto.each +}; + +function set$2(object, f) { + var set = new Set; + + // Copy constructor. + if (object instanceof Set) object.each(function(value) { set.add(value); }); + + // Otherwise, assume it’s an array. + else if (object) { + var i = -1, n = object.length; + if (f == null) while (++i < n) set.add(object[i]); + else while (++i < n) set.add(f(object[i], i, object)); + } + + return set; +} + +function keys(map) { + var keys = []; + for (var key in map) keys.push(key); + return keys; +} + +function values(map) { + var values = []; + for (var key in map) values.push(map[key]); + return values; +} + +function entries(map) { + var entries = []; + for (var key in map) entries.push({key: key, value: map[key]}); + return entries; +} + +var array$2 = Array.prototype; + +var slice$3 = array$2.slice; + +function ascending$2(a, b) { + return a - b; +} + +function area(ring) { + var i = 0, n = ring.length, area = ring[n - 1][1] * ring[0][0] - ring[n - 1][0] * ring[0][1]; + while (++i < n) area += ring[i - 1][1] * ring[i][0] - ring[i - 1][0] * ring[i][1]; + return area; +} + +function constant$6(x) { + return function() { + return x; + }; +} + +function contains(ring, hole) { + var i = -1, n = hole.length, c; + while (++i < n) if (c = ringContains(ring, hole[i])) return c; + return 0; +} + +function ringContains(ring, point) { + var x = point[0], y = point[1], contains = -1; + for (var i = 0, n = ring.length, j = n - 1; i < n; j = i++) { + var pi = ring[i], xi = pi[0], yi = pi[1], pj = ring[j], xj = pj[0], yj = pj[1]; + if (segmentContains(pi, pj, point)) return 0; + if (((yi > y) !== (yj > y)) && ((x < (xj - xi) * (y - yi) / (yj - yi) + xi))) contains = -contains; + } + return contains; +} + +function segmentContains(a, b, c) { + var i; return collinear(a, b, c) && within(a[i = +(a[0] === b[0])], c[i], b[i]); +} + +function collinear(a, b, c) { + return (b[0] - a[0]) * (c[1] - a[1]) === (c[0] - a[0]) * (b[1] - a[1]); +} + +function within(p, q, r) { + return p <= q && q <= r || r <= q && q <= p; +} + +function noop$1() {} + +var cases = [ + [], + [[[1.0, 1.5], [0.5, 1.0]]], + [[[1.5, 1.0], [1.0, 1.5]]], + [[[1.5, 1.0], [0.5, 1.0]]], + [[[1.0, 0.5], [1.5, 1.0]]], + [[[1.0, 1.5], [0.5, 1.0]], [[1.0, 0.5], [1.5, 1.0]]], + [[[1.0, 0.5], [1.0, 1.5]]], + [[[1.0, 0.5], [0.5, 1.0]]], + [[[0.5, 1.0], [1.0, 0.5]]], + [[[1.0, 1.5], [1.0, 0.5]]], + [[[0.5, 1.0], [1.0, 0.5]], [[1.5, 1.0], [1.0, 1.5]]], + [[[1.5, 1.0], [1.0, 0.5]]], + [[[0.5, 1.0], [1.5, 1.0]]], + [[[1.0, 1.5], [1.5, 1.0]]], + [[[0.5, 1.0], [1.0, 1.5]]], + [] +]; + +function contours() { + var dx = 1, + dy = 1, + threshold$$1 = thresholdSturges, + smooth = smoothLinear; + + function contours(values) { + var tz = threshold$$1(values); + + // Convert number of thresholds into uniform thresholds. + if (!Array.isArray(tz)) { + var domain = extent(values), start = domain[0], stop = domain[1]; + tz = tickStep(start, stop, tz); + tz = sequence(Math.floor(start / tz) * tz, Math.floor(stop / tz) * tz, tz); + } else { + tz = tz.slice().sort(ascending$2); + } + + return tz.map(function(value) { + return contour(values, value); + }); + } + + // Accumulate, smooth contour rings, assign holes to exterior rings. + // Based on https://github.com/mbostock/shapefile/blob/v0.6.2/shp/polygon.js + function contour(values, value) { + var polygons = [], + holes = []; + + isorings(values, value, function(ring) { + smooth(ring, values, value); + if (area(ring) > 0) polygons.push([ring]); + else holes.push(ring); + }); + + holes.forEach(function(hole) { + for (var i = 0, n = polygons.length, polygon; i < n; ++i) { + if (contains((polygon = polygons[i])[0], hole) !== -1) { + polygon.push(hole); + return; + } + } + }); + + return { + type: "MultiPolygon", + value: value, + coordinates: polygons + }; + } + + // Marching squares with isolines stitched into rings. + // Based on https://github.com/topojson/topojson-client/blob/v3.0.0/src/stitch.js + function isorings(values, value, callback) { + var fragmentByStart = new Array, + fragmentByEnd = new Array, + x, y, t0, t1, t2, t3; + + // Special case for the first row (y = -1, t2 = t3 = 0). + x = y = -1; + t1 = values[0] >= value; + cases[t1 << 1].forEach(stitch); + while (++x < dx - 1) { + t0 = t1, t1 = values[x + 1] >= value; + cases[t0 | t1 << 1].forEach(stitch); + } + cases[t1 << 0].forEach(stitch); + + // General case for the intermediate rows. + while (++y < dy - 1) { + x = -1; + t1 = values[y * dx + dx] >= value; + t2 = values[y * dx] >= value; + cases[t1 << 1 | t2 << 2].forEach(stitch); + while (++x < dx - 1) { + t0 = t1, t1 = values[y * dx + dx + x + 1] >= value; + t3 = t2, t2 = values[y * dx + x + 1] >= value; + cases[t0 | t1 << 1 | t2 << 2 | t3 << 3].forEach(stitch); + } + cases[t1 | t2 << 3].forEach(stitch); + } + + // Special case for the last row (y = dy - 1, t0 = t1 = 0). + x = -1; + t2 = values[y * dx] >= value; + cases[t2 << 2].forEach(stitch); + while (++x < dx - 1) { + t3 = t2, t2 = values[y * dx + x + 1] >= value; + cases[t2 << 2 | t3 << 3].forEach(stitch); + } + cases[t2 << 3].forEach(stitch); + + function stitch(line) { + var start = [line[0][0] + x, line[0][1] + y], + end = [line[1][0] + x, line[1][1] + y], + startIndex = index(start), + endIndex = index(end), + f, g; + if (f = fragmentByEnd[startIndex]) { + if (g = fragmentByStart[endIndex]) { + delete fragmentByEnd[f.end]; + delete fragmentByStart[g.start]; + if (f === g) { + f.ring.push(end); + callback(f.ring); + } else { + fragmentByStart[f.start] = fragmentByEnd[g.end] = {start: f.start, end: g.end, ring: f.ring.concat(g.ring)}; + } + } else { + delete fragmentByEnd[f.end]; + f.ring.push(end); + fragmentByEnd[f.end = endIndex] = f; + } + } else if (f = fragmentByStart[endIndex]) { + if (g = fragmentByEnd[startIndex]) { + delete fragmentByStart[f.start]; + delete fragmentByEnd[g.end]; + if (f === g) { + f.ring.push(end); + callback(f.ring); + } else { + fragmentByStart[g.start] = fragmentByEnd[f.end] = {start: g.start, end: f.end, ring: g.ring.concat(f.ring)}; + } + } else { + delete fragmentByStart[f.start]; + f.ring.unshift(start); + fragmentByStart[f.start = startIndex] = f; + } + } else { + fragmentByStart[startIndex] = fragmentByEnd[endIndex] = {start: startIndex, end: endIndex, ring: [start, end]}; + } + } + } + + function index(point) { + return point[0] * 2 + point[1] * (dx + 1) * 4; + } + + function smoothLinear(ring, values, value) { + ring.forEach(function(point) { + var x = point[0], + y = point[1], + xt = x | 0, + yt = y | 0, + v0, + v1 = values[yt * dx + xt]; + if (x > 0 && x < dx && xt === x) { + v0 = values[yt * dx + xt - 1]; + point[0] = x + (value - v0) / (v1 - v0) - 0.5; + } + if (y > 0 && y < dy && yt === y) { + v0 = values[(yt - 1) * dx + xt]; + point[1] = y + (value - v0) / (v1 - v0) - 0.5; + } + }); + } + + contours.contour = contour; + + contours.size = function(_) { + if (!arguments.length) return [dx, dy]; + var _0 = Math.ceil(_[0]), _1 = Math.ceil(_[1]); + if (!(_0 > 0) || !(_1 > 0)) throw new Error("invalid size"); + return dx = _0, dy = _1, contours; + }; + + contours.thresholds = function(_) { + return arguments.length ? (threshold$$1 = typeof _ === "function" ? _ : Array.isArray(_) ? constant$6(slice$3.call(_)) : constant$6(_), contours) : threshold$$1; + }; + + contours.smooth = function(_) { + return arguments.length ? (smooth = _ ? smoothLinear : noop$1, contours) : smooth === smoothLinear; + }; + + return contours; +} + +// TODO Optimize edge cases. +// TODO Optimize index calculation. +// TODO Optimize arguments. +function blurX(source, target, r) { + var n = source.width, + m = source.height, + w = (r << 1) + 1; + for (var j = 0; j < m; ++j) { + for (var i = 0, sr = 0; i < n + r; ++i) { + if (i < n) { + sr += source.data[i + j * n]; + } + if (i >= r) { + if (i >= w) { + sr -= source.data[i - w + j * n]; + } + target.data[i - r + j * n] = sr / Math.min(i + 1, n - 1 + w - i, w); + } + } + } +} + +// TODO Optimize edge cases. +// TODO Optimize index calculation. +// TODO Optimize arguments. +function blurY(source, target, r) { + var n = source.width, + m = source.height, + w = (r << 1) + 1; + for (var i = 0; i < n; ++i) { + for (var j = 0, sr = 0; j < m + r; ++j) { + if (j < m) { + sr += source.data[i + j * n]; + } + if (j >= r) { + if (j >= w) { + sr -= source.data[i + (j - w) * n]; + } + target.data[i + (j - r) * n] = sr / Math.min(j + 1, m - 1 + w - j, w); + } + } + } +} + +function defaultX(d) { + return d[0]; +} + +function defaultY(d) { + return d[1]; +} + +function density() { + var x = defaultX, + y = defaultY, + dx = 960, + dy = 500, + r = 20, // blur radius + k = 2, // log2(grid cell size) + o = r * 3, // grid offset, to pad for blur + n = (dx + o * 2) >> k, // grid width + m = (dy + o * 2) >> k, // grid height + threshold$$1 = constant$6(20); + + function density(data) { + var values0 = new Float32Array(n * m), + values1 = new Float32Array(n * m); + + data.forEach(function(d, i, data) { + var xi = (x(d, i, data) + o) >> k, + yi = (y(d, i, data) + o) >> k; + if (xi >= 0 && xi < n && yi >= 0 && yi < m) { + ++values0[xi + yi * n]; + } + }); + + // TODO Optimize. + blurX({width: n, height: m, data: values0}, {width: n, height: m, data: values1}, r >> k); + blurY({width: n, height: m, data: values1}, {width: n, height: m, data: values0}, r >> k); + blurX({width: n, height: m, data: values0}, {width: n, height: m, data: values1}, r >> k); + blurY({width: n, height: m, data: values1}, {width: n, height: m, data: values0}, r >> k); + blurX({width: n, height: m, data: values0}, {width: n, height: m, data: values1}, r >> k); + blurY({width: n, height: m, data: values1}, {width: n, height: m, data: values0}, r >> k); + + var tz = threshold$$1(values0); + + // Convert number of thresholds into uniform thresholds. + if (!Array.isArray(tz)) { + var stop = max(values0); + tz = tickStep(0, stop, tz); + tz = sequence(0, Math.floor(stop / tz) * tz, tz); + tz.shift(); + } + + return contours() + .thresholds(tz) + .size([n, m]) + (values0) + .map(transform); + } + + function transform(geometry) { + geometry.value *= Math.pow(2, -2 * k); // Density in points per square pixel. + geometry.coordinates.forEach(transformPolygon); + return geometry; + } + + function transformPolygon(coordinates) { + coordinates.forEach(transformRing); + } + + function transformRing(coordinates) { + coordinates.forEach(transformPoint); + } + + // TODO Optimize. + function transformPoint(coordinates) { + coordinates[0] = coordinates[0] * Math.pow(2, k) - o; + coordinates[1] = coordinates[1] * Math.pow(2, k) - o; + } + + function resize() { + o = r * 3; + n = (dx + o * 2) >> k; + m = (dy + o * 2) >> k; + return density; + } + + density.x = function(_) { + return arguments.length ? (x = typeof _ === "function" ? _ : constant$6(+_), density) : x; + }; + + density.y = function(_) { + return arguments.length ? (y = typeof _ === "function" ? _ : constant$6(+_), density) : y; + }; + + density.size = function(_) { + if (!arguments.length) return [dx, dy]; + var _0 = Math.ceil(_[0]), _1 = Math.ceil(_[1]); + if (!(_0 >= 0) && !(_0 >= 0)) throw new Error("invalid size"); + return dx = _0, dy = _1, resize(); + }; + + density.cellSize = function(_) { + if (!arguments.length) return 1 << k; + if (!((_ = +_) >= 1)) throw new Error("invalid cell size"); + return k = Math.floor(Math.log(_) / Math.LN2), resize(); + }; + + density.thresholds = function(_) { + return arguments.length ? (threshold$$1 = typeof _ === "function" ? _ : Array.isArray(_) ? constant$6(slice$3.call(_)) : constant$6(_), density) : threshold$$1; + }; + + density.bandwidth = function(_) { + if (!arguments.length) return Math.sqrt(r * (r + 1)); + if (!((_ = +_) >= 0)) throw new Error("invalid bandwidth"); + return r = Math.round((Math.sqrt(4 * _ * _ + 1) - 1) / 2), resize(); + }; + + return density; +} + +var EOL = {}, + EOF = {}, + QUOTE = 34, + NEWLINE = 10, + RETURN = 13; + +function objectConverter(columns) { + return new Function("d", "return {" + columns.map(function(name, i) { + return JSON.stringify(name) + ": d[" + i + "]"; + }).join(",") + "}"); +} + +function customConverter(columns, f) { + var object = objectConverter(columns); + return function(row, i) { + return f(object(row), i, columns); + }; +} + +// Compute unique columns in order of discovery. +function inferColumns(rows) { + var columnSet = Object.create(null), + columns = []; + + rows.forEach(function(row) { + for (var column in row) { + if (!(column in columnSet)) { + columns.push(columnSet[column] = column); + } + } + }); + + return columns; +} + +function dsvFormat(delimiter) { + var reFormat = new RegExp("[\"" + delimiter + "\n\r]"), + DELIMITER = delimiter.charCodeAt(0); + + function parse(text, f) { + var convert, columns, rows = parseRows(text, function(row, i) { + if (convert) return convert(row, i - 1); + columns = row, convert = f ? customConverter(row, f) : objectConverter(row); + }); + rows.columns = columns || []; + return rows; + } + + function parseRows(text, f) { + var rows = [], // output rows + N = text.length, + I = 0, // current character index + n = 0, // current line number + t, // current token + eof = N <= 0, // current token followed by EOF? + eol = false; // current token followed by EOL? + + // Strip the trailing newline. + if (text.charCodeAt(N - 1) === NEWLINE) --N; + if (text.charCodeAt(N - 1) === RETURN) --N; + + function token() { + if (eof) return EOF; + if (eol) return eol = false, EOL; + + // Unescape quotes. + var i, j = I, c; + if (text.charCodeAt(j) === QUOTE) { + while (I++ < N && text.charCodeAt(I) !== QUOTE || text.charCodeAt(++I) === QUOTE); + if ((i = I) >= N) eof = true; + else if ((c = text.charCodeAt(I++)) === NEWLINE) eol = true; + else if (c === RETURN) { eol = true; if (text.charCodeAt(I) === NEWLINE) ++I; } + return text.slice(j + 1, i - 1).replace(/""/g, "\""); + } + + // Find next delimiter or newline. + while (I < N) { + if ((c = text.charCodeAt(i = I++)) === NEWLINE) eol = true; + else if (c === RETURN) { eol = true; if (text.charCodeAt(I) === NEWLINE) ++I; } + else if (c !== DELIMITER) continue; + return text.slice(j, i); + } + + // Return last token before EOF. + return eof = true, text.slice(j, N); + } + + while ((t = token()) !== EOF) { + var row = []; + while (t !== EOL && t !== EOF) row.push(t), t = token(); + if (f && (row = f(row, n++)) == null) continue; + rows.push(row); + } + + return rows; + } + + function format(rows, columns) { + if (columns == null) columns = inferColumns(rows); + return [columns.map(formatValue).join(delimiter)].concat(rows.map(function(row) { + return columns.map(function(column) { + return formatValue(row[column]); + }).join(delimiter); + })).join("\n"); + } + + function formatRows(rows) { + return rows.map(formatRow).join("\n"); + } + + function formatRow(row) { + return row.map(formatValue).join(delimiter); + } + + function formatValue(text) { + return text == null ? "" + : reFormat.test(text += "") ? "\"" + text.replace(/"/g, "\"\"") + "\"" + : text; + } + + return { + parse: parse, + parseRows: parseRows, + format: format, + formatRows: formatRows + }; +} + +var csv = dsvFormat(","); + +var csvParse = csv.parse; +var csvParseRows = csv.parseRows; +var csvFormat = csv.format; +var csvFormatRows = csv.formatRows; + +var tsv = dsvFormat("\t"); + +var tsvParse = tsv.parse; +var tsvParseRows = tsv.parseRows; +var tsvFormat = tsv.format; +var tsvFormatRows = tsv.formatRows; + +function responseBlob(response) { + if (!response.ok) throw new Error(response.status + " " + response.statusText); + return response.blob(); +} + +function blob(input, init) { + return fetch(input, init).then(responseBlob); +} + +function responseArrayBuffer(response) { + if (!response.ok) throw new Error(response.status + " " + response.statusText); + return response.arrayBuffer(); +} + +function buffer(input, init) { + return fetch(input, init).then(responseArrayBuffer); +} + +function responseText(response) { + if (!response.ok) throw new Error(response.status + " " + response.statusText); + return response.text(); +} + +function text(input, init) { + return fetch(input, init).then(responseText); +} + +function dsvParse(parse) { + return function(input, init, row) { + if (arguments.length === 2 && typeof init === "function") row = init, init = undefined; + return text(input, init).then(function(response) { + return parse(response, row); + }); + }; +} + +function dsv(delimiter, input, init, row) { + if (arguments.length === 3 && typeof init === "function") row = init, init = undefined; + var format = dsvFormat(delimiter); + return text(input, init).then(function(response) { + return format.parse(response, row); + }); +} + +var csv$1 = dsvParse(csvParse); +var tsv$1 = dsvParse(tsvParse); + +function image(input, init) { + return new Promise(function(resolve, reject) { + var image = new Image; + for (var key in init) image[key] = init[key]; + image.onerror = reject; + image.onload = function() { resolve(image); }; + image.src = input; + }); +} + +function responseJson(response) { + if (!response.ok) throw new Error(response.status + " " + response.statusText); + return response.json(); +} + +function json(input, init) { + return fetch(input, init).then(responseJson); +} + +function parser(type) { + return function(input, init) { + return text(input, init).then(function(text$$1) { + return (new DOMParser).parseFromString(text$$1, type); + }); + }; +} + +var xml = parser("application/xml"); + +var html = parser("text/html"); + +var svg = parser("image/svg+xml"); + +function center$1(x, y) { + var nodes; + + if (x == null) x = 0; + if (y == null) y = 0; + + function force() { + var i, + n = nodes.length, + node, + sx = 0, + sy = 0; + + for (i = 0; i < n; ++i) { + node = nodes[i], sx += node.x, sy += node.y; + } + + for (sx = sx / n - x, sy = sy / n - y, i = 0; i < n; ++i) { + node = nodes[i], node.x -= sx, node.y -= sy; + } + } + + force.initialize = function(_) { + nodes = _; + }; + + force.x = function(_) { + return arguments.length ? (x = +_, force) : x; + }; + + force.y = function(_) { + return arguments.length ? (y = +_, force) : y; + }; + + return force; +} + +function constant$7(x) { + return function() { + return x; + }; +} + +function jiggle() { + return (Math.random() - 0.5) * 1e-6; +} + +function tree_add(d) { + var x = +this._x.call(null, d), + y = +this._y.call(null, d); + return add(this.cover(x, y), x, y, d); +} + +function add(tree, x, y, d) { + if (isNaN(x) || isNaN(y)) return tree; // ignore invalid points + + var parent, + node = tree._root, + leaf = {data: d}, + x0 = tree._x0, + y0 = tree._y0, + x1 = tree._x1, + y1 = tree._y1, + xm, + ym, + xp, + yp, + right, + bottom, + i, + j; + + // If the tree is empty, initialize the root as a leaf. + if (!node) return tree._root = leaf, tree; + + // Find the existing leaf for the new point, or add it. + while (node.length) { + if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm; + if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym; + if (parent = node, !(node = node[i = bottom << 1 | right])) return parent[i] = leaf, tree; + } + + // Is the new point is exactly coincident with the existing point? + xp = +tree._x.call(null, node.data); + yp = +tree._y.call(null, node.data); + if (x === xp && y === yp) return leaf.next = node, parent ? parent[i] = leaf : tree._root = leaf, tree; + + // Otherwise, split the leaf node until the old and new point are separated. + do { + parent = parent ? parent[i] = new Array(4) : tree._root = new Array(4); + if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm; + if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym; + } while ((i = bottom << 1 | right) === (j = (yp >= ym) << 1 | (xp >= xm))); + return parent[j] = node, parent[i] = leaf, tree; +} + +function addAll(data) { + var d, i, n = data.length, + x, + y, + xz = new Array(n), + yz = new Array(n), + x0 = Infinity, + y0 = Infinity, + x1 = -Infinity, + y1 = -Infinity; + + // Compute the points and their extent. + for (i = 0; i < n; ++i) { + if (isNaN(x = +this._x.call(null, d = data[i])) || isNaN(y = +this._y.call(null, d))) continue; + xz[i] = x; + yz[i] = y; + if (x < x0) x0 = x; + if (x > x1) x1 = x; + if (y < y0) y0 = y; + if (y > y1) y1 = y; + } + + // If there were no (valid) points, inherit the existing extent. + if (x1 < x0) x0 = this._x0, x1 = this._x1; + if (y1 < y0) y0 = this._y0, y1 = this._y1; + + // Expand the tree to cover the new points. + this.cover(x0, y0).cover(x1, y1); + + // Add the new points. + for (i = 0; i < n; ++i) { + add(this, xz[i], yz[i], data[i]); + } + + return this; +} + +function tree_cover(x, y) { + if (isNaN(x = +x) || isNaN(y = +y)) return this; // ignore invalid points + + var x0 = this._x0, + y0 = this._y0, + x1 = this._x1, + y1 = this._y1; + + // If the quadtree has no extent, initialize them. + // Integer extent are necessary so that if we later double the extent, + // the existing quadrant boundaries don’t change due to floating point error! + if (isNaN(x0)) { + x1 = (x0 = Math.floor(x)) + 1; + y1 = (y0 = Math.floor(y)) + 1; + } + + // Otherwise, double repeatedly to cover. + else if (x0 > x || x > x1 || y0 > y || y > y1) { + var z = x1 - x0, + node = this._root, + parent, + i; + + switch (i = (y < (y0 + y1) / 2) << 1 | (x < (x0 + x1) / 2)) { + case 0: { + do parent = new Array(4), parent[i] = node, node = parent; + while (z *= 2, x1 = x0 + z, y1 = y0 + z, x > x1 || y > y1); + break; + } + case 1: { + do parent = new Array(4), parent[i] = node, node = parent; + while (z *= 2, x0 = x1 - z, y1 = y0 + z, x0 > x || y > y1); + break; + } + case 2: { + do parent = new Array(4), parent[i] = node, node = parent; + while (z *= 2, x1 = x0 + z, y0 = y1 - z, x > x1 || y0 > y); + break; + } + case 3: { + do parent = new Array(4), parent[i] = node, node = parent; + while (z *= 2, x0 = x1 - z, y0 = y1 - z, x0 > x || y0 > y); + break; + } + } + + if (this._root && this._root.length) this._root = node; + } + + // If the quadtree covers the point already, just return. + else return this; + + this._x0 = x0; + this._y0 = y0; + this._x1 = x1; + this._y1 = y1; + return this; +} + +function tree_data() { + var data = []; + this.visit(function(node) { + if (!node.length) do data.push(node.data); while (node = node.next) + }); + return data; +} + +function tree_extent(_) { + return arguments.length + ? this.cover(+_[0][0], +_[0][1]).cover(+_[1][0], +_[1][1]) + : isNaN(this._x0) ? undefined : [[this._x0, this._y0], [this._x1, this._y1]]; +} + +function Quad(node, x0, y0, x1, y1) { + this.node = node; + this.x0 = x0; + this.y0 = y0; + this.x1 = x1; + this.y1 = y1; +} + +function tree_find(x, y, radius) { + var data, + x0 = this._x0, + y0 = this._y0, + x1, + y1, + x2, + y2, + x3 = this._x1, + y3 = this._y1, + quads = [], + node = this._root, + q, + i; + + if (node) quads.push(new Quad(node, x0, y0, x3, y3)); + if (radius == null) radius = Infinity; + else { + x0 = x - radius, y0 = y - radius; + x3 = x + radius, y3 = y + radius; + radius *= radius; + } + + while (q = quads.pop()) { + + // Stop searching if this quadrant can’t contain a closer node. + if (!(node = q.node) + || (x1 = q.x0) > x3 + || (y1 = q.y0) > y3 + || (x2 = q.x1) < x0 + || (y2 = q.y1) < y0) continue; + + // Bisect the current quadrant. + if (node.length) { + var xm = (x1 + x2) / 2, + ym = (y1 + y2) / 2; + + quads.push( + new Quad(node[3], xm, ym, x2, y2), + new Quad(node[2], x1, ym, xm, y2), + new Quad(node[1], xm, y1, x2, ym), + new Quad(node[0], x1, y1, xm, ym) + ); + + // Visit the closest quadrant first. + if (i = (y >= ym) << 1 | (x >= xm)) { + q = quads[quads.length - 1]; + quads[quads.length - 1] = quads[quads.length - 1 - i]; + quads[quads.length - 1 - i] = q; + } + } + + // Visit this point. (Visiting coincident points isn’t necessary!) + else { + var dx = x - +this._x.call(null, node.data), + dy = y - +this._y.call(null, node.data), + d2 = dx * dx + dy * dy; + if (d2 < radius) { + var d = Math.sqrt(radius = d2); + x0 = x - d, y0 = y - d; + x3 = x + d, y3 = y + d; + data = node.data; + } + } + } + + return data; +} + +function tree_remove(d) { + if (isNaN(x = +this._x.call(null, d)) || isNaN(y = +this._y.call(null, d))) return this; // ignore invalid points + + var parent, + node = this._root, + retainer, + previous, + next, + x0 = this._x0, + y0 = this._y0, + x1 = this._x1, + y1 = this._y1, + x, + y, + xm, + ym, + right, + bottom, + i, + j; + + // If the tree is empty, initialize the root as a leaf. + if (!node) return this; + + // Find the leaf node for the point. + // While descending, also retain the deepest parent with a non-removed sibling. + if (node.length) while (true) { + if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm; + if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym; + if (!(parent = node, node = node[i = bottom << 1 | right])) return this; + if (!node.length) break; + if (parent[(i + 1) & 3] || parent[(i + 2) & 3] || parent[(i + 3) & 3]) retainer = parent, j = i; + } + + // Find the point to remove. + while (node.data !== d) if (!(previous = node, node = node.next)) return this; + if (next = node.next) delete node.next; + + // If there are multiple coincident points, remove just the point. + if (previous) return next ? previous.next = next : delete previous.next, this; + + // If this is the root point, remove it. + if (!parent) return this._root = next, this; + + // Remove this leaf. + next ? parent[i] = next : delete parent[i]; + + // If the parent now contains exactly one leaf, collapse superfluous parents. + if ((node = parent[0] || parent[1] || parent[2] || parent[3]) + && node === (parent[3] || parent[2] || parent[1] || parent[0]) + && !node.length) { + if (retainer) retainer[j] = node; + else this._root = node; + } + + return this; +} + +function removeAll(data) { + for (var i = 0, n = data.length; i < n; ++i) this.remove(data[i]); + return this; +} + +function tree_root() { + return this._root; +} + +function tree_size() { + var size = 0; + this.visit(function(node) { + if (!node.length) do ++size; while (node = node.next) + }); + return size; +} + +function tree_visit(callback) { + var quads = [], q, node = this._root, child, x0, y0, x1, y1; + if (node) quads.push(new Quad(node, this._x0, this._y0, this._x1, this._y1)); + while (q = quads.pop()) { + if (!callback(node = q.node, x0 = q.x0, y0 = q.y0, x1 = q.x1, y1 = q.y1) && node.length) { + var xm = (x0 + x1) / 2, ym = (y0 + y1) / 2; + if (child = node[3]) quads.push(new Quad(child, xm, ym, x1, y1)); + if (child = node[2]) quads.push(new Quad(child, x0, ym, xm, y1)); + if (child = node[1]) quads.push(new Quad(child, xm, y0, x1, ym)); + if (child = node[0]) quads.push(new Quad(child, x0, y0, xm, ym)); + } + } + return this; +} + +function tree_visitAfter(callback) { + var quads = [], next = [], q; + if (this._root) quads.push(new Quad(this._root, this._x0, this._y0, this._x1, this._y1)); + while (q = quads.pop()) { + var node = q.node; + if (node.length) { + var child, x0 = q.x0, y0 = q.y0, x1 = q.x1, y1 = q.y1, xm = (x0 + x1) / 2, ym = (y0 + y1) / 2; + if (child = node[0]) quads.push(new Quad(child, x0, y0, xm, ym)); + if (child = node[1]) quads.push(new Quad(child, xm, y0, x1, ym)); + if (child = node[2]) quads.push(new Quad(child, x0, ym, xm, y1)); + if (child = node[3]) quads.push(new Quad(child, xm, ym, x1, y1)); + } + next.push(q); + } + while (q = next.pop()) { + callback(q.node, q.x0, q.y0, q.x1, q.y1); + } + return this; +} + +function defaultX$1(d) { + return d[0]; +} + +function tree_x(_) { + return arguments.length ? (this._x = _, this) : this._x; +} + +function defaultY$1(d) { + return d[1]; +} + +function tree_y(_) { + return arguments.length ? (this._y = _, this) : this._y; +} + +function quadtree(nodes, x, y) { + var tree = new Quadtree(x == null ? defaultX$1 : x, y == null ? defaultY$1 : y, NaN, NaN, NaN, NaN); + return nodes == null ? tree : tree.addAll(nodes); +} + +function Quadtree(x, y, x0, y0, x1, y1) { + this._x = x; + this._y = y; + this._x0 = x0; + this._y0 = y0; + this._x1 = x1; + this._y1 = y1; + this._root = undefined; +} + +function leaf_copy(leaf) { + var copy = {data: leaf.data}, next = copy; + while (leaf = leaf.next) next = next.next = {data: leaf.data}; + return copy; +} + +var treeProto = quadtree.prototype = Quadtree.prototype; + +treeProto.copy = function() { + var copy = new Quadtree(this._x, this._y, this._x0, this._y0, this._x1, this._y1), + node = this._root, + nodes, + child; + + if (!node) return copy; + + if (!node.length) return copy._root = leaf_copy(node), copy; + + nodes = [{source: node, target: copy._root = new Array(4)}]; + while (node = nodes.pop()) { + for (var i = 0; i < 4; ++i) { + if (child = node.source[i]) { + if (child.length) nodes.push({source: child, target: node.target[i] = new Array(4)}); + else node.target[i] = leaf_copy(child); + } + } + } + + return copy; +}; + +treeProto.add = tree_add; +treeProto.addAll = addAll; +treeProto.cover = tree_cover; +treeProto.data = tree_data; +treeProto.extent = tree_extent; +treeProto.find = tree_find; +treeProto.remove = tree_remove; +treeProto.removeAll = removeAll; +treeProto.root = tree_root; +treeProto.size = tree_size; +treeProto.visit = tree_visit; +treeProto.visitAfter = tree_visitAfter; +treeProto.x = tree_x; +treeProto.y = tree_y; + +function x(d) { + return d.x + d.vx; +} + +function y(d) { + return d.y + d.vy; +} + +function collide(radius) { + var nodes, + radii, + strength = 1, + iterations = 1; + + if (typeof radius !== "function") radius = constant$7(radius == null ? 1 : +radius); + + function force() { + var i, n = nodes.length, + tree, + node, + xi, + yi, + ri, + ri2; + + for (var k = 0; k < iterations; ++k) { + tree = quadtree(nodes, x, y).visitAfter(prepare); + for (i = 0; i < n; ++i) { + node = nodes[i]; + ri = radii[node.index], ri2 = ri * ri; + xi = node.x + node.vx; + yi = node.y + node.vy; + tree.visit(apply); + } + } + + function apply(quad, x0, y0, x1, y1) { + var data = quad.data, rj = quad.r, r = ri + rj; + if (data) { + if (data.index > node.index) { + var x = xi - data.x - data.vx, + y = yi - data.y - data.vy, + l = x * x + y * y; + if (l < r * r) { + if (x === 0) x = jiggle(), l += x * x; + if (y === 0) y = jiggle(), l += y * y; + l = (r - (l = Math.sqrt(l))) / l * strength; + node.vx += (x *= l) * (r = (rj *= rj) / (ri2 + rj)); + node.vy += (y *= l) * r; + data.vx -= x * (r = 1 - r); + data.vy -= y * r; + } + } + return; + } + return x0 > xi + r || x1 < xi - r || y0 > yi + r || y1 < yi - r; + } + } + + function prepare(quad) { + if (quad.data) return quad.r = radii[quad.data.index]; + for (var i = quad.r = 0; i < 4; ++i) { + if (quad[i] && quad[i].r > quad.r) { + quad.r = quad[i].r; + } + } + } + + function initialize() { + if (!nodes) return; + var i, n = nodes.length, node; + radii = new Array(n); + for (i = 0; i < n; ++i) node = nodes[i], radii[node.index] = +radius(node, i, nodes); + } + + force.initialize = function(_) { + nodes = _; + initialize(); + }; + + force.iterations = function(_) { + return arguments.length ? (iterations = +_, force) : iterations; + }; + + force.strength = function(_) { + return arguments.length ? (strength = +_, force) : strength; + }; + + force.radius = function(_) { + return arguments.length ? (radius = typeof _ === "function" ? _ : constant$7(+_), initialize(), force) : radius; + }; + + return force; +} + +function index(d) { + return d.index; +} + +function find(nodeById, nodeId) { + var node = nodeById.get(nodeId); + if (!node) throw new Error("missing: " + nodeId); + return node; +} + +function link(links) { + var id = index, + strength = defaultStrength, + strengths, + distance = constant$7(30), + distances, + nodes, + count, + bias, + iterations = 1; + + if (links == null) links = []; + + function defaultStrength(link) { + return 1 / Math.min(count[link.source.index], count[link.target.index]); + } + + function force(alpha) { + for (var k = 0, n = links.length; k < iterations; ++k) { + for (var i = 0, link, source, target, x, y, l, b; i < n; ++i) { + link = links[i], source = link.source, target = link.target; + x = target.x + target.vx - source.x - source.vx || jiggle(); + y = target.y + target.vy - source.y - source.vy || jiggle(); + l = Math.sqrt(x * x + y * y); + l = (l - distances[i]) / l * alpha * strengths[i]; + x *= l, y *= l; + target.vx -= x * (b = bias[i]); + target.vy -= y * b; + source.vx += x * (b = 1 - b); + source.vy += y * b; + } + } + } + + function initialize() { + if (!nodes) return; + + var i, + n = nodes.length, + m = links.length, + nodeById = map$1(nodes, id), + link; + + for (i = 0, count = new Array(n); i < m; ++i) { + link = links[i], link.index = i; + if (typeof link.source !== "object") link.source = find(nodeById, link.source); + if (typeof link.target !== "object") link.target = find(nodeById, link.target); + count[link.source.index] = (count[link.source.index] || 0) + 1; + count[link.target.index] = (count[link.target.index] || 0) + 1; + } + + for (i = 0, bias = new Array(m); i < m; ++i) { + link = links[i], bias[i] = count[link.source.index] / (count[link.source.index] + count[link.target.index]); + } + + strengths = new Array(m), initializeStrength(); + distances = new Array(m), initializeDistance(); + } + + function initializeStrength() { + if (!nodes) return; + + for (var i = 0, n = links.length; i < n; ++i) { + strengths[i] = +strength(links[i], i, links); + } + } + + function initializeDistance() { + if (!nodes) return; + + for (var i = 0, n = links.length; i < n; ++i) { + distances[i] = +distance(links[i], i, links); + } + } + + force.initialize = function(_) { + nodes = _; + initialize(); + }; + + force.links = function(_) { + return arguments.length ? (links = _, initialize(), force) : links; + }; + + force.id = function(_) { + return arguments.length ? (id = _, force) : id; + }; + + force.iterations = function(_) { + return arguments.length ? (iterations = +_, force) : iterations; + }; + + force.strength = function(_) { + return arguments.length ? (strength = typeof _ === "function" ? _ : constant$7(+_), initializeStrength(), force) : strength; + }; + + force.distance = function(_) { + return arguments.length ? (distance = typeof _ === "function" ? _ : constant$7(+_), initializeDistance(), force) : distance; + }; + + return force; +} + +function x$1(d) { + return d.x; +} + +function y$1(d) { + return d.y; +} + +var initialRadius = 10, + initialAngle = Math.PI * (3 - Math.sqrt(5)); + +function simulation(nodes) { + var simulation, + alpha = 1, + alphaMin = 0.001, + alphaDecay = 1 - Math.pow(alphaMin, 1 / 300), + alphaTarget = 0, + velocityDecay = 0.6, + forces = map$1(), + stepper = timer(step), + event = dispatch("tick", "end"); + + if (nodes == null) nodes = []; + + function step() { + tick(); + event.call("tick", simulation); + if (alpha < alphaMin) { + stepper.stop(); + event.call("end", simulation); + } + } + + function tick() { + var i, n = nodes.length, node; + + alpha += (alphaTarget - alpha) * alphaDecay; + + forces.each(function(force) { + force(alpha); + }); + + for (i = 0; i < n; ++i) { + node = nodes[i]; + if (node.fx == null) node.x += node.vx *= velocityDecay; + else node.x = node.fx, node.vx = 0; + if (node.fy == null) node.y += node.vy *= velocityDecay; + else node.y = node.fy, node.vy = 0; + } + } + + function initializeNodes() { + for (var i = 0, n = nodes.length, node; i < n; ++i) { + node = nodes[i], node.index = i; + if (isNaN(node.x) || isNaN(node.y)) { + var radius = initialRadius * Math.sqrt(i), angle = i * initialAngle; + node.x = radius * Math.cos(angle); + node.y = radius * Math.sin(angle); + } + if (isNaN(node.vx) || isNaN(node.vy)) { + node.vx = node.vy = 0; + } + } + } + + function initializeForce(force) { + if (force.initialize) force.initialize(nodes); + return force; + } + + initializeNodes(); + + return simulation = { + tick: tick, + + restart: function() { + return stepper.restart(step), simulation; + }, + + stop: function() { + return stepper.stop(), simulation; + }, + + nodes: function(_) { + return arguments.length ? (nodes = _, initializeNodes(), forces.each(initializeForce), simulation) : nodes; + }, + + alpha: function(_) { + return arguments.length ? (alpha = +_, simulation) : alpha; + }, + + alphaMin: function(_) { + return arguments.length ? (alphaMin = +_, simulation) : alphaMin; + }, + + alphaDecay: function(_) { + return arguments.length ? (alphaDecay = +_, simulation) : +alphaDecay; + }, + + alphaTarget: function(_) { + return arguments.length ? (alphaTarget = +_, simulation) : alphaTarget; + }, + + velocityDecay: function(_) { + return arguments.length ? (velocityDecay = 1 - _, simulation) : 1 - velocityDecay; + }, + + force: function(name, _) { + return arguments.length > 1 ? (_ == null ? forces.remove(name) : forces.set(name, initializeForce(_)), simulation) : forces.get(name); + }, + + find: function(x, y, radius) { + var i = 0, + n = nodes.length, + dx, + dy, + d2, + node, + closest; + + if (radius == null) radius = Infinity; + else radius *= radius; + + for (i = 0; i < n; ++i) { + node = nodes[i]; + dx = x - node.x; + dy = y - node.y; + d2 = dx * dx + dy * dy; + if (d2 < radius) closest = node, radius = d2; + } + + return closest; + }, + + on: function(name, _) { + return arguments.length > 1 ? (event.on(name, _), simulation) : event.on(name); + } + }; +} + +function manyBody() { + var nodes, + node, + alpha, + strength = constant$7(-30), + strengths, + distanceMin2 = 1, + distanceMax2 = Infinity, + theta2 = 0.81; + + function force(_) { + var i, n = nodes.length, tree = quadtree(nodes, x$1, y$1).visitAfter(accumulate); + for (alpha = _, i = 0; i < n; ++i) node = nodes[i], tree.visit(apply); + } + + function initialize() { + if (!nodes) return; + var i, n = nodes.length, node; + strengths = new Array(n); + for (i = 0; i < n; ++i) node = nodes[i], strengths[node.index] = +strength(node, i, nodes); + } + + function accumulate(quad) { + var strength = 0, q, c, weight = 0, x, y, i; + + // For internal nodes, accumulate forces from child quadrants. + if (quad.length) { + for (x = y = i = 0; i < 4; ++i) { + if ((q = quad[i]) && (c = Math.abs(q.value))) { + strength += q.value, weight += c, x += c * q.x, y += c * q.y; + } + } + quad.x = x / weight; + quad.y = y / weight; + } + + // For leaf nodes, accumulate forces from coincident quadrants. + else { + q = quad; + q.x = q.data.x; + q.y = q.data.y; + do strength += strengths[q.data.index]; + while (q = q.next); + } + + quad.value = strength; + } + + function apply(quad, x1, _, x2) { + if (!quad.value) return true; + + var x = quad.x - node.x, + y = quad.y - node.y, + w = x2 - x1, + l = x * x + y * y; + + // Apply the Barnes-Hut approximation if possible. + // Limit forces for very close nodes; randomize direction if coincident. + if (w * w / theta2 < l) { + if (l < distanceMax2) { + if (x === 0) x = jiggle(), l += x * x; + if (y === 0) y = jiggle(), l += y * y; + if (l < distanceMin2) l = Math.sqrt(distanceMin2 * l); + node.vx += x * quad.value * alpha / l; + node.vy += y * quad.value * alpha / l; + } + return true; + } + + // Otherwise, process points directly. + else if (quad.length || l >= distanceMax2) return; + + // Limit forces for very close nodes; randomize direction if coincident. + if (quad.data !== node || quad.next) { + if (x === 0) x = jiggle(), l += x * x; + if (y === 0) y = jiggle(), l += y * y; + if (l < distanceMin2) l = Math.sqrt(distanceMin2 * l); + } + + do if (quad.data !== node) { + w = strengths[quad.data.index] * alpha / l; + node.vx += x * w; + node.vy += y * w; + } while (quad = quad.next); + } + + force.initialize = function(_) { + nodes = _; + initialize(); + }; + + force.strength = function(_) { + return arguments.length ? (strength = typeof _ === "function" ? _ : constant$7(+_), initialize(), force) : strength; + }; + + force.distanceMin = function(_) { + return arguments.length ? (distanceMin2 = _ * _, force) : Math.sqrt(distanceMin2); + }; + + force.distanceMax = function(_) { + return arguments.length ? (distanceMax2 = _ * _, force) : Math.sqrt(distanceMax2); + }; + + force.theta = function(_) { + return arguments.length ? (theta2 = _ * _, force) : Math.sqrt(theta2); + }; + + return force; +} + +function radial(radius, x, y) { + var nodes, + strength = constant$7(0.1), + strengths, + radiuses; + + if (typeof radius !== "function") radius = constant$7(+radius); + if (x == null) x = 0; + if (y == null) y = 0; + + function force(alpha) { + for (var i = 0, n = nodes.length; i < n; ++i) { + var node = nodes[i], + dx = node.x - x || 1e-6, + dy = node.y - y || 1e-6, + r = Math.sqrt(dx * dx + dy * dy), + k = (radiuses[i] - r) * strengths[i] * alpha / r; + node.vx += dx * k; + node.vy += dy * k; + } + } + + function initialize() { + if (!nodes) return; + var i, n = nodes.length; + strengths = new Array(n); + radiuses = new Array(n); + for (i = 0; i < n; ++i) { + radiuses[i] = +radius(nodes[i], i, nodes); + strengths[i] = isNaN(radiuses[i]) ? 0 : +strength(nodes[i], i, nodes); + } + } + + force.initialize = function(_) { + nodes = _, initialize(); + }; + + force.strength = function(_) { + return arguments.length ? (strength = typeof _ === "function" ? _ : constant$7(+_), initialize(), force) : strength; + }; + + force.radius = function(_) { + return arguments.length ? (radius = typeof _ === "function" ? _ : constant$7(+_), initialize(), force) : radius; + }; + + force.x = function(_) { + return arguments.length ? (x = +_, force) : x; + }; + + force.y = function(_) { + return arguments.length ? (y = +_, force) : y; + }; + + return force; +} + +function x$2(x) { + var strength = constant$7(0.1), + nodes, + strengths, + xz; + + if (typeof x !== "function") x = constant$7(x == null ? 0 : +x); + + function force(alpha) { + for (var i = 0, n = nodes.length, node; i < n; ++i) { + node = nodes[i], node.vx += (xz[i] - node.x) * strengths[i] * alpha; + } + } + + function initialize() { + if (!nodes) return; + var i, n = nodes.length; + strengths = new Array(n); + xz = new Array(n); + for (i = 0; i < n; ++i) { + strengths[i] = isNaN(xz[i] = +x(nodes[i], i, nodes)) ? 0 : +strength(nodes[i], i, nodes); + } + } + + force.initialize = function(_) { + nodes = _; + initialize(); + }; + + force.strength = function(_) { + return arguments.length ? (strength = typeof _ === "function" ? _ : constant$7(+_), initialize(), force) : strength; + }; + + force.x = function(_) { + return arguments.length ? (x = typeof _ === "function" ? _ : constant$7(+_), initialize(), force) : x; + }; + + return force; +} + +function y$2(y) { + var strength = constant$7(0.1), + nodes, + strengths, + yz; + + if (typeof y !== "function") y = constant$7(y == null ? 0 : +y); + + function force(alpha) { + for (var i = 0, n = nodes.length, node; i < n; ++i) { + node = nodes[i], node.vy += (yz[i] - node.y) * strengths[i] * alpha; + } + } + + function initialize() { + if (!nodes) return; + var i, n = nodes.length; + strengths = new Array(n); + yz = new Array(n); + for (i = 0; i < n; ++i) { + strengths[i] = isNaN(yz[i] = +y(nodes[i], i, nodes)) ? 0 : +strength(nodes[i], i, nodes); + } + } + + force.initialize = function(_) { + nodes = _; + initialize(); + }; + + force.strength = function(_) { + return arguments.length ? (strength = typeof _ === "function" ? _ : constant$7(+_), initialize(), force) : strength; + }; + + force.y = function(_) { + return arguments.length ? (y = typeof _ === "function" ? _ : constant$7(+_), initialize(), force) : y; + }; + + return force; +} + +// Computes the decimal coefficient and exponent of the specified number x with +// significant digits p, where x is positive and p is in [1, 21] or undefined. +// For example, formatDecimal(1.23) returns ["123", 0]. +function formatDecimal(x, p) { + if ((i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf("e")) < 0) return null; // NaN, ±Infinity + var i, coefficient = x.slice(0, i); + + // The string returned by toExponential either has the form \d\.\d+e[-+]\d+ + // (e.g., 1.2e+3) or the form \de[-+]\d+ (e.g., 1e+3). + return [ + coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient, + +x.slice(i + 1) + ]; +} + +function exponent$1(x) { + return x = formatDecimal(Math.abs(x)), x ? x[1] : NaN; +} + +function formatGroup(grouping, thousands) { + return function(value, width) { + var i = value.length, + t = [], + j = 0, + g = grouping[0], + length = 0; + + while (i > 0 && g > 0) { + if (length + g + 1 > width) g = Math.max(1, width - length); + t.push(value.substring(i -= g, i + g)); + if ((length += g + 1) > width) break; + g = grouping[j = (j + 1) % grouping.length]; + } + + return t.reverse().join(thousands); + }; +} + +function formatNumerals(numerals) { + return function(value) { + return value.replace(/[0-9]/g, function(i) { + return numerals[+i]; + }); + }; +} + +function formatDefault(x, p) { + x = x.toPrecision(p); + + out: for (var n = x.length, i = 1, i0 = -1, i1; i < n; ++i) { + switch (x[i]) { + case ".": i0 = i1 = i; break; + case "0": if (i0 === 0) i0 = i; i1 = i; break; + case "e": break out; + default: if (i0 > 0) i0 = 0; break; + } + } + + return i0 > 0 ? x.slice(0, i0) + x.slice(i1 + 1) : x; +} + +var prefixExponent; + +function formatPrefixAuto(x, p) { + var d = formatDecimal(x, p); + if (!d) return x + ""; + var coefficient = d[0], + exponent = d[1], + i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1, + n = coefficient.length; + return i === n ? coefficient + : i > n ? coefficient + new Array(i - n + 1).join("0") + : i > 0 ? coefficient.slice(0, i) + "." + coefficient.slice(i) + : "0." + new Array(1 - i).join("0") + formatDecimal(x, Math.max(0, p + i - 1))[0]; // less than 1y! +} + +function formatRounded(x, p) { + var d = formatDecimal(x, p); + if (!d) return x + ""; + var coefficient = d[0], + exponent = d[1]; + return exponent < 0 ? "0." + new Array(-exponent).join("0") + coefficient + : coefficient.length > exponent + 1 ? coefficient.slice(0, exponent + 1) + "." + coefficient.slice(exponent + 1) + : coefficient + new Array(exponent - coefficient.length + 2).join("0"); +} + +var formatTypes = { + "": formatDefault, + "%": function(x, p) { return (x * 100).toFixed(p); }, + "b": function(x) { return Math.round(x).toString(2); }, + "c": function(x) { return x + ""; }, + "d": function(x) { return Math.round(x).toString(10); }, + "e": function(x, p) { return x.toExponential(p); }, + "f": function(x, p) { return x.toFixed(p); }, + "g": function(x, p) { return x.toPrecision(p); }, + "o": function(x) { return Math.round(x).toString(8); }, + "p": function(x, p) { return formatRounded(x * 100, p); }, + "r": formatRounded, + "s": formatPrefixAuto, + "X": function(x) { return Math.round(x).toString(16).toUpperCase(); }, + "x": function(x) { return Math.round(x).toString(16); } +}; + +// [[fill]align][sign][symbol][0][width][,][.precision][type] +var re = /^(?:(.)?([<>=^]))?([+\-\( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?([a-z%])?$/i; + +function formatSpecifier(specifier) { + return new FormatSpecifier(specifier); +} + +formatSpecifier.prototype = FormatSpecifier.prototype; // instanceof + +function FormatSpecifier(specifier) { + if (!(match = re.exec(specifier))) throw new Error("invalid format: " + specifier); + + var match, + fill = match[1] || " ", + align = match[2] || ">", + sign = match[3] || "-", + symbol = match[4] || "", + zero = !!match[5], + width = match[6] && +match[6], + comma = !!match[7], + precision = match[8] && +match[8].slice(1), + type = match[9] || ""; + + // The "n" type is an alias for ",g". + if (type === "n") comma = true, type = "g"; + + // Map invalid types to the default format. + else if (!formatTypes[type]) type = ""; + + // If zero fill is specified, padding goes after sign and before digits. + if (zero || (fill === "0" && align === "=")) zero = true, fill = "0", align = "="; + + this.fill = fill; + this.align = align; + this.sign = sign; + this.symbol = symbol; + this.zero = zero; + this.width = width; + this.comma = comma; + this.precision = precision; + this.type = type; +} + +FormatSpecifier.prototype.toString = function() { + return this.fill + + this.align + + this.sign + + this.symbol + + (this.zero ? "0" : "") + + (this.width == null ? "" : Math.max(1, this.width | 0)) + + (this.comma ? "," : "") + + (this.precision == null ? "" : "." + Math.max(0, this.precision | 0)) + + this.type; +}; + +function identity$3(x) { + return x; +} + +var prefixes = ["y","z","a","f","p","n","\xB5","m","","k","M","G","T","P","E","Z","Y"]; + +function formatLocale(locale) { + var group = locale.grouping && locale.thousands ? formatGroup(locale.grouping, locale.thousands) : identity$3, + currency = locale.currency, + decimal = locale.decimal, + numerals = locale.numerals ? formatNumerals(locale.numerals) : identity$3, + percent = locale.percent || "%"; + + function newFormat(specifier) { + specifier = formatSpecifier(specifier); + + var fill = specifier.fill, + align = specifier.align, + sign = specifier.sign, + symbol = specifier.symbol, + zero = specifier.zero, + width = specifier.width, + comma = specifier.comma, + precision = specifier.precision, + type = specifier.type; + + // Compute the prefix and suffix. + // For SI-prefix, the suffix is lazily computed. + var prefix = symbol === "$" ? currency[0] : symbol === "#" && /[boxX]/.test(type) ? "0" + type.toLowerCase() : "", + suffix = symbol === "$" ? currency[1] : /[%p]/.test(type) ? percent : ""; + + // What format function should we use? + // Is this an integer type? + // Can this type generate exponential notation? + var formatType = formatTypes[type], + maybeSuffix = !type || /[defgprs%]/.test(type); + + // Set the default precision if not specified, + // or clamp the specified precision to the supported range. + // For significant precision, it must be in [1, 21]. + // For fixed precision, it must be in [0, 20]. + precision = precision == null ? (type ? 6 : 12) + : /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision)) + : Math.max(0, Math.min(20, precision)); + + function format(value) { + var valuePrefix = prefix, + valueSuffix = suffix, + i, n, c; + + if (type === "c") { + valueSuffix = formatType(value) + valueSuffix; + value = ""; + } else { + value = +value; + + // Perform the initial formatting. + var valueNegative = value < 0; + value = formatType(Math.abs(value), precision); + + // If a negative value rounds to zero during formatting, treat as positive. + if (valueNegative && +value === 0) valueNegative = false; + + // Compute the prefix and suffix. + valuePrefix = (valueNegative ? (sign === "(" ? sign : "-") : sign === "-" || sign === "(" ? "" : sign) + valuePrefix; + valueSuffix = (type === "s" ? prefixes[8 + prefixExponent / 3] : "") + valueSuffix + (valueNegative && sign === "(" ? ")" : ""); + + // Break the formatted value into the integer “value” part that can be + // grouped, and fractional or exponential “suffix” part that is not. + if (maybeSuffix) { + i = -1, n = value.length; + while (++i < n) { + if (c = value.charCodeAt(i), 48 > c || c > 57) { + valueSuffix = (c === 46 ? decimal + value.slice(i + 1) : value.slice(i)) + valueSuffix; + value = value.slice(0, i); + break; + } + } + } + } + + // If the fill character is not "0", grouping is applied before padding. + if (comma && !zero) value = group(value, Infinity); + + // Compute the padding. + var length = valuePrefix.length + value.length + valueSuffix.length, + padding = length < width ? new Array(width - length + 1).join(fill) : ""; + + // If the fill character is "0", grouping is applied after padding. + if (comma && zero) value = group(padding + value, padding.length ? width - valueSuffix.length : Infinity), padding = ""; + + // Reconstruct the final output based on the desired alignment. + switch (align) { + case "<": value = valuePrefix + value + valueSuffix + padding; break; + case "=": value = valuePrefix + padding + value + valueSuffix; break; + case "^": value = padding.slice(0, length = padding.length >> 1) + valuePrefix + value + valueSuffix + padding.slice(length); break; + default: value = padding + valuePrefix + value + valueSuffix; break; + } + + return numerals(value); + } + + format.toString = function() { + return specifier + ""; + }; + + return format; + } + + function formatPrefix(specifier, value) { + var f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier)), + e = Math.max(-8, Math.min(8, Math.floor(exponent$1(value) / 3))) * 3, + k = Math.pow(10, -e), + prefix = prefixes[8 + e / 3]; + return function(value) { + return f(k * value) + prefix; + }; + } + + return { + format: newFormat, + formatPrefix: formatPrefix + }; +} + +var locale; + +defaultLocale({ + decimal: ".", + thousands: ",", + grouping: [3], + currency: ["$", ""] +}); + +function defaultLocale(definition) { + locale = formatLocale(definition); + exports.format = locale.format; + exports.formatPrefix = locale.formatPrefix; + return locale; +} + +function precisionFixed(step) { + return Math.max(0, -exponent$1(Math.abs(step))); +} + +function precisionPrefix(step, value) { + return Math.max(0, Math.max(-8, Math.min(8, Math.floor(exponent$1(value) / 3))) * 3 - exponent$1(Math.abs(step))); +} + +function precisionRound(step, max) { + step = Math.abs(step), max = Math.abs(max) - step; + return Math.max(0, exponent$1(max) - exponent$1(step)) + 1; +} + +// Adds floating point numbers with twice the normal precision. +// Reference: J. R. Shewchuk, Adaptive Precision Floating-Point Arithmetic and +// Fast Robust Geometric Predicates, Discrete & Computational Geometry 18(3) +// 305–363 (1997). +// Code adapted from GeographicLib by Charles F. F. Karney, +// http://geographiclib.sourceforge.net/ + +function adder() { + return new Adder; +} + +function Adder() { + this.reset(); +} + +Adder.prototype = { + constructor: Adder, + reset: function() { + this.s = // rounded value + this.t = 0; // exact error + }, + add: function(y) { + add$1(temp, y, this.t); + add$1(this, temp.s, this.s); + if (this.s) this.t += temp.t; + else this.s = temp.t; + }, + valueOf: function() { + return this.s; + } +}; + +var temp = new Adder; + +function add$1(adder, a, b) { + var x = adder.s = a + b, + bv = x - a, + av = x - bv; + adder.t = (a - av) + (b - bv); +} + +var epsilon$2 = 1e-6; +var epsilon2$1 = 1e-12; +var pi$3 = Math.PI; +var halfPi$2 = pi$3 / 2; +var quarterPi = pi$3 / 4; +var tau$3 = pi$3 * 2; + +var degrees$1 = 180 / pi$3; +var radians = pi$3 / 180; + +var abs = Math.abs; +var atan = Math.atan; +var atan2 = Math.atan2; +var cos$1 = Math.cos; +var ceil = Math.ceil; +var exp = Math.exp; +var log = Math.log; +var pow = Math.pow; +var sin$1 = Math.sin; +var sign = Math.sign || function(x) { return x > 0 ? 1 : x < 0 ? -1 : 0; }; +var sqrt = Math.sqrt; +var tan = Math.tan; + +function acos(x) { + return x > 1 ? 0 : x < -1 ? pi$3 : Math.acos(x); +} + +function asin(x) { + return x > 1 ? halfPi$2 : x < -1 ? -halfPi$2 : Math.asin(x); +} + +function haversin(x) { + return (x = sin$1(x / 2)) * x; +} + +function noop$2() {} + +function streamGeometry(geometry, stream) { + if (geometry && streamGeometryType.hasOwnProperty(geometry.type)) { + streamGeometryType[geometry.type](geometry, stream); + } +} + +var streamObjectType = { + Feature: function(object, stream) { + streamGeometry(object.geometry, stream); + }, + FeatureCollection: function(object, stream) { + var features = object.features, i = -1, n = features.length; + while (++i < n) streamGeometry(features[i].geometry, stream); + } +}; + +var streamGeometryType = { + Sphere: function(object, stream) { + stream.sphere(); + }, + Point: function(object, stream) { + object = object.coordinates; + stream.point(object[0], object[1], object[2]); + }, + MultiPoint: function(object, stream) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) object = coordinates[i], stream.point(object[0], object[1], object[2]); + }, + LineString: function(object, stream) { + streamLine(object.coordinates, stream, 0); + }, + MultiLineString: function(object, stream) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) streamLine(coordinates[i], stream, 0); + }, + Polygon: function(object, stream) { + streamPolygon(object.coordinates, stream); + }, + MultiPolygon: function(object, stream) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) streamPolygon(coordinates[i], stream); + }, + GeometryCollection: function(object, stream) { + var geometries = object.geometries, i = -1, n = geometries.length; + while (++i < n) streamGeometry(geometries[i], stream); + } +}; + +function streamLine(coordinates, stream, closed) { + var i = -1, n = coordinates.length - closed, coordinate; + stream.lineStart(); + while (++i < n) coordinate = coordinates[i], stream.point(coordinate[0], coordinate[1], coordinate[2]); + stream.lineEnd(); +} + +function streamPolygon(coordinates, stream) { + var i = -1, n = coordinates.length; + stream.polygonStart(); + while (++i < n) streamLine(coordinates[i], stream, 1); + stream.polygonEnd(); +} + +function geoStream(object, stream) { + if (object && streamObjectType.hasOwnProperty(object.type)) { + streamObjectType[object.type](object, stream); + } else { + streamGeometry(object, stream); + } +} + +var areaRingSum = adder(); + +var areaSum = adder(), + lambda00, + phi00, + lambda0, + cosPhi0, + sinPhi0; + +var areaStream = { + point: noop$2, + lineStart: noop$2, + lineEnd: noop$2, + polygonStart: function() { + areaRingSum.reset(); + areaStream.lineStart = areaRingStart; + areaStream.lineEnd = areaRingEnd; + }, + polygonEnd: function() { + var areaRing = +areaRingSum; + areaSum.add(areaRing < 0 ? tau$3 + areaRing : areaRing); + this.lineStart = this.lineEnd = this.point = noop$2; + }, + sphere: function() { + areaSum.add(tau$3); + } +}; + +function areaRingStart() { + areaStream.point = areaPointFirst; +} + +function areaRingEnd() { + areaPoint(lambda00, phi00); +} + +function areaPointFirst(lambda, phi) { + areaStream.point = areaPoint; + lambda00 = lambda, phi00 = phi; + lambda *= radians, phi *= radians; + lambda0 = lambda, cosPhi0 = cos$1(phi = phi / 2 + quarterPi), sinPhi0 = sin$1(phi); +} + +function areaPoint(lambda, phi) { + lambda *= radians, phi *= radians; + phi = phi / 2 + quarterPi; // half the angular distance from south pole + + // Spherical excess E for a spherical triangle with vertices: south pole, + // previous point, current point. Uses a formula derived from Cagnoli’s + // theorem. See Todhunter, Spherical Trig. (1871), Sec. 103, Eq. (2). + var dLambda = lambda - lambda0, + sdLambda = dLambda >= 0 ? 1 : -1, + adLambda = sdLambda * dLambda, + cosPhi = cos$1(phi), + sinPhi = sin$1(phi), + k = sinPhi0 * sinPhi, + u = cosPhi0 * cosPhi + k * cos$1(adLambda), + v = k * sdLambda * sin$1(adLambda); + areaRingSum.add(atan2(v, u)); + + // Advance the previous points. + lambda0 = lambda, cosPhi0 = cosPhi, sinPhi0 = sinPhi; +} + +function area$1(object) { + areaSum.reset(); + geoStream(object, areaStream); + return areaSum * 2; +} + +function spherical(cartesian) { + return [atan2(cartesian[1], cartesian[0]), asin(cartesian[2])]; +} + +function cartesian(spherical) { + var lambda = spherical[0], phi = spherical[1], cosPhi = cos$1(phi); + return [cosPhi * cos$1(lambda), cosPhi * sin$1(lambda), sin$1(phi)]; +} + +function cartesianDot(a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; +} + +function cartesianCross(a, b) { + return [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]]; +} + +// TODO return a +function cartesianAddInPlace(a, b) { + a[0] += b[0], a[1] += b[1], a[2] += b[2]; +} + +function cartesianScale(vector, k) { + return [vector[0] * k, vector[1] * k, vector[2] * k]; +} + +// TODO return d +function cartesianNormalizeInPlace(d) { + var l = sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]); + d[0] /= l, d[1] /= l, d[2] /= l; +} + +var lambda0$1, phi0, lambda1, phi1, // bounds + lambda2, // previous lambda-coordinate + lambda00$1, phi00$1, // first point + p0, // previous 3D point + deltaSum = adder(), + ranges, + range; + +var boundsStream = { + point: boundsPoint, + lineStart: boundsLineStart, + lineEnd: boundsLineEnd, + polygonStart: function() { + boundsStream.point = boundsRingPoint; + boundsStream.lineStart = boundsRingStart; + boundsStream.lineEnd = boundsRingEnd; + deltaSum.reset(); + areaStream.polygonStart(); + }, + polygonEnd: function() { + areaStream.polygonEnd(); + boundsStream.point = boundsPoint; + boundsStream.lineStart = boundsLineStart; + boundsStream.lineEnd = boundsLineEnd; + if (areaRingSum < 0) lambda0$1 = -(lambda1 = 180), phi0 = -(phi1 = 90); + else if (deltaSum > epsilon$2) phi1 = 90; + else if (deltaSum < -epsilon$2) phi0 = -90; + range[0] = lambda0$1, range[1] = lambda1; + } +}; + +function boundsPoint(lambda, phi) { + ranges.push(range = [lambda0$1 = lambda, lambda1 = lambda]); + if (phi < phi0) phi0 = phi; + if (phi > phi1) phi1 = phi; +} + +function linePoint(lambda, phi) { + var p = cartesian([lambda * radians, phi * radians]); + if (p0) { + var normal = cartesianCross(p0, p), + equatorial = [normal[1], -normal[0], 0], + inflection = cartesianCross(equatorial, normal); + cartesianNormalizeInPlace(inflection); + inflection = spherical(inflection); + var delta = lambda - lambda2, + sign$$1 = delta > 0 ? 1 : -1, + lambdai = inflection[0] * degrees$1 * sign$$1, + phii, + antimeridian = abs(delta) > 180; + if (antimeridian ^ (sign$$1 * lambda2 < lambdai && lambdai < sign$$1 * lambda)) { + phii = inflection[1] * degrees$1; + if (phii > phi1) phi1 = phii; + } else if (lambdai = (lambdai + 360) % 360 - 180, antimeridian ^ (sign$$1 * lambda2 < lambdai && lambdai < sign$$1 * lambda)) { + phii = -inflection[1] * degrees$1; + if (phii < phi0) phi0 = phii; + } else { + if (phi < phi0) phi0 = phi; + if (phi > phi1) phi1 = phi; + } + if (antimeridian) { + if (lambda < lambda2) { + if (angle(lambda0$1, lambda) > angle(lambda0$1, lambda1)) lambda1 = lambda; + } else { + if (angle(lambda, lambda1) > angle(lambda0$1, lambda1)) lambda0$1 = lambda; + } + } else { + if (lambda1 >= lambda0$1) { + if (lambda < lambda0$1) lambda0$1 = lambda; + if (lambda > lambda1) lambda1 = lambda; + } else { + if (lambda > lambda2) { + if (angle(lambda0$1, lambda) > angle(lambda0$1, lambda1)) lambda1 = lambda; + } else { + if (angle(lambda, lambda1) > angle(lambda0$1, lambda1)) lambda0$1 = lambda; + } + } + } + } else { + ranges.push(range = [lambda0$1 = lambda, lambda1 = lambda]); + } + if (phi < phi0) phi0 = phi; + if (phi > phi1) phi1 = phi; + p0 = p, lambda2 = lambda; +} + +function boundsLineStart() { + boundsStream.point = linePoint; +} + +function boundsLineEnd() { + range[0] = lambda0$1, range[1] = lambda1; + boundsStream.point = boundsPoint; + p0 = null; +} + +function boundsRingPoint(lambda, phi) { + if (p0) { + var delta = lambda - lambda2; + deltaSum.add(abs(delta) > 180 ? delta + (delta > 0 ? 360 : -360) : delta); + } else { + lambda00$1 = lambda, phi00$1 = phi; + } + areaStream.point(lambda, phi); + linePoint(lambda, phi); +} + +function boundsRingStart() { + areaStream.lineStart(); +} + +function boundsRingEnd() { + boundsRingPoint(lambda00$1, phi00$1); + areaStream.lineEnd(); + if (abs(deltaSum) > epsilon$2) lambda0$1 = -(lambda1 = 180); + range[0] = lambda0$1, range[1] = lambda1; + p0 = null; +} + +// Finds the left-right distance between two longitudes. +// This is almost the same as (lambda1 - lambda0 + 360°) % 360°, except that we want +// the distance between ±180° to be 360°. +function angle(lambda0, lambda1) { + return (lambda1 -= lambda0) < 0 ? lambda1 + 360 : lambda1; +} + +function rangeCompare(a, b) { + return a[0] - b[0]; +} + +function rangeContains(range, x) { + return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x; +} + +function bounds(feature) { + var i, n, a, b, merged, deltaMax, delta; + + phi1 = lambda1 = -(lambda0$1 = phi0 = Infinity); + ranges = []; + geoStream(feature, boundsStream); + + // First, sort ranges by their minimum longitudes. + if (n = ranges.length) { + ranges.sort(rangeCompare); + + // Then, merge any ranges that overlap. + for (i = 1, a = ranges[0], merged = [a]; i < n; ++i) { + b = ranges[i]; + if (rangeContains(a, b[0]) || rangeContains(a, b[1])) { + if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1]; + if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0]; + } else { + merged.push(a = b); + } + } + + // Finally, find the largest gap between the merged ranges. + // The final bounding box will be the inverse of this gap. + for (deltaMax = -Infinity, n = merged.length - 1, i = 0, a = merged[n]; i <= n; a = b, ++i) { + b = merged[i]; + if ((delta = angle(a[1], b[0])) > deltaMax) deltaMax = delta, lambda0$1 = b[0], lambda1 = a[1]; + } + } + + ranges = range = null; + + return lambda0$1 === Infinity || phi0 === Infinity + ? [[NaN, NaN], [NaN, NaN]] + : [[lambda0$1, phi0], [lambda1, phi1]]; +} + +var W0, W1, + X0, Y0, Z0, + X1, Y1, Z1, + X2, Y2, Z2, + lambda00$2, phi00$2, // first point + x0, y0, z0; // previous point + +var centroidStream = { + sphere: noop$2, + point: centroidPoint, + lineStart: centroidLineStart, + lineEnd: centroidLineEnd, + polygonStart: function() { + centroidStream.lineStart = centroidRingStart; + centroidStream.lineEnd = centroidRingEnd; + }, + polygonEnd: function() { + centroidStream.lineStart = centroidLineStart; + centroidStream.lineEnd = centroidLineEnd; + } +}; + +// Arithmetic mean of Cartesian vectors. +function centroidPoint(lambda, phi) { + lambda *= radians, phi *= radians; + var cosPhi = cos$1(phi); + centroidPointCartesian(cosPhi * cos$1(lambda), cosPhi * sin$1(lambda), sin$1(phi)); +} + +function centroidPointCartesian(x, y, z) { + ++W0; + X0 += (x - X0) / W0; + Y0 += (y - Y0) / W0; + Z0 += (z - Z0) / W0; +} + +function centroidLineStart() { + centroidStream.point = centroidLinePointFirst; +} + +function centroidLinePointFirst(lambda, phi) { + lambda *= radians, phi *= radians; + var cosPhi = cos$1(phi); + x0 = cosPhi * cos$1(lambda); + y0 = cosPhi * sin$1(lambda); + z0 = sin$1(phi); + centroidStream.point = centroidLinePoint; + centroidPointCartesian(x0, y0, z0); +} + +function centroidLinePoint(lambda, phi) { + lambda *= radians, phi *= radians; + var cosPhi = cos$1(phi), + x = cosPhi * cos$1(lambda), + y = cosPhi * sin$1(lambda), + z = sin$1(phi), + w = atan2(sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w), x0 * x + y0 * y + z0 * z); + W1 += w; + X1 += w * (x0 + (x0 = x)); + Y1 += w * (y0 + (y0 = y)); + Z1 += w * (z0 + (z0 = z)); + centroidPointCartesian(x0, y0, z0); +} + +function centroidLineEnd() { + centroidStream.point = centroidPoint; +} + +// See J. E. Brock, The Inertia Tensor for a Spherical Triangle, +// J. Applied Mechanics 42, 239 (1975). +function centroidRingStart() { + centroidStream.point = centroidRingPointFirst; +} + +function centroidRingEnd() { + centroidRingPoint(lambda00$2, phi00$2); + centroidStream.point = centroidPoint; +} + +function centroidRingPointFirst(lambda, phi) { + lambda00$2 = lambda, phi00$2 = phi; + lambda *= radians, phi *= radians; + centroidStream.point = centroidRingPoint; + var cosPhi = cos$1(phi); + x0 = cosPhi * cos$1(lambda); + y0 = cosPhi * sin$1(lambda); + z0 = sin$1(phi); + centroidPointCartesian(x0, y0, z0); +} + +function centroidRingPoint(lambda, phi) { + lambda *= radians, phi *= radians; + var cosPhi = cos$1(phi), + x = cosPhi * cos$1(lambda), + y = cosPhi * sin$1(lambda), + z = sin$1(phi), + cx = y0 * z - z0 * y, + cy = z0 * x - x0 * z, + cz = x0 * y - y0 * x, + m = sqrt(cx * cx + cy * cy + cz * cz), + w = asin(m), // line weight = angle + v = m && -w / m; // area weight multiplier + X2 += v * cx; + Y2 += v * cy; + Z2 += v * cz; + W1 += w; + X1 += w * (x0 + (x0 = x)); + Y1 += w * (y0 + (y0 = y)); + Z1 += w * (z0 + (z0 = z)); + centroidPointCartesian(x0, y0, z0); +} + +function centroid(object) { + W0 = W1 = + X0 = Y0 = Z0 = + X1 = Y1 = Z1 = + X2 = Y2 = Z2 = 0; + geoStream(object, centroidStream); + + var x = X2, + y = Y2, + z = Z2, + m = x * x + y * y + z * z; + + // If the area-weighted ccentroid is undefined, fall back to length-weighted ccentroid. + if (m < epsilon2$1) { + x = X1, y = Y1, z = Z1; + // If the feature has zero length, fall back to arithmetic mean of point vectors. + if (W1 < epsilon$2) x = X0, y = Y0, z = Z0; + m = x * x + y * y + z * z; + // If the feature still has an undefined ccentroid, then return. + if (m < epsilon2$1) return [NaN, NaN]; + } + + return [atan2(y, x) * degrees$1, asin(z / sqrt(m)) * degrees$1]; +} + +function constant$8(x) { + return function() { + return x; + }; +} + +function compose(a, b) { + + function compose(x, y) { + return x = a(x, y), b(x[0], x[1]); + } + + if (a.invert && b.invert) compose.invert = function(x, y) { + return x = b.invert(x, y), x && a.invert(x[0], x[1]); + }; + + return compose; +} + +function rotationIdentity(lambda, phi) { + return [lambda > pi$3 ? lambda - tau$3 : lambda < -pi$3 ? lambda + tau$3 : lambda, phi]; +} + +rotationIdentity.invert = rotationIdentity; + +function rotateRadians(deltaLambda, deltaPhi, deltaGamma) { + return (deltaLambda %= tau$3) ? (deltaPhi || deltaGamma ? compose(rotationLambda(deltaLambda), rotationPhiGamma(deltaPhi, deltaGamma)) + : rotationLambda(deltaLambda)) + : (deltaPhi || deltaGamma ? rotationPhiGamma(deltaPhi, deltaGamma) + : rotationIdentity); +} + +function forwardRotationLambda(deltaLambda) { + return function(lambda, phi) { + return lambda += deltaLambda, [lambda > pi$3 ? lambda - tau$3 : lambda < -pi$3 ? lambda + tau$3 : lambda, phi]; + }; +} + +function rotationLambda(deltaLambda) { + var rotation = forwardRotationLambda(deltaLambda); + rotation.invert = forwardRotationLambda(-deltaLambda); + return rotation; +} + +function rotationPhiGamma(deltaPhi, deltaGamma) { + var cosDeltaPhi = cos$1(deltaPhi), + sinDeltaPhi = sin$1(deltaPhi), + cosDeltaGamma = cos$1(deltaGamma), + sinDeltaGamma = sin$1(deltaGamma); + + function rotation(lambda, phi) { + var cosPhi = cos$1(phi), + x = cos$1(lambda) * cosPhi, + y = sin$1(lambda) * cosPhi, + z = sin$1(phi), + k = z * cosDeltaPhi + x * sinDeltaPhi; + return [ + atan2(y * cosDeltaGamma - k * sinDeltaGamma, x * cosDeltaPhi - z * sinDeltaPhi), + asin(k * cosDeltaGamma + y * sinDeltaGamma) + ]; + } + + rotation.invert = function(lambda, phi) { + var cosPhi = cos$1(phi), + x = cos$1(lambda) * cosPhi, + y = sin$1(lambda) * cosPhi, + z = sin$1(phi), + k = z * cosDeltaGamma - y * sinDeltaGamma; + return [ + atan2(y * cosDeltaGamma + z * sinDeltaGamma, x * cosDeltaPhi + k * sinDeltaPhi), + asin(k * cosDeltaPhi - x * sinDeltaPhi) + ]; + }; + + return rotation; +} + +function rotation(rotate) { + rotate = rotateRadians(rotate[0] * radians, rotate[1] * radians, rotate.length > 2 ? rotate[2] * radians : 0); + + function forward(coordinates) { + coordinates = rotate(coordinates[0] * radians, coordinates[1] * radians); + return coordinates[0] *= degrees$1, coordinates[1] *= degrees$1, coordinates; + } + + forward.invert = function(coordinates) { + coordinates = rotate.invert(coordinates[0] * radians, coordinates[1] * radians); + return coordinates[0] *= degrees$1, coordinates[1] *= degrees$1, coordinates; + }; + + return forward; +} + +// Generates a circle centered at [0°, 0°], with a given radius and precision. +function circleStream(stream, radius, delta, direction, t0, t1) { + if (!delta) return; + var cosRadius = cos$1(radius), + sinRadius = sin$1(radius), + step = direction * delta; + if (t0 == null) { + t0 = radius + direction * tau$3; + t1 = radius - step / 2; + } else { + t0 = circleRadius(cosRadius, t0); + t1 = circleRadius(cosRadius, t1); + if (direction > 0 ? t0 < t1 : t0 > t1) t0 += direction * tau$3; + } + for (var point, t = t0; direction > 0 ? t > t1 : t < t1; t -= step) { + point = spherical([cosRadius, -sinRadius * cos$1(t), -sinRadius * sin$1(t)]); + stream.point(point[0], point[1]); + } +} + +// Returns the signed angle of a cartesian point relative to [cosRadius, 0, 0]. +function circleRadius(cosRadius, point) { + point = cartesian(point), point[0] -= cosRadius; + cartesianNormalizeInPlace(point); + var radius = acos(-point[1]); + return ((-point[2] < 0 ? -radius : radius) + tau$3 - epsilon$2) % tau$3; +} + +function circle() { + var center = constant$8([0, 0]), + radius = constant$8(90), + precision = constant$8(6), + ring, + rotate, + stream = {point: point}; + + function point(x, y) { + ring.push(x = rotate(x, y)); + x[0] *= degrees$1, x[1] *= degrees$1; + } + + function circle() { + var c = center.apply(this, arguments), + r = radius.apply(this, arguments) * radians, + p = precision.apply(this, arguments) * radians; + ring = []; + rotate = rotateRadians(-c[0] * radians, -c[1] * radians, 0).invert; + circleStream(stream, r, p, 1); + c = {type: "Polygon", coordinates: [ring]}; + ring = rotate = null; + return c; + } + + circle.center = function(_) { + return arguments.length ? (center = typeof _ === "function" ? _ : constant$8([+_[0], +_[1]]), circle) : center; + }; + + circle.radius = function(_) { + return arguments.length ? (radius = typeof _ === "function" ? _ : constant$8(+_), circle) : radius; + }; + + circle.precision = function(_) { + return arguments.length ? (precision = typeof _ === "function" ? _ : constant$8(+_), circle) : precision; + }; + + return circle; +} + +function clipBuffer() { + var lines = [], + line; + return { + point: function(x, y) { + line.push([x, y]); + }, + lineStart: function() { + lines.push(line = []); + }, + lineEnd: noop$2, + rejoin: function() { + if (lines.length > 1) lines.push(lines.pop().concat(lines.shift())); + }, + result: function() { + var result = lines; + lines = []; + line = null; + return result; + } + }; +} + +function pointEqual(a, b) { + return abs(a[0] - b[0]) < epsilon$2 && abs(a[1] - b[1]) < epsilon$2; +} + +function Intersection(point, points, other, entry) { + this.x = point; + this.z = points; + this.o = other; // another intersection + this.e = entry; // is an entry? + this.v = false; // visited + this.n = this.p = null; // next & previous +} + +// A generalized polygon clipping algorithm: given a polygon that has been cut +// into its visible line segments, and rejoins the segments by interpolating +// along the clip edge. +function clipRejoin(segments, compareIntersection, startInside, interpolate, stream) { + var subject = [], + clip = [], + i, + n; + + segments.forEach(function(segment) { + if ((n = segment.length - 1) <= 0) return; + var n, p0 = segment[0], p1 = segment[n], x; + + // If the first and last points of a segment are coincident, then treat as a + // closed ring. TODO if all rings are closed, then the winding order of the + // exterior ring should be checked. + if (pointEqual(p0, p1)) { + stream.lineStart(); + for (i = 0; i < n; ++i) stream.point((p0 = segment[i])[0], p0[1]); + stream.lineEnd(); + return; + } + + subject.push(x = new Intersection(p0, segment, null, true)); + clip.push(x.o = new Intersection(p0, null, x, false)); + subject.push(x = new Intersection(p1, segment, null, false)); + clip.push(x.o = new Intersection(p1, null, x, true)); + }); + + if (!subject.length) return; + + clip.sort(compareIntersection); + link$1(subject); + link$1(clip); + + for (i = 0, n = clip.length; i < n; ++i) { + clip[i].e = startInside = !startInside; + } + + var start = subject[0], + points, + point; + + while (1) { + // Find first unvisited intersection. + var current = start, + isSubject = true; + while (current.v) if ((current = current.n) === start) return; + points = current.z; + stream.lineStart(); + do { + current.v = current.o.v = true; + if (current.e) { + if (isSubject) { + for (i = 0, n = points.length; i < n; ++i) stream.point((point = points[i])[0], point[1]); + } else { + interpolate(current.x, current.n.x, 1, stream); + } + current = current.n; + } else { + if (isSubject) { + points = current.p.z; + for (i = points.length - 1; i >= 0; --i) stream.point((point = points[i])[0], point[1]); + } else { + interpolate(current.x, current.p.x, -1, stream); + } + current = current.p; + } + current = current.o; + points = current.z; + isSubject = !isSubject; + } while (!current.v); + stream.lineEnd(); + } +} + +function link$1(array) { + if (!(n = array.length)) return; + var n, + i = 0, + a = array[0], + b; + while (++i < n) { + a.n = b = array[i]; + b.p = a; + a = b; + } + a.n = b = array[0]; + b.p = a; +} + +var sum$1 = adder(); + +function polygonContains(polygon, point) { + var lambda = point[0], + phi = point[1], + sinPhi = sin$1(phi), + normal = [sin$1(lambda), -cos$1(lambda), 0], + angle = 0, + winding = 0; + + sum$1.reset(); + + if (sinPhi === 1) phi = halfPi$2 + epsilon$2; + else if (sinPhi === -1) phi = -halfPi$2 - epsilon$2; + + for (var i = 0, n = polygon.length; i < n; ++i) { + if (!(m = (ring = polygon[i]).length)) continue; + var ring, + m, + point0 = ring[m - 1], + lambda0 = point0[0], + phi0 = point0[1] / 2 + quarterPi, + sinPhi0 = sin$1(phi0), + cosPhi0 = cos$1(phi0); + + for (var j = 0; j < m; ++j, lambda0 = lambda1, sinPhi0 = sinPhi1, cosPhi0 = cosPhi1, point0 = point1) { + var point1 = ring[j], + lambda1 = point1[0], + phi1 = point1[1] / 2 + quarterPi, + sinPhi1 = sin$1(phi1), + cosPhi1 = cos$1(phi1), + delta = lambda1 - lambda0, + sign$$1 = delta >= 0 ? 1 : -1, + absDelta = sign$$1 * delta, + antimeridian = absDelta > pi$3, + k = sinPhi0 * sinPhi1; + + sum$1.add(atan2(k * sign$$1 * sin$1(absDelta), cosPhi0 * cosPhi1 + k * cos$1(absDelta))); + angle += antimeridian ? delta + sign$$1 * tau$3 : delta; + + // Are the longitudes either side of the point’s meridian (lambda), + // and are the latitudes smaller than the parallel (phi)? + if (antimeridian ^ lambda0 >= lambda ^ lambda1 >= lambda) { + var arc = cartesianCross(cartesian(point0), cartesian(point1)); + cartesianNormalizeInPlace(arc); + var intersection = cartesianCross(normal, arc); + cartesianNormalizeInPlace(intersection); + var phiArc = (antimeridian ^ delta >= 0 ? -1 : 1) * asin(intersection[2]); + if (phi > phiArc || phi === phiArc && (arc[0] || arc[1])) { + winding += antimeridian ^ delta >= 0 ? 1 : -1; + } + } + } + } + + // First, determine whether the South pole is inside or outside: + // + // It is inside if: + // * the polygon winds around it in a clockwise direction. + // * the polygon does not (cumulatively) wind around it, but has a negative + // (counter-clockwise) area. + // + // Second, count the (signed) number of times a segment crosses a lambda + // from the point to the South pole. If it is zero, then the point is the + // same side as the South pole. + + return (angle < -epsilon$2 || angle < epsilon$2 && sum$1 < -epsilon$2) ^ (winding & 1); +} + +function clip(pointVisible, clipLine, interpolate, start) { + return function(sink) { + var line = clipLine(sink), + ringBuffer = clipBuffer(), + ringSink = clipLine(ringBuffer), + polygonStarted = false, + polygon, + segments, + ring; + + var clip = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, + polygonStart: function() { + clip.point = pointRing; + clip.lineStart = ringStart; + clip.lineEnd = ringEnd; + segments = []; + polygon = []; + }, + polygonEnd: function() { + clip.point = point; + clip.lineStart = lineStart; + clip.lineEnd = lineEnd; + segments = merge(segments); + var startInside = polygonContains(polygon, start); + if (segments.length) { + if (!polygonStarted) sink.polygonStart(), polygonStarted = true; + clipRejoin(segments, compareIntersection, startInside, interpolate, sink); + } else if (startInside) { + if (!polygonStarted) sink.polygonStart(), polygonStarted = true; + sink.lineStart(); + interpolate(null, null, 1, sink); + sink.lineEnd(); + } + if (polygonStarted) sink.polygonEnd(), polygonStarted = false; + segments = polygon = null; + }, + sphere: function() { + sink.polygonStart(); + sink.lineStart(); + interpolate(null, null, 1, sink); + sink.lineEnd(); + sink.polygonEnd(); + } + }; + + function point(lambda, phi) { + if (pointVisible(lambda, phi)) sink.point(lambda, phi); + } + + function pointLine(lambda, phi) { + line.point(lambda, phi); + } + + function lineStart() { + clip.point = pointLine; + line.lineStart(); + } + + function lineEnd() { + clip.point = point; + line.lineEnd(); + } + + function pointRing(lambda, phi) { + ring.push([lambda, phi]); + ringSink.point(lambda, phi); + } + + function ringStart() { + ringSink.lineStart(); + ring = []; + } + + function ringEnd() { + pointRing(ring[0][0], ring[0][1]); + ringSink.lineEnd(); + + var clean = ringSink.clean(), + ringSegments = ringBuffer.result(), + i, n = ringSegments.length, m, + segment, + point; + + ring.pop(); + polygon.push(ring); + ring = null; + + if (!n) return; + + // No intersections. + if (clean & 1) { + segment = ringSegments[0]; + if ((m = segment.length - 1) > 0) { + if (!polygonStarted) sink.polygonStart(), polygonStarted = true; + sink.lineStart(); + for (i = 0; i < m; ++i) sink.point((point = segment[i])[0], point[1]); + sink.lineEnd(); + } + return; + } + + // Rejoin connected segments. + // TODO reuse ringBuffer.rejoin()? + if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift())); + + segments.push(ringSegments.filter(validSegment)); + } + + return clip; + }; +} + +function validSegment(segment) { + return segment.length > 1; +} + +// Intersections are sorted along the clip edge. For both antimeridian cutting +// and circle clipping, the same comparison is used. +function compareIntersection(a, b) { + return ((a = a.x)[0] < 0 ? a[1] - halfPi$2 - epsilon$2 : halfPi$2 - a[1]) + - ((b = b.x)[0] < 0 ? b[1] - halfPi$2 - epsilon$2 : halfPi$2 - b[1]); +} + +var clipAntimeridian = clip( + function() { return true; }, + clipAntimeridianLine, + clipAntimeridianInterpolate, + [-pi$3, -halfPi$2] +); + +// Takes a line and cuts into visible segments. Return values: 0 - there were +// intersections or the line was empty; 1 - no intersections; 2 - there were +// intersections, and the first and last segments should be rejoined. +function clipAntimeridianLine(stream) { + var lambda0 = NaN, + phi0 = NaN, + sign0 = NaN, + clean; // no intersections + + return { + lineStart: function() { + stream.lineStart(); + clean = 1; + }, + point: function(lambda1, phi1) { + var sign1 = lambda1 > 0 ? pi$3 : -pi$3, + delta = abs(lambda1 - lambda0); + if (abs(delta - pi$3) < epsilon$2) { // line crosses a pole + stream.point(lambda0, phi0 = (phi0 + phi1) / 2 > 0 ? halfPi$2 : -halfPi$2); + stream.point(sign0, phi0); + stream.lineEnd(); + stream.lineStart(); + stream.point(sign1, phi0); + stream.point(lambda1, phi0); + clean = 0; + } else if (sign0 !== sign1 && delta >= pi$3) { // line crosses antimeridian + if (abs(lambda0 - sign0) < epsilon$2) lambda0 -= sign0 * epsilon$2; // handle degeneracies + if (abs(lambda1 - sign1) < epsilon$2) lambda1 -= sign1 * epsilon$2; + phi0 = clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1); + stream.point(sign0, phi0); + stream.lineEnd(); + stream.lineStart(); + stream.point(sign1, phi0); + clean = 0; + } + stream.point(lambda0 = lambda1, phi0 = phi1); + sign0 = sign1; + }, + lineEnd: function() { + stream.lineEnd(); + lambda0 = phi0 = NaN; + }, + clean: function() { + return 2 - clean; // if intersections, rejoin first and last segments + } + }; +} + +function clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1) { + var cosPhi0, + cosPhi1, + sinLambda0Lambda1 = sin$1(lambda0 - lambda1); + return abs(sinLambda0Lambda1) > epsilon$2 + ? atan((sin$1(phi0) * (cosPhi1 = cos$1(phi1)) * sin$1(lambda1) + - sin$1(phi1) * (cosPhi0 = cos$1(phi0)) * sin$1(lambda0)) + / (cosPhi0 * cosPhi1 * sinLambda0Lambda1)) + : (phi0 + phi1) / 2; +} + +function clipAntimeridianInterpolate(from, to, direction, stream) { + var phi; + if (from == null) { + phi = direction * halfPi$2; + stream.point(-pi$3, phi); + stream.point(0, phi); + stream.point(pi$3, phi); + stream.point(pi$3, 0); + stream.point(pi$3, -phi); + stream.point(0, -phi); + stream.point(-pi$3, -phi); + stream.point(-pi$3, 0); + stream.point(-pi$3, phi); + } else if (abs(from[0] - to[0]) > epsilon$2) { + var lambda = from[0] < to[0] ? pi$3 : -pi$3; + phi = direction * lambda / 2; + stream.point(-lambda, phi); + stream.point(0, phi); + stream.point(lambda, phi); + } else { + stream.point(to[0], to[1]); + } +} + +function clipCircle(radius) { + var cr = cos$1(radius), + delta = 6 * radians, + smallRadius = cr > 0, + notHemisphere = abs(cr) > epsilon$2; // TODO optimise for this common case + + function interpolate(from, to, direction, stream) { + circleStream(stream, radius, delta, direction, from, to); + } + + function visible(lambda, phi) { + return cos$1(lambda) * cos$1(phi) > cr; + } + + // Takes a line and cuts into visible segments. Return values used for polygon + // clipping: 0 - there were intersections or the line was empty; 1 - no + // intersections 2 - there were intersections, and the first and last segments + // should be rejoined. + function clipLine(stream) { + var point0, // previous point + c0, // code for previous point + v0, // visibility of previous point + v00, // visibility of first point + clean; // no intersections + return { + lineStart: function() { + v00 = v0 = false; + clean = 1; + }, + point: function(lambda, phi) { + var point1 = [lambda, phi], + point2, + v = visible(lambda, phi), + c = smallRadius + ? v ? 0 : code(lambda, phi) + : v ? code(lambda + (lambda < 0 ? pi$3 : -pi$3), phi) : 0; + if (!point0 && (v00 = v0 = v)) stream.lineStart(); + // Handle degeneracies. + // TODO ignore if not clipping polygons. + if (v !== v0) { + point2 = intersect(point0, point1); + if (!point2 || pointEqual(point0, point2) || pointEqual(point1, point2)) { + point1[0] += epsilon$2; + point1[1] += epsilon$2; + v = visible(point1[0], point1[1]); + } + } + if (v !== v0) { + clean = 0; + if (v) { + // outside going in + stream.lineStart(); + point2 = intersect(point1, point0); + stream.point(point2[0], point2[1]); + } else { + // inside going out + point2 = intersect(point0, point1); + stream.point(point2[0], point2[1]); + stream.lineEnd(); + } + point0 = point2; + } else if (notHemisphere && point0 && smallRadius ^ v) { + var t; + // If the codes for two points are different, or are both zero, + // and there this segment intersects with the small circle. + if (!(c & c0) && (t = intersect(point1, point0, true))) { + clean = 0; + if (smallRadius) { + stream.lineStart(); + stream.point(t[0][0], t[0][1]); + stream.point(t[1][0], t[1][1]); + stream.lineEnd(); + } else { + stream.point(t[1][0], t[1][1]); + stream.lineEnd(); + stream.lineStart(); + stream.point(t[0][0], t[0][1]); + } + } + } + if (v && (!point0 || !pointEqual(point0, point1))) { + stream.point(point1[0], point1[1]); + } + point0 = point1, v0 = v, c0 = c; + }, + lineEnd: function() { + if (v0) stream.lineEnd(); + point0 = null; + }, + // Rejoin first and last segments if there were intersections and the first + // and last points were visible. + clean: function() { + return clean | ((v00 && v0) << 1); + } + }; + } + + // Intersects the great circle between a and b with the clip circle. + function intersect(a, b, two) { + var pa = cartesian(a), + pb = cartesian(b); + + // We have two planes, n1.p = d1 and n2.p = d2. + // Find intersection line p(t) = c1 n1 + c2 n2 + t (n1 ⨯ n2). + var n1 = [1, 0, 0], // normal + n2 = cartesianCross(pa, pb), + n2n2 = cartesianDot(n2, n2), + n1n2 = n2[0], // cartesianDot(n1, n2), + determinant = n2n2 - n1n2 * n1n2; + + // Two polar points. + if (!determinant) return !two && a; + + var c1 = cr * n2n2 / determinant, + c2 = -cr * n1n2 / determinant, + n1xn2 = cartesianCross(n1, n2), + A = cartesianScale(n1, c1), + B = cartesianScale(n2, c2); + cartesianAddInPlace(A, B); + + // Solve |p(t)|^2 = 1. + var u = n1xn2, + w = cartesianDot(A, u), + uu = cartesianDot(u, u), + t2 = w * w - uu * (cartesianDot(A, A) - 1); + + if (t2 < 0) return; + + var t = sqrt(t2), + q = cartesianScale(u, (-w - t) / uu); + cartesianAddInPlace(q, A); + q = spherical(q); + + if (!two) return q; + + // Two intersection points. + var lambda0 = a[0], + lambda1 = b[0], + phi0 = a[1], + phi1 = b[1], + z; + + if (lambda1 < lambda0) z = lambda0, lambda0 = lambda1, lambda1 = z; + + var delta = lambda1 - lambda0, + polar = abs(delta - pi$3) < epsilon$2, + meridian = polar || delta < epsilon$2; + + if (!polar && phi1 < phi0) z = phi0, phi0 = phi1, phi1 = z; + + // Check that the first point is between a and b. + if (meridian + ? polar + ? phi0 + phi1 > 0 ^ q[1] < (abs(q[0] - lambda0) < epsilon$2 ? phi0 : phi1) + : phi0 <= q[1] && q[1] <= phi1 + : delta > pi$3 ^ (lambda0 <= q[0] && q[0] <= lambda1)) { + var q1 = cartesianScale(u, (-w + t) / uu); + cartesianAddInPlace(q1, A); + return [q, spherical(q1)]; + } + } + + // Generates a 4-bit vector representing the location of a point relative to + // the small circle's bounding box. + function code(lambda, phi) { + var r = smallRadius ? radius : pi$3 - radius, + code = 0; + if (lambda < -r) code |= 1; // left + else if (lambda > r) code |= 2; // right + if (phi < -r) code |= 4; // below + else if (phi > r) code |= 8; // above + return code; + } + + return clip(visible, clipLine, interpolate, smallRadius ? [0, -radius] : [-pi$3, radius - pi$3]); +} + +function clipLine(a, b, x0, y0, x1, y1) { + var ax = a[0], + ay = a[1], + bx = b[0], + by = b[1], + t0 = 0, + t1 = 1, + dx = bx - ax, + dy = by - ay, + r; + + r = x0 - ax; + if (!dx && r > 0) return; + r /= dx; + if (dx < 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } else if (dx > 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } + + r = x1 - ax; + if (!dx && r < 0) return; + r /= dx; + if (dx < 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } else if (dx > 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } + + r = y0 - ay; + if (!dy && r > 0) return; + r /= dy; + if (dy < 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } else if (dy > 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } + + r = y1 - ay; + if (!dy && r < 0) return; + r /= dy; + if (dy < 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } else if (dy > 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } + + if (t0 > 0) a[0] = ax + t0 * dx, a[1] = ay + t0 * dy; + if (t1 < 1) b[0] = ax + t1 * dx, b[1] = ay + t1 * dy; + return true; +} + +var clipMax = 1e9, clipMin = -clipMax; + +// TODO Use d3-polygon’s polygonContains here for the ring check? +// TODO Eliminate duplicate buffering in clipBuffer and polygon.push? + +function clipRectangle(x0, y0, x1, y1) { + + function visible(x, y) { + return x0 <= x && x <= x1 && y0 <= y && y <= y1; + } + + function interpolate(from, to, direction, stream) { + var a = 0, a1 = 0; + if (from == null + || (a = corner(from, direction)) !== (a1 = corner(to, direction)) + || comparePoint(from, to) < 0 ^ direction > 0) { + do stream.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0); + while ((a = (a + direction + 4) % 4) !== a1); + } else { + stream.point(to[0], to[1]); + } + } + + function corner(p, direction) { + return abs(p[0] - x0) < epsilon$2 ? direction > 0 ? 0 : 3 + : abs(p[0] - x1) < epsilon$2 ? direction > 0 ? 2 : 1 + : abs(p[1] - y0) < epsilon$2 ? direction > 0 ? 1 : 0 + : direction > 0 ? 3 : 2; // abs(p[1] - y1) < epsilon + } + + function compareIntersection(a, b) { + return comparePoint(a.x, b.x); + } + + function comparePoint(a, b) { + var ca = corner(a, 1), + cb = corner(b, 1); + return ca !== cb ? ca - cb + : ca === 0 ? b[1] - a[1] + : ca === 1 ? a[0] - b[0] + : ca === 2 ? a[1] - b[1] + : b[0] - a[0]; + } + + return function(stream) { + var activeStream = stream, + bufferStream = clipBuffer(), + segments, + polygon, + ring, + x__, y__, v__, // first point + x_, y_, v_, // previous point + first, + clean; + + var clipStream = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, + polygonStart: polygonStart, + polygonEnd: polygonEnd + }; + + function point(x, y) { + if (visible(x, y)) activeStream.point(x, y); + } + + function polygonInside() { + var winding = 0; + + for (var i = 0, n = polygon.length; i < n; ++i) { + for (var ring = polygon[i], j = 1, m = ring.length, point = ring[0], a0, a1, b0 = point[0], b1 = point[1]; j < m; ++j) { + a0 = b0, a1 = b1, point = ring[j], b0 = point[0], b1 = point[1]; + if (a1 <= y1) { if (b1 > y1 && (b0 - a0) * (y1 - a1) > (b1 - a1) * (x0 - a0)) ++winding; } + else { if (b1 <= y1 && (b0 - a0) * (y1 - a1) < (b1 - a1) * (x0 - a0)) --winding; } + } + } + + return winding; + } + + // Buffer geometry within a polygon and then clip it en masse. + function polygonStart() { + activeStream = bufferStream, segments = [], polygon = [], clean = true; + } + + function polygonEnd() { + var startInside = polygonInside(), + cleanInside = clean && startInside, + visible = (segments = merge(segments)).length; + if (cleanInside || visible) { + stream.polygonStart(); + if (cleanInside) { + stream.lineStart(); + interpolate(null, null, 1, stream); + stream.lineEnd(); + } + if (visible) { + clipRejoin(segments, compareIntersection, startInside, interpolate, stream); + } + stream.polygonEnd(); + } + activeStream = stream, segments = polygon = ring = null; + } + + function lineStart() { + clipStream.point = linePoint; + if (polygon) polygon.push(ring = []); + first = true; + v_ = false; + x_ = y_ = NaN; + } + + // TODO rather than special-case polygons, simply handle them separately. + // Ideally, coincident intersection points should be jittered to avoid + // clipping issues. + function lineEnd() { + if (segments) { + linePoint(x__, y__); + if (v__ && v_) bufferStream.rejoin(); + segments.push(bufferStream.result()); + } + clipStream.point = point; + if (v_) activeStream.lineEnd(); + } + + function linePoint(x, y) { + var v = visible(x, y); + if (polygon) ring.push([x, y]); + if (first) { + x__ = x, y__ = y, v__ = v; + first = false; + if (v) { + activeStream.lineStart(); + activeStream.point(x, y); + } + } else { + if (v && v_) activeStream.point(x, y); + else { + var a = [x_ = Math.max(clipMin, Math.min(clipMax, x_)), y_ = Math.max(clipMin, Math.min(clipMax, y_))], + b = [x = Math.max(clipMin, Math.min(clipMax, x)), y = Math.max(clipMin, Math.min(clipMax, y))]; + if (clipLine(a, b, x0, y0, x1, y1)) { + if (!v_) { + activeStream.lineStart(); + activeStream.point(a[0], a[1]); + } + activeStream.point(b[0], b[1]); + if (!v) activeStream.lineEnd(); + clean = false; + } else if (v) { + activeStream.lineStart(); + activeStream.point(x, y); + clean = false; + } + } + } + x_ = x, y_ = y, v_ = v; + } + + return clipStream; + }; +} + +function extent$1() { + var x0 = 0, + y0 = 0, + x1 = 960, + y1 = 500, + cache, + cacheStream, + clip; + + return clip = { + stream: function(stream) { + return cache && cacheStream === stream ? cache : cache = clipRectangle(x0, y0, x1, y1)(cacheStream = stream); + }, + extent: function(_) { + return arguments.length ? (x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1], cache = cacheStream = null, clip) : [[x0, y0], [x1, y1]]; + } + }; +} + +var lengthSum = adder(), + lambda0$2, + sinPhi0$1, + cosPhi0$1; + +var lengthStream = { + sphere: noop$2, + point: noop$2, + lineStart: lengthLineStart, + lineEnd: noop$2, + polygonStart: noop$2, + polygonEnd: noop$2 +}; + +function lengthLineStart() { + lengthStream.point = lengthPointFirst; + lengthStream.lineEnd = lengthLineEnd; +} + +function lengthLineEnd() { + lengthStream.point = lengthStream.lineEnd = noop$2; +} + +function lengthPointFirst(lambda, phi) { + lambda *= radians, phi *= radians; + lambda0$2 = lambda, sinPhi0$1 = sin$1(phi), cosPhi0$1 = cos$1(phi); + lengthStream.point = lengthPoint; +} + +function lengthPoint(lambda, phi) { + lambda *= radians, phi *= radians; + var sinPhi = sin$1(phi), + cosPhi = cos$1(phi), + delta = abs(lambda - lambda0$2), + cosDelta = cos$1(delta), + sinDelta = sin$1(delta), + x = cosPhi * sinDelta, + y = cosPhi0$1 * sinPhi - sinPhi0$1 * cosPhi * cosDelta, + z = sinPhi0$1 * sinPhi + cosPhi0$1 * cosPhi * cosDelta; + lengthSum.add(atan2(sqrt(x * x + y * y), z)); + lambda0$2 = lambda, sinPhi0$1 = sinPhi, cosPhi0$1 = cosPhi; +} + +function length$1(object) { + lengthSum.reset(); + geoStream(object, lengthStream); + return +lengthSum; +} + +var coordinates = [null, null], + object$1 = {type: "LineString", coordinates: coordinates}; + +function distance(a, b) { + coordinates[0] = a; + coordinates[1] = b; + return length$1(object$1); +} + +var containsObjectType = { + Feature: function(object, point) { + return containsGeometry(object.geometry, point); + }, + FeatureCollection: function(object, point) { + var features = object.features, i = -1, n = features.length; + while (++i < n) if (containsGeometry(features[i].geometry, point)) return true; + return false; + } +}; + +var containsGeometryType = { + Sphere: function() { + return true; + }, + Point: function(object, point) { + return containsPoint(object.coordinates, point); + }, + MultiPoint: function(object, point) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) if (containsPoint(coordinates[i], point)) return true; + return false; + }, + LineString: function(object, point) { + return containsLine(object.coordinates, point); + }, + MultiLineString: function(object, point) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) if (containsLine(coordinates[i], point)) return true; + return false; + }, + Polygon: function(object, point) { + return containsPolygon(object.coordinates, point); + }, + MultiPolygon: function(object, point) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) if (containsPolygon(coordinates[i], point)) return true; + return false; + }, + GeometryCollection: function(object, point) { + var geometries = object.geometries, i = -1, n = geometries.length; + while (++i < n) if (containsGeometry(geometries[i], point)) return true; + return false; + } +}; + +function containsGeometry(geometry, point) { + return geometry && containsGeometryType.hasOwnProperty(geometry.type) + ? containsGeometryType[geometry.type](geometry, point) + : false; +} + +function containsPoint(coordinates, point) { + return distance(coordinates, point) === 0; +} + +function containsLine(coordinates, point) { + var ab = distance(coordinates[0], coordinates[1]), + ao = distance(coordinates[0], point), + ob = distance(point, coordinates[1]); + return ao + ob <= ab + epsilon$2; +} + +function containsPolygon(coordinates, point) { + return !!polygonContains(coordinates.map(ringRadians), pointRadians(point)); +} + +function ringRadians(ring) { + return ring = ring.map(pointRadians), ring.pop(), ring; +} + +function pointRadians(point) { + return [point[0] * radians, point[1] * radians]; +} + +function contains$1(object, point) { + return (object && containsObjectType.hasOwnProperty(object.type) + ? containsObjectType[object.type] + : containsGeometry)(object, point); +} + +function graticuleX(y0, y1, dy) { + var y = sequence(y0, y1 - epsilon$2, dy).concat(y1); + return function(x) { return y.map(function(y) { return [x, y]; }); }; +} + +function graticuleY(x0, x1, dx) { + var x = sequence(x0, x1 - epsilon$2, dx).concat(x1); + return function(y) { return x.map(function(x) { return [x, y]; }); }; +} + +function graticule() { + var x1, x0, X1, X0, + y1, y0, Y1, Y0, + dx = 10, dy = dx, DX = 90, DY = 360, + x, y, X, Y, + precision = 2.5; + + function graticule() { + return {type: "MultiLineString", coordinates: lines()}; + } + + function lines() { + return sequence(ceil(X0 / DX) * DX, X1, DX).map(X) + .concat(sequence(ceil(Y0 / DY) * DY, Y1, DY).map(Y)) + .concat(sequence(ceil(x0 / dx) * dx, x1, dx).filter(function(x) { return abs(x % DX) > epsilon$2; }).map(x)) + .concat(sequence(ceil(y0 / dy) * dy, y1, dy).filter(function(y) { return abs(y % DY) > epsilon$2; }).map(y)); + } + + graticule.lines = function() { + return lines().map(function(coordinates) { return {type: "LineString", coordinates: coordinates}; }); + }; + + graticule.outline = function() { + return { + type: "Polygon", + coordinates: [ + X(X0).concat( + Y(Y1).slice(1), + X(X1).reverse().slice(1), + Y(Y0).reverse().slice(1)) + ] + }; + }; + + graticule.extent = function(_) { + if (!arguments.length) return graticule.extentMinor(); + return graticule.extentMajor(_).extentMinor(_); + }; + + graticule.extentMajor = function(_) { + if (!arguments.length) return [[X0, Y0], [X1, Y1]]; + X0 = +_[0][0], X1 = +_[1][0]; + Y0 = +_[0][1], Y1 = +_[1][1]; + if (X0 > X1) _ = X0, X0 = X1, X1 = _; + if (Y0 > Y1) _ = Y0, Y0 = Y1, Y1 = _; + return graticule.precision(precision); + }; + + graticule.extentMinor = function(_) { + if (!arguments.length) return [[x0, y0], [x1, y1]]; + x0 = +_[0][0], x1 = +_[1][0]; + y0 = +_[0][1], y1 = +_[1][1]; + if (x0 > x1) _ = x0, x0 = x1, x1 = _; + if (y0 > y1) _ = y0, y0 = y1, y1 = _; + return graticule.precision(precision); + }; + + graticule.step = function(_) { + if (!arguments.length) return graticule.stepMinor(); + return graticule.stepMajor(_).stepMinor(_); + }; + + graticule.stepMajor = function(_) { + if (!arguments.length) return [DX, DY]; + DX = +_[0], DY = +_[1]; + return graticule; + }; + + graticule.stepMinor = function(_) { + if (!arguments.length) return [dx, dy]; + dx = +_[0], dy = +_[1]; + return graticule; + }; + + graticule.precision = function(_) { + if (!arguments.length) return precision; + precision = +_; + x = graticuleX(y0, y1, 90); + y = graticuleY(x0, x1, precision); + X = graticuleX(Y0, Y1, 90); + Y = graticuleY(X0, X1, precision); + return graticule; + }; + + return graticule + .extentMajor([[-180, -90 + epsilon$2], [180, 90 - epsilon$2]]) + .extentMinor([[-180, -80 - epsilon$2], [180, 80 + epsilon$2]]); +} + +function graticule10() { + return graticule()(); +} + +function interpolate$1(a, b) { + var x0 = a[0] * radians, + y0 = a[1] * radians, + x1 = b[0] * radians, + y1 = b[1] * radians, + cy0 = cos$1(y0), + sy0 = sin$1(y0), + cy1 = cos$1(y1), + sy1 = sin$1(y1), + kx0 = cy0 * cos$1(x0), + ky0 = cy0 * sin$1(x0), + kx1 = cy1 * cos$1(x1), + ky1 = cy1 * sin$1(x1), + d = 2 * asin(sqrt(haversin(y1 - y0) + cy0 * cy1 * haversin(x1 - x0))), + k = sin$1(d); + + var interpolate = d ? function(t) { + var B = sin$1(t *= d) / k, + A = sin$1(d - t) / k, + x = A * kx0 + B * kx1, + y = A * ky0 + B * ky1, + z = A * sy0 + B * sy1; + return [ + atan2(y, x) * degrees$1, + atan2(z, sqrt(x * x + y * y)) * degrees$1 + ]; + } : function() { + return [x0 * degrees$1, y0 * degrees$1]; + }; + + interpolate.distance = d; + + return interpolate; +} + +function identity$4(x) { + return x; +} + +var areaSum$1 = adder(), + areaRingSum$1 = adder(), + x00, + y00, + x0$1, + y0$1; + +var areaStream$1 = { + point: noop$2, + lineStart: noop$2, + lineEnd: noop$2, + polygonStart: function() { + areaStream$1.lineStart = areaRingStart$1; + areaStream$1.lineEnd = areaRingEnd$1; + }, + polygonEnd: function() { + areaStream$1.lineStart = areaStream$1.lineEnd = areaStream$1.point = noop$2; + areaSum$1.add(abs(areaRingSum$1)); + areaRingSum$1.reset(); + }, + result: function() { + var area = areaSum$1 / 2; + areaSum$1.reset(); + return area; + } +}; + +function areaRingStart$1() { + areaStream$1.point = areaPointFirst$1; +} + +function areaPointFirst$1(x, y) { + areaStream$1.point = areaPoint$1; + x00 = x0$1 = x, y00 = y0$1 = y; +} + +function areaPoint$1(x, y) { + areaRingSum$1.add(y0$1 * x - x0$1 * y); + x0$1 = x, y0$1 = y; +} + +function areaRingEnd$1() { + areaPoint$1(x00, y00); +} + +var x0$2 = Infinity, + y0$2 = x0$2, + x1 = -x0$2, + y1 = x1; + +var boundsStream$1 = { + point: boundsPoint$1, + lineStart: noop$2, + lineEnd: noop$2, + polygonStart: noop$2, + polygonEnd: noop$2, + result: function() { + var bounds = [[x0$2, y0$2], [x1, y1]]; + x1 = y1 = -(y0$2 = x0$2 = Infinity); + return bounds; + } +}; + +function boundsPoint$1(x, y) { + if (x < x0$2) x0$2 = x; + if (x > x1) x1 = x; + if (y < y0$2) y0$2 = y; + if (y > y1) y1 = y; +} + +// TODO Enforce positive area for exterior, negative area for interior? + +var X0$1 = 0, + Y0$1 = 0, + Z0$1 = 0, + X1$1 = 0, + Y1$1 = 0, + Z1$1 = 0, + X2$1 = 0, + Y2$1 = 0, + Z2$1 = 0, + x00$1, + y00$1, + x0$3, + y0$3; + +var centroidStream$1 = { + point: centroidPoint$1, + lineStart: centroidLineStart$1, + lineEnd: centroidLineEnd$1, + polygonStart: function() { + centroidStream$1.lineStart = centroidRingStart$1; + centroidStream$1.lineEnd = centroidRingEnd$1; + }, + polygonEnd: function() { + centroidStream$1.point = centroidPoint$1; + centroidStream$1.lineStart = centroidLineStart$1; + centroidStream$1.lineEnd = centroidLineEnd$1; + }, + result: function() { + var centroid = Z2$1 ? [X2$1 / Z2$1, Y2$1 / Z2$1] + : Z1$1 ? [X1$1 / Z1$1, Y1$1 / Z1$1] + : Z0$1 ? [X0$1 / Z0$1, Y0$1 / Z0$1] + : [NaN, NaN]; + X0$1 = Y0$1 = Z0$1 = + X1$1 = Y1$1 = Z1$1 = + X2$1 = Y2$1 = Z2$1 = 0; + return centroid; + } +}; + +function centroidPoint$1(x, y) { + X0$1 += x; + Y0$1 += y; + ++Z0$1; +} + +function centroidLineStart$1() { + centroidStream$1.point = centroidPointFirstLine; +} + +function centroidPointFirstLine(x, y) { + centroidStream$1.point = centroidPointLine; + centroidPoint$1(x0$3 = x, y0$3 = y); +} + +function centroidPointLine(x, y) { + var dx = x - x0$3, dy = y - y0$3, z = sqrt(dx * dx + dy * dy); + X1$1 += z * (x0$3 + x) / 2; + Y1$1 += z * (y0$3 + y) / 2; + Z1$1 += z; + centroidPoint$1(x0$3 = x, y0$3 = y); +} + +function centroidLineEnd$1() { + centroidStream$1.point = centroidPoint$1; +} + +function centroidRingStart$1() { + centroidStream$1.point = centroidPointFirstRing; +} + +function centroidRingEnd$1() { + centroidPointRing(x00$1, y00$1); +} + +function centroidPointFirstRing(x, y) { + centroidStream$1.point = centroidPointRing; + centroidPoint$1(x00$1 = x0$3 = x, y00$1 = y0$3 = y); +} + +function centroidPointRing(x, y) { + var dx = x - x0$3, + dy = y - y0$3, + z = sqrt(dx * dx + dy * dy); + + X1$1 += z * (x0$3 + x) / 2; + Y1$1 += z * (y0$3 + y) / 2; + Z1$1 += z; + + z = y0$3 * x - x0$3 * y; + X2$1 += z * (x0$3 + x); + Y2$1 += z * (y0$3 + y); + Z2$1 += z * 3; + centroidPoint$1(x0$3 = x, y0$3 = y); +} + +function PathContext(context) { + this._context = context; +} + +PathContext.prototype = { + _radius: 4.5, + pointRadius: function(_) { + return this._radius = _, this; + }, + polygonStart: function() { + this._line = 0; + }, + polygonEnd: function() { + this._line = NaN; + }, + lineStart: function() { + this._point = 0; + }, + lineEnd: function() { + if (this._line === 0) this._context.closePath(); + this._point = NaN; + }, + point: function(x, y) { + switch (this._point) { + case 0: { + this._context.moveTo(x, y); + this._point = 1; + break; + } + case 1: { + this._context.lineTo(x, y); + break; + } + default: { + this._context.moveTo(x + this._radius, y); + this._context.arc(x, y, this._radius, 0, tau$3); + break; + } + } + }, + result: noop$2 +}; + +var lengthSum$1 = adder(), + lengthRing, + x00$2, + y00$2, + x0$4, + y0$4; + +var lengthStream$1 = { + point: noop$2, + lineStart: function() { + lengthStream$1.point = lengthPointFirst$1; + }, + lineEnd: function() { + if (lengthRing) lengthPoint$1(x00$2, y00$2); + lengthStream$1.point = noop$2; + }, + polygonStart: function() { + lengthRing = true; + }, + polygonEnd: function() { + lengthRing = null; + }, + result: function() { + var length = +lengthSum$1; + lengthSum$1.reset(); + return length; + } +}; + +function lengthPointFirst$1(x, y) { + lengthStream$1.point = lengthPoint$1; + x00$2 = x0$4 = x, y00$2 = y0$4 = y; +} + +function lengthPoint$1(x, y) { + x0$4 -= x, y0$4 -= y; + lengthSum$1.add(sqrt(x0$4 * x0$4 + y0$4 * y0$4)); + x0$4 = x, y0$4 = y; +} + +function PathString() { + this._string = []; +} + +PathString.prototype = { + _radius: 4.5, + _circle: circle$1(4.5), + pointRadius: function(_) { + if ((_ = +_) !== this._radius) this._radius = _, this._circle = null; + return this; + }, + polygonStart: function() { + this._line = 0; + }, + polygonEnd: function() { + this._line = NaN; + }, + lineStart: function() { + this._point = 0; + }, + lineEnd: function() { + if (this._line === 0) this._string.push("Z"); + this._point = NaN; + }, + point: function(x, y) { + switch (this._point) { + case 0: { + this._string.push("M", x, ",", y); + this._point = 1; + break; + } + case 1: { + this._string.push("L", x, ",", y); + break; + } + default: { + if (this._circle == null) this._circle = circle$1(this._radius); + this._string.push("M", x, ",", y, this._circle); + break; + } + } + }, + result: function() { + if (this._string.length) { + var result = this._string.join(""); + this._string = []; + return result; + } else { + return null; + } + } +}; + +function circle$1(radius) { + return "m0," + radius + + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius + + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius + + "z"; +} + +function index$1(projection, context) { + var pointRadius = 4.5, + projectionStream, + contextStream; + + function path(object) { + if (object) { + if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments)); + geoStream(object, projectionStream(contextStream)); + } + return contextStream.result(); + } + + path.area = function(object) { + geoStream(object, projectionStream(areaStream$1)); + return areaStream$1.result(); + }; + + path.measure = function(object) { + geoStream(object, projectionStream(lengthStream$1)); + return lengthStream$1.result(); + }; + + path.bounds = function(object) { + geoStream(object, projectionStream(boundsStream$1)); + return boundsStream$1.result(); + }; + + path.centroid = function(object) { + geoStream(object, projectionStream(centroidStream$1)); + return centroidStream$1.result(); + }; + + path.projection = function(_) { + return arguments.length ? (projectionStream = _ == null ? (projection = null, identity$4) : (projection = _).stream, path) : projection; + }; + + path.context = function(_) { + if (!arguments.length) return context; + contextStream = _ == null ? (context = null, new PathString) : new PathContext(context = _); + if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius); + return path; + }; + + path.pointRadius = function(_) { + if (!arguments.length) return pointRadius; + pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_); + return path; + }; + + return path.projection(projection).context(context); +} + +function transform(methods) { + return { + stream: transformer(methods) + }; +} + +function transformer(methods) { + return function(stream) { + var s = new TransformStream; + for (var key in methods) s[key] = methods[key]; + s.stream = stream; + return s; + }; +} + +function TransformStream() {} + +TransformStream.prototype = { + constructor: TransformStream, + point: function(x, y) { this.stream.point(x, y); }, + sphere: function() { this.stream.sphere(); }, + lineStart: function() { this.stream.lineStart(); }, + lineEnd: function() { this.stream.lineEnd(); }, + polygonStart: function() { this.stream.polygonStart(); }, + polygonEnd: function() { this.stream.polygonEnd(); } +}; + +function fit(projection, fitBounds, object) { + var clip = projection.clipExtent && projection.clipExtent(); + projection.scale(150).translate([0, 0]); + if (clip != null) projection.clipExtent(null); + geoStream(object, projection.stream(boundsStream$1)); + fitBounds(boundsStream$1.result()); + if (clip != null) projection.clipExtent(clip); + return projection; +} + +function fitExtent(projection, extent, object) { + return fit(projection, function(b) { + var w = extent[1][0] - extent[0][0], + h = extent[1][1] - extent[0][1], + k = Math.min(w / (b[1][0] - b[0][0]), h / (b[1][1] - b[0][1])), + x = +extent[0][0] + (w - k * (b[1][0] + b[0][0])) / 2, + y = +extent[0][1] + (h - k * (b[1][1] + b[0][1])) / 2; + projection.scale(150 * k).translate([x, y]); + }, object); +} + +function fitSize(projection, size, object) { + return fitExtent(projection, [[0, 0], size], object); +} + +function fitWidth(projection, width, object) { + return fit(projection, function(b) { + var w = +width, + k = w / (b[1][0] - b[0][0]), + x = (w - k * (b[1][0] + b[0][0])) / 2, + y = -k * b[0][1]; + projection.scale(150 * k).translate([x, y]); + }, object); +} + +function fitHeight(projection, height, object) { + return fit(projection, function(b) { + var h = +height, + k = h / (b[1][1] - b[0][1]), + x = -k * b[0][0], + y = (h - k * (b[1][1] + b[0][1])) / 2; + projection.scale(150 * k).translate([x, y]); + }, object); +} + +var maxDepth = 16, // maximum depth of subdivision + cosMinDistance = cos$1(30 * radians); // cos(minimum angular distance) + +function resample(project, delta2) { + return +delta2 ? resample$1(project, delta2) : resampleNone(project); +} + +function resampleNone(project) { + return transformer({ + point: function(x, y) { + x = project(x, y); + this.stream.point(x[0], x[1]); + } + }); +} + +function resample$1(project, delta2) { + + function resampleLineTo(x0, y0, lambda0, a0, b0, c0, x1, y1, lambda1, a1, b1, c1, depth, stream) { + var dx = x1 - x0, + dy = y1 - y0, + d2 = dx * dx + dy * dy; + if (d2 > 4 * delta2 && depth--) { + var a = a0 + a1, + b = b0 + b1, + c = c0 + c1, + m = sqrt(a * a + b * b + c * c), + phi2 = asin(c /= m), + lambda2 = abs(abs(c) - 1) < epsilon$2 || abs(lambda0 - lambda1) < epsilon$2 ? (lambda0 + lambda1) / 2 : atan2(b, a), + p = project(lambda2, phi2), + x2 = p[0], + y2 = p[1], + dx2 = x2 - x0, + dy2 = y2 - y0, + dz = dy * dx2 - dx * dy2; + if (dz * dz / d2 > delta2 // perpendicular projected distance + || abs((dx * dx2 + dy * dy2) / d2 - 0.5) > 0.3 // midpoint close to an end + || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) { // angular distance + resampleLineTo(x0, y0, lambda0, a0, b0, c0, x2, y2, lambda2, a /= m, b /= m, c, depth, stream); + stream.point(x2, y2); + resampleLineTo(x2, y2, lambda2, a, b, c, x1, y1, lambda1, a1, b1, c1, depth, stream); + } + } + } + return function(stream) { + var lambda00, x00, y00, a00, b00, c00, // first point + lambda0, x0, y0, a0, b0, c0; // previous point + + var resampleStream = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, + polygonStart: function() { stream.polygonStart(); resampleStream.lineStart = ringStart; }, + polygonEnd: function() { stream.polygonEnd(); resampleStream.lineStart = lineStart; } + }; + + function point(x, y) { + x = project(x, y); + stream.point(x[0], x[1]); + } + + function lineStart() { + x0 = NaN; + resampleStream.point = linePoint; + stream.lineStart(); + } + + function linePoint(lambda, phi) { + var c = cartesian([lambda, phi]), p = project(lambda, phi); + resampleLineTo(x0, y0, lambda0, a0, b0, c0, x0 = p[0], y0 = p[1], lambda0 = lambda, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream); + stream.point(x0, y0); + } + + function lineEnd() { + resampleStream.point = point; + stream.lineEnd(); + } + + function ringStart() { + lineStart(); + resampleStream.point = ringPoint; + resampleStream.lineEnd = ringEnd; + } + + function ringPoint(lambda, phi) { + linePoint(lambda00 = lambda, phi), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0; + resampleStream.point = linePoint; + } + + function ringEnd() { + resampleLineTo(x0, y0, lambda0, a0, b0, c0, x00, y00, lambda00, a00, b00, c00, maxDepth, stream); + resampleStream.lineEnd = lineEnd; + lineEnd(); + } + + return resampleStream; + }; +} + +var transformRadians = transformer({ + point: function(x, y) { + this.stream.point(x * radians, y * radians); + } +}); + +function transformRotate(rotate) { + return transformer({ + point: function(x, y) { + var r = rotate(x, y); + return this.stream.point(r[0], r[1]); + } + }); +} + +function scaleTranslate(k, dx, dy) { + function transform$$1(x, y) { + return [dx + k * x, dy - k * y]; + } + transform$$1.invert = function(x, y) { + return [(x - dx) / k, (dy - y) / k]; + }; + return transform$$1; +} + +function scaleTranslateRotate(k, dx, dy, alpha) { + var cosAlpha = cos$1(alpha), + sinAlpha = sin$1(alpha), + a = cosAlpha * k, + b = sinAlpha * k, + ai = cosAlpha / k, + bi = sinAlpha / k, + ci = (sinAlpha * dy - cosAlpha * dx) / k, + fi = (sinAlpha * dx + cosAlpha * dy) / k; + function transform$$1(x, y) { + return [a * x - b * y + dx, dy - b * x - a * y]; + } + transform$$1.invert = function(x, y) { + return [ai * x - bi * y + ci, fi - bi * x - ai * y]; + }; + return transform$$1; +} + +function projection(project) { + return projectionMutator(function() { return project; })(); +} + +function projectionMutator(projectAt) { + var project, + k = 150, // scale + x = 480, y = 250, // translate + lambda = 0, phi = 0, // center + deltaLambda = 0, deltaPhi = 0, deltaGamma = 0, rotate, // pre-rotate + alpha = 0, // post-rotate + theta = null, preclip = clipAntimeridian, // pre-clip angle + x0 = null, y0, x1, y1, postclip = identity$4, // post-clip extent + delta2 = 0.5, // precision + projectResample, + projectTransform, + projectRotateTransform, + cache, + cacheStream; + + function projection(point) { + return projectRotateTransform(point[0] * radians, point[1] * radians); + } + + function invert(point) { + point = projectRotateTransform.invert(point[0], point[1]); + return point && [point[0] * degrees$1, point[1] * degrees$1]; + } + + projection.stream = function(stream) { + return cache && cacheStream === stream ? cache : cache = transformRadians(transformRotate(rotate)(preclip(projectResample(postclip(cacheStream = stream))))); + }; + + projection.preclip = function(_) { + return arguments.length ? (preclip = _, theta = undefined, reset()) : preclip; + }; + + projection.postclip = function(_) { + return arguments.length ? (postclip = _, x0 = y0 = x1 = y1 = null, reset()) : postclip; + }; + + projection.clipAngle = function(_) { + return arguments.length ? (preclip = +_ ? clipCircle(theta = _ * radians) : (theta = null, clipAntimeridian), reset()) : theta * degrees$1; + }; + + projection.clipExtent = function(_) { + return arguments.length ? (postclip = _ == null ? (x0 = y0 = x1 = y1 = null, identity$4) : clipRectangle(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reset()) : x0 == null ? null : [[x0, y0], [x1, y1]]; + }; + + projection.scale = function(_) { + return arguments.length ? (k = +_, recenter()) : k; + }; + + projection.translate = function(_) { + return arguments.length ? (x = +_[0], y = +_[1], recenter()) : [x, y]; + }; + + projection.center = function(_) { + return arguments.length ? (lambda = _[0] % 360 * radians, phi = _[1] % 360 * radians, recenter()) : [lambda * degrees$1, phi * degrees$1]; + }; + + projection.rotate = function(_) { + return arguments.length ? (deltaLambda = _[0] % 360 * radians, deltaPhi = _[1] % 360 * radians, deltaGamma = _.length > 2 ? _[2] % 360 * radians : 0, recenter()) : [deltaLambda * degrees$1, deltaPhi * degrees$1, deltaGamma * degrees$1]; + }; + + projection.angle = function(_) { + return arguments.length ? (alpha = _ % 360 * radians, recenter()) : alpha * degrees$1; + }; + + projection.precision = function(_) { + return arguments.length ? (projectResample = resample(projectTransform, delta2 = _ * _), reset()) : sqrt(delta2); + }; + + projection.fitExtent = function(extent, object) { + return fitExtent(projection, extent, object); + }; + + projection.fitSize = function(size, object) { + return fitSize(projection, size, object); + }; + + projection.fitWidth = function(width, object) { + return fitWidth(projection, width, object); + }; + + projection.fitHeight = function(height, object) { + return fitHeight(projection, height, object); + }; + + function recenter() { + var center = scaleTranslateRotate(k, 0, 0, alpha).apply(null, project(lambda, phi)), + transform$$1 = (alpha ? scaleTranslateRotate : scaleTranslate)(k, x - center[0], y - center[1], alpha); + rotate = rotateRadians(deltaLambda, deltaPhi, deltaGamma); + projectTransform = compose(project, transform$$1); + projectRotateTransform = compose(rotate, projectTransform); + projectResample = resample(projectTransform, delta2); + return reset(); + } + + function reset() { + cache = cacheStream = null; + return projection; + } + + return function() { + project = projectAt.apply(this, arguments); + projection.invert = project.invert && invert; + return recenter(); + }; +} + +function conicProjection(projectAt) { + var phi0 = 0, + phi1 = pi$3 / 3, + m = projectionMutator(projectAt), + p = m(phi0, phi1); + + p.parallels = function(_) { + return arguments.length ? m(phi0 = _[0] * radians, phi1 = _[1] * radians) : [phi0 * degrees$1, phi1 * degrees$1]; + }; + + return p; +} + +function cylindricalEqualAreaRaw(phi0) { + var cosPhi0 = cos$1(phi0); + + function forward(lambda, phi) { + return [lambda * cosPhi0, sin$1(phi) / cosPhi0]; + } + + forward.invert = function(x, y) { + return [x / cosPhi0, asin(y * cosPhi0)]; + }; + + return forward; +} + +function conicEqualAreaRaw(y0, y1) { + var sy0 = sin$1(y0), n = (sy0 + sin$1(y1)) / 2; + + // Are the parallels symmetrical around the Equator? + if (abs(n) < epsilon$2) return cylindricalEqualAreaRaw(y0); + + var c = 1 + sy0 * (2 * n - sy0), r0 = sqrt(c) / n; + + function project(x, y) { + var r = sqrt(c - 2 * n * sin$1(y)) / n; + return [r * sin$1(x *= n), r0 - r * cos$1(x)]; + } + + project.invert = function(x, y) { + var r0y = r0 - y; + return [atan2(x, abs(r0y)) / n * sign(r0y), asin((c - (x * x + r0y * r0y) * n * n) / (2 * n))]; + }; + + return project; +} + +function conicEqualArea() { + return conicProjection(conicEqualAreaRaw) + .scale(155.424) + .center([0, 33.6442]); +} + +function albers() { + return conicEqualArea() + .parallels([29.5, 45.5]) + .scale(1070) + .translate([480, 250]) + .rotate([96, 0]) + .center([-0.6, 38.7]); +} + +// The projections must have mutually exclusive clip regions on the sphere, +// as this will avoid emitting interleaving lines and polygons. +function multiplex(streams) { + var n = streams.length; + return { + point: function(x, y) { var i = -1; while (++i < n) streams[i].point(x, y); }, + sphere: function() { var i = -1; while (++i < n) streams[i].sphere(); }, + lineStart: function() { var i = -1; while (++i < n) streams[i].lineStart(); }, + lineEnd: function() { var i = -1; while (++i < n) streams[i].lineEnd(); }, + polygonStart: function() { var i = -1; while (++i < n) streams[i].polygonStart(); }, + polygonEnd: function() { var i = -1; while (++i < n) streams[i].polygonEnd(); } + }; +} + +// A composite projection for the United States, configured by default for +// 960×500. The projection also works quite well at 960×600 if you change the +// scale to 1285 and adjust the translate accordingly. The set of standard +// parallels for each region comes from USGS, which is published here: +// http://egsc.usgs.gov/isb/pubs/MapProjections/projections.html#albers +function albersUsa() { + var cache, + cacheStream, + lower48 = albers(), lower48Point, + alaska = conicEqualArea().rotate([154, 0]).center([-2, 58.5]).parallels([55, 65]), alaskaPoint, // EPSG:3338 + hawaii = conicEqualArea().rotate([157, 0]).center([-3, 19.9]).parallels([8, 18]), hawaiiPoint, // ESRI:102007 + point, pointStream = {point: function(x, y) { point = [x, y]; }}; + + function albersUsa(coordinates) { + var x = coordinates[0], y = coordinates[1]; + return point = null, (lower48Point.point(x, y), point) + || (alaskaPoint.point(x, y), point) + || (hawaiiPoint.point(x, y), point); + } + + albersUsa.invert = function(coordinates) { + var k = lower48.scale(), + t = lower48.translate(), + x = (coordinates[0] - t[0]) / k, + y = (coordinates[1] - t[1]) / k; + return (y >= 0.120 && y < 0.234 && x >= -0.425 && x < -0.214 ? alaska + : y >= 0.166 && y < 0.234 && x >= -0.214 && x < -0.115 ? hawaii + : lower48).invert(coordinates); + }; + + albersUsa.stream = function(stream) { + return cache && cacheStream === stream ? cache : cache = multiplex([lower48.stream(cacheStream = stream), alaska.stream(stream), hawaii.stream(stream)]); + }; + + albersUsa.precision = function(_) { + if (!arguments.length) return lower48.precision(); + lower48.precision(_), alaska.precision(_), hawaii.precision(_); + return reset(); + }; + + albersUsa.scale = function(_) { + if (!arguments.length) return lower48.scale(); + lower48.scale(_), alaska.scale(_ * 0.35), hawaii.scale(_); + return albersUsa.translate(lower48.translate()); + }; + + albersUsa.translate = function(_) { + if (!arguments.length) return lower48.translate(); + var k = lower48.scale(), x = +_[0], y = +_[1]; + + lower48Point = lower48 + .translate(_) + .clipExtent([[x - 0.455 * k, y - 0.238 * k], [x + 0.455 * k, y + 0.238 * k]]) + .stream(pointStream); + + alaskaPoint = alaska + .translate([x - 0.307 * k, y + 0.201 * k]) + .clipExtent([[x - 0.425 * k + epsilon$2, y + 0.120 * k + epsilon$2], [x - 0.214 * k - epsilon$2, y + 0.234 * k - epsilon$2]]) + .stream(pointStream); + + hawaiiPoint = hawaii + .translate([x - 0.205 * k, y + 0.212 * k]) + .clipExtent([[x - 0.214 * k + epsilon$2, y + 0.166 * k + epsilon$2], [x - 0.115 * k - epsilon$2, y + 0.234 * k - epsilon$2]]) + .stream(pointStream); + + return reset(); + }; + + albersUsa.fitExtent = function(extent, object) { + return fitExtent(albersUsa, extent, object); + }; + + albersUsa.fitSize = function(size, object) { + return fitSize(albersUsa, size, object); + }; + + albersUsa.fitWidth = function(width, object) { + return fitWidth(albersUsa, width, object); + }; + + albersUsa.fitHeight = function(height, object) { + return fitHeight(albersUsa, height, object); + }; + + function reset() { + cache = cacheStream = null; + return albersUsa; + } + + return albersUsa.scale(1070); +} + +function azimuthalRaw(scale) { + return function(x, y) { + var cx = cos$1(x), + cy = cos$1(y), + k = scale(cx * cy); + return [ + k * cy * sin$1(x), + k * sin$1(y) + ]; + } +} + +function azimuthalInvert(angle) { + return function(x, y) { + var z = sqrt(x * x + y * y), + c = angle(z), + sc = sin$1(c), + cc = cos$1(c); + return [ + atan2(x * sc, z * cc), + asin(z && y * sc / z) + ]; + } +} + +var azimuthalEqualAreaRaw = azimuthalRaw(function(cxcy) { + return sqrt(2 / (1 + cxcy)); +}); + +azimuthalEqualAreaRaw.invert = azimuthalInvert(function(z) { + return 2 * asin(z / 2); +}); + +function azimuthalEqualArea() { + return projection(azimuthalEqualAreaRaw) + .scale(124.75) + .clipAngle(180 - 1e-3); +} + +var azimuthalEquidistantRaw = azimuthalRaw(function(c) { + return (c = acos(c)) && c / sin$1(c); +}); + +azimuthalEquidistantRaw.invert = azimuthalInvert(function(z) { + return z; +}); + +function azimuthalEquidistant() { + return projection(azimuthalEquidistantRaw) + .scale(79.4188) + .clipAngle(180 - 1e-3); +} + +function mercatorRaw(lambda, phi) { + return [lambda, log(tan((halfPi$2 + phi) / 2))]; +} + +mercatorRaw.invert = function(x, y) { + return [x, 2 * atan(exp(y)) - halfPi$2]; +}; + +function mercator() { + return mercatorProjection(mercatorRaw) + .scale(961 / tau$3); +} + +function mercatorProjection(project) { + var m = projection(project), + center = m.center, + scale = m.scale, + translate = m.translate, + clipExtent = m.clipExtent, + x0 = null, y0, x1, y1; // clip extent + + m.scale = function(_) { + return arguments.length ? (scale(_), reclip()) : scale(); + }; + + m.translate = function(_) { + return arguments.length ? (translate(_), reclip()) : translate(); + }; + + m.center = function(_) { + return arguments.length ? (center(_), reclip()) : center(); + }; + + m.clipExtent = function(_) { + return arguments.length ? (_ == null ? x0 = y0 = x1 = y1 = null : (x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reclip()) : x0 == null ? null : [[x0, y0], [x1, y1]]; + }; + + function reclip() { + var k = pi$3 * scale(), + t = m(rotation(m.rotate()).invert([0, 0])); + return clipExtent(x0 == null + ? [[t[0] - k, t[1] - k], [t[0] + k, t[1] + k]] : project === mercatorRaw + ? [[Math.max(t[0] - k, x0), y0], [Math.min(t[0] + k, x1), y1]] + : [[x0, Math.max(t[1] - k, y0)], [x1, Math.min(t[1] + k, y1)]]); + } + + return reclip(); +} + +function tany(y) { + return tan((halfPi$2 + y) / 2); +} + +function conicConformalRaw(y0, y1) { + var cy0 = cos$1(y0), + n = y0 === y1 ? sin$1(y0) : log(cy0 / cos$1(y1)) / log(tany(y1) / tany(y0)), + f = cy0 * pow(tany(y0), n) / n; + + if (!n) return mercatorRaw; + + function project(x, y) { + if (f > 0) { if (y < -halfPi$2 + epsilon$2) y = -halfPi$2 + epsilon$2; } + else { if (y > halfPi$2 - epsilon$2) y = halfPi$2 - epsilon$2; } + var r = f / pow(tany(y), n); + return [r * sin$1(n * x), f - r * cos$1(n * x)]; + } + + project.invert = function(x, y) { + var fy = f - y, r = sign(n) * sqrt(x * x + fy * fy); + return [atan2(x, abs(fy)) / n * sign(fy), 2 * atan(pow(f / r, 1 / n)) - halfPi$2]; + }; + + return project; +} + +function conicConformal() { + return conicProjection(conicConformalRaw) + .scale(109.5) + .parallels([30, 30]); +} + +function equirectangularRaw(lambda, phi) { + return [lambda, phi]; +} + +equirectangularRaw.invert = equirectangularRaw; + +function equirectangular() { + return projection(equirectangularRaw) + .scale(152.63); +} + +function conicEquidistantRaw(y0, y1) { + var cy0 = cos$1(y0), + n = y0 === y1 ? sin$1(y0) : (cy0 - cos$1(y1)) / (y1 - y0), + g = cy0 / n + y0; + + if (abs(n) < epsilon$2) return equirectangularRaw; + + function project(x, y) { + var gy = g - y, nx = n * x; + return [gy * sin$1(nx), g - gy * cos$1(nx)]; + } + + project.invert = function(x, y) { + var gy = g - y; + return [atan2(x, abs(gy)) / n * sign(gy), g - sign(n) * sqrt(x * x + gy * gy)]; + }; + + return project; +} + +function conicEquidistant() { + return conicProjection(conicEquidistantRaw) + .scale(131.154) + .center([0, 13.9389]); +} + +function gnomonicRaw(x, y) { + var cy = cos$1(y), k = cos$1(x) * cy; + return [cy * sin$1(x) / k, sin$1(y) / k]; +} + +gnomonicRaw.invert = azimuthalInvert(atan); + +function gnomonic() { + return projection(gnomonicRaw) + .scale(144.049) + .clipAngle(60); +} + +function scaleTranslate$1(kx, ky, tx, ty) { + return kx === 1 && ky === 1 && tx === 0 && ty === 0 ? identity$4 : transformer({ + point: function(x, y) { + this.stream.point(x * kx + tx, y * ky + ty); + } + }); +} + +function identity$5() { + var k = 1, tx = 0, ty = 0, sx = 1, sy = 1, transform$$1 = identity$4, // scale, translate and reflect + x0 = null, y0, x1, y1, // clip extent + postclip = identity$4, + cache, + cacheStream, + projection; + + function reset() { + cache = cacheStream = null; + return projection; + } + + return projection = { + stream: function(stream) { + return cache && cacheStream === stream ? cache : cache = transform$$1(postclip(cacheStream = stream)); + }, + postclip: function(_) { + return arguments.length ? (postclip = _, x0 = y0 = x1 = y1 = null, reset()) : postclip; + }, + clipExtent: function(_) { + return arguments.length ? (postclip = _ == null ? (x0 = y0 = x1 = y1 = null, identity$4) : clipRectangle(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reset()) : x0 == null ? null : [[x0, y0], [x1, y1]]; + }, + scale: function(_) { + return arguments.length ? (transform$$1 = scaleTranslate$1((k = +_) * sx, k * sy, tx, ty), reset()) : k; + }, + translate: function(_) { + return arguments.length ? (transform$$1 = scaleTranslate$1(k * sx, k * sy, tx = +_[0], ty = +_[1]), reset()) : [tx, ty]; + }, + reflectX: function(_) { + return arguments.length ? (transform$$1 = scaleTranslate$1(k * (sx = _ ? -1 : 1), k * sy, tx, ty), reset()) : sx < 0; + }, + reflectY: function(_) { + return arguments.length ? (transform$$1 = scaleTranslate$1(k * sx, k * (sy = _ ? -1 : 1), tx, ty), reset()) : sy < 0; + }, + fitExtent: function(extent, object) { + return fitExtent(projection, extent, object); + }, + fitSize: function(size, object) { + return fitSize(projection, size, object); + }, + fitWidth: function(width, object) { + return fitWidth(projection, width, object); + }, + fitHeight: function(height, object) { + return fitHeight(projection, height, object); + } + }; +} + +function naturalEarth1Raw(lambda, phi) { + var phi2 = phi * phi, phi4 = phi2 * phi2; + return [ + lambda * (0.8707 - 0.131979 * phi2 + phi4 * (-0.013791 + phi4 * (0.003971 * phi2 - 0.001529 * phi4))), + phi * (1.007226 + phi2 * (0.015085 + phi4 * (-0.044475 + 0.028874 * phi2 - 0.005916 * phi4))) + ]; +} + +naturalEarth1Raw.invert = function(x, y) { + var phi = y, i = 25, delta; + do { + var phi2 = phi * phi, phi4 = phi2 * phi2; + phi -= delta = (phi * (1.007226 + phi2 * (0.015085 + phi4 * (-0.044475 + 0.028874 * phi2 - 0.005916 * phi4))) - y) / + (1.007226 + phi2 * (0.015085 * 3 + phi4 * (-0.044475 * 7 + 0.028874 * 9 * phi2 - 0.005916 * 11 * phi4))); + } while (abs(delta) > epsilon$2 && --i > 0); + return [ + x / (0.8707 + (phi2 = phi * phi) * (-0.131979 + phi2 * (-0.013791 + phi2 * phi2 * phi2 * (0.003971 - 0.001529 * phi2)))), + phi + ]; +}; + +function naturalEarth1() { + return projection(naturalEarth1Raw) + .scale(175.295); +} + +function orthographicRaw(x, y) { + return [cos$1(y) * sin$1(x), sin$1(y)]; +} + +orthographicRaw.invert = azimuthalInvert(asin); + +function orthographic() { + return projection(orthographicRaw) + .scale(249.5) + .clipAngle(90 + epsilon$2); +} + +function stereographicRaw(x, y) { + var cy = cos$1(y), k = 1 + cos$1(x) * cy; + return [cy * sin$1(x) / k, sin$1(y) / k]; +} + +stereographicRaw.invert = azimuthalInvert(function(z) { + return 2 * atan(z); +}); + +function stereographic() { + return projection(stereographicRaw) + .scale(250) + .clipAngle(142); +} + +function transverseMercatorRaw(lambda, phi) { + return [log(tan((halfPi$2 + phi) / 2)), -lambda]; +} + +transverseMercatorRaw.invert = function(x, y) { + return [-y, 2 * atan(exp(x)) - halfPi$2]; +}; + +function transverseMercator() { + var m = mercatorProjection(transverseMercatorRaw), + center = m.center, + rotate = m.rotate; + + m.center = function(_) { + return arguments.length ? center([-_[1], _[0]]) : (_ = center(), [_[1], -_[0]]); + }; + + m.rotate = function(_) { + return arguments.length ? rotate([_[0], _[1], _.length > 2 ? _[2] + 90 : 90]) : (_ = rotate(), [_[0], _[1], _[2] - 90]); + }; + + return rotate([0, 0, 90]) + .scale(159.155); +} + +function defaultSeparation(a, b) { + return a.parent === b.parent ? 1 : 2; +} + +function meanX(children) { + return children.reduce(meanXReduce, 0) / children.length; +} + +function meanXReduce(x, c) { + return x + c.x; +} + +function maxY(children) { + return 1 + children.reduce(maxYReduce, 0); +} + +function maxYReduce(y, c) { + return Math.max(y, c.y); +} + +function leafLeft(node) { + var children; + while (children = node.children) node = children[0]; + return node; +} + +function leafRight(node) { + var children; + while (children = node.children) node = children[children.length - 1]; + return node; +} + +function cluster() { + var separation = defaultSeparation, + dx = 1, + dy = 1, + nodeSize = false; + + function cluster(root) { + var previousNode, + x = 0; + + // First walk, computing the initial x & y values. + root.eachAfter(function(node) { + var children = node.children; + if (children) { + node.x = meanX(children); + node.y = maxY(children); + } else { + node.x = previousNode ? x += separation(node, previousNode) : 0; + node.y = 0; + previousNode = node; + } + }); + + var left = leafLeft(root), + right = leafRight(root), + x0 = left.x - separation(left, right) / 2, + x1 = right.x + separation(right, left) / 2; + + // Second walk, normalizing x & y to the desired size. + return root.eachAfter(nodeSize ? function(node) { + node.x = (node.x - root.x) * dx; + node.y = (root.y - node.y) * dy; + } : function(node) { + node.x = (node.x - x0) / (x1 - x0) * dx; + node.y = (1 - (root.y ? node.y / root.y : 1)) * dy; + }); + } + + cluster.separation = function(x) { + return arguments.length ? (separation = x, cluster) : separation; + }; + + cluster.size = function(x) { + return arguments.length ? (nodeSize = false, dx = +x[0], dy = +x[1], cluster) : (nodeSize ? null : [dx, dy]); + }; + + cluster.nodeSize = function(x) { + return arguments.length ? (nodeSize = true, dx = +x[0], dy = +x[1], cluster) : (nodeSize ? [dx, dy] : null); + }; + + return cluster; +} + +function count(node) { + var sum = 0, + children = node.children, + i = children && children.length; + if (!i) sum = 1; + else while (--i >= 0) sum += children[i].value; + node.value = sum; +} + +function node_count() { + return this.eachAfter(count); +} + +function node_each(callback) { + var node = this, current, next = [node], children, i, n; + do { + current = next.reverse(), next = []; + while (node = current.pop()) { + callback(node), children = node.children; + if (children) for (i = 0, n = children.length; i < n; ++i) { + next.push(children[i]); + } + } + } while (next.length); + return this; +} + +function node_eachBefore(callback) { + var node = this, nodes = [node], children, i; + while (node = nodes.pop()) { + callback(node), children = node.children; + if (children) for (i = children.length - 1; i >= 0; --i) { + nodes.push(children[i]); + } + } + return this; +} + +function node_eachAfter(callback) { + var node = this, nodes = [node], next = [], children, i, n; + while (node = nodes.pop()) { + next.push(node), children = node.children; + if (children) for (i = 0, n = children.length; i < n; ++i) { + nodes.push(children[i]); + } + } + while (node = next.pop()) { + callback(node); + } + return this; +} + +function node_sum(value) { + return this.eachAfter(function(node) { + var sum = +value(node.data) || 0, + children = node.children, + i = children && children.length; + while (--i >= 0) sum += children[i].value; + node.value = sum; + }); +} + +function node_sort(compare) { + return this.eachBefore(function(node) { + if (node.children) { + node.children.sort(compare); + } + }); +} + +function node_path(end) { + var start = this, + ancestor = leastCommonAncestor(start, end), + nodes = [start]; + while (start !== ancestor) { + start = start.parent; + nodes.push(start); + } + var k = nodes.length; + while (end !== ancestor) { + nodes.splice(k, 0, end); + end = end.parent; + } + return nodes; +} + +function leastCommonAncestor(a, b) { + if (a === b) return a; + var aNodes = a.ancestors(), + bNodes = b.ancestors(), + c = null; + a = aNodes.pop(); + b = bNodes.pop(); + while (a === b) { + c = a; + a = aNodes.pop(); + b = bNodes.pop(); + } + return c; +} + +function node_ancestors() { + var node = this, nodes = [node]; + while (node = node.parent) { + nodes.push(node); + } + return nodes; +} + +function node_descendants() { + var nodes = []; + this.each(function(node) { + nodes.push(node); + }); + return nodes; +} + +function node_leaves() { + var leaves = []; + this.eachBefore(function(node) { + if (!node.children) { + leaves.push(node); + } + }); + return leaves; +} + +function node_links() { + var root = this, links = []; + root.each(function(node) { + if (node !== root) { // Don’t include the root’s parent, if any. + links.push({source: node.parent, target: node}); + } + }); + return links; +} + +function hierarchy(data, children) { + var root = new Node(data), + valued = +data.value && (root.value = data.value), + node, + nodes = [root], + child, + childs, + i, + n; + + if (children == null) children = defaultChildren; + + while (node = nodes.pop()) { + if (valued) node.value = +node.data.value; + if ((childs = children(node.data)) && (n = childs.length)) { + node.children = new Array(n); + for (i = n - 1; i >= 0; --i) { + nodes.push(child = node.children[i] = new Node(childs[i])); + child.parent = node; + child.depth = node.depth + 1; + } + } + } + + return root.eachBefore(computeHeight); +} + +function node_copy() { + return hierarchy(this).eachBefore(copyData); +} + +function defaultChildren(d) { + return d.children; +} + +function copyData(node) { + node.data = node.data.data; +} + +function computeHeight(node) { + var height = 0; + do node.height = height; + while ((node = node.parent) && (node.height < ++height)); +} + +function Node(data) { + this.data = data; + this.depth = + this.height = 0; + this.parent = null; +} + +Node.prototype = hierarchy.prototype = { + constructor: Node, + count: node_count, + each: node_each, + eachAfter: node_eachAfter, + eachBefore: node_eachBefore, + sum: node_sum, + sort: node_sort, + path: node_path, + ancestors: node_ancestors, + descendants: node_descendants, + leaves: node_leaves, + links: node_links, + copy: node_copy +}; + +var slice$4 = Array.prototype.slice; + +function shuffle$1(array) { + var m = array.length, + t, + i; + + while (m) { + i = Math.random() * m-- | 0; + t = array[m]; + array[m] = array[i]; + array[i] = t; + } + + return array; +} + +function enclose(circles) { + var i = 0, n = (circles = shuffle$1(slice$4.call(circles))).length, B = [], p, e; + + while (i < n) { + p = circles[i]; + if (e && enclosesWeak(e, p)) ++i; + else e = encloseBasis(B = extendBasis(B, p)), i = 0; + } + + return e; +} + +function extendBasis(B, p) { + var i, j; + + if (enclosesWeakAll(p, B)) return [p]; + + // If we get here then B must have at least one element. + for (i = 0; i < B.length; ++i) { + if (enclosesNot(p, B[i]) + && enclosesWeakAll(encloseBasis2(B[i], p), B)) { + return [B[i], p]; + } + } + + // If we get here then B must have at least two elements. + for (i = 0; i < B.length - 1; ++i) { + for (j = i + 1; j < B.length; ++j) { + if (enclosesNot(encloseBasis2(B[i], B[j]), p) + && enclosesNot(encloseBasis2(B[i], p), B[j]) + && enclosesNot(encloseBasis2(B[j], p), B[i]) + && enclosesWeakAll(encloseBasis3(B[i], B[j], p), B)) { + return [B[i], B[j], p]; + } + } + } + + // If we get here then something is very wrong. + throw new Error; +} + +function enclosesNot(a, b) { + var dr = a.r - b.r, dx = b.x - a.x, dy = b.y - a.y; + return dr < 0 || dr * dr < dx * dx + dy * dy; +} + +function enclosesWeak(a, b) { + var dr = a.r - b.r + 1e-6, dx = b.x - a.x, dy = b.y - a.y; + return dr > 0 && dr * dr > dx * dx + dy * dy; +} + +function enclosesWeakAll(a, B) { + for (var i = 0; i < B.length; ++i) { + if (!enclosesWeak(a, B[i])) { + return false; + } + } + return true; +} + +function encloseBasis(B) { + switch (B.length) { + case 1: return encloseBasis1(B[0]); + case 2: return encloseBasis2(B[0], B[1]); + case 3: return encloseBasis3(B[0], B[1], B[2]); + } +} + +function encloseBasis1(a) { + return { + x: a.x, + y: a.y, + r: a.r + }; +} + +function encloseBasis2(a, b) { + var x1 = a.x, y1 = a.y, r1 = a.r, + x2 = b.x, y2 = b.y, r2 = b.r, + x21 = x2 - x1, y21 = y2 - y1, r21 = r2 - r1, + l = Math.sqrt(x21 * x21 + y21 * y21); + return { + x: (x1 + x2 + x21 / l * r21) / 2, + y: (y1 + y2 + y21 / l * r21) / 2, + r: (l + r1 + r2) / 2 + }; +} + +function encloseBasis3(a, b, c) { + var x1 = a.x, y1 = a.y, r1 = a.r, + x2 = b.x, y2 = b.y, r2 = b.r, + x3 = c.x, y3 = c.y, r3 = c.r, + a2 = x1 - x2, + a3 = x1 - x3, + b2 = y1 - y2, + b3 = y1 - y3, + c2 = r2 - r1, + c3 = r3 - r1, + d1 = x1 * x1 + y1 * y1 - r1 * r1, + d2 = d1 - x2 * x2 - y2 * y2 + r2 * r2, + d3 = d1 - x3 * x3 - y3 * y3 + r3 * r3, + ab = a3 * b2 - a2 * b3, + xa = (b2 * d3 - b3 * d2) / (ab * 2) - x1, + xb = (b3 * c2 - b2 * c3) / ab, + ya = (a3 * d2 - a2 * d3) / (ab * 2) - y1, + yb = (a2 * c3 - a3 * c2) / ab, + A = xb * xb + yb * yb - 1, + B = 2 * (r1 + xa * xb + ya * yb), + C = xa * xa + ya * ya - r1 * r1, + r = -(A ? (B + Math.sqrt(B * B - 4 * A * C)) / (2 * A) : C / B); + return { + x: x1 + xa + xb * r, + y: y1 + ya + yb * r, + r: r + }; +} + +function place(a, b, c) { + var ax = a.x, + ay = a.y, + da = b.r + c.r, + db = a.r + c.r, + dx = b.x - ax, + dy = b.y - ay, + dc = dx * dx + dy * dy; + if (dc) { + var x = 0.5 + ((db *= db) - (da *= da)) / (2 * dc), + y = Math.sqrt(Math.max(0, 2 * da * (db + dc) - (db -= dc) * db - da * da)) / (2 * dc); + c.x = ax + x * dx + y * dy; + c.y = ay + x * dy - y * dx; + } else { + c.x = ax + db; + c.y = ay; + } +} + +function intersects(a, b) { + var dx = b.x - a.x, + dy = b.y - a.y, + dr = a.r + b.r; + return dr * dr - 1e-6 > dx * dx + dy * dy; +} + +function score(node) { + var a = node._, + b = node.next._, + ab = a.r + b.r, + dx = (a.x * b.r + b.x * a.r) / ab, + dy = (a.y * b.r + b.y * a.r) / ab; + return dx * dx + dy * dy; +} + +function Node$1(circle) { + this._ = circle; + this.next = null; + this.previous = null; +} + +function packEnclose(circles) { + if (!(n = circles.length)) return 0; + + var a, b, c, n, aa, ca, i, j, k, sj, sk; + + // Place the first circle. + a = circles[0], a.x = 0, a.y = 0; + if (!(n > 1)) return a.r; + + // Place the second circle. + b = circles[1], a.x = -b.r, b.x = a.r, b.y = 0; + if (!(n > 2)) return a.r + b.r; + + // Place the third circle. + place(b, a, c = circles[2]); + + // Initialize the front-chain using the first three circles a, b and c. + a = new Node$1(a), b = new Node$1(b), c = new Node$1(c); + a.next = c.previous = b; + b.next = a.previous = c; + c.next = b.previous = a; + + // Attempt to place each remaining circle… + pack: for (i = 3; i < n; ++i) { + place(a._, b._, c = circles[i]), c = new Node$1(c); + + // Find the closest intersecting circle on the front-chain, if any. + // “Closeness” is determined by linear distance along the front-chain. + // “Ahead” or “behind” is likewise determined by linear distance. + j = b.next, k = a.previous, sj = b._.r, sk = a._.r; + do { + if (sj <= sk) { + if (intersects(j._, c._)) { + b = j, a.next = b, b.previous = a, --i; + continue pack; + } + sj += j._.r, j = j.next; + } else { + if (intersects(k._, c._)) { + a = k, a.next = b, b.previous = a, --i; + continue pack; + } + sk += k._.r, k = k.previous; + } + } while (j !== k.next); + + // Success! Insert the new circle c between a and b. + c.previous = a, c.next = b, a.next = b.previous = b = c; + + // Compute the new closest circle pair to the centroid. + aa = score(a); + while ((c = c.next) !== b) { + if ((ca = score(c)) < aa) { + a = c, aa = ca; + } + } + b = a.next; + } + + // Compute the enclosing circle of the front chain. + a = [b._], c = b; while ((c = c.next) !== b) a.push(c._); c = enclose(a); + + // Translate the circles to put the enclosing circle around the origin. + for (i = 0; i < n; ++i) a = circles[i], a.x -= c.x, a.y -= c.y; + + return c.r; +} + +function siblings(circles) { + packEnclose(circles); + return circles; +} + +function optional(f) { + return f == null ? null : required(f); +} + +function required(f) { + if (typeof f !== "function") throw new Error; + return f; +} + +function constantZero() { + return 0; +} + +function constant$9(x) { + return function() { + return x; + }; +} + +function defaultRadius$1(d) { + return Math.sqrt(d.value); +} + +function index$2() { + var radius = null, + dx = 1, + dy = 1, + padding = constantZero; + + function pack(root) { + root.x = dx / 2, root.y = dy / 2; + if (radius) { + root.eachBefore(radiusLeaf(radius)) + .eachAfter(packChildren(padding, 0.5)) + .eachBefore(translateChild(1)); + } else { + root.eachBefore(radiusLeaf(defaultRadius$1)) + .eachAfter(packChildren(constantZero, 1)) + .eachAfter(packChildren(padding, root.r / Math.min(dx, dy))) + .eachBefore(translateChild(Math.min(dx, dy) / (2 * root.r))); + } + return root; + } + + pack.radius = function(x) { + return arguments.length ? (radius = optional(x), pack) : radius; + }; + + pack.size = function(x) { + return arguments.length ? (dx = +x[0], dy = +x[1], pack) : [dx, dy]; + }; + + pack.padding = function(x) { + return arguments.length ? (padding = typeof x === "function" ? x : constant$9(+x), pack) : padding; + }; + + return pack; +} + +function radiusLeaf(radius) { + return function(node) { + if (!node.children) { + node.r = Math.max(0, +radius(node) || 0); + } + }; +} + +function packChildren(padding, k) { + return function(node) { + if (children = node.children) { + var children, + i, + n = children.length, + r = padding(node) * k || 0, + e; + + if (r) for (i = 0; i < n; ++i) children[i].r += r; + e = packEnclose(children); + if (r) for (i = 0; i < n; ++i) children[i].r -= r; + node.r = e + r; + } + }; +} + +function translateChild(k) { + return function(node) { + var parent = node.parent; + node.r *= k; + if (parent) { + node.x = parent.x + k * node.x; + node.y = parent.y + k * node.y; + } + }; +} + +function roundNode(node) { + node.x0 = Math.round(node.x0); + node.y0 = Math.round(node.y0); + node.x1 = Math.round(node.x1); + node.y1 = Math.round(node.y1); +} + +function treemapDice(parent, x0, y0, x1, y1) { + var nodes = parent.children, + node, + i = -1, + n = nodes.length, + k = parent.value && (x1 - x0) / parent.value; + + while (++i < n) { + node = nodes[i], node.y0 = y0, node.y1 = y1; + node.x0 = x0, node.x1 = x0 += node.value * k; + } +} + +function partition() { + var dx = 1, + dy = 1, + padding = 0, + round = false; + + function partition(root) { + var n = root.height + 1; + root.x0 = + root.y0 = padding; + root.x1 = dx; + root.y1 = dy / n; + root.eachBefore(positionNode(dy, n)); + if (round) root.eachBefore(roundNode); + return root; + } + + function positionNode(dy, n) { + return function(node) { + if (node.children) { + treemapDice(node, node.x0, dy * (node.depth + 1) / n, node.x1, dy * (node.depth + 2) / n); + } + var x0 = node.x0, + y0 = node.y0, + x1 = node.x1 - padding, + y1 = node.y1 - padding; + if (x1 < x0) x0 = x1 = (x0 + x1) / 2; + if (y1 < y0) y0 = y1 = (y0 + y1) / 2; + node.x0 = x0; + node.y0 = y0; + node.x1 = x1; + node.y1 = y1; + }; + } + + partition.round = function(x) { + return arguments.length ? (round = !!x, partition) : round; + }; + + partition.size = function(x) { + return arguments.length ? (dx = +x[0], dy = +x[1], partition) : [dx, dy]; + }; + + partition.padding = function(x) { + return arguments.length ? (padding = +x, partition) : padding; + }; + + return partition; +} + +var keyPrefix$1 = "$", // Protect against keys like “__proto__”. + preroot = {depth: -1}, + ambiguous = {}; + +function defaultId(d) { + return d.id; +} + +function defaultParentId(d) { + return d.parentId; +} + +function stratify() { + var id = defaultId, + parentId = defaultParentId; + + function stratify(data) { + var d, + i, + n = data.length, + root, + parent, + node, + nodes = new Array(n), + nodeId, + nodeKey, + nodeByKey = {}; + + for (i = 0; i < n; ++i) { + d = data[i], node = nodes[i] = new Node(d); + if ((nodeId = id(d, i, data)) != null && (nodeId += "")) { + nodeKey = keyPrefix$1 + (node.id = nodeId); + nodeByKey[nodeKey] = nodeKey in nodeByKey ? ambiguous : node; + } + } + + for (i = 0; i < n; ++i) { + node = nodes[i], nodeId = parentId(data[i], i, data); + if (nodeId == null || !(nodeId += "")) { + if (root) throw new Error("multiple roots"); + root = node; + } else { + parent = nodeByKey[keyPrefix$1 + nodeId]; + if (!parent) throw new Error("missing: " + nodeId); + if (parent === ambiguous) throw new Error("ambiguous: " + nodeId); + if (parent.children) parent.children.push(node); + else parent.children = [node]; + node.parent = parent; + } + } + + if (!root) throw new Error("no root"); + root.parent = preroot; + root.eachBefore(function(node) { node.depth = node.parent.depth + 1; --n; }).eachBefore(computeHeight); + root.parent = null; + if (n > 0) throw new Error("cycle"); + + return root; + } + + stratify.id = function(x) { + return arguments.length ? (id = required(x), stratify) : id; + }; + + stratify.parentId = function(x) { + return arguments.length ? (parentId = required(x), stratify) : parentId; + }; + + return stratify; +} + +function defaultSeparation$1(a, b) { + return a.parent === b.parent ? 1 : 2; +} + +// function radialSeparation(a, b) { +// return (a.parent === b.parent ? 1 : 2) / a.depth; +// } + +// This function is used to traverse the left contour of a subtree (or +// subforest). It returns the successor of v on this contour. This successor is +// either given by the leftmost child of v or by the thread of v. The function +// returns null if and only if v is on the highest level of its subtree. +function nextLeft(v) { + var children = v.children; + return children ? children[0] : v.t; +} + +// This function works analogously to nextLeft. +function nextRight(v) { + var children = v.children; + return children ? children[children.length - 1] : v.t; +} + +// Shifts the current subtree rooted at w+. This is done by increasing +// prelim(w+) and mod(w+) by shift. +function moveSubtree(wm, wp, shift) { + var change = shift / (wp.i - wm.i); + wp.c -= change; + wp.s += shift; + wm.c += change; + wp.z += shift; + wp.m += shift; +} + +// All other shifts, applied to the smaller subtrees between w- and w+, are +// performed by this function. To prepare the shifts, we have to adjust +// change(w+), shift(w+), and change(w-). +function executeShifts(v) { + var shift = 0, + change = 0, + children = v.children, + i = children.length, + w; + while (--i >= 0) { + w = children[i]; + w.z += shift; + w.m += shift; + shift += w.s + (change += w.c); + } +} + +// If vi-’s ancestor is a sibling of v, returns vi-’s ancestor. Otherwise, +// returns the specified (default) ancestor. +function nextAncestor(vim, v, ancestor) { + return vim.a.parent === v.parent ? vim.a : ancestor; +} + +function TreeNode(node, i) { + this._ = node; + this.parent = null; + this.children = null; + this.A = null; // default ancestor + this.a = this; // ancestor + this.z = 0; // prelim + this.m = 0; // mod + this.c = 0; // change + this.s = 0; // shift + this.t = null; // thread + this.i = i; // number +} + +TreeNode.prototype = Object.create(Node.prototype); + +function treeRoot(root) { + var tree = new TreeNode(root, 0), + node, + nodes = [tree], + child, + children, + i, + n; + + while (node = nodes.pop()) { + if (children = node._.children) { + node.children = new Array(n = children.length); + for (i = n - 1; i >= 0; --i) { + nodes.push(child = node.children[i] = new TreeNode(children[i], i)); + child.parent = node; + } + } + } + + (tree.parent = new TreeNode(null, 0)).children = [tree]; + return tree; +} + +// Node-link tree diagram using the Reingold-Tilford "tidy" algorithm +function tree() { + var separation = defaultSeparation$1, + dx = 1, + dy = 1, + nodeSize = null; + + function tree(root) { + var t = treeRoot(root); + + // Compute the layout using Buchheim et al.’s algorithm. + t.eachAfter(firstWalk), t.parent.m = -t.z; + t.eachBefore(secondWalk); + + // If a fixed node size is specified, scale x and y. + if (nodeSize) root.eachBefore(sizeNode); + + // If a fixed tree size is specified, scale x and y based on the extent. + // Compute the left-most, right-most, and depth-most nodes for extents. + else { + var left = root, + right = root, + bottom = root; + root.eachBefore(function(node) { + if (node.x < left.x) left = node; + if (node.x > right.x) right = node; + if (node.depth > bottom.depth) bottom = node; + }); + var s = left === right ? 1 : separation(left, right) / 2, + tx = s - left.x, + kx = dx / (right.x + s + tx), + ky = dy / (bottom.depth || 1); + root.eachBefore(function(node) { + node.x = (node.x + tx) * kx; + node.y = node.depth * ky; + }); + } + + return root; + } + + // Computes a preliminary x-coordinate for v. Before that, FIRST WALK is + // applied recursively to the children of v, as well as the function + // APPORTION. After spacing out the children by calling EXECUTE SHIFTS, the + // node v is placed to the midpoint of its outermost children. + function firstWalk(v) { + var children = v.children, + siblings = v.parent.children, + w = v.i ? siblings[v.i - 1] : null; + if (children) { + executeShifts(v); + var midpoint = (children[0].z + children[children.length - 1].z) / 2; + if (w) { + v.z = w.z + separation(v._, w._); + v.m = v.z - midpoint; + } else { + v.z = midpoint; + } + } else if (w) { + v.z = w.z + separation(v._, w._); + } + v.parent.A = apportion(v, w, v.parent.A || siblings[0]); + } + + // Computes all real x-coordinates by summing up the modifiers recursively. + function secondWalk(v) { + v._.x = v.z + v.parent.m; + v.m += v.parent.m; + } + + // The core of the algorithm. Here, a new subtree is combined with the + // previous subtrees. Threads are used to traverse the inside and outside + // contours of the left and right subtree up to the highest common level. The + // vertices used for the traversals are vi+, vi-, vo-, and vo+, where the + // superscript o means outside and i means inside, the subscript - means left + // subtree and + means right subtree. For summing up the modifiers along the + // contour, we use respective variables si+, si-, so-, and so+. Whenever two + // nodes of the inside contours conflict, we compute the left one of the + // greatest uncommon ancestors using the function ANCESTOR and call MOVE + // SUBTREE to shift the subtree and prepare the shifts of smaller subtrees. + // Finally, we add a new thread (if necessary). + function apportion(v, w, ancestor) { + if (w) { + var vip = v, + vop = v, + vim = w, + vom = vip.parent.children[0], + sip = vip.m, + sop = vop.m, + sim = vim.m, + som = vom.m, + shift; + while (vim = nextRight(vim), vip = nextLeft(vip), vim && vip) { + vom = nextLeft(vom); + vop = nextRight(vop); + vop.a = v; + shift = vim.z + sim - vip.z - sip + separation(vim._, vip._); + if (shift > 0) { + moveSubtree(nextAncestor(vim, v, ancestor), v, shift); + sip += shift; + sop += shift; + } + sim += vim.m; + sip += vip.m; + som += vom.m; + sop += vop.m; + } + if (vim && !nextRight(vop)) { + vop.t = vim; + vop.m += sim - sop; + } + if (vip && !nextLeft(vom)) { + vom.t = vip; + vom.m += sip - som; + ancestor = v; + } + } + return ancestor; + } + + function sizeNode(node) { + node.x *= dx; + node.y = node.depth * dy; + } + + tree.separation = function(x) { + return arguments.length ? (separation = x, tree) : separation; + }; + + tree.size = function(x) { + return arguments.length ? (nodeSize = false, dx = +x[0], dy = +x[1], tree) : (nodeSize ? null : [dx, dy]); + }; + + tree.nodeSize = function(x) { + return arguments.length ? (nodeSize = true, dx = +x[0], dy = +x[1], tree) : (nodeSize ? [dx, dy] : null); + }; + + return tree; +} + +function treemapSlice(parent, x0, y0, x1, y1) { + var nodes = parent.children, + node, + i = -1, + n = nodes.length, + k = parent.value && (y1 - y0) / parent.value; + + while (++i < n) { + node = nodes[i], node.x0 = x0, node.x1 = x1; + node.y0 = y0, node.y1 = y0 += node.value * k; + } +} + +var phi = (1 + Math.sqrt(5)) / 2; + +function squarifyRatio(ratio, parent, x0, y0, x1, y1) { + var rows = [], + nodes = parent.children, + row, + nodeValue, + i0 = 0, + i1 = 0, + n = nodes.length, + dx, dy, + value = parent.value, + sumValue, + minValue, + maxValue, + newRatio, + minRatio, + alpha, + beta; + + while (i0 < n) { + dx = x1 - x0, dy = y1 - y0; + + // Find the next non-empty node. + do sumValue = nodes[i1++].value; while (!sumValue && i1 < n); + minValue = maxValue = sumValue; + alpha = Math.max(dy / dx, dx / dy) / (value * ratio); + beta = sumValue * sumValue * alpha; + minRatio = Math.max(maxValue / beta, beta / minValue); + + // Keep adding nodes while the aspect ratio maintains or improves. + for (; i1 < n; ++i1) { + sumValue += nodeValue = nodes[i1].value; + if (nodeValue < minValue) minValue = nodeValue; + if (nodeValue > maxValue) maxValue = nodeValue; + beta = sumValue * sumValue * alpha; + newRatio = Math.max(maxValue / beta, beta / minValue); + if (newRatio > minRatio) { sumValue -= nodeValue; break; } + minRatio = newRatio; + } + + // Position and record the row orientation. + rows.push(row = {value: sumValue, dice: dx < dy, children: nodes.slice(i0, i1)}); + if (row.dice) treemapDice(row, x0, y0, x1, value ? y0 += dy * sumValue / value : y1); + else treemapSlice(row, x0, y0, value ? x0 += dx * sumValue / value : x1, y1); + value -= sumValue, i0 = i1; + } + + return rows; +} + +var squarify = (function custom(ratio) { + + function squarify(parent, x0, y0, x1, y1) { + squarifyRatio(ratio, parent, x0, y0, x1, y1); + } + + squarify.ratio = function(x) { + return custom((x = +x) > 1 ? x : 1); + }; + + return squarify; +})(phi); + +function index$3() { + var tile = squarify, + round = false, + dx = 1, + dy = 1, + paddingStack = [0], + paddingInner = constantZero, + paddingTop = constantZero, + paddingRight = constantZero, + paddingBottom = constantZero, + paddingLeft = constantZero; + + function treemap(root) { + root.x0 = + root.y0 = 0; + root.x1 = dx; + root.y1 = dy; + root.eachBefore(positionNode); + paddingStack = [0]; + if (round) root.eachBefore(roundNode); + return root; + } + + function positionNode(node) { + var p = paddingStack[node.depth], + x0 = node.x0 + p, + y0 = node.y0 + p, + x1 = node.x1 - p, + y1 = node.y1 - p; + if (x1 < x0) x0 = x1 = (x0 + x1) / 2; + if (y1 < y0) y0 = y1 = (y0 + y1) / 2; + node.x0 = x0; + node.y0 = y0; + node.x1 = x1; + node.y1 = y1; + if (node.children) { + p = paddingStack[node.depth + 1] = paddingInner(node) / 2; + x0 += paddingLeft(node) - p; + y0 += paddingTop(node) - p; + x1 -= paddingRight(node) - p; + y1 -= paddingBottom(node) - p; + if (x1 < x0) x0 = x1 = (x0 + x1) / 2; + if (y1 < y0) y0 = y1 = (y0 + y1) / 2; + tile(node, x0, y0, x1, y1); + } + } + + treemap.round = function(x) { + return arguments.length ? (round = !!x, treemap) : round; + }; + + treemap.size = function(x) { + return arguments.length ? (dx = +x[0], dy = +x[1], treemap) : [dx, dy]; + }; + + treemap.tile = function(x) { + return arguments.length ? (tile = required(x), treemap) : tile; + }; + + treemap.padding = function(x) { + return arguments.length ? treemap.paddingInner(x).paddingOuter(x) : treemap.paddingInner(); + }; + + treemap.paddingInner = function(x) { + return arguments.length ? (paddingInner = typeof x === "function" ? x : constant$9(+x), treemap) : paddingInner; + }; + + treemap.paddingOuter = function(x) { + return arguments.length ? treemap.paddingTop(x).paddingRight(x).paddingBottom(x).paddingLeft(x) : treemap.paddingTop(); + }; + + treemap.paddingTop = function(x) { + return arguments.length ? (paddingTop = typeof x === "function" ? x : constant$9(+x), treemap) : paddingTop; + }; + + treemap.paddingRight = function(x) { + return arguments.length ? (paddingRight = typeof x === "function" ? x : constant$9(+x), treemap) : paddingRight; + }; + + treemap.paddingBottom = function(x) { + return arguments.length ? (paddingBottom = typeof x === "function" ? x : constant$9(+x), treemap) : paddingBottom; + }; + + treemap.paddingLeft = function(x) { + return arguments.length ? (paddingLeft = typeof x === "function" ? x : constant$9(+x), treemap) : paddingLeft; + }; + + return treemap; +} + +function binary(parent, x0, y0, x1, y1) { + var nodes = parent.children, + i, n = nodes.length, + sum, sums = new Array(n + 1); + + for (sums[0] = sum = i = 0; i < n; ++i) { + sums[i + 1] = sum += nodes[i].value; + } + + partition(0, n, parent.value, x0, y0, x1, y1); + + function partition(i, j, value, x0, y0, x1, y1) { + if (i >= j - 1) { + var node = nodes[i]; + node.x0 = x0, node.y0 = y0; + node.x1 = x1, node.y1 = y1; + return; + } + + var valueOffset = sums[i], + valueTarget = (value / 2) + valueOffset, + k = i + 1, + hi = j - 1; + + while (k < hi) { + var mid = k + hi >>> 1; + if (sums[mid] < valueTarget) k = mid + 1; + else hi = mid; + } + + if ((valueTarget - sums[k - 1]) < (sums[k] - valueTarget) && i + 1 < k) --k; + + var valueLeft = sums[k] - valueOffset, + valueRight = value - valueLeft; + + if ((x1 - x0) > (y1 - y0)) { + var xk = (x0 * valueRight + x1 * valueLeft) / value; + partition(i, k, valueLeft, x0, y0, xk, y1); + partition(k, j, valueRight, xk, y0, x1, y1); + } else { + var yk = (y0 * valueRight + y1 * valueLeft) / value; + partition(i, k, valueLeft, x0, y0, x1, yk); + partition(k, j, valueRight, x0, yk, x1, y1); + } + } +} + +function sliceDice(parent, x0, y0, x1, y1) { + (parent.depth & 1 ? treemapSlice : treemapDice)(parent, x0, y0, x1, y1); +} + +var resquarify = (function custom(ratio) { + + function resquarify(parent, x0, y0, x1, y1) { + if ((rows = parent._squarify) && (rows.ratio === ratio)) { + var rows, + row, + nodes, + i, + j = -1, + n, + m = rows.length, + value = parent.value; + + while (++j < m) { + row = rows[j], nodes = row.children; + for (i = row.value = 0, n = nodes.length; i < n; ++i) row.value += nodes[i].value; + if (row.dice) treemapDice(row, x0, y0, x1, y0 += (y1 - y0) * row.value / value); + else treemapSlice(row, x0, y0, x0 += (x1 - x0) * row.value / value, y1); + value -= row.value; + } + } else { + parent._squarify = rows = squarifyRatio(ratio, parent, x0, y0, x1, y1); + rows.ratio = ratio; + } + } + + resquarify.ratio = function(x) { + return custom((x = +x) > 1 ? x : 1); + }; + + return resquarify; +})(phi); + +function area$2(polygon) { + var i = -1, + n = polygon.length, + a, + b = polygon[n - 1], + area = 0; + + while (++i < n) { + a = b; + b = polygon[i]; + area += a[1] * b[0] - a[0] * b[1]; + } + + return area / 2; +} + +function centroid$1(polygon) { + var i = -1, + n = polygon.length, + x = 0, + y = 0, + a, + b = polygon[n - 1], + c, + k = 0; + + while (++i < n) { + a = b; + b = polygon[i]; + k += c = a[0] * b[1] - b[0] * a[1]; + x += (a[0] + b[0]) * c; + y += (a[1] + b[1]) * c; + } + + return k *= 3, [x / k, y / k]; +} + +// Returns the 2D cross product of AB and AC vectors, i.e., the z-component of +// the 3D cross product in a quadrant I Cartesian coordinate system (+x is +// right, +y is up). Returns a positive value if ABC is counter-clockwise, +// negative if clockwise, and zero if the points are collinear. +function cross$1(a, b, c) { + return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]); +} + +function lexicographicOrder(a, b) { + return a[0] - b[0] || a[1] - b[1]; +} + +// Computes the upper convex hull per the monotone chain algorithm. +// Assumes points.length >= 3, is sorted by x, unique in y. +// Returns an array of indices into points in left-to-right order. +function computeUpperHullIndexes(points) { + var n = points.length, + indexes = [0, 1], + size = 2; + + for (var i = 2; i < n; ++i) { + while (size > 1 && cross$1(points[indexes[size - 2]], points[indexes[size - 1]], points[i]) <= 0) --size; + indexes[size++] = i; + } + + return indexes.slice(0, size); // remove popped points +} + +function hull(points) { + if ((n = points.length) < 3) return null; + + var i, + n, + sortedPoints = new Array(n), + flippedPoints = new Array(n); + + for (i = 0; i < n; ++i) sortedPoints[i] = [+points[i][0], +points[i][1], i]; + sortedPoints.sort(lexicographicOrder); + for (i = 0; i < n; ++i) flippedPoints[i] = [sortedPoints[i][0], -sortedPoints[i][1]]; + + var upperIndexes = computeUpperHullIndexes(sortedPoints), + lowerIndexes = computeUpperHullIndexes(flippedPoints); + + // Construct the hull polygon, removing possible duplicate endpoints. + var skipLeft = lowerIndexes[0] === upperIndexes[0], + skipRight = lowerIndexes[lowerIndexes.length - 1] === upperIndexes[upperIndexes.length - 1], + hull = []; + + // Add upper hull in right-to-l order. + // Then add lower hull in left-to-right order. + for (i = upperIndexes.length - 1; i >= 0; --i) hull.push(points[sortedPoints[upperIndexes[i]][2]]); + for (i = +skipLeft; i < lowerIndexes.length - skipRight; ++i) hull.push(points[sortedPoints[lowerIndexes[i]][2]]); + + return hull; +} + +function contains$2(polygon, point) { + var n = polygon.length, + p = polygon[n - 1], + x = point[0], y = point[1], + x0 = p[0], y0 = p[1], + x1, y1, + inside = false; + + for (var i = 0; i < n; ++i) { + p = polygon[i], x1 = p[0], y1 = p[1]; + if (((y1 > y) !== (y0 > y)) && (x < (x0 - x1) * (y - y1) / (y0 - y1) + x1)) inside = !inside; + x0 = x1, y0 = y1; + } + + return inside; +} + +function length$2(polygon) { + var i = -1, + n = polygon.length, + b = polygon[n - 1], + xa, + ya, + xb = b[0], + yb = b[1], + perimeter = 0; + + while (++i < n) { + xa = xb; + ya = yb; + b = polygon[i]; + xb = b[0]; + yb = b[1]; + xa -= xb; + ya -= yb; + perimeter += Math.sqrt(xa * xa + ya * ya); + } + + return perimeter; +} + +function defaultSource$1() { + return Math.random(); +} + +var uniform = (function sourceRandomUniform(source) { + function randomUniform(min, max) { + min = min == null ? 0 : +min; + max = max == null ? 1 : +max; + if (arguments.length === 1) max = min, min = 0; + else max -= min; + return function() { + return source() * max + min; + }; + } + + randomUniform.source = sourceRandomUniform; + + return randomUniform; +})(defaultSource$1); + +var normal = (function sourceRandomNormal(source) { + function randomNormal(mu, sigma) { + var x, r; + mu = mu == null ? 0 : +mu; + sigma = sigma == null ? 1 : +sigma; + return function() { + var y; + + // If available, use the second previously-generated uniform random. + if (x != null) y = x, x = null; + + // Otherwise, generate a new x and y. + else do { + x = source() * 2 - 1; + y = source() * 2 - 1; + r = x * x + y * y; + } while (!r || r > 1); + + return mu + sigma * y * Math.sqrt(-2 * Math.log(r) / r); + }; + } + + randomNormal.source = sourceRandomNormal; + + return randomNormal; +})(defaultSource$1); + +var logNormal = (function sourceRandomLogNormal(source) { + function randomLogNormal() { + var randomNormal = normal.source(source).apply(this, arguments); + return function() { + return Math.exp(randomNormal()); + }; + } + + randomLogNormal.source = sourceRandomLogNormal; + + return randomLogNormal; +})(defaultSource$1); + +var irwinHall = (function sourceRandomIrwinHall(source) { + function randomIrwinHall(n) { + return function() { + for (var sum = 0, i = 0; i < n; ++i) sum += source(); + return sum; + }; + } + + randomIrwinHall.source = sourceRandomIrwinHall; + + return randomIrwinHall; +})(defaultSource$1); + +var bates = (function sourceRandomBates(source) { + function randomBates(n) { + var randomIrwinHall = irwinHall.source(source)(n); + return function() { + return randomIrwinHall() / n; + }; + } + + randomBates.source = sourceRandomBates; + + return randomBates; +})(defaultSource$1); + +var exponential$1 = (function sourceRandomExponential(source) { + function randomExponential(lambda) { + return function() { + return -Math.log(1 - source()) / lambda; + }; + } + + randomExponential.source = sourceRandomExponential; + + return randomExponential; +})(defaultSource$1); + +var array$3 = Array.prototype; + +var map$2 = array$3.map; +var slice$5 = array$3.slice; + +var implicit = {name: "implicit"}; + +function ordinal(range) { + var index = map$1(), + domain = [], + unknown = implicit; + + range = range == null ? [] : slice$5.call(range); + + function scale(d) { + var key = d + "", i = index.get(key); + if (!i) { + if (unknown !== implicit) return unknown; + index.set(key, i = domain.push(d)); + } + return range[(i - 1) % range.length]; + } + + scale.domain = function(_) { + if (!arguments.length) return domain.slice(); + domain = [], index = map$1(); + var i = -1, n = _.length, d, key; + while (++i < n) if (!index.has(key = (d = _[i]) + "")) index.set(key, domain.push(d)); + return scale; + }; + + scale.range = function(_) { + return arguments.length ? (range = slice$5.call(_), scale) : range.slice(); + }; + + scale.unknown = function(_) { + return arguments.length ? (unknown = _, scale) : unknown; + }; + + scale.copy = function() { + return ordinal() + .domain(domain) + .range(range) + .unknown(unknown); + }; + + return scale; +} + +function band() { + var scale = ordinal().unknown(undefined), + domain = scale.domain, + ordinalRange = scale.range, + range$$1 = [0, 1], + step, + bandwidth, + round = false, + paddingInner = 0, + paddingOuter = 0, + align = 0.5; + + delete scale.unknown; + + function rescale() { + var n = domain().length, + reverse = range$$1[1] < range$$1[0], + start = range$$1[reverse - 0], + stop = range$$1[1 - reverse]; + step = (stop - start) / Math.max(1, n - paddingInner + paddingOuter * 2); + if (round) step = Math.floor(step); + start += (stop - start - step * (n - paddingInner)) * align; + bandwidth = step * (1 - paddingInner); + if (round) start = Math.round(start), bandwidth = Math.round(bandwidth); + var values = sequence(n).map(function(i) { return start + step * i; }); + return ordinalRange(reverse ? values.reverse() : values); + } + + scale.domain = function(_) { + return arguments.length ? (domain(_), rescale()) : domain(); + }; + + scale.range = function(_) { + return arguments.length ? (range$$1 = [+_[0], +_[1]], rescale()) : range$$1.slice(); + }; + + scale.rangeRound = function(_) { + return range$$1 = [+_[0], +_[1]], round = true, rescale(); + }; + + scale.bandwidth = function() { + return bandwidth; + }; + + scale.step = function() { + return step; + }; + + scale.round = function(_) { + return arguments.length ? (round = !!_, rescale()) : round; + }; + + scale.padding = function(_) { + return arguments.length ? (paddingInner = paddingOuter = Math.max(0, Math.min(1, _)), rescale()) : paddingInner; + }; + + scale.paddingInner = function(_) { + return arguments.length ? (paddingInner = Math.max(0, Math.min(1, _)), rescale()) : paddingInner; + }; + + scale.paddingOuter = function(_) { + return arguments.length ? (paddingOuter = Math.max(0, Math.min(1, _)), rescale()) : paddingOuter; + }; + + scale.align = function(_) { + return arguments.length ? (align = Math.max(0, Math.min(1, _)), rescale()) : align; + }; + + scale.copy = function() { + return band() + .domain(domain()) + .range(range$$1) + .round(round) + .paddingInner(paddingInner) + .paddingOuter(paddingOuter) + .align(align); + }; + + return rescale(); +} + +function pointish(scale) { + var copy = scale.copy; + + scale.padding = scale.paddingOuter; + delete scale.paddingInner; + delete scale.paddingOuter; + + scale.copy = function() { + return pointish(copy()); + }; + + return scale; +} + +function point$1() { + return pointish(band().paddingInner(1)); +} + +function constant$10(x) { + return function() { + return x; + }; +} + +function number$2(x) { + return +x; +} + +var unit = [0, 1]; + +function deinterpolateLinear(a, b) { + return (b -= (a = +a)) + ? function(x) { return (x - a) / b; } + : constant$10(b); +} + +function deinterpolateClamp(deinterpolate) { + return function(a, b) { + var d = deinterpolate(a = +a, b = +b); + return function(x) { return x <= a ? 0 : x >= b ? 1 : d(x); }; + }; +} + +function reinterpolateClamp(reinterpolate$$1) { + return function(a, b) { + var r = reinterpolate$$1(a = +a, b = +b); + return function(t) { return t <= 0 ? a : t >= 1 ? b : r(t); }; + }; +} + +function bimap(domain, range, deinterpolate, reinterpolate$$1) { + var d0 = domain[0], d1 = domain[1], r0 = range[0], r1 = range[1]; + if (d1 < d0) d0 = deinterpolate(d1, d0), r0 = reinterpolate$$1(r1, r0); + else d0 = deinterpolate(d0, d1), r0 = reinterpolate$$1(r0, r1); + return function(x) { return r0(d0(x)); }; +} + +function polymap(domain, range, deinterpolate, reinterpolate$$1) { + var j = Math.min(domain.length, range.length) - 1, + d = new Array(j), + r = new Array(j), + i = -1; + + // Reverse descending domains. + if (domain[j] < domain[0]) { + domain = domain.slice().reverse(); + range = range.slice().reverse(); + } + + while (++i < j) { + d[i] = deinterpolate(domain[i], domain[i + 1]); + r[i] = reinterpolate$$1(range[i], range[i + 1]); + } + + return function(x) { + var i = bisectRight(domain, x, 1, j) - 1; + return r[i](d[i](x)); + }; +} + +function copy(source, target) { + return target + .domain(source.domain()) + .range(source.range()) + .interpolate(source.interpolate()) + .clamp(source.clamp()); +} + +// deinterpolate(a, b)(x) takes a domain value x in [a,b] and returns the corresponding parameter t in [0,1]. +// reinterpolate(a, b)(t) takes a parameter t in [0,1] and returns the corresponding domain value x in [a,b]. +function continuous(deinterpolate, reinterpolate$$1) { + var domain = unit, + range = unit, + interpolate$$1 = interpolateValue, + clamp = false, + piecewise, + output, + input; + + function rescale() { + piecewise = Math.min(domain.length, range.length) > 2 ? polymap : bimap; + output = input = null; + return scale; + } + + function scale(x) { + return (output || (output = piecewise(domain, range, clamp ? deinterpolateClamp(deinterpolate) : deinterpolate, interpolate$$1)))(+x); + } + + scale.invert = function(y) { + return (input || (input = piecewise(range, domain, deinterpolateLinear, clamp ? reinterpolateClamp(reinterpolate$$1) : reinterpolate$$1)))(+y); + }; + + scale.domain = function(_) { + return arguments.length ? (domain = map$2.call(_, number$2), rescale()) : domain.slice(); + }; + + scale.range = function(_) { + return arguments.length ? (range = slice$5.call(_), rescale()) : range.slice(); + }; + + scale.rangeRound = function(_) { + return range = slice$5.call(_), interpolate$$1 = interpolateRound, rescale(); + }; + + scale.clamp = function(_) { + return arguments.length ? (clamp = !!_, rescale()) : clamp; + }; + + scale.interpolate = function(_) { + return arguments.length ? (interpolate$$1 = _, rescale()) : interpolate$$1; + }; + + return rescale(); +} + +function tickFormat(domain, count, specifier) { + var start = domain[0], + stop = domain[domain.length - 1], + step = tickStep(start, stop, count == null ? 10 : count), + precision; + specifier = formatSpecifier(specifier == null ? ",f" : specifier); + switch (specifier.type) { + case "s": { + var value = Math.max(Math.abs(start), Math.abs(stop)); + if (specifier.precision == null && !isNaN(precision = precisionPrefix(step, value))) specifier.precision = precision; + return exports.formatPrefix(specifier, value); + } + case "": + case "e": + case "g": + case "p": + case "r": { + if (specifier.precision == null && !isNaN(precision = precisionRound(step, Math.max(Math.abs(start), Math.abs(stop))))) specifier.precision = precision - (specifier.type === "e"); + break; + } + case "f": + case "%": { + if (specifier.precision == null && !isNaN(precision = precisionFixed(step))) specifier.precision = precision - (specifier.type === "%") * 2; + break; + } + } + return exports.format(specifier); +} + +function linearish(scale) { + var domain = scale.domain; + + scale.ticks = function(count) { + var d = domain(); + return ticks(d[0], d[d.length - 1], count == null ? 10 : count); + }; + + scale.tickFormat = function(count, specifier) { + return tickFormat(domain(), count, specifier); + }; + + scale.nice = function(count) { + if (count == null) count = 10; + + var d = domain(), + i0 = 0, + i1 = d.length - 1, + start = d[i0], + stop = d[i1], + step; + + if (stop < start) { + step = start, start = stop, stop = step; + step = i0, i0 = i1, i1 = step; + } + + step = tickIncrement(start, stop, count); + + if (step > 0) { + start = Math.floor(start / step) * step; + stop = Math.ceil(stop / step) * step; + step = tickIncrement(start, stop, count); + } else if (step < 0) { + start = Math.ceil(start * step) / step; + stop = Math.floor(stop * step) / step; + step = tickIncrement(start, stop, count); + } + + if (step > 0) { + d[i0] = Math.floor(start / step) * step; + d[i1] = Math.ceil(stop / step) * step; + domain(d); + } else if (step < 0) { + d[i0] = Math.ceil(start * step) / step; + d[i1] = Math.floor(stop * step) / step; + domain(d); + } + + return scale; + }; + + return scale; +} + +function linear$2() { + var scale = continuous(deinterpolateLinear, reinterpolate); + + scale.copy = function() { + return copy(scale, linear$2()); + }; + + return linearish(scale); +} + +function identity$6() { + var domain = [0, 1]; + + function scale(x) { + return +x; + } + + scale.invert = scale; + + scale.domain = scale.range = function(_) { + return arguments.length ? (domain = map$2.call(_, number$2), scale) : domain.slice(); + }; + + scale.copy = function() { + return identity$6().domain(domain); + }; + + return linearish(scale); +} + +function nice(domain, interval) { + domain = domain.slice(); + + var i0 = 0, + i1 = domain.length - 1, + x0 = domain[i0], + x1 = domain[i1], + t; + + if (x1 < x0) { + t = i0, i0 = i1, i1 = t; + t = x0, x0 = x1, x1 = t; + } + + domain[i0] = interval.floor(x0); + domain[i1] = interval.ceil(x1); + return domain; +} + +function deinterpolate(a, b) { + return (b = Math.log(b / a)) + ? function(x) { return Math.log(x / a) / b; } + : constant$10(b); +} + +function reinterpolate$1(a, b) { + return a < 0 + ? function(t) { return -Math.pow(-b, t) * Math.pow(-a, 1 - t); } + : function(t) { return Math.pow(b, t) * Math.pow(a, 1 - t); }; +} + +function pow10(x) { + return isFinite(x) ? +("1e" + x) : x < 0 ? 0 : x; +} + +function powp(base) { + return base === 10 ? pow10 + : base === Math.E ? Math.exp + : function(x) { return Math.pow(base, x); }; +} + +function logp(base) { + return base === Math.E ? Math.log + : base === 10 && Math.log10 + || base === 2 && Math.log2 + || (base = Math.log(base), function(x) { return Math.log(x) / base; }); +} + +function reflect(f) { + return function(x) { + return -f(-x); + }; +} + +function log$1() { + var scale = continuous(deinterpolate, reinterpolate$1).domain([1, 10]), + domain = scale.domain, + base = 10, + logs = logp(10), + pows = powp(10); + + function rescale() { + logs = logp(base), pows = powp(base); + if (domain()[0] < 0) logs = reflect(logs), pows = reflect(pows); + return scale; + } + + scale.base = function(_) { + return arguments.length ? (base = +_, rescale()) : base; + }; + + scale.domain = function(_) { + return arguments.length ? (domain(_), rescale()) : domain(); + }; + + scale.ticks = function(count) { + var d = domain(), + u = d[0], + v = d[d.length - 1], + r; + + if (r = v < u) i = u, u = v, v = i; + + var i = logs(u), + j = logs(v), + p, + k, + t, + n = count == null ? 10 : +count, + z = []; + + if (!(base % 1) && j - i < n) { + i = Math.round(i) - 1, j = Math.round(j) + 1; + if (u > 0) for (; i < j; ++i) { + for (k = 1, p = pows(i); k < base; ++k) { + t = p * k; + if (t < u) continue; + if (t > v) break; + z.push(t); + } + } else for (; i < j; ++i) { + for (k = base - 1, p = pows(i); k >= 1; --k) { + t = p * k; + if (t < u) continue; + if (t > v) break; + z.push(t); + } + } + } else { + z = ticks(i, j, Math.min(j - i, n)).map(pows); + } + + return r ? z.reverse() : z; + }; + + scale.tickFormat = function(count, specifier) { + if (specifier == null) specifier = base === 10 ? ".0e" : ","; + if (typeof specifier !== "function") specifier = exports.format(specifier); + if (count === Infinity) return specifier; + if (count == null) count = 10; + var k = Math.max(1, base * count / scale.ticks().length); // TODO fast estimate? + return function(d) { + var i = d / pows(Math.round(logs(d))); + if (i * base < base - 0.5) i *= base; + return i <= k ? specifier(d) : ""; + }; + }; + + scale.nice = function() { + return domain(nice(domain(), { + floor: function(x) { return pows(Math.floor(logs(x))); }, + ceil: function(x) { return pows(Math.ceil(logs(x))); } + })); + }; + + scale.copy = function() { + return copy(scale, log$1().base(base)); + }; + + return scale; +} + +function raise$1(x, exponent) { + return x < 0 ? -Math.pow(-x, exponent) : Math.pow(x, exponent); +} + +function pow$1() { + var exponent = 1, + scale = continuous(deinterpolate, reinterpolate), + domain = scale.domain; + + function deinterpolate(a, b) { + return (b = raise$1(b, exponent) - (a = raise$1(a, exponent))) + ? function(x) { return (raise$1(x, exponent) - a) / b; } + : constant$10(b); + } + + function reinterpolate(a, b) { + b = raise$1(b, exponent) - (a = raise$1(a, exponent)); + return function(t) { return raise$1(a + b * t, 1 / exponent); }; + } + + scale.exponent = function(_) { + return arguments.length ? (exponent = +_, domain(domain())) : exponent; + }; + + scale.copy = function() { + return copy(scale, pow$1().exponent(exponent)); + }; + + return linearish(scale); +} + +function sqrt$1() { + return pow$1().exponent(0.5); +} + +function quantile$$1() { + var domain = [], + range = [], + thresholds = []; + + function rescale() { + var i = 0, n = Math.max(1, range.length); + thresholds = new Array(n - 1); + while (++i < n) thresholds[i - 1] = threshold(domain, i / n); + return scale; + } + + function scale(x) { + if (!isNaN(x = +x)) return range[bisectRight(thresholds, x)]; + } + + scale.invertExtent = function(y) { + var i = range.indexOf(y); + return i < 0 ? [NaN, NaN] : [ + i > 0 ? thresholds[i - 1] : domain[0], + i < thresholds.length ? thresholds[i] : domain[domain.length - 1] + ]; + }; + + scale.domain = function(_) { + if (!arguments.length) return domain.slice(); + domain = []; + for (var i = 0, n = _.length, d; i < n; ++i) if (d = _[i], d != null && !isNaN(d = +d)) domain.push(d); + domain.sort(ascending); + return rescale(); + }; + + scale.range = function(_) { + return arguments.length ? (range = slice$5.call(_), rescale()) : range.slice(); + }; + + scale.quantiles = function() { + return thresholds.slice(); + }; + + scale.copy = function() { + return quantile$$1() + .domain(domain) + .range(range); + }; + + return scale; +} + +function quantize$1() { + var x0 = 0, + x1 = 1, + n = 1, + domain = [0.5], + range = [0, 1]; + + function scale(x) { + if (x <= x) return range[bisectRight(domain, x, 0, n)]; + } + + function rescale() { + var i = -1; + domain = new Array(n); + while (++i < n) domain[i] = ((i + 1) * x1 - (i - n) * x0) / (n + 1); + return scale; + } + + scale.domain = function(_) { + return arguments.length ? (x0 = +_[0], x1 = +_[1], rescale()) : [x0, x1]; + }; + + scale.range = function(_) { + return arguments.length ? (n = (range = slice$5.call(_)).length - 1, rescale()) : range.slice(); + }; + + scale.invertExtent = function(y) { + var i = range.indexOf(y); + return i < 0 ? [NaN, NaN] + : i < 1 ? [x0, domain[0]] + : i >= n ? [domain[n - 1], x1] + : [domain[i - 1], domain[i]]; + }; + + scale.copy = function() { + return quantize$1() + .domain([x0, x1]) + .range(range); + }; + + return linearish(scale); +} + +function threshold$1() { + var domain = [0.5], + range = [0, 1], + n = 1; + + function scale(x) { + if (x <= x) return range[bisectRight(domain, x, 0, n)]; + } + + scale.domain = function(_) { + return arguments.length ? (domain = slice$5.call(_), n = Math.min(domain.length, range.length - 1), scale) : domain.slice(); + }; + + scale.range = function(_) { + return arguments.length ? (range = slice$5.call(_), n = Math.min(domain.length, range.length - 1), scale) : range.slice(); + }; + + scale.invertExtent = function(y) { + var i = range.indexOf(y); + return [domain[i - 1], domain[i]]; + }; + + scale.copy = function() { + return threshold$1() + .domain(domain) + .range(range); + }; + + return scale; +} + +var t0$1 = new Date, + t1$1 = new Date; + +function newInterval(floori, offseti, count, field) { + + function interval(date) { + return floori(date = new Date(+date)), date; + } + + interval.floor = interval; + + interval.ceil = function(date) { + return floori(date = new Date(date - 1)), offseti(date, 1), floori(date), date; + }; + + interval.round = function(date) { + var d0 = interval(date), + d1 = interval.ceil(date); + return date - d0 < d1 - date ? d0 : d1; + }; + + interval.offset = function(date, step) { + return offseti(date = new Date(+date), step == null ? 1 : Math.floor(step)), date; + }; + + interval.range = function(start, stop, step) { + var range = [], previous; + start = interval.ceil(start); + step = step == null ? 1 : Math.floor(step); + if (!(start < stop) || !(step > 0)) return range; // also handles Invalid Date + do range.push(previous = new Date(+start)), offseti(start, step), floori(start); + while (previous < start && start < stop); + return range; + }; + + interval.filter = function(test) { + return newInterval(function(date) { + if (date >= date) while (floori(date), !test(date)) date.setTime(date - 1); + }, function(date, step) { + if (date >= date) { + if (step < 0) while (++step <= 0) { + while (offseti(date, -1), !test(date)) {} // eslint-disable-line no-empty + } else while (--step >= 0) { + while (offseti(date, +1), !test(date)) {} // eslint-disable-line no-empty + } + } + }); + }; + + if (count) { + interval.count = function(start, end) { + t0$1.setTime(+start), t1$1.setTime(+end); + floori(t0$1), floori(t1$1); + return Math.floor(count(t0$1, t1$1)); + }; + + interval.every = function(step) { + step = Math.floor(step); + return !isFinite(step) || !(step > 0) ? null + : !(step > 1) ? interval + : interval.filter(field + ? function(d) { return field(d) % step === 0; } + : function(d) { return interval.count(0, d) % step === 0; }); + }; + } + + return interval; +} + +var millisecond = newInterval(function() { + // noop +}, function(date, step) { + date.setTime(+date + step); +}, function(start, end) { + return end - start; +}); + +// An optimized implementation for this simple case. +millisecond.every = function(k) { + k = Math.floor(k); + if (!isFinite(k) || !(k > 0)) return null; + if (!(k > 1)) return millisecond; + return newInterval(function(date) { + date.setTime(Math.floor(date / k) * k); + }, function(date, step) { + date.setTime(+date + step * k); + }, function(start, end) { + return (end - start) / k; + }); +}; +var milliseconds = millisecond.range; + +var durationSecond = 1e3; +var durationMinute = 6e4; +var durationHour = 36e5; +var durationDay = 864e5; +var durationWeek = 6048e5; + +var second = newInterval(function(date) { + date.setTime(Math.floor(date / durationSecond) * durationSecond); +}, function(date, step) { + date.setTime(+date + step * durationSecond); +}, function(start, end) { + return (end - start) / durationSecond; +}, function(date) { + return date.getUTCSeconds(); +}); +var seconds = second.range; + +var minute = newInterval(function(date) { + date.setTime(Math.floor(date / durationMinute) * durationMinute); +}, function(date, step) { + date.setTime(+date + step * durationMinute); +}, function(start, end) { + return (end - start) / durationMinute; +}, function(date) { + return date.getMinutes(); +}); +var minutes = minute.range; + +var hour = newInterval(function(date) { + var offset = date.getTimezoneOffset() * durationMinute % durationHour; + if (offset < 0) offset += durationHour; + date.setTime(Math.floor((+date - offset) / durationHour) * durationHour + offset); +}, function(date, step) { + date.setTime(+date + step * durationHour); +}, function(start, end) { + return (end - start) / durationHour; +}, function(date) { + return date.getHours(); +}); +var hours = hour.range; + +var day = newInterval(function(date) { + date.setHours(0, 0, 0, 0); +}, function(date, step) { + date.setDate(date.getDate() + step); +}, function(start, end) { + return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute) / durationDay; +}, function(date) { + return date.getDate() - 1; +}); +var days = day.range; + +function weekday(i) { + return newInterval(function(date) { + date.setDate(date.getDate() - (date.getDay() + 7 - i) % 7); + date.setHours(0, 0, 0, 0); + }, function(date, step) { + date.setDate(date.getDate() + step * 7); + }, function(start, end) { + return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute) / durationWeek; + }); +} + +var sunday = weekday(0); +var monday = weekday(1); +var tuesday = weekday(2); +var wednesday = weekday(3); +var thursday = weekday(4); +var friday = weekday(5); +var saturday = weekday(6); + +var sundays = sunday.range; +var mondays = monday.range; +var tuesdays = tuesday.range; +var wednesdays = wednesday.range; +var thursdays = thursday.range; +var fridays = friday.range; +var saturdays = saturday.range; + +var month = newInterval(function(date) { + date.setDate(1); + date.setHours(0, 0, 0, 0); +}, function(date, step) { + date.setMonth(date.getMonth() + step); +}, function(start, end) { + return end.getMonth() - start.getMonth() + (end.getFullYear() - start.getFullYear()) * 12; +}, function(date) { + return date.getMonth(); +}); +var months = month.range; + +var year = newInterval(function(date) { + date.setMonth(0, 1); + date.setHours(0, 0, 0, 0); +}, function(date, step) { + date.setFullYear(date.getFullYear() + step); +}, function(start, end) { + return end.getFullYear() - start.getFullYear(); +}, function(date) { + return date.getFullYear(); +}); + +// An optimized implementation for this simple case. +year.every = function(k) { + return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : newInterval(function(date) { + date.setFullYear(Math.floor(date.getFullYear() / k) * k); + date.setMonth(0, 1); + date.setHours(0, 0, 0, 0); + }, function(date, step) { + date.setFullYear(date.getFullYear() + step * k); + }); +}; +var years = year.range; + +var utcMinute = newInterval(function(date) { + date.setUTCSeconds(0, 0); +}, function(date, step) { + date.setTime(+date + step * durationMinute); +}, function(start, end) { + return (end - start) / durationMinute; +}, function(date) { + return date.getUTCMinutes(); +}); +var utcMinutes = utcMinute.range; + +var utcHour = newInterval(function(date) { + date.setUTCMinutes(0, 0, 0); +}, function(date, step) { + date.setTime(+date + step * durationHour); +}, function(start, end) { + return (end - start) / durationHour; +}, function(date) { + return date.getUTCHours(); +}); +var utcHours = utcHour.range; + +var utcDay = newInterval(function(date) { + date.setUTCHours(0, 0, 0, 0); +}, function(date, step) { + date.setUTCDate(date.getUTCDate() + step); +}, function(start, end) { + return (end - start) / durationDay; +}, function(date) { + return date.getUTCDate() - 1; +}); +var utcDays = utcDay.range; + +function utcWeekday(i) { + return newInterval(function(date) { + date.setUTCDate(date.getUTCDate() - (date.getUTCDay() + 7 - i) % 7); + date.setUTCHours(0, 0, 0, 0); + }, function(date, step) { + date.setUTCDate(date.getUTCDate() + step * 7); + }, function(start, end) { + return (end - start) / durationWeek; + }); +} + +var utcSunday = utcWeekday(0); +var utcMonday = utcWeekday(1); +var utcTuesday = utcWeekday(2); +var utcWednesday = utcWeekday(3); +var utcThursday = utcWeekday(4); +var utcFriday = utcWeekday(5); +var utcSaturday = utcWeekday(6); + +var utcSundays = utcSunday.range; +var utcMondays = utcMonday.range; +var utcTuesdays = utcTuesday.range; +var utcWednesdays = utcWednesday.range; +var utcThursdays = utcThursday.range; +var utcFridays = utcFriday.range; +var utcSaturdays = utcSaturday.range; + +var utcMonth = newInterval(function(date) { + date.setUTCDate(1); + date.setUTCHours(0, 0, 0, 0); +}, function(date, step) { + date.setUTCMonth(date.getUTCMonth() + step); +}, function(start, end) { + return end.getUTCMonth() - start.getUTCMonth() + (end.getUTCFullYear() - start.getUTCFullYear()) * 12; +}, function(date) { + return date.getUTCMonth(); +}); +var utcMonths = utcMonth.range; + +var utcYear = newInterval(function(date) { + date.setUTCMonth(0, 1); + date.setUTCHours(0, 0, 0, 0); +}, function(date, step) { + date.setUTCFullYear(date.getUTCFullYear() + step); +}, function(start, end) { + return end.getUTCFullYear() - start.getUTCFullYear(); +}, function(date) { + return date.getUTCFullYear(); +}); + +// An optimized implementation for this simple case. +utcYear.every = function(k) { + return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : newInterval(function(date) { + date.setUTCFullYear(Math.floor(date.getUTCFullYear() / k) * k); + date.setUTCMonth(0, 1); + date.setUTCHours(0, 0, 0, 0); + }, function(date, step) { + date.setUTCFullYear(date.getUTCFullYear() + step * k); + }); +}; +var utcYears = utcYear.range; + +function localDate(d) { + if (0 <= d.y && d.y < 100) { + var date = new Date(-1, d.m, d.d, d.H, d.M, d.S, d.L); + date.setFullYear(d.y); + return date; + } + return new Date(d.y, d.m, d.d, d.H, d.M, d.S, d.L); +} + +function utcDate(d) { + if (0 <= d.y && d.y < 100) { + var date = new Date(Date.UTC(-1, d.m, d.d, d.H, d.M, d.S, d.L)); + date.setUTCFullYear(d.y); + return date; + } + return new Date(Date.UTC(d.y, d.m, d.d, d.H, d.M, d.S, d.L)); +} + +function newYear(y) { + return {y: y, m: 0, d: 1, H: 0, M: 0, S: 0, L: 0}; +} + +function formatLocale$1(locale) { + var locale_dateTime = locale.dateTime, + locale_date = locale.date, + locale_time = locale.time, + locale_periods = locale.periods, + locale_weekdays = locale.days, + locale_shortWeekdays = locale.shortDays, + locale_months = locale.months, + locale_shortMonths = locale.shortMonths; + + var periodRe = formatRe(locale_periods), + periodLookup = formatLookup(locale_periods), + weekdayRe = formatRe(locale_weekdays), + weekdayLookup = formatLookup(locale_weekdays), + shortWeekdayRe = formatRe(locale_shortWeekdays), + shortWeekdayLookup = formatLookup(locale_shortWeekdays), + monthRe = formatRe(locale_months), + monthLookup = formatLookup(locale_months), + shortMonthRe = formatRe(locale_shortMonths), + shortMonthLookup = formatLookup(locale_shortMonths); + + var formats = { + "a": formatShortWeekday, + "A": formatWeekday, + "b": formatShortMonth, + "B": formatMonth, + "c": null, + "d": formatDayOfMonth, + "e": formatDayOfMonth, + "f": formatMicroseconds, + "H": formatHour24, + "I": formatHour12, + "j": formatDayOfYear, + "L": formatMilliseconds, + "m": formatMonthNumber, + "M": formatMinutes, + "p": formatPeriod, + "Q": formatUnixTimestamp, + "s": formatUnixTimestampSeconds, + "S": formatSeconds, + "u": formatWeekdayNumberMonday, + "U": formatWeekNumberSunday, + "V": formatWeekNumberISO, + "w": formatWeekdayNumberSunday, + "W": formatWeekNumberMonday, + "x": null, + "X": null, + "y": formatYear, + "Y": formatFullYear, + "Z": formatZone, + "%": formatLiteralPercent + }; + + var utcFormats = { + "a": formatUTCShortWeekday, + "A": formatUTCWeekday, + "b": formatUTCShortMonth, + "B": formatUTCMonth, + "c": null, + "d": formatUTCDayOfMonth, + "e": formatUTCDayOfMonth, + "f": formatUTCMicroseconds, + "H": formatUTCHour24, + "I": formatUTCHour12, + "j": formatUTCDayOfYear, + "L": formatUTCMilliseconds, + "m": formatUTCMonthNumber, + "M": formatUTCMinutes, + "p": formatUTCPeriod, + "Q": formatUnixTimestamp, + "s": formatUnixTimestampSeconds, + "S": formatUTCSeconds, + "u": formatUTCWeekdayNumberMonday, + "U": formatUTCWeekNumberSunday, + "V": formatUTCWeekNumberISO, + "w": formatUTCWeekdayNumberSunday, + "W": formatUTCWeekNumberMonday, + "x": null, + "X": null, + "y": formatUTCYear, + "Y": formatUTCFullYear, + "Z": formatUTCZone, + "%": formatLiteralPercent + }; + + var parses = { + "a": parseShortWeekday, + "A": parseWeekday, + "b": parseShortMonth, + "B": parseMonth, + "c": parseLocaleDateTime, + "d": parseDayOfMonth, + "e": parseDayOfMonth, + "f": parseMicroseconds, + "H": parseHour24, + "I": parseHour24, + "j": parseDayOfYear, + "L": parseMilliseconds, + "m": parseMonthNumber, + "M": parseMinutes, + "p": parsePeriod, + "Q": parseUnixTimestamp, + "s": parseUnixTimestampSeconds, + "S": parseSeconds, + "u": parseWeekdayNumberMonday, + "U": parseWeekNumberSunday, + "V": parseWeekNumberISO, + "w": parseWeekdayNumberSunday, + "W": parseWeekNumberMonday, + "x": parseLocaleDate, + "X": parseLocaleTime, + "y": parseYear, + "Y": parseFullYear, + "Z": parseZone, + "%": parseLiteralPercent + }; + + // These recursive directive definitions must be deferred. + formats.x = newFormat(locale_date, formats); + formats.X = newFormat(locale_time, formats); + formats.c = newFormat(locale_dateTime, formats); + utcFormats.x = newFormat(locale_date, utcFormats); + utcFormats.X = newFormat(locale_time, utcFormats); + utcFormats.c = newFormat(locale_dateTime, utcFormats); + + function newFormat(specifier, formats) { + return function(date) { + var string = [], + i = -1, + j = 0, + n = specifier.length, + c, + pad, + format; + + if (!(date instanceof Date)) date = new Date(+date); + + while (++i < n) { + if (specifier.charCodeAt(i) === 37) { + string.push(specifier.slice(j, i)); + if ((pad = pads[c = specifier.charAt(++i)]) != null) c = specifier.charAt(++i); + else pad = c === "e" ? " " : "0"; + if (format = formats[c]) c = format(date, pad); + string.push(c); + j = i + 1; + } + } + + string.push(specifier.slice(j, i)); + return string.join(""); + }; + } + + function newParse(specifier, newDate) { + return function(string) { + var d = newYear(1900), + i = parseSpecifier(d, specifier, string += "", 0), + week, day$$1; + if (i != string.length) return null; + + // If a UNIX timestamp is specified, return it. + if ("Q" in d) return new Date(d.Q); + + // The am-pm flag is 0 for AM, and 1 for PM. + if ("p" in d) d.H = d.H % 12 + d.p * 12; + + // Convert day-of-week and week-of-year to day-of-year. + if ("V" in d) { + if (d.V < 1 || d.V > 53) return null; + if (!("w" in d)) d.w = 1; + if ("Z" in d) { + week = utcDate(newYear(d.y)), day$$1 = week.getUTCDay(); + week = day$$1 > 4 || day$$1 === 0 ? utcMonday.ceil(week) : utcMonday(week); + week = utcDay.offset(week, (d.V - 1) * 7); + d.y = week.getUTCFullYear(); + d.m = week.getUTCMonth(); + d.d = week.getUTCDate() + (d.w + 6) % 7; + } else { + week = newDate(newYear(d.y)), day$$1 = week.getDay(); + week = day$$1 > 4 || day$$1 === 0 ? monday.ceil(week) : monday(week); + week = day.offset(week, (d.V - 1) * 7); + d.y = week.getFullYear(); + d.m = week.getMonth(); + d.d = week.getDate() + (d.w + 6) % 7; + } + } else if ("W" in d || "U" in d) { + if (!("w" in d)) d.w = "u" in d ? d.u % 7 : "W" in d ? 1 : 0; + day$$1 = "Z" in d ? utcDate(newYear(d.y)).getUTCDay() : newDate(newYear(d.y)).getDay(); + d.m = 0; + 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; + } + + // If a time zone is specified, all fields are interpreted as UTC and then + // offset according to the specified time zone. + if ("Z" in d) { + d.H += d.Z / 100 | 0; + d.M += d.Z % 100; + return utcDate(d); + } + + // Otherwise, all fields are in local time. + return newDate(d); + }; + } + + function parseSpecifier(d, specifier, string, j) { + var i = 0, + n = specifier.length, + m = string.length, + c, + parse; + + while (i < n) { + if (j >= m) return -1; + c = specifier.charCodeAt(i++); + if (c === 37) { + c = specifier.charAt(i++); + parse = parses[c in pads ? specifier.charAt(i++) : c]; + if (!parse || ((j = parse(d, string, j)) < 0)) return -1; + } else if (c != string.charCodeAt(j++)) { + return -1; + } + } + + return j; + } + + function parsePeriod(d, string, i) { + var n = periodRe.exec(string.slice(i)); + return n ? (d.p = periodLookup[n[0].toLowerCase()], i + n[0].length) : -1; + } + + function parseShortWeekday(d, string, i) { + var n = shortWeekdayRe.exec(string.slice(i)); + return n ? (d.w = shortWeekdayLookup[n[0].toLowerCase()], i + n[0].length) : -1; + } + + function parseWeekday(d, string, i) { + var n = weekdayRe.exec(string.slice(i)); + return n ? (d.w = weekdayLookup[n[0].toLowerCase()], i + n[0].length) : -1; + } + + function parseShortMonth(d, string, i) { + var n = shortMonthRe.exec(string.slice(i)); + return n ? (d.m = shortMonthLookup[n[0].toLowerCase()], i + n[0].length) : -1; + } + + function parseMonth(d, string, i) { + var n = monthRe.exec(string.slice(i)); + return n ? (d.m = monthLookup[n[0].toLowerCase()], i + n[0].length) : -1; + } + + function parseLocaleDateTime(d, string, i) { + return parseSpecifier(d, locale_dateTime, string, i); + } + + function parseLocaleDate(d, string, i) { + return parseSpecifier(d, locale_date, string, i); + } + + function parseLocaleTime(d, string, i) { + return parseSpecifier(d, locale_time, string, i); + } + + function formatShortWeekday(d) { + return locale_shortWeekdays[d.getDay()]; + } + + function formatWeekday(d) { + return locale_weekdays[d.getDay()]; + } + + function formatShortMonth(d) { + return locale_shortMonths[d.getMonth()]; + } + + function formatMonth(d) { + return locale_months[d.getMonth()]; + } + + function formatPeriod(d) { + return locale_periods[+(d.getHours() >= 12)]; + } + + function formatUTCShortWeekday(d) { + return locale_shortWeekdays[d.getUTCDay()]; + } + + function formatUTCWeekday(d) { + return locale_weekdays[d.getUTCDay()]; + } + + function formatUTCShortMonth(d) { + return locale_shortMonths[d.getUTCMonth()]; + } + + function formatUTCMonth(d) { + return locale_months[d.getUTCMonth()]; + } + + function formatUTCPeriod(d) { + return locale_periods[+(d.getUTCHours() >= 12)]; + } + + return { + format: function(specifier) { + var f = newFormat(specifier += "", formats); + f.toString = function() { return specifier; }; + return f; + }, + parse: function(specifier) { + var p = newParse(specifier += "", localDate); + p.toString = function() { return specifier; }; + return p; + }, + utcFormat: function(specifier) { + var f = newFormat(specifier += "", utcFormats); + f.toString = function() { return specifier; }; + return f; + }, + utcParse: function(specifier) { + var p = newParse(specifier, utcDate); + p.toString = function() { return specifier; }; + return p; + } + }; +} + +var pads = {"-": "", "_": " ", "0": "0"}, + numberRe = /^\s*\d+/, // note: ignores next directive + percentRe = /^%/, + requoteRe = /[\\^$*+?|[\]().{}]/g; + +function pad(value, fill, width) { + var sign = value < 0 ? "-" : "", + string = (sign ? -value : value) + "", + length = string.length; + return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string); +} + +function requote(s) { + return s.replace(requoteRe, "\\$&"); +} + +function formatRe(names) { + return new RegExp("^(?:" + names.map(requote).join("|") + ")", "i"); +} + +function formatLookup(names) { + var map = {}, i = -1, n = names.length; + while (++i < n) map[names[i].toLowerCase()] = i; + return map; +} + +function parseWeekdayNumberSunday(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 1)); + return n ? (d.w = +n[0], i + n[0].length) : -1; +} + +function parseWeekdayNumberMonday(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 1)); + return n ? (d.u = +n[0], i + n[0].length) : -1; +} + +function parseWeekNumberSunday(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 2)); + return n ? (d.U = +n[0], i + n[0].length) : -1; +} + +function parseWeekNumberISO(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 2)); + return n ? (d.V = +n[0], i + n[0].length) : -1; +} + +function parseWeekNumberMonday(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 2)); + return n ? (d.W = +n[0], i + n[0].length) : -1; +} + +function parseFullYear(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 4)); + return n ? (d.y = +n[0], i + n[0].length) : -1; +} + +function parseYear(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 2)); + return n ? (d.y = +n[0] + (+n[0] > 68 ? 1900 : 2000), i + n[0].length) : -1; +} + +function parseZone(d, string, i) { + var n = /^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(string.slice(i, i + 6)); + return n ? (d.Z = n[1] ? 0 : -(n[2] + (n[3] || "00")), i + n[0].length) : -1; +} + +function parseMonthNumber(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 2)); + return n ? (d.m = n[0] - 1, i + n[0].length) : -1; +} + +function parseDayOfMonth(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 2)); + return n ? (d.d = +n[0], i + n[0].length) : -1; +} + +function parseDayOfYear(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 3)); + return n ? (d.m = 0, d.d = +n[0], i + n[0].length) : -1; +} + +function parseHour24(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 2)); + return n ? (d.H = +n[0], i + n[0].length) : -1; +} + +function parseMinutes(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 2)); + return n ? (d.M = +n[0], i + n[0].length) : -1; +} + +function parseSeconds(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 2)); + return n ? (d.S = +n[0], i + n[0].length) : -1; +} + +function parseMilliseconds(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 3)); + return n ? (d.L = +n[0], i + n[0].length) : -1; +} + +function parseMicroseconds(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 6)); + return n ? (d.L = Math.floor(n[0] / 1000), i + n[0].length) : -1; +} + +function parseLiteralPercent(d, string, i) { + var n = percentRe.exec(string.slice(i, i + 1)); + return n ? i + n[0].length : -1; +} + +function parseUnixTimestamp(d, string, i) { + var n = numberRe.exec(string.slice(i)); + return n ? (d.Q = +n[0], i + n[0].length) : -1; +} + +function parseUnixTimestampSeconds(d, string, i) { + var n = numberRe.exec(string.slice(i)); + return n ? (d.Q = (+n[0]) * 1000, i + n[0].length) : -1; +} + +function formatDayOfMonth(d, p) { + return pad(d.getDate(), p, 2); +} + +function formatHour24(d, p) { + return pad(d.getHours(), p, 2); +} + +function formatHour12(d, p) { + return pad(d.getHours() % 12 || 12, p, 2); +} + +function formatDayOfYear(d, p) { + return pad(1 + day.count(year(d), d), p, 3); +} + +function formatMilliseconds(d, p) { + return pad(d.getMilliseconds(), p, 3); +} + +function formatMicroseconds(d, p) { + return formatMilliseconds(d, p) + "000"; +} + +function formatMonthNumber(d, p) { + return pad(d.getMonth() + 1, p, 2); +} + +function formatMinutes(d, p) { + return pad(d.getMinutes(), p, 2); +} + +function formatSeconds(d, p) { + return pad(d.getSeconds(), p, 2); +} + +function formatWeekdayNumberMonday(d) { + var day$$1 = d.getDay(); + return day$$1 === 0 ? 7 : day$$1; +} + +function formatWeekNumberSunday(d, p) { + return pad(sunday.count(year(d), d), p, 2); +} + +function formatWeekNumberISO(d, p) { + var day$$1 = d.getDay(); + d = (day$$1 >= 4 || day$$1 === 0) ? thursday(d) : thursday.ceil(d); + return pad(thursday.count(year(d), d) + (year(d).getDay() === 4), p, 2); +} + +function formatWeekdayNumberSunday(d) { + return d.getDay(); +} + +function formatWeekNumberMonday(d, p) { + return pad(monday.count(year(d), d), p, 2); +} + +function formatYear(d, p) { + return pad(d.getFullYear() % 100, p, 2); +} + +function formatFullYear(d, p) { + return pad(d.getFullYear() % 10000, p, 4); +} + +function formatZone(d) { + var z = d.getTimezoneOffset(); + return (z > 0 ? "-" : (z *= -1, "+")) + + pad(z / 60 | 0, "0", 2) + + pad(z % 60, "0", 2); +} + +function formatUTCDayOfMonth(d, p) { + return pad(d.getUTCDate(), p, 2); +} + +function formatUTCHour24(d, p) { + return pad(d.getUTCHours(), p, 2); +} + +function formatUTCHour12(d, p) { + return pad(d.getUTCHours() % 12 || 12, p, 2); +} + +function formatUTCDayOfYear(d, p) { + return pad(1 + utcDay.count(utcYear(d), d), p, 3); +} + +function formatUTCMilliseconds(d, p) { + return pad(d.getUTCMilliseconds(), p, 3); +} + +function formatUTCMicroseconds(d, p) { + return formatUTCMilliseconds(d, p) + "000"; +} + +function formatUTCMonthNumber(d, p) { + return pad(d.getUTCMonth() + 1, p, 2); +} + +function formatUTCMinutes(d, p) { + return pad(d.getUTCMinutes(), p, 2); +} + +function formatUTCSeconds(d, p) { + return pad(d.getUTCSeconds(), p, 2); +} + +function formatUTCWeekdayNumberMonday(d) { + var dow = d.getUTCDay(); + return dow === 0 ? 7 : dow; +} + +function formatUTCWeekNumberSunday(d, p) { + return pad(utcSunday.count(utcYear(d), d), p, 2); +} + +function formatUTCWeekNumberISO(d, p) { + var day$$1 = d.getUTCDay(); + d = (day$$1 >= 4 || day$$1 === 0) ? utcThursday(d) : utcThursday.ceil(d); + return pad(utcThursday.count(utcYear(d), d) + (utcYear(d).getUTCDay() === 4), p, 2); +} + +function formatUTCWeekdayNumberSunday(d) { + return d.getUTCDay(); +} + +function formatUTCWeekNumberMonday(d, p) { + return pad(utcMonday.count(utcYear(d), d), p, 2); +} + +function formatUTCYear(d, p) { + return pad(d.getUTCFullYear() % 100, p, 2); +} + +function formatUTCFullYear(d, p) { + return pad(d.getUTCFullYear() % 10000, p, 4); +} + +function formatUTCZone() { + return "+0000"; +} + +function formatLiteralPercent() { + return "%"; +} + +function formatUnixTimestamp(d) { + return +d; +} + +function formatUnixTimestampSeconds(d) { + return Math.floor(+d / 1000); +} + +var locale$1; + +defaultLocale$1({ + dateTime: "%x, %X", + date: "%-m/%-d/%Y", + time: "%-I:%M:%S %p", + periods: ["AM", "PM"], + days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], + shortDays: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], + months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], + shortMonths: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] +}); + +function defaultLocale$1(definition) { + locale$1 = formatLocale$1(definition); + exports.timeFormat = locale$1.format; + exports.timeParse = locale$1.parse; + exports.utcFormat = locale$1.utcFormat; + exports.utcParse = locale$1.utcParse; + return locale$1; +} + +var isoSpecifier = "%Y-%m-%dT%H:%M:%S.%LZ"; + +function formatIsoNative(date) { + return date.toISOString(); +} + +var formatIso = Date.prototype.toISOString + ? formatIsoNative + : exports.utcFormat(isoSpecifier); + +function parseIsoNative(string) { + var date = new Date(string); + return isNaN(date) ? null : date; +} + +var parseIso = +new Date("2000-01-01T00:00:00.000Z") + ? parseIsoNative + : exports.utcParse(isoSpecifier); + +var durationSecond$1 = 1000, + durationMinute$1 = durationSecond$1 * 60, + durationHour$1 = durationMinute$1 * 60, + durationDay$1 = durationHour$1 * 24, + durationWeek$1 = durationDay$1 * 7, + durationMonth = durationDay$1 * 30, + durationYear = durationDay$1 * 365; + +function date$1(t) { + return new Date(t); +} + +function number$3(t) { + return t instanceof Date ? +t : +new Date(+t); +} + +function calendar(year$$1, month$$1, week, day$$1, hour$$1, minute$$1, second$$1, millisecond$$1, format) { + var scale = continuous(deinterpolateLinear, reinterpolate), + invert = scale.invert, + domain = scale.domain; + + var formatMillisecond = format(".%L"), + formatSecond = format(":%S"), + formatMinute = format("%I:%M"), + formatHour = format("%I %p"), + formatDay = format("%a %d"), + formatWeek = format("%b %d"), + formatMonth = format("%B"), + formatYear = format("%Y"); + + var tickIntervals = [ + [second$$1, 1, durationSecond$1], + [second$$1, 5, 5 * durationSecond$1], + [second$$1, 15, 15 * durationSecond$1], + [second$$1, 30, 30 * durationSecond$1], + [minute$$1, 1, durationMinute$1], + [minute$$1, 5, 5 * durationMinute$1], + [minute$$1, 15, 15 * durationMinute$1], + [minute$$1, 30, 30 * durationMinute$1], + [ hour$$1, 1, durationHour$1 ], + [ hour$$1, 3, 3 * durationHour$1 ], + [ hour$$1, 6, 6 * durationHour$1 ], + [ hour$$1, 12, 12 * durationHour$1 ], + [ day$$1, 1, durationDay$1 ], + [ day$$1, 2, 2 * durationDay$1 ], + [ week, 1, durationWeek$1 ], + [ month$$1, 1, durationMonth ], + [ month$$1, 3, 3 * durationMonth ], + [ year$$1, 1, durationYear ] + ]; + + function tickFormat(date$$1) { + return (second$$1(date$$1) < date$$1 ? formatMillisecond + : minute$$1(date$$1) < date$$1 ? formatSecond + : hour$$1(date$$1) < date$$1 ? formatMinute + : day$$1(date$$1) < date$$1 ? formatHour + : month$$1(date$$1) < date$$1 ? (week(date$$1) < date$$1 ? formatDay : formatWeek) + : year$$1(date$$1) < date$$1 ? formatMonth + : formatYear)(date$$1); + } + + function tickInterval(interval, start, stop, step) { + if (interval == null) interval = 10; + + // If a desired tick count is specified, pick a reasonable tick interval + // based on the extent of the domain and a rough estimate of tick size. + // Otherwise, assume interval is already a time interval and use it. + if (typeof interval === "number") { + var target = Math.abs(stop - start) / interval, + i = bisector(function(i) { return i[2]; }).right(tickIntervals, target); + if (i === tickIntervals.length) { + step = tickStep(start / durationYear, stop / durationYear, interval); + interval = year$$1; + } else if (i) { + i = tickIntervals[target / tickIntervals[i - 1][2] < tickIntervals[i][2] / target ? i - 1 : i]; + step = i[1]; + interval = i[0]; + } else { + step = Math.max(tickStep(start, stop, interval), 1); + interval = millisecond$$1; + } + } + + return step == null ? interval : interval.every(step); + } + + scale.invert = function(y) { + return new Date(invert(y)); + }; + + scale.domain = function(_) { + return arguments.length ? domain(map$2.call(_, number$3)) : domain().map(date$1); + }; + + scale.ticks = function(interval, step) { + var d = domain(), + t0 = d[0], + t1 = d[d.length - 1], + r = t1 < t0, + t; + if (r) t = t0, t0 = t1, t1 = t; + t = tickInterval(interval, t0, t1, step); + t = t ? t.range(t0, t1 + 1) : []; // inclusive stop + return r ? t.reverse() : t; + }; + + scale.tickFormat = function(count, specifier) { + return specifier == null ? tickFormat : format(specifier); + }; + + scale.nice = function(interval, step) { + var d = domain(); + return (interval = tickInterval(interval, d[0], d[d.length - 1], step)) + ? domain(nice(d, interval)) + : scale; + }; + + scale.copy = function() { + return copy(scale, calendar(year$$1, month$$1, week, day$$1, hour$$1, minute$$1, second$$1, millisecond$$1, format)); + }; + + return scale; +} + +function time() { + return calendar(year, month, sunday, day, hour, minute, second, millisecond, exports.timeFormat).domain([new Date(2000, 0, 1), new Date(2000, 0, 2)]); +} + +function utcTime() { + return calendar(utcYear, utcMonth, utcSunday, utcDay, utcHour, utcMinute, second, millisecond, exports.utcFormat).domain([Date.UTC(2000, 0, 1), Date.UTC(2000, 0, 2)]); +} + +function sequential(interpolator) { + var x0 = 0, + x1 = 1, + clamp = false; + + function scale(x) { + var t = (x - x0) / (x1 - x0); + return interpolator(clamp ? Math.max(0, Math.min(1, t)) : t); + } + + scale.domain = function(_) { + return arguments.length ? (x0 = +_[0], x1 = +_[1], scale) : [x0, x1]; + }; + + scale.clamp = function(_) { + return arguments.length ? (clamp = !!_, scale) : clamp; + }; + + scale.interpolator = function(_) { + return arguments.length ? (interpolator = _, scale) : interpolator; + }; + + scale.copy = function() { + return sequential(interpolator).domain([x0, x1]).clamp(clamp); + }; + + return linearish(scale); +} + +function colors(specifier) { + var n = specifier.length / 6 | 0, colors = new Array(n), i = 0; + while (i < n) colors[i] = "#" + specifier.slice(i * 6, ++i * 6); + return colors; +} + +var category10 = colors("1f77b4ff7f0e2ca02cd627289467bd8c564be377c27f7f7fbcbd2217becf"); + +var Accent = colors("7fc97fbeaed4fdc086ffff99386cb0f0027fbf5b17666666"); + +var Dark2 = colors("1b9e77d95f027570b3e7298a66a61ee6ab02a6761d666666"); + +var Paired = colors("a6cee31f78b4b2df8a33a02cfb9a99e31a1cfdbf6fff7f00cab2d66a3d9affff99b15928"); + +var Pastel1 = colors("fbb4aeb3cde3ccebc5decbe4fed9a6ffffcce5d8bdfddaecf2f2f2"); + +var Pastel2 = colors("b3e2cdfdcdaccbd5e8f4cae4e6f5c9fff2aef1e2cccccccc"); + +var Set1 = colors("e41a1c377eb84daf4a984ea3ff7f00ffff33a65628f781bf999999"); + +var Set2 = colors("66c2a5fc8d628da0cbe78ac3a6d854ffd92fe5c494b3b3b3"); + +var Set3 = colors("8dd3c7ffffb3bebadafb807280b1d3fdb462b3de69fccde5d9d9d9bc80bdccebc5ffed6f"); + +function ramp(scheme) { + return rgbBasis(scheme[scheme.length - 1]); +} + +var scheme = new Array(3).concat( + "d8b365f5f5f55ab4ac", + "a6611adfc27d80cdc1018571", + "a6611adfc27df5f5f580cdc1018571", + "8c510ad8b365f6e8c3c7eae55ab4ac01665e", + "8c510ad8b365f6e8c3f5f5f5c7eae55ab4ac01665e", + "8c510abf812ddfc27df6e8c3c7eae580cdc135978f01665e", + "8c510abf812ddfc27df6e8c3f5f5f5c7eae580cdc135978f01665e", + "5430058c510abf812ddfc27df6e8c3c7eae580cdc135978f01665e003c30", + "5430058c510abf812ddfc27df6e8c3f5f5f5c7eae580cdc135978f01665e003c30" +).map(colors); + +var BrBG = ramp(scheme); + +var scheme$1 = new Array(3).concat( + "af8dc3f7f7f77fbf7b", + "7b3294c2a5cfa6dba0008837", + "7b3294c2a5cff7f7f7a6dba0008837", + "762a83af8dc3e7d4e8d9f0d37fbf7b1b7837", + "762a83af8dc3e7d4e8f7f7f7d9f0d37fbf7b1b7837", + "762a839970abc2a5cfe7d4e8d9f0d3a6dba05aae611b7837", + "762a839970abc2a5cfe7d4e8f7f7f7d9f0d3a6dba05aae611b7837", + "40004b762a839970abc2a5cfe7d4e8d9f0d3a6dba05aae611b783700441b", + "40004b762a839970abc2a5cfe7d4e8f7f7f7d9f0d3a6dba05aae611b783700441b" +).map(colors); + +var PRGn = ramp(scheme$1); + +var scheme$2 = new Array(3).concat( + "e9a3c9f7f7f7a1d76a", + "d01c8bf1b6dab8e1864dac26", + "d01c8bf1b6daf7f7f7b8e1864dac26", + "c51b7de9a3c9fde0efe6f5d0a1d76a4d9221", + "c51b7de9a3c9fde0eff7f7f7e6f5d0a1d76a4d9221", + "c51b7dde77aef1b6dafde0efe6f5d0b8e1867fbc414d9221", + "c51b7dde77aef1b6dafde0eff7f7f7e6f5d0b8e1867fbc414d9221", + "8e0152c51b7dde77aef1b6dafde0efe6f5d0b8e1867fbc414d9221276419", + "8e0152c51b7dde77aef1b6dafde0eff7f7f7e6f5d0b8e1867fbc414d9221276419" +).map(colors); + +var PiYG = ramp(scheme$2); + +var scheme$3 = new Array(3).concat( + "998ec3f7f7f7f1a340", + "5e3c99b2abd2fdb863e66101", + "5e3c99b2abd2f7f7f7fdb863e66101", + "542788998ec3d8daebfee0b6f1a340b35806", + "542788998ec3d8daebf7f7f7fee0b6f1a340b35806", + "5427888073acb2abd2d8daebfee0b6fdb863e08214b35806", + "5427888073acb2abd2d8daebf7f7f7fee0b6fdb863e08214b35806", + "2d004b5427888073acb2abd2d8daebfee0b6fdb863e08214b358067f3b08", + "2d004b5427888073acb2abd2d8daebf7f7f7fee0b6fdb863e08214b358067f3b08" +).map(colors); + +var PuOr = ramp(scheme$3); + +var scheme$4 = new Array(3).concat( + "ef8a62f7f7f767a9cf", + "ca0020f4a58292c5de0571b0", + "ca0020f4a582f7f7f792c5de0571b0", + "b2182bef8a62fddbc7d1e5f067a9cf2166ac", + "b2182bef8a62fddbc7f7f7f7d1e5f067a9cf2166ac", + "b2182bd6604df4a582fddbc7d1e5f092c5de4393c32166ac", + "b2182bd6604df4a582fddbc7f7f7f7d1e5f092c5de4393c32166ac", + "67001fb2182bd6604df4a582fddbc7d1e5f092c5de4393c32166ac053061", + "67001fb2182bd6604df4a582fddbc7f7f7f7d1e5f092c5de4393c32166ac053061" +).map(colors); + +var RdBu = ramp(scheme$4); + +var scheme$5 = new Array(3).concat( + "ef8a62ffffff999999", + "ca0020f4a582bababa404040", + "ca0020f4a582ffffffbababa404040", + "b2182bef8a62fddbc7e0e0e09999994d4d4d", + "b2182bef8a62fddbc7ffffffe0e0e09999994d4d4d", + "b2182bd6604df4a582fddbc7e0e0e0bababa8787874d4d4d", + "b2182bd6604df4a582fddbc7ffffffe0e0e0bababa8787874d4d4d", + "67001fb2182bd6604df4a582fddbc7e0e0e0bababa8787874d4d4d1a1a1a", + "67001fb2182bd6604df4a582fddbc7ffffffe0e0e0bababa8787874d4d4d1a1a1a" +).map(colors); + +var RdGy = ramp(scheme$5); + +var scheme$6 = new Array(3).concat( + "fc8d59ffffbf91bfdb", + "d7191cfdae61abd9e92c7bb6", + "d7191cfdae61ffffbfabd9e92c7bb6", + "d73027fc8d59fee090e0f3f891bfdb4575b4", + "d73027fc8d59fee090ffffbfe0f3f891bfdb4575b4", + "d73027f46d43fdae61fee090e0f3f8abd9e974add14575b4", + "d73027f46d43fdae61fee090ffffbfe0f3f8abd9e974add14575b4", + "a50026d73027f46d43fdae61fee090e0f3f8abd9e974add14575b4313695", + "a50026d73027f46d43fdae61fee090ffffbfe0f3f8abd9e974add14575b4313695" +).map(colors); + +var RdYlBu = ramp(scheme$6); + +var scheme$7 = new Array(3).concat( + "fc8d59ffffbf91cf60", + "d7191cfdae61a6d96a1a9641", + "d7191cfdae61ffffbfa6d96a1a9641", + "d73027fc8d59fee08bd9ef8b91cf601a9850", + "d73027fc8d59fee08bffffbfd9ef8b91cf601a9850", + "d73027f46d43fdae61fee08bd9ef8ba6d96a66bd631a9850", + "d73027f46d43fdae61fee08bffffbfd9ef8ba6d96a66bd631a9850", + "a50026d73027f46d43fdae61fee08bd9ef8ba6d96a66bd631a9850006837", + "a50026d73027f46d43fdae61fee08bffffbfd9ef8ba6d96a66bd631a9850006837" +).map(colors); + +var RdYlGn = ramp(scheme$7); + +var scheme$8 = new Array(3).concat( + "fc8d59ffffbf99d594", + "d7191cfdae61abdda42b83ba", + "d7191cfdae61ffffbfabdda42b83ba", + "d53e4ffc8d59fee08be6f59899d5943288bd", + "d53e4ffc8d59fee08bffffbfe6f59899d5943288bd", + "d53e4ff46d43fdae61fee08be6f598abdda466c2a53288bd", + "d53e4ff46d43fdae61fee08bffffbfe6f598abdda466c2a53288bd", + "9e0142d53e4ff46d43fdae61fee08be6f598abdda466c2a53288bd5e4fa2", + "9e0142d53e4ff46d43fdae61fee08bffffbfe6f598abdda466c2a53288bd5e4fa2" +).map(colors); + +var Spectral = ramp(scheme$8); + +var scheme$9 = new Array(3).concat( + "e5f5f999d8c92ca25f", + "edf8fbb2e2e266c2a4238b45", + "edf8fbb2e2e266c2a42ca25f006d2c", + "edf8fbccece699d8c966c2a42ca25f006d2c", + "edf8fbccece699d8c966c2a441ae76238b45005824", + "f7fcfde5f5f9ccece699d8c966c2a441ae76238b45005824", + "f7fcfde5f5f9ccece699d8c966c2a441ae76238b45006d2c00441b" +).map(colors); + +var BuGn = ramp(scheme$9); + +var scheme$10 = new Array(3).concat( + "e0ecf49ebcda8856a7", + "edf8fbb3cde38c96c688419d", + "edf8fbb3cde38c96c68856a7810f7c", + "edf8fbbfd3e69ebcda8c96c68856a7810f7c", + "edf8fbbfd3e69ebcda8c96c68c6bb188419d6e016b", + "f7fcfde0ecf4bfd3e69ebcda8c96c68c6bb188419d6e016b", + "f7fcfde0ecf4bfd3e69ebcda8c96c68c6bb188419d810f7c4d004b" +).map(colors); + +var BuPu = ramp(scheme$10); + +var scheme$11 = new Array(3).concat( + "e0f3dba8ddb543a2ca", + "f0f9e8bae4bc7bccc42b8cbe", + "f0f9e8bae4bc7bccc443a2ca0868ac", + "f0f9e8ccebc5a8ddb57bccc443a2ca0868ac", + "f0f9e8ccebc5a8ddb57bccc44eb3d32b8cbe08589e", + "f7fcf0e0f3dbccebc5a8ddb57bccc44eb3d32b8cbe08589e", + "f7fcf0e0f3dbccebc5a8ddb57bccc44eb3d32b8cbe0868ac084081" +).map(colors); + +var GnBu = ramp(scheme$11); + +var scheme$12 = new Array(3).concat( + "fee8c8fdbb84e34a33", + "fef0d9fdcc8afc8d59d7301f", + "fef0d9fdcc8afc8d59e34a33b30000", + "fef0d9fdd49efdbb84fc8d59e34a33b30000", + "fef0d9fdd49efdbb84fc8d59ef6548d7301f990000", + "fff7ecfee8c8fdd49efdbb84fc8d59ef6548d7301f990000", + "fff7ecfee8c8fdd49efdbb84fc8d59ef6548d7301fb300007f0000" +).map(colors); + +var OrRd = ramp(scheme$12); + +var scheme$13 = new Array(3).concat( + "ece2f0a6bddb1c9099", + "f6eff7bdc9e167a9cf02818a", + "f6eff7bdc9e167a9cf1c9099016c59", + "f6eff7d0d1e6a6bddb67a9cf1c9099016c59", + "f6eff7d0d1e6a6bddb67a9cf3690c002818a016450", + "fff7fbece2f0d0d1e6a6bddb67a9cf3690c002818a016450", + "fff7fbece2f0d0d1e6a6bddb67a9cf3690c002818a016c59014636" +).map(colors); + +var PuBuGn = ramp(scheme$13); + +var scheme$14 = new Array(3).concat( + "ece7f2a6bddb2b8cbe", + "f1eef6bdc9e174a9cf0570b0", + "f1eef6bdc9e174a9cf2b8cbe045a8d", + "f1eef6d0d1e6a6bddb74a9cf2b8cbe045a8d", + "f1eef6d0d1e6a6bddb74a9cf3690c00570b0034e7b", + "fff7fbece7f2d0d1e6a6bddb74a9cf3690c00570b0034e7b", + "fff7fbece7f2d0d1e6a6bddb74a9cf3690c00570b0045a8d023858" +).map(colors); + +var PuBu = ramp(scheme$14); + +var scheme$15 = new Array(3).concat( + "e7e1efc994c7dd1c77", + "f1eef6d7b5d8df65b0ce1256", + "f1eef6d7b5d8df65b0dd1c77980043", + "f1eef6d4b9dac994c7df65b0dd1c77980043", + "f1eef6d4b9dac994c7df65b0e7298ace125691003f", + "f7f4f9e7e1efd4b9dac994c7df65b0e7298ace125691003f", + "f7f4f9e7e1efd4b9dac994c7df65b0e7298ace125698004367001f" +).map(colors); + +var PuRd = ramp(scheme$15); + +var scheme$16 = new Array(3).concat( + "fde0ddfa9fb5c51b8a", + "feebe2fbb4b9f768a1ae017e", + "feebe2fbb4b9f768a1c51b8a7a0177", + "feebe2fcc5c0fa9fb5f768a1c51b8a7a0177", + "feebe2fcc5c0fa9fb5f768a1dd3497ae017e7a0177", + "fff7f3fde0ddfcc5c0fa9fb5f768a1dd3497ae017e7a0177", + "fff7f3fde0ddfcc5c0fa9fb5f768a1dd3497ae017e7a017749006a" +).map(colors); + +var RdPu = ramp(scheme$16); + +var scheme$17 = new Array(3).concat( + "edf8b17fcdbb2c7fb8", + "ffffcca1dab441b6c4225ea8", + "ffffcca1dab441b6c42c7fb8253494", + "ffffccc7e9b47fcdbb41b6c42c7fb8253494", + "ffffccc7e9b47fcdbb41b6c41d91c0225ea80c2c84", + "ffffd9edf8b1c7e9b47fcdbb41b6c41d91c0225ea80c2c84", + "ffffd9edf8b1c7e9b47fcdbb41b6c41d91c0225ea8253494081d58" +).map(colors); + +var YlGnBu = ramp(scheme$17); + +var scheme$18 = new Array(3).concat( + "f7fcb9addd8e31a354", + "ffffccc2e69978c679238443", + "ffffccc2e69978c67931a354006837", + "ffffccd9f0a3addd8e78c67931a354006837", + "ffffccd9f0a3addd8e78c67941ab5d238443005a32", + "ffffe5f7fcb9d9f0a3addd8e78c67941ab5d238443005a32", + "ffffe5f7fcb9d9f0a3addd8e78c67941ab5d238443006837004529" +).map(colors); + +var YlGn = ramp(scheme$18); + +var scheme$19 = new Array(3).concat( + "fff7bcfec44fd95f0e", + "ffffd4fed98efe9929cc4c02", + "ffffd4fed98efe9929d95f0e993404", + "ffffd4fee391fec44ffe9929d95f0e993404", + "ffffd4fee391fec44ffe9929ec7014cc4c028c2d04", + "ffffe5fff7bcfee391fec44ffe9929ec7014cc4c028c2d04", + "ffffe5fff7bcfee391fec44ffe9929ec7014cc4c02993404662506" +).map(colors); + +var YlOrBr = ramp(scheme$19); + +var scheme$20 = new Array(3).concat( + "ffeda0feb24cf03b20", + "ffffb2fecc5cfd8d3ce31a1c", + "ffffb2fecc5cfd8d3cf03b20bd0026", + "ffffb2fed976feb24cfd8d3cf03b20bd0026", + "ffffb2fed976feb24cfd8d3cfc4e2ae31a1cb10026", + "ffffccffeda0fed976feb24cfd8d3cfc4e2ae31a1cb10026", + "ffffccffeda0fed976feb24cfd8d3cfc4e2ae31a1cbd0026800026" +).map(colors); + +var YlOrRd = ramp(scheme$20); + +var scheme$21 = new Array(3).concat( + "deebf79ecae13182bd", + "eff3ffbdd7e76baed62171b5", + "eff3ffbdd7e76baed63182bd08519c", + "eff3ffc6dbef9ecae16baed63182bd08519c", + "eff3ffc6dbef9ecae16baed64292c62171b5084594", + "f7fbffdeebf7c6dbef9ecae16baed64292c62171b5084594", + "f7fbffdeebf7c6dbef9ecae16baed64292c62171b508519c08306b" +).map(colors); + +var Blues = ramp(scheme$21); + +var scheme$22 = new Array(3).concat( + "e5f5e0a1d99b31a354", + "edf8e9bae4b374c476238b45", + "edf8e9bae4b374c47631a354006d2c", + "edf8e9c7e9c0a1d99b74c47631a354006d2c", + "edf8e9c7e9c0a1d99b74c47641ab5d238b45005a32", + "f7fcf5e5f5e0c7e9c0a1d99b74c47641ab5d238b45005a32", + "f7fcf5e5f5e0c7e9c0a1d99b74c47641ab5d238b45006d2c00441b" +).map(colors); + +var Greens = ramp(scheme$22); + +var scheme$23 = new Array(3).concat( + "f0f0f0bdbdbd636363", + "f7f7f7cccccc969696525252", + "f7f7f7cccccc969696636363252525", + "f7f7f7d9d9d9bdbdbd969696636363252525", + "f7f7f7d9d9d9bdbdbd969696737373525252252525", + "fffffff0f0f0d9d9d9bdbdbd969696737373525252252525", + "fffffff0f0f0d9d9d9bdbdbd969696737373525252252525000000" +).map(colors); + +var Greys = ramp(scheme$23); + +var scheme$24 = new Array(3).concat( + "efedf5bcbddc756bb1", + "f2f0f7cbc9e29e9ac86a51a3", + "f2f0f7cbc9e29e9ac8756bb154278f", + "f2f0f7dadaebbcbddc9e9ac8756bb154278f", + "f2f0f7dadaebbcbddc9e9ac8807dba6a51a34a1486", + "fcfbfdefedf5dadaebbcbddc9e9ac8807dba6a51a34a1486", + "fcfbfdefedf5dadaebbcbddc9e9ac8807dba6a51a354278f3f007d" +).map(colors); + +var Purples = ramp(scheme$24); + +var scheme$25 = new Array(3).concat( + "fee0d2fc9272de2d26", + "fee5d9fcae91fb6a4acb181d", + "fee5d9fcae91fb6a4ade2d26a50f15", + "fee5d9fcbba1fc9272fb6a4ade2d26a50f15", + "fee5d9fcbba1fc9272fb6a4aef3b2ccb181d99000d", + "fff5f0fee0d2fcbba1fc9272fb6a4aef3b2ccb181d99000d", + "fff5f0fee0d2fcbba1fc9272fb6a4aef3b2ccb181da50f1567000d" +).map(colors); + +var Reds = ramp(scheme$25); + +var scheme$26 = new Array(3).concat( + "fee6cefdae6be6550d", + "feeddefdbe85fd8d3cd94701", + "feeddefdbe85fd8d3ce6550da63603", + "feeddefdd0a2fdae6bfd8d3ce6550da63603", + "feeddefdd0a2fdae6bfd8d3cf16913d948018c2d04", + "fff5ebfee6cefdd0a2fdae6bfd8d3cf16913d948018c2d04", + "fff5ebfee6cefdd0a2fdae6bfd8d3cf16913d94801a636037f2704" +).map(colors); + +var Oranges = ramp(scheme$26); + +var cubehelix$3 = cubehelixLong(cubehelix(300, 0.5, 0.0), cubehelix(-240, 0.5, 1.0)); + +var warm = cubehelixLong(cubehelix(-100, 0.75, 0.35), cubehelix(80, 1.50, 0.8)); + +var cool = cubehelixLong(cubehelix(260, 0.75, 0.35), cubehelix(80, 1.50, 0.8)); + +var rainbow = cubehelix(); + +function rainbow$1(t) { + if (t < 0 || t > 1) t -= Math.floor(t); + var ts = Math.abs(t - 0.5); + rainbow.h = 360 * t - 100; + rainbow.s = 1.5 - 1.5 * ts; + rainbow.l = 0.8 - 0.9 * ts; + return rainbow + ""; +} + +function ramp$1(range) { + var n = range.length; + return function(t) { + return range[Math.max(0, Math.min(n - 1, Math.floor(t * n)))]; + }; +} + +var viridis = ramp$1(colors("44015444025645045745055946075a46085c460a5d460b5e470d60470e6147106347116447136548146748166848176948186a481a6c481b6d481c6e481d6f481f70482071482173482374482475482576482677482878482979472a7a472c7a472d7b472e7c472f7d46307e46327e46337f463480453581453781453882443983443a83443b84433d84433e85423f854240864241864142874144874045884046883f47883f48893e49893e4a893e4c8a3d4d8a3d4e8a3c4f8a3c508b3b518b3b528b3a538b3a548c39558c39568c38588c38598c375a8c375b8d365c8d365d8d355e8d355f8d34608d34618d33628d33638d32648e32658e31668e31678e31688e30698e306a8e2f6b8e2f6c8e2e6d8e2e6e8e2e6f8e2d708e2d718e2c718e2c728e2c738e2b748e2b758e2a768e2a778e2a788e29798e297a8e297b8e287c8e287d8e277e8e277f8e27808e26818e26828e26828e25838e25848e25858e24868e24878e23888e23898e238a8d228b8d228c8d228d8d218e8d218f8d21908d21918c20928c20928c20938c1f948c1f958b1f968b1f978b1f988b1f998a1f9a8a1e9b8a1e9c891e9d891f9e891f9f881fa0881fa1881fa1871fa28720a38620a48621a58521a68522a78522a88423a98324aa8325ab8225ac8226ad8127ad8128ae8029af7f2ab07f2cb17e2db27d2eb37c2fb47c31b57b32b67a34b67935b77937b87838b9773aba763bbb753dbc743fbc7340bd7242be7144bf7046c06f48c16e4ac16d4cc26c4ec36b50c46a52c56954c56856c66758c7655ac8645cc8635ec96260ca6063cb5f65cb5e67cc5c69cd5b6ccd5a6ece5870cf5773d05675d05477d1537ad1517cd2507fd34e81d34d84d44b86d54989d5488bd6468ed64590d74393d74195d84098d83e9bd93c9dd93ba0da39a2da37a5db36a8db34aadc32addc30b0dd2fb2dd2db5de2bb8de29bade28bddf26c0df25c2df23c5e021c8e020cae11fcde11dd0e11cd2e21bd5e21ad8e219dae319dde318dfe318e2e418e5e419e7e419eae51aece51befe51cf1e51df4e61ef6e620f8e621fbe723fde725")); + +var magma = ramp$1(colors("00000401000501010601010802010902020b02020d03030f03031204041405041606051806051a07061c08071e0907200a08220b09240c09260d0a290e0b2b100b2d110c2f120d31130d34140e36150e38160f3b180f3d19103f1a10421c10441d11471e114920114b21114e22115024125325125527125829115a2a115c2c115f2d11612f116331116533106734106936106b38106c390f6e3b0f703d0f713f0f72400f74420f75440f764510774710784910784a10794c117a4e117b4f127b51127c52137c54137d56147d57157e59157e5a167e5c167f5d177f5f187f601880621980641a80651a80671b80681c816a1c816b1d816d1d816e1e81701f81721f817320817521817621817822817922827b23827c23827e24828025828125818326818426818627818827818928818b29818c29818e2a81902a81912b81932b80942c80962c80982d80992d809b2e7f9c2e7f9e2f7fa02f7fa1307ea3307ea5317ea6317da8327daa337dab337cad347cae347bb0357bb2357bb3367ab5367ab73779b83779ba3878bc3978bd3977bf3a77c03a76c23b75c43c75c53c74c73d73c83e73ca3e72cc3f71cd4071cf4070d0416fd2426fd3436ed5446dd6456cd8456cd9466bdb476adc4869de4968df4a68e04c67e24d66e34e65e44f64e55064e75263e85362e95462ea5661eb5760ec5860ed5a5fee5b5eef5d5ef05f5ef1605df2625df2645cf3655cf4675cf4695cf56b5cf66c5cf66e5cf7705cf7725cf8745cf8765cf9785df9795df97b5dfa7d5efa7f5efa815ffb835ffb8560fb8761fc8961fc8a62fc8c63fc8e64fc9065fd9266fd9467fd9668fd9869fd9a6afd9b6bfe9d6cfe9f6dfea16efea36ffea571fea772fea973feaa74feac76feae77feb078feb27afeb47bfeb67cfeb77efeb97ffebb81febd82febf84fec185fec287fec488fec68afec88cfeca8dfecc8ffecd90fecf92fed194fed395fed597fed799fed89afdda9cfddc9efddea0fde0a1fde2a3fde3a5fde5a7fde7a9fde9aafdebacfcecaefceeb0fcf0b2fcf2b4fcf4b6fcf6b8fcf7b9fcf9bbfcfbbdfcfdbf")); + +var inferno = ramp$1(colors("00000401000501010601010802010a02020c02020e03021004031204031405041706041907051b08051d09061f0a07220b07240c08260d08290e092b10092d110a30120a32140b34150b37160b39180c3c190c3e1b0c411c0c431e0c451f0c48210c4a230c4c240c4f260c51280b53290b552b0b572d0b592f0a5b310a5c320a5e340a5f3609613809623909633b09643d09653e0966400a67420a68440a68450a69470b6a490b6a4a0c6b4c0c6b4d0d6c4f0d6c510e6c520e6d540f6d550f6d57106e59106e5a116e5c126e5d126e5f136e61136e62146e64156e65156e67166e69166e6a176e6c186e6d186e6f196e71196e721a6e741a6e751b6e771c6d781c6d7a1d6d7c1d6d7d1e6d7f1e6c801f6c82206c84206b85216b87216b88226a8a226a8c23698d23698f24699025689225689326679526679727669827669a28659b29649d29649f2a63a02a63a22b62a32c61a52c60a62d60a82e5fa92e5eab2f5ead305dae305cb0315bb1325ab3325ab43359b63458b73557b93556ba3655bc3754bd3853bf3952c03a51c13a50c33b4fc43c4ec63d4dc73e4cc83f4bca404acb4149cc4248ce4347cf4446d04545d24644d34743d44842d54a41d74b3fd84c3ed94d3dda4e3cdb503bdd513ade5238df5337e05536e15635e25734e35933e45a31e55c30e65d2fe75e2ee8602de9612bea632aeb6429eb6628ec6726ed6925ee6a24ef6c23ef6e21f06f20f1711ff1731df2741cf3761bf37819f47918f57b17f57d15f67e14f68013f78212f78410f8850ff8870ef8890cf98b0bf98c0af98e09fa9008fa9207fa9407fb9606fb9706fb9906fb9b06fb9d07fc9f07fca108fca309fca50afca60cfca80dfcaa0ffcac11fcae12fcb014fcb216fcb418fbb61afbb81dfbba1ffbbc21fbbe23fac026fac228fac42afac62df9c72ff9c932f9cb35f8cd37f8cf3af7d13df7d340f6d543f6d746f5d949f5db4cf4dd4ff4df53f4e156f3e35af3e55df2e661f2e865f2ea69f1ec6df1ed71f1ef75f1f179f2f27df2f482f3f586f3f68af4f88ef5f992f6fa96f8fb9af9fc9dfafda1fcffa4")); + +var plasma = ramp$1(colors("0d088710078813078916078a19068c1b068d1d068e20068f2206902406912605912805922a05932c05942e05952f059631059733059735049837049938049a3a049a3c049b3e049c3f049c41049d43039e44039e46039f48039f4903a04b03a14c02a14e02a25002a25102a35302a35502a45601a45801a45901a55b01a55c01a65e01a66001a66100a76300a76400a76600a76700a86900a86a00a86c00a86e00a86f00a87100a87201a87401a87501a87701a87801a87a02a87b02a87d03a87e03a88004a88104a78305a78405a78606a68707a68808a68a09a58b0aa58d0ba58e0ca48f0da4910ea3920fa39410a29511a19613a19814a099159f9a169f9c179e9d189d9e199da01a9ca11b9ba21d9aa31e9aa51f99a62098a72197a82296aa2395ab2494ac2694ad2793ae2892b02991b12a90b22b8fb32c8eb42e8db52f8cb6308bb7318ab83289ba3388bb3488bc3587bd3786be3885bf3984c03a83c13b82c23c81c33d80c43e7fc5407ec6417dc7427cc8437bc9447aca457acb4679cc4778cc4977cd4a76ce4b75cf4c74d04d73d14e72d24f71d35171d45270d5536fd5546ed6556dd7566cd8576bd9586ada5a6ada5b69db5c68dc5d67dd5e66de5f65de6164df6263e06363e16462e26561e26660e3685fe4695ee56a5de56b5de66c5ce76e5be76f5ae87059e97158e97257ea7457eb7556eb7655ec7754ed7953ed7a52ee7b51ef7c51ef7e50f07f4ff0804ef1814df1834cf2844bf3854bf3874af48849f48948f58b47f58c46f68d45f68f44f79044f79143f79342f89441f89540f9973ff9983ef99a3efa9b3dfa9c3cfa9e3bfb9f3afba139fba238fca338fca537fca636fca835fca934fdab33fdac33fdae32fdaf31fdb130fdb22ffdb42ffdb52efeb72dfeb82cfeba2cfebb2bfebd2afebe2afec029fdc229fdc328fdc527fdc627fdc827fdca26fdcb26fccd25fcce25fcd025fcd225fbd324fbd524fbd724fad824fada24f9dc24f9dd25f8df25f8e125f7e225f7e425f6e626f6e826f5e926f5eb27f4ed27f3ee27f3f027f2f227f1f426f1f525f0f724f0f921")); + +function constant$11(x) { + return function constant() { + return x; + }; +} + +var abs$1 = Math.abs; +var atan2$1 = Math.atan2; +var cos$2 = Math.cos; +var max$2 = Math.max; +var min$1 = Math.min; +var sin$2 = Math.sin; +var sqrt$2 = Math.sqrt; + +var epsilon$3 = 1e-12; +var pi$4 = Math.PI; +var halfPi$3 = pi$4 / 2; +var tau$4 = 2 * pi$4; + +function acos$1(x) { + return x > 1 ? 0 : x < -1 ? pi$4 : Math.acos(x); +} + +function asin$1(x) { + return x >= 1 ? halfPi$3 : x <= -1 ? -halfPi$3 : Math.asin(x); +} + +function arcInnerRadius(d) { + return d.innerRadius; +} + +function arcOuterRadius(d) { + return d.outerRadius; +} + +function arcStartAngle(d) { + return d.startAngle; +} + +function arcEndAngle(d) { + return d.endAngle; +} + +function arcPadAngle(d) { + return d && d.padAngle; // Note: optional! +} + +function intersect(x0, y0, x1, y1, x2, y2, x3, y3) { + var x10 = x1 - x0, y10 = y1 - y0, + x32 = x3 - x2, y32 = y3 - y2, + t = (x32 * (y0 - y2) - y32 * (x0 - x2)) / (y32 * x10 - x32 * y10); + return [x0 + t * x10, y0 + t * y10]; +} + +// Compute perpendicular offset line of length rc. +// http://mathworld.wolfram.com/Circle-LineIntersection.html +function cornerTangents(x0, y0, x1, y1, r1, rc, cw) { + var x01 = x0 - x1, + y01 = y0 - y1, + lo = (cw ? rc : -rc) / sqrt$2(x01 * x01 + y01 * y01), + ox = lo * y01, + oy = -lo * x01, + x11 = x0 + ox, + y11 = y0 + oy, + x10 = x1 + ox, + y10 = y1 + oy, + x00 = (x11 + x10) / 2, + y00 = (y11 + y10) / 2, + dx = x10 - x11, + dy = y10 - y11, + d2 = dx * dx + dy * dy, + r = r1 - rc, + D = x11 * y10 - x10 * y11, + d = (dy < 0 ? -1 : 1) * sqrt$2(max$2(0, r * r * d2 - D * D)), + cx0 = (D * dy - dx * d) / d2, + cy0 = (-D * dx - dy * d) / d2, + cx1 = (D * dy + dx * d) / d2, + cy1 = (-D * dx + dy * d) / d2, + dx0 = cx0 - x00, + dy0 = cy0 - y00, + dx1 = cx1 - x00, + dy1 = cy1 - y00; + + // Pick the closer of the two intersection points. + // TODO Is there a faster way to determine which intersection to use? + if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) cx0 = cx1, cy0 = cy1; + + return { + cx: cx0, + cy: cy0, + x01: -ox, + y01: -oy, + x11: cx0 * (r1 / r - 1), + y11: cy0 * (r1 / r - 1) + }; +} + +function arc() { + var innerRadius = arcInnerRadius, + outerRadius = arcOuterRadius, + cornerRadius = constant$11(0), + padRadius = null, + startAngle = arcStartAngle, + endAngle = arcEndAngle, + padAngle = arcPadAngle, + context = null; + + function arc() { + var buffer, + r, + r0 = +innerRadius.apply(this, arguments), + r1 = +outerRadius.apply(this, arguments), + a0 = startAngle.apply(this, arguments) - halfPi$3, + a1 = endAngle.apply(this, arguments) - halfPi$3, + da = abs$1(a1 - a0), + cw = a1 > a0; + + if (!context) context = buffer = path(); + + // Ensure that the outer radius is always larger than the inner radius. + if (r1 < r0) r = r1, r1 = r0, r0 = r; + + // Is it a point? + if (!(r1 > epsilon$3)) context.moveTo(0, 0); + + // Or is it a circle or annulus? + else if (da > tau$4 - epsilon$3) { + context.moveTo(r1 * cos$2(a0), r1 * sin$2(a0)); + context.arc(0, 0, r1, a0, a1, !cw); + if (r0 > epsilon$3) { + context.moveTo(r0 * cos$2(a1), r0 * sin$2(a1)); + context.arc(0, 0, r0, a1, a0, cw); + } + } + + // Or is it a circular or annular sector? + else { + var a01 = a0, + a11 = a1, + a00 = a0, + a10 = a1, + da0 = da, + da1 = da, + ap = padAngle.apply(this, arguments) / 2, + rp = (ap > epsilon$3) && (padRadius ? +padRadius.apply(this, arguments) : sqrt$2(r0 * r0 + r1 * r1)), + rc = min$1(abs$1(r1 - r0) / 2, +cornerRadius.apply(this, arguments)), + rc0 = rc, + rc1 = rc, + t0, + t1; + + // Apply padding? Note that since r1 ≥ r0, da1 ≥ da0. + if (rp > epsilon$3) { + var p0 = asin$1(rp / r0 * sin$2(ap)), + p1 = asin$1(rp / r1 * sin$2(ap)); + if ((da0 -= p0 * 2) > epsilon$3) p0 *= (cw ? 1 : -1), a00 += p0, a10 -= p0; + else da0 = 0, a00 = a10 = (a0 + a1) / 2; + if ((da1 -= p1 * 2) > epsilon$3) p1 *= (cw ? 1 : -1), a01 += p1, a11 -= p1; + else da1 = 0, a01 = a11 = (a0 + a1) / 2; + } + + var x01 = r1 * cos$2(a01), + y01 = r1 * sin$2(a01), + x10 = r0 * cos$2(a10), + y10 = r0 * sin$2(a10); + + // Apply rounded corners? + if (rc > epsilon$3) { + var x11 = r1 * cos$2(a11), + y11 = r1 * sin$2(a11), + x00 = r0 * cos$2(a00), + y00 = r0 * sin$2(a00); + + // Restrict the corner radius according to the sector angle. + if (da < pi$4) { + var oc = da0 > epsilon$3 ? intersect(x01, y01, x00, y00, x11, y11, x10, y10) : [x10, y10], + ax = x01 - oc[0], + ay = y01 - oc[1], + bx = x11 - oc[0], + by = y11 - oc[1], + kc = 1 / sin$2(acos$1((ax * bx + ay * by) / (sqrt$2(ax * ax + ay * ay) * sqrt$2(bx * bx + by * by))) / 2), + lc = sqrt$2(oc[0] * oc[0] + oc[1] * oc[1]); + rc0 = min$1(rc, (r0 - lc) / (kc - 1)); + rc1 = min$1(rc, (r1 - lc) / (kc + 1)); + } + } + + // Is the sector collapsed to a line? + if (!(da1 > epsilon$3)) context.moveTo(x01, y01); + + // Does the sector’s outer ring have rounded corners? + else if (rc1 > epsilon$3) { + t0 = cornerTangents(x00, y00, x01, y01, r1, rc1, cw); + t1 = cornerTangents(x11, y11, x10, y10, r1, rc1, cw); + + context.moveTo(t0.cx + t0.x01, t0.cy + t0.y01); + + // Have the corners merged? + if (rc1 < rc) context.arc(t0.cx, t0.cy, rc1, atan2$1(t0.y01, t0.x01), atan2$1(t1.y01, t1.x01), !cw); + + // Otherwise, draw the two corners and the ring. + else { + context.arc(t0.cx, t0.cy, rc1, atan2$1(t0.y01, t0.x01), atan2$1(t0.y11, t0.x11), !cw); + context.arc(0, 0, r1, atan2$1(t0.cy + t0.y11, t0.cx + t0.x11), atan2$1(t1.cy + t1.y11, t1.cx + t1.x11), !cw); + context.arc(t1.cx, t1.cy, rc1, atan2$1(t1.y11, t1.x11), atan2$1(t1.y01, t1.x01), !cw); + } + } + + // Or is the outer ring just a circular arc? + else context.moveTo(x01, y01), context.arc(0, 0, r1, a01, a11, !cw); + + // Is there no inner ring, and it’s a circular sector? + // Or perhaps it’s an annular sector collapsed due to padding? + if (!(r0 > epsilon$3) || !(da0 > epsilon$3)) context.lineTo(x10, y10); + + // Does the sector’s inner ring (or point) have rounded corners? + else if (rc0 > epsilon$3) { + t0 = cornerTangents(x10, y10, x11, y11, r0, -rc0, cw); + t1 = cornerTangents(x01, y01, x00, y00, r0, -rc0, cw); + + context.lineTo(t0.cx + t0.x01, t0.cy + t0.y01); + + // Have the corners merged? + if (rc0 < rc) context.arc(t0.cx, t0.cy, rc0, atan2$1(t0.y01, t0.x01), atan2$1(t1.y01, t1.x01), !cw); + + // Otherwise, draw the two corners and the ring. + else { + context.arc(t0.cx, t0.cy, rc0, atan2$1(t0.y01, t0.x01), atan2$1(t0.y11, t0.x11), !cw); + context.arc(0, 0, r0, atan2$1(t0.cy + t0.y11, t0.cx + t0.x11), atan2$1(t1.cy + t1.y11, t1.cx + t1.x11), cw); + context.arc(t1.cx, t1.cy, rc0, atan2$1(t1.y11, t1.x11), atan2$1(t1.y01, t1.x01), !cw); + } + } + + // Or is the inner ring just a circular arc? + else context.arc(0, 0, r0, a10, a00, cw); + } + + context.closePath(); + + if (buffer) return context = null, buffer + "" || null; + } + + arc.centroid = function() { + var r = (+innerRadius.apply(this, arguments) + +outerRadius.apply(this, arguments)) / 2, + a = (+startAngle.apply(this, arguments) + +endAngle.apply(this, arguments)) / 2 - pi$4 / 2; + return [cos$2(a) * r, sin$2(a) * r]; + }; + + arc.innerRadius = function(_) { + return arguments.length ? (innerRadius = typeof _ === "function" ? _ : constant$11(+_), arc) : innerRadius; + }; + + arc.outerRadius = function(_) { + return arguments.length ? (outerRadius = typeof _ === "function" ? _ : constant$11(+_), arc) : outerRadius; + }; + + arc.cornerRadius = function(_) { + return arguments.length ? (cornerRadius = typeof _ === "function" ? _ : constant$11(+_), arc) : cornerRadius; + }; + + arc.padRadius = function(_) { + return arguments.length ? (padRadius = _ == null ? null : typeof _ === "function" ? _ : constant$11(+_), arc) : padRadius; + }; + + arc.startAngle = function(_) { + return arguments.length ? (startAngle = typeof _ === "function" ? _ : constant$11(+_), arc) : startAngle; + }; + + arc.endAngle = function(_) { + return arguments.length ? (endAngle = typeof _ === "function" ? _ : constant$11(+_), arc) : endAngle; + }; + + arc.padAngle = function(_) { + return arguments.length ? (padAngle = typeof _ === "function" ? _ : constant$11(+_), arc) : padAngle; + }; + + arc.context = function(_) { + return arguments.length ? (context = _ == null ? null : _, arc) : context; + }; + + return arc; +} + +function Linear(context) { + this._context = context; +} + +Linear.prototype = { + areaStart: function() { + this._line = 0; + }, + areaEnd: function() { + this._line = NaN; + }, + lineStart: function() { + this._point = 0; + }, + lineEnd: function() { + if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath(); + this._line = 1 - this._line; + }, + point: function(x, y) { + x = +x, y = +y; + switch (this._point) { + case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break; + case 1: this._point = 2; // proceed + default: this._context.lineTo(x, y); break; + } + } +}; + +function curveLinear(context) { + return new Linear(context); +} + +function x$3(p) { + return p[0]; +} + +function y$3(p) { + return p[1]; +} + +function line() { + var x$$1 = x$3, + y$$1 = y$3, + defined = constant$11(true), + context = null, + curve = curveLinear, + output = null; + + function line(data) { + var i, + n = data.length, + d, + defined0 = false, + buffer; + + if (context == null) output = curve(buffer = path()); + + for (i = 0; i <= n; ++i) { + if (!(i < n && defined(d = data[i], i, data)) === defined0) { + if (defined0 = !defined0) output.lineStart(); + else output.lineEnd(); + } + if (defined0) output.point(+x$$1(d, i, data), +y$$1(d, i, data)); + } + + if (buffer) return output = null, buffer + "" || null; + } + + line.x = function(_) { + return arguments.length ? (x$$1 = typeof _ === "function" ? _ : constant$11(+_), line) : x$$1; + }; + + line.y = function(_) { + return arguments.length ? (y$$1 = typeof _ === "function" ? _ : constant$11(+_), line) : y$$1; + }; + + line.defined = function(_) { + return arguments.length ? (defined = typeof _ === "function" ? _ : constant$11(!!_), line) : defined; + }; + + line.curve = function(_) { + return arguments.length ? (curve = _, context != null && (output = curve(context)), line) : curve; + }; + + line.context = function(_) { + return arguments.length ? (_ == null ? context = output = null : output = curve(context = _), line) : context; + }; + + return line; +} + +function area$3() { + var x0 = x$3, + x1 = null, + y0 = constant$11(0), + y1 = y$3, + defined = constant$11(true), + context = null, + curve = curveLinear, + output = null; + + function area(data) { + var i, + j, + k, + n = data.length, + d, + defined0 = false, + buffer, + x0z = new Array(n), + y0z = new Array(n); + + if (context == null) output = curve(buffer = path()); + + for (i = 0; i <= n; ++i) { + if (!(i < n && defined(d = data[i], i, data)) === defined0) { + if (defined0 = !defined0) { + j = i; + output.areaStart(); + output.lineStart(); + } else { + output.lineEnd(); + output.lineStart(); + for (k = i - 1; k >= j; --k) { + output.point(x0z[k], y0z[k]); + } + output.lineEnd(); + output.areaEnd(); + } + } + if (defined0) { + x0z[i] = +x0(d, i, data), y0z[i] = +y0(d, i, data); + output.point(x1 ? +x1(d, i, data) : x0z[i], y1 ? +y1(d, i, data) : y0z[i]); + } + } + + if (buffer) return output = null, buffer + "" || null; + } + + function arealine() { + return line().defined(defined).curve(curve).context(context); + } + + area.x = function(_) { + return arguments.length ? (x0 = typeof _ === "function" ? _ : constant$11(+_), x1 = null, area) : x0; + }; + + area.x0 = function(_) { + return arguments.length ? (x0 = typeof _ === "function" ? _ : constant$11(+_), area) : x0; + }; + + area.x1 = function(_) { + return arguments.length ? (x1 = _ == null ? null : typeof _ === "function" ? _ : constant$11(+_), area) : x1; + }; + + area.y = function(_) { + return arguments.length ? (y0 = typeof _ === "function" ? _ : constant$11(+_), y1 = null, area) : y0; + }; + + area.y0 = function(_) { + return arguments.length ? (y0 = typeof _ === "function" ? _ : constant$11(+_), area) : y0; + }; + + area.y1 = function(_) { + return arguments.length ? (y1 = _ == null ? null : typeof _ === "function" ? _ : constant$11(+_), area) : y1; + }; + + area.lineX0 = + area.lineY0 = function() { + return arealine().x(x0).y(y0); + }; + + area.lineY1 = function() { + return arealine().x(x0).y(y1); + }; + + area.lineX1 = function() { + return arealine().x(x1).y(y0); + }; + + area.defined = function(_) { + return arguments.length ? (defined = typeof _ === "function" ? _ : constant$11(!!_), area) : defined; + }; + + area.curve = function(_) { + return arguments.length ? (curve = _, context != null && (output = curve(context)), area) : curve; + }; + + area.context = function(_) { + return arguments.length ? (_ == null ? context = output = null : output = curve(context = _), area) : context; + }; + + return area; +} + +function descending$1(a, b) { + return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN; +} + +function identity$7(d) { + return d; +} + +function pie() { + var value = identity$7, + sortValues = descending$1, + sort = null, + startAngle = constant$11(0), + endAngle = constant$11(tau$4), + padAngle = constant$11(0); + + function pie(data) { + var i, + n = data.length, + j, + k, + sum = 0, + index = new Array(n), + arcs = new Array(n), + a0 = +startAngle.apply(this, arguments), + da = Math.min(tau$4, Math.max(-tau$4, endAngle.apply(this, arguments) - a0)), + a1, + p = Math.min(Math.abs(da) / n, padAngle.apply(this, arguments)), + pa = p * (da < 0 ? -1 : 1), + v; + + for (i = 0; i < n; ++i) { + if ((v = arcs[index[i] = i] = +value(data[i], i, data)) > 0) { + sum += v; + } + } + + // Optionally sort the arcs by previously-computed values or by data. + if (sortValues != null) index.sort(function(i, j) { return sortValues(arcs[i], arcs[j]); }); + else if (sort != null) index.sort(function(i, j) { return sort(data[i], data[j]); }); + + // Compute the arcs! They are stored in the original data's order. + for (i = 0, k = sum ? (da - n * pa) / sum : 0; i < n; ++i, a0 = a1) { + j = index[i], v = arcs[j], a1 = a0 + (v > 0 ? v * k : 0) + pa, arcs[j] = { + data: data[j], + index: i, + value: v, + startAngle: a0, + endAngle: a1, + padAngle: p + }; + } + + return arcs; + } + + pie.value = function(_) { + return arguments.length ? (value = typeof _ === "function" ? _ : constant$11(+_), pie) : value; + }; + + pie.sortValues = function(_) { + return arguments.length ? (sortValues = _, sort = null, pie) : sortValues; + }; + + pie.sort = function(_) { + return arguments.length ? (sort = _, sortValues = null, pie) : sort; + }; + + pie.startAngle = function(_) { + return arguments.length ? (startAngle = typeof _ === "function" ? _ : constant$11(+_), pie) : startAngle; + }; + + pie.endAngle = function(_) { + return arguments.length ? (endAngle = typeof _ === "function" ? _ : constant$11(+_), pie) : endAngle; + }; + + pie.padAngle = function(_) { + return arguments.length ? (padAngle = typeof _ === "function" ? _ : constant$11(+_), pie) : padAngle; + }; + + return pie; +} + +var curveRadialLinear = curveRadial(curveLinear); + +function Radial(curve) { + this._curve = curve; +} + +Radial.prototype = { + areaStart: function() { + this._curve.areaStart(); + }, + areaEnd: function() { + this._curve.areaEnd(); + }, + lineStart: function() { + this._curve.lineStart(); + }, + lineEnd: function() { + this._curve.lineEnd(); + }, + point: function(a, r) { + this._curve.point(r * Math.sin(a), r * -Math.cos(a)); + } +}; + +function curveRadial(curve) { + + function radial(context) { + return new Radial(curve(context)); + } + + radial._curve = curve; + + return radial; +} + +function lineRadial(l) { + var c = l.curve; + + l.angle = l.x, delete l.x; + l.radius = l.y, delete l.y; + + l.curve = function(_) { + return arguments.length ? c(curveRadial(_)) : c()._curve; + }; + + return l; +} + +function lineRadial$1() { + return lineRadial(line().curve(curveRadialLinear)); +} + +function areaRadial() { + var a = area$3().curve(curveRadialLinear), + c = a.curve, + x0 = a.lineX0, + x1 = a.lineX1, + y0 = a.lineY0, + y1 = a.lineY1; + + a.angle = a.x, delete a.x; + a.startAngle = a.x0, delete a.x0; + a.endAngle = a.x1, delete a.x1; + a.radius = a.y, delete a.y; + a.innerRadius = a.y0, delete a.y0; + a.outerRadius = a.y1, delete a.y1; + a.lineStartAngle = function() { return lineRadial(x0()); }, delete a.lineX0; + a.lineEndAngle = function() { return lineRadial(x1()); }, delete a.lineX1; + a.lineInnerRadius = function() { return lineRadial(y0()); }, delete a.lineY0; + a.lineOuterRadius = function() { return lineRadial(y1()); }, delete a.lineY1; + + a.curve = function(_) { + return arguments.length ? c(curveRadial(_)) : c()._curve; + }; + + return a; +} + +function pointRadial(x, y) { + return [(y = +y) * Math.cos(x -= Math.PI / 2), y * Math.sin(x)]; +} + +var slice$6 = Array.prototype.slice; + +function linkSource(d) { + return d.source; +} + +function linkTarget(d) { + return d.target; +} + +function link$2(curve) { + var source = linkSource, + target = linkTarget, + x$$1 = x$3, + y$$1 = y$3, + context = null; + + function link() { + var buffer, argv = slice$6.call(arguments), s = source.apply(this, argv), t = target.apply(this, argv); + if (!context) context = buffer = path(); + curve(context, +x$$1.apply(this, (argv[0] = s, argv)), +y$$1.apply(this, argv), +x$$1.apply(this, (argv[0] = t, argv)), +y$$1.apply(this, argv)); + if (buffer) return context = null, buffer + "" || null; + } + + link.source = function(_) { + return arguments.length ? (source = _, link) : source; + }; + + link.target = function(_) { + return arguments.length ? (target = _, link) : target; + }; + + link.x = function(_) { + return arguments.length ? (x$$1 = typeof _ === "function" ? _ : constant$11(+_), link) : x$$1; + }; + + link.y = function(_) { + return arguments.length ? (y$$1 = typeof _ === "function" ? _ : constant$11(+_), link) : y$$1; + }; + + link.context = function(_) { + return arguments.length ? (context = _ == null ? null : _, link) : context; + }; + + return link; +} + +function curveHorizontal(context, x0, y0, x1, y1) { + context.moveTo(x0, y0); + context.bezierCurveTo(x0 = (x0 + x1) / 2, y0, x0, y1, x1, y1); +} + +function curveVertical(context, x0, y0, x1, y1) { + context.moveTo(x0, y0); + context.bezierCurveTo(x0, y0 = (y0 + y1) / 2, x1, y0, x1, y1); +} + +function curveRadial$1(context, x0, y0, x1, y1) { + var p0 = pointRadial(x0, y0), + p1 = pointRadial(x0, y0 = (y0 + y1) / 2), + p2 = pointRadial(x1, y0), + p3 = pointRadial(x1, y1); + context.moveTo(p0[0], p0[1]); + context.bezierCurveTo(p1[0], p1[1], p2[0], p2[1], p3[0], p3[1]); +} + +function linkHorizontal() { + return link$2(curveHorizontal); +} + +function linkVertical() { + return link$2(curveVertical); +} + +function linkRadial() { + var l = link$2(curveRadial$1); + l.angle = l.x, delete l.x; + l.radius = l.y, delete l.y; + return l; +} + +var circle$2 = { + draw: function(context, size) { + var r = Math.sqrt(size / pi$4); + context.moveTo(r, 0); + context.arc(0, 0, r, 0, tau$4); + } +}; + +var cross$2 = { + draw: function(context, size) { + var r = Math.sqrt(size / 5) / 2; + context.moveTo(-3 * r, -r); + context.lineTo(-r, -r); + context.lineTo(-r, -3 * r); + context.lineTo(r, -3 * r); + context.lineTo(r, -r); + context.lineTo(3 * r, -r); + context.lineTo(3 * r, r); + context.lineTo(r, r); + context.lineTo(r, 3 * r); + context.lineTo(-r, 3 * r); + context.lineTo(-r, r); + context.lineTo(-3 * r, r); + context.closePath(); + } +}; + +var tan30 = Math.sqrt(1 / 3), + tan30_2 = tan30 * 2; + +var diamond = { + draw: function(context, size) { + var y = Math.sqrt(size / tan30_2), + x = y * tan30; + context.moveTo(0, -y); + context.lineTo(x, 0); + context.lineTo(0, y); + context.lineTo(-x, 0); + context.closePath(); + } +}; + +var ka = 0.89081309152928522810, + kr = Math.sin(pi$4 / 10) / Math.sin(7 * pi$4 / 10), + kx = Math.sin(tau$4 / 10) * kr, + ky = -Math.cos(tau$4 / 10) * kr; + +var star = { + draw: function(context, size) { + var r = Math.sqrt(size * ka), + x = kx * r, + y = ky * r; + context.moveTo(0, -r); + context.lineTo(x, y); + for (var i = 1; i < 5; ++i) { + var a = tau$4 * i / 5, + c = Math.cos(a), + s = Math.sin(a); + context.lineTo(s * r, -c * r); + context.lineTo(c * x - s * y, s * x + c * y); + } + context.closePath(); + } +}; + +var square = { + draw: function(context, size) { + var w = Math.sqrt(size), + x = -w / 2; + context.rect(x, x, w, w); + } +}; + +var sqrt3 = Math.sqrt(3); + +var triangle = { + draw: function(context, size) { + var y = -Math.sqrt(size / (sqrt3 * 3)); + context.moveTo(0, y * 2); + context.lineTo(-sqrt3 * y, -y); + context.lineTo(sqrt3 * y, -y); + context.closePath(); + } +}; + +var c = -0.5, + s = Math.sqrt(3) / 2, + k = 1 / Math.sqrt(12), + a = (k / 2 + 1) * 3; + +var wye = { + draw: function(context, size) { + var r = Math.sqrt(size / a), + x0 = r / 2, + y0 = r * k, + x1 = x0, + y1 = r * k + r, + x2 = -x1, + y2 = y1; + context.moveTo(x0, y0); + context.lineTo(x1, y1); + context.lineTo(x2, y2); + context.lineTo(c * x0 - s * y0, s * x0 + c * y0); + context.lineTo(c * x1 - s * y1, s * x1 + c * y1); + context.lineTo(c * x2 - s * y2, s * x2 + c * y2); + context.lineTo(c * x0 + s * y0, c * y0 - s * x0); + context.lineTo(c * x1 + s * y1, c * y1 - s * x1); + context.lineTo(c * x2 + s * y2, c * y2 - s * x2); + context.closePath(); + } +}; + +var symbols = [ + circle$2, + cross$2, + diamond, + square, + star, + triangle, + wye +]; + +function symbol() { + var type = constant$11(circle$2), + size = constant$11(64), + context = null; + + function symbol() { + var buffer; + if (!context) context = buffer = path(); + type.apply(this, arguments).draw(context, +size.apply(this, arguments)); + if (buffer) return context = null, buffer + "" || null; + } + + symbol.type = function(_) { + return arguments.length ? (type = typeof _ === "function" ? _ : constant$11(_), symbol) : type; + }; + + symbol.size = function(_) { + return arguments.length ? (size = typeof _ === "function" ? _ : constant$11(+_), symbol) : size; + }; + + symbol.context = function(_) { + return arguments.length ? (context = _ == null ? null : _, symbol) : context; + }; + + return symbol; +} + +function noop$3() {} + +function point$2(that, x, y) { + that._context.bezierCurveTo( + (2 * that._x0 + that._x1) / 3, + (2 * that._y0 + that._y1) / 3, + (that._x0 + 2 * that._x1) / 3, + (that._y0 + 2 * that._y1) / 3, + (that._x0 + 4 * that._x1 + x) / 6, + (that._y0 + 4 * that._y1 + y) / 6 + ); +} + +function Basis(context) { + this._context = context; +} + +Basis.prototype = { + areaStart: function() { + this._line = 0; + }, + areaEnd: function() { + this._line = NaN; + }, + lineStart: function() { + this._x0 = this._x1 = + this._y0 = this._y1 = NaN; + this._point = 0; + }, + lineEnd: function() { + switch (this._point) { + case 3: point$2(this, this._x1, this._y1); // proceed + case 2: this._context.lineTo(this._x1, this._y1); break; + } + if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath(); + this._line = 1 - this._line; + }, + point: function(x, y) { + x = +x, y = +y; + switch (this._point) { + case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break; + case 1: this._point = 2; break; + case 2: this._point = 3; this._context.lineTo((5 * this._x0 + this._x1) / 6, (5 * this._y0 + this._y1) / 6); // proceed + default: point$2(this, x, y); break; + } + this._x0 = this._x1, this._x1 = x; + this._y0 = this._y1, this._y1 = y; + } +}; + +function basis$2(context) { + return new Basis(context); +} + +function BasisClosed(context) { + this._context = context; +} + +BasisClosed.prototype = { + areaStart: noop$3, + areaEnd: noop$3, + lineStart: function() { + this._x0 = this._x1 = this._x2 = this._x3 = this._x4 = + this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = NaN; + this._point = 0; + }, + lineEnd: function() { + switch (this._point) { + case 1: { + this._context.moveTo(this._x2, this._y2); + this._context.closePath(); + break; + } + case 2: { + this._context.moveTo((this._x2 + 2 * this._x3) / 3, (this._y2 + 2 * this._y3) / 3); + this._context.lineTo((this._x3 + 2 * this._x2) / 3, (this._y3 + 2 * this._y2) / 3); + this._context.closePath(); + break; + } + case 3: { + this.point(this._x2, this._y2); + this.point(this._x3, this._y3); + this.point(this._x4, this._y4); + break; + } + } + }, + point: function(x, y) { + x = +x, y = +y; + switch (this._point) { + case 0: this._point = 1; this._x2 = x, this._y2 = y; break; + case 1: this._point = 2; this._x3 = x, this._y3 = y; break; + case 2: this._point = 3; this._x4 = x, this._y4 = y; this._context.moveTo((this._x0 + 4 * this._x1 + x) / 6, (this._y0 + 4 * this._y1 + y) / 6); break; + default: point$2(this, x, y); break; + } + this._x0 = this._x1, this._x1 = x; + this._y0 = this._y1, this._y1 = y; + } +}; + +function basisClosed$1(context) { + return new BasisClosed(context); +} + +function BasisOpen(context) { + this._context = context; +} + +BasisOpen.prototype = { + areaStart: function() { + this._line = 0; + }, + areaEnd: function() { + this._line = NaN; + }, + lineStart: function() { + this._x0 = this._x1 = + this._y0 = this._y1 = NaN; + this._point = 0; + }, + lineEnd: function() { + if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath(); + this._line = 1 - this._line; + }, + point: function(x, y) { + x = +x, y = +y; + switch (this._point) { + case 0: this._point = 1; break; + case 1: this._point = 2; break; + case 2: this._point = 3; var x0 = (this._x0 + 4 * this._x1 + x) / 6, y0 = (this._y0 + 4 * this._y1 + y) / 6; this._line ? this._context.lineTo(x0, y0) : this._context.moveTo(x0, y0); break; + case 3: this._point = 4; // proceed + default: point$2(this, x, y); break; + } + this._x0 = this._x1, this._x1 = x; + this._y0 = this._y1, this._y1 = y; + } +}; + +function basisOpen(context) { + return new BasisOpen(context); +} + +function Bundle(context, beta) { + this._basis = new Basis(context); + this._beta = beta; +} + +Bundle.prototype = { + lineStart: function() { + this._x = []; + this._y = []; + this._basis.lineStart(); + }, + lineEnd: function() { + var x = this._x, + y = this._y, + j = x.length - 1; + + if (j > 0) { + var x0 = x[0], + y0 = y[0], + dx = x[j] - x0, + dy = y[j] - y0, + i = -1, + t; + + while (++i <= j) { + t = i / j; + this._basis.point( + this._beta * x[i] + (1 - this._beta) * (x0 + t * dx), + this._beta * y[i] + (1 - this._beta) * (y0 + t * dy) + ); + } + } + + this._x = this._y = null; + this._basis.lineEnd(); + }, + point: function(x, y) { + this._x.push(+x); + this._y.push(+y); + } +}; + +var bundle = (function custom(beta) { + + function bundle(context) { + return beta === 1 ? new Basis(context) : new Bundle(context, beta); + } + + bundle.beta = function(beta) { + return custom(+beta); + }; + + return bundle; +})(0.85); + +function point$3(that, x, y) { + that._context.bezierCurveTo( + that._x1 + that._k * (that._x2 - that._x0), + that._y1 + that._k * (that._y2 - that._y0), + that._x2 + that._k * (that._x1 - x), + that._y2 + that._k * (that._y1 - y), + that._x2, + that._y2 + ); +} + +function Cardinal(context, tension) { + this._context = context; + this._k = (1 - tension) / 6; +} + +Cardinal.prototype = { + areaStart: function() { + this._line = 0; + }, + areaEnd: function() { + this._line = NaN; + }, + lineStart: function() { + this._x0 = this._x1 = this._x2 = + this._y0 = this._y1 = this._y2 = NaN; + this._point = 0; + }, + lineEnd: function() { + switch (this._point) { + case 2: this._context.lineTo(this._x2, this._y2); break; + case 3: point$3(this, this._x1, this._y1); break; + } + if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath(); + this._line = 1 - this._line; + }, + point: function(x, y) { + x = +x, y = +y; + switch (this._point) { + case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break; + case 1: this._point = 2; this._x1 = x, this._y1 = y; break; + case 2: this._point = 3; // proceed + default: point$3(this, x, y); break; + } + this._x0 = this._x1, this._x1 = this._x2, this._x2 = x; + this._y0 = this._y1, this._y1 = this._y2, this._y2 = y; + } +}; + +var cardinal = (function custom(tension) { + + function cardinal(context) { + return new Cardinal(context, tension); + } + + cardinal.tension = function(tension) { + return custom(+tension); + }; + + return cardinal; +})(0); + +function CardinalClosed(context, tension) { + this._context = context; + this._k = (1 - tension) / 6; +} + +CardinalClosed.prototype = { + areaStart: noop$3, + areaEnd: noop$3, + lineStart: function() { + this._x0 = this._x1 = this._x2 = this._x3 = this._x4 = this._x5 = + this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = this._y5 = NaN; + this._point = 0; + }, + lineEnd: function() { + switch (this._point) { + case 1: { + this._context.moveTo(this._x3, this._y3); + this._context.closePath(); + break; + } + case 2: { + this._context.lineTo(this._x3, this._y3); + this._context.closePath(); + break; + } + case 3: { + this.point(this._x3, this._y3); + this.point(this._x4, this._y4); + this.point(this._x5, this._y5); + break; + } + } + }, + point: function(x, y) { + x = +x, y = +y; + switch (this._point) { + case 0: this._point = 1; this._x3 = x, this._y3 = y; break; + case 1: this._point = 2; this._context.moveTo(this._x4 = x, this._y4 = y); break; + case 2: this._point = 3; this._x5 = x, this._y5 = y; break; + default: point$3(this, x, y); break; + } + this._x0 = this._x1, this._x1 = this._x2, this._x2 = x; + this._y0 = this._y1, this._y1 = this._y2, this._y2 = y; + } +}; + +var cardinalClosed = (function custom(tension) { + + function cardinal$$1(context) { + return new CardinalClosed(context, tension); + } + + cardinal$$1.tension = function(tension) { + return custom(+tension); + }; + + return cardinal$$1; +})(0); + +function CardinalOpen(context, tension) { + this._context = context; + this._k = (1 - tension) / 6; +} + +CardinalOpen.prototype = { + areaStart: function() { + this._line = 0; + }, + areaEnd: function() { + this._line = NaN; + }, + lineStart: function() { + this._x0 = this._x1 = this._x2 = + this._y0 = this._y1 = this._y2 = NaN; + this._point = 0; + }, + lineEnd: function() { + if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath(); + this._line = 1 - this._line; + }, + point: function(x, y) { + x = +x, y = +y; + switch (this._point) { + case 0: this._point = 1; break; + case 1: this._point = 2; break; + case 2: this._point = 3; this._line ? this._context.lineTo(this._x2, this._y2) : this._context.moveTo(this._x2, this._y2); break; + case 3: this._point = 4; // proceed + default: point$3(this, x, y); break; + } + this._x0 = this._x1, this._x1 = this._x2, this._x2 = x; + this._y0 = this._y1, this._y1 = this._y2, this._y2 = y; + } +}; + +var cardinalOpen = (function custom(tension) { + + function cardinal$$1(context) { + return new CardinalOpen(context, tension); + } + + cardinal$$1.tension = function(tension) { + return custom(+tension); + }; + + return cardinal$$1; +})(0); + +function point$4(that, x, y) { + var x1 = that._x1, + y1 = that._y1, + x2 = that._x2, + y2 = that._y2; + + if (that._l01_a > epsilon$3) { + var a = 2 * that._l01_2a + 3 * that._l01_a * that._l12_a + that._l12_2a, + n = 3 * that._l01_a * (that._l01_a + that._l12_a); + x1 = (x1 * a - that._x0 * that._l12_2a + that._x2 * that._l01_2a) / n; + y1 = (y1 * a - that._y0 * that._l12_2a + that._y2 * that._l01_2a) / n; + } + + if (that._l23_a > epsilon$3) { + var b = 2 * that._l23_2a + 3 * that._l23_a * that._l12_a + that._l12_2a, + m = 3 * that._l23_a * (that._l23_a + that._l12_a); + x2 = (x2 * b + that._x1 * that._l23_2a - x * that._l12_2a) / m; + y2 = (y2 * b + that._y1 * that._l23_2a - y * that._l12_2a) / m; + } + + that._context.bezierCurveTo(x1, y1, x2, y2, that._x2, that._y2); +} + +function CatmullRom(context, alpha) { + this._context = context; + this._alpha = alpha; +} + +CatmullRom.prototype = { + areaStart: function() { + this._line = 0; + }, + areaEnd: function() { + this._line = NaN; + }, + lineStart: function() { + this._x0 = this._x1 = this._x2 = + this._y0 = this._y1 = this._y2 = NaN; + this._l01_a = this._l12_a = this._l23_a = + this._l01_2a = this._l12_2a = this._l23_2a = + this._point = 0; + }, + lineEnd: function() { + switch (this._point) { + case 2: this._context.lineTo(this._x2, this._y2); break; + case 3: this.point(this._x2, this._y2); break; + } + if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath(); + this._line = 1 - this._line; + }, + point: function(x, y) { + x = +x, y = +y; + + if (this._point) { + var x23 = this._x2 - x, + y23 = this._y2 - y; + this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha)); + } + + switch (this._point) { + case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break; + case 1: this._point = 2; break; + case 2: this._point = 3; // proceed + default: point$4(this, x, y); break; + } + + this._l01_a = this._l12_a, this._l12_a = this._l23_a; + this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a; + this._x0 = this._x1, this._x1 = this._x2, this._x2 = x; + this._y0 = this._y1, this._y1 = this._y2, this._y2 = y; + } +}; + +var catmullRom = (function custom(alpha) { + + function catmullRom(context) { + return alpha ? new CatmullRom(context, alpha) : new Cardinal(context, 0); + } + + catmullRom.alpha = function(alpha) { + return custom(+alpha); + }; + + return catmullRom; +})(0.5); + +function CatmullRomClosed(context, alpha) { + this._context = context; + this._alpha = alpha; +} + +CatmullRomClosed.prototype = { + areaStart: noop$3, + areaEnd: noop$3, + lineStart: function() { + this._x0 = this._x1 = this._x2 = this._x3 = this._x4 = this._x5 = + this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = this._y5 = NaN; + this._l01_a = this._l12_a = this._l23_a = + this._l01_2a = this._l12_2a = this._l23_2a = + this._point = 0; + }, + lineEnd: function() { + switch (this._point) { + case 1: { + this._context.moveTo(this._x3, this._y3); + this._context.closePath(); + break; + } + case 2: { + this._context.lineTo(this._x3, this._y3); + this._context.closePath(); + break; + } + case 3: { + this.point(this._x3, this._y3); + this.point(this._x4, this._y4); + this.point(this._x5, this._y5); + break; + } + } + }, + point: function(x, y) { + x = +x, y = +y; + + if (this._point) { + var x23 = this._x2 - x, + y23 = this._y2 - y; + this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha)); + } + + switch (this._point) { + case 0: this._point = 1; this._x3 = x, this._y3 = y; break; + case 1: this._point = 2; this._context.moveTo(this._x4 = x, this._y4 = y); break; + case 2: this._point = 3; this._x5 = x, this._y5 = y; break; + default: point$4(this, x, y); break; + } + + this._l01_a = this._l12_a, this._l12_a = this._l23_a; + this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a; + this._x0 = this._x1, this._x1 = this._x2, this._x2 = x; + this._y0 = this._y1, this._y1 = this._y2, this._y2 = y; + } +}; + +var catmullRomClosed = (function custom(alpha) { + + function catmullRom$$1(context) { + return alpha ? new CatmullRomClosed(context, alpha) : new CardinalClosed(context, 0); + } + + catmullRom$$1.alpha = function(alpha) { + return custom(+alpha); + }; + + return catmullRom$$1; +})(0.5); + +function CatmullRomOpen(context, alpha) { + this._context = context; + this._alpha = alpha; +} + +CatmullRomOpen.prototype = { + areaStart: function() { + this._line = 0; + }, + areaEnd: function() { + this._line = NaN; + }, + lineStart: function() { + this._x0 = this._x1 = this._x2 = + this._y0 = this._y1 = this._y2 = NaN; + this._l01_a = this._l12_a = this._l23_a = + this._l01_2a = this._l12_2a = this._l23_2a = + this._point = 0; + }, + lineEnd: function() { + if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath(); + this._line = 1 - this._line; + }, + point: function(x, y) { + x = +x, y = +y; + + if (this._point) { + var x23 = this._x2 - x, + y23 = this._y2 - y; + this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha)); + } + + switch (this._point) { + case 0: this._point = 1; break; + case 1: this._point = 2; break; + case 2: this._point = 3; this._line ? this._context.lineTo(this._x2, this._y2) : this._context.moveTo(this._x2, this._y2); break; + case 3: this._point = 4; // proceed + default: point$4(this, x, y); break; + } + + this._l01_a = this._l12_a, this._l12_a = this._l23_a; + this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a; + this._x0 = this._x1, this._x1 = this._x2, this._x2 = x; + this._y0 = this._y1, this._y1 = this._y2, this._y2 = y; + } +}; + +var catmullRomOpen = (function custom(alpha) { + + function catmullRom$$1(context) { + return alpha ? new CatmullRomOpen(context, alpha) : new CardinalOpen(context, 0); + } + + catmullRom$$1.alpha = function(alpha) { + return custom(+alpha); + }; + + return catmullRom$$1; +})(0.5); + +function LinearClosed(context) { + this._context = context; +} + +LinearClosed.prototype = { + areaStart: noop$3, + areaEnd: noop$3, + lineStart: function() { + this._point = 0; + }, + lineEnd: function() { + if (this._point) this._context.closePath(); + }, + point: function(x, y) { + x = +x, y = +y; + if (this._point) this._context.lineTo(x, y); + else this._point = 1, this._context.moveTo(x, y); + } +}; + +function linearClosed(context) { + return new LinearClosed(context); +} + +function sign$1(x) { + return x < 0 ? -1 : 1; +} + +// Calculate the slopes of the tangents (Hermite-type interpolation) based on +// the following paper: Steffen, M. 1990. A Simple Method for Monotonic +// Interpolation in One Dimension. Astronomy and Astrophysics, Vol. 239, NO. +// NOV(II), P. 443, 1990. +function slope3(that, x2, y2) { + var h0 = that._x1 - that._x0, + h1 = x2 - that._x1, + s0 = (that._y1 - that._y0) / (h0 || h1 < 0 && -0), + s1 = (y2 - that._y1) / (h1 || h0 < 0 && -0), + p = (s0 * h1 + s1 * h0) / (h0 + h1); + return (sign$1(s0) + sign$1(s1)) * Math.min(Math.abs(s0), Math.abs(s1), 0.5 * Math.abs(p)) || 0; +} + +// Calculate a one-sided slope. +function slope2(that, t) { + var h = that._x1 - that._x0; + return h ? (3 * (that._y1 - that._y0) / h - t) / 2 : t; +} + +// According to https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Representations +// "you can express cubic Hermite interpolation in terms of cubic Bézier curves +// with respect to the four values p0, p0 + m0 / 3, p1 - m1 / 3, p1". +function point$5(that, t0, t1) { + var x0 = that._x0, + y0 = that._y0, + x1 = that._x1, + y1 = that._y1, + dx = (x1 - x0) / 3; + that._context.bezierCurveTo(x0 + dx, y0 + dx * t0, x1 - dx, y1 - dx * t1, x1, y1); +} + +function MonotoneX(context) { + this._context = context; +} + +MonotoneX.prototype = { + areaStart: function() { + this._line = 0; + }, + areaEnd: function() { + this._line = NaN; + }, + lineStart: function() { + this._x0 = this._x1 = + this._y0 = this._y1 = + this._t0 = NaN; + this._point = 0; + }, + lineEnd: function() { + switch (this._point) { + case 2: this._context.lineTo(this._x1, this._y1); break; + case 3: point$5(this, this._t0, slope2(this, this._t0)); break; + } + if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath(); + this._line = 1 - this._line; + }, + point: function(x, y) { + var t1 = NaN; + + x = +x, y = +y; + if (x === this._x1 && y === this._y1) return; // Ignore coincident points. + switch (this._point) { + case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break; + case 1: this._point = 2; break; + case 2: this._point = 3; point$5(this, slope2(this, t1 = slope3(this, x, y)), t1); break; + default: point$5(this, this._t0, t1 = slope3(this, x, y)); break; + } + + this._x0 = this._x1, this._x1 = x; + this._y0 = this._y1, this._y1 = y; + this._t0 = t1; + } +}; + +function MonotoneY(context) { + this._context = new ReflectContext(context); +} + +(MonotoneY.prototype = Object.create(MonotoneX.prototype)).point = function(x, y) { + MonotoneX.prototype.point.call(this, y, x); +}; + +function ReflectContext(context) { + this._context = context; +} + +ReflectContext.prototype = { + moveTo: function(x, y) { this._context.moveTo(y, x); }, + closePath: function() { this._context.closePath(); }, + lineTo: function(x, y) { this._context.lineTo(y, x); }, + bezierCurveTo: function(x1, y1, x2, y2, x, y) { this._context.bezierCurveTo(y1, x1, y2, x2, y, x); } +}; + +function monotoneX(context) { + return new MonotoneX(context); +} + +function monotoneY(context) { + return new MonotoneY(context); +} + +function Natural(context) { + this._context = context; +} + +Natural.prototype = { + areaStart: function() { + this._line = 0; + }, + areaEnd: function() { + this._line = NaN; + }, + lineStart: function() { + this._x = []; + this._y = []; + }, + lineEnd: function() { + var x = this._x, + y = this._y, + n = x.length; + + if (n) { + this._line ? this._context.lineTo(x[0], y[0]) : this._context.moveTo(x[0], y[0]); + if (n === 2) { + this._context.lineTo(x[1], y[1]); + } else { + var px = controlPoints(x), + py = controlPoints(y); + for (var i0 = 0, i1 = 1; i1 < n; ++i0, ++i1) { + this._context.bezierCurveTo(px[0][i0], py[0][i0], px[1][i0], py[1][i0], x[i1], y[i1]); + } + } + } + + if (this._line || (this._line !== 0 && n === 1)) this._context.closePath(); + this._line = 1 - this._line; + this._x = this._y = null; + }, + point: function(x, y) { + this._x.push(+x); + this._y.push(+y); + } +}; + +// See https://www.particleincell.com/2012/bezier-splines/ for derivation. +function controlPoints(x) { + var i, + n = x.length - 1, + m, + a = new Array(n), + b = new Array(n), + r = new Array(n); + a[0] = 0, b[0] = 2, r[0] = x[0] + 2 * x[1]; + for (i = 1; i < n - 1; ++i) a[i] = 1, b[i] = 4, r[i] = 4 * x[i] + 2 * x[i + 1]; + a[n - 1] = 2, b[n - 1] = 7, r[n - 1] = 8 * x[n - 1] + x[n]; + for (i = 1; i < n; ++i) m = a[i] / b[i - 1], b[i] -= m, r[i] -= m * r[i - 1]; + a[n - 1] = r[n - 1] / b[n - 1]; + for (i = n - 2; i >= 0; --i) a[i] = (r[i] - a[i + 1]) / b[i]; + b[n - 1] = (x[n] + a[n - 1]) / 2; + for (i = 0; i < n - 1; ++i) b[i] = 2 * x[i + 1] - a[i + 1]; + return [a, b]; +} + +function natural(context) { + return new Natural(context); +} + +function Step(context, t) { + this._context = context; + this._t = t; +} + +Step.prototype = { + areaStart: function() { + this._line = 0; + }, + areaEnd: function() { + this._line = NaN; + }, + lineStart: function() { + this._x = this._y = NaN; + this._point = 0; + }, + lineEnd: function() { + if (0 < this._t && this._t < 1 && this._point === 2) this._context.lineTo(this._x, this._y); + if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath(); + if (this._line >= 0) this._t = 1 - this._t, this._line = 1 - this._line; + }, + point: function(x, y) { + x = +x, y = +y; + switch (this._point) { + case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break; + case 1: this._point = 2; // proceed + default: { + if (this._t <= 0) { + this._context.lineTo(this._x, y); + this._context.lineTo(x, y); + } else { + var x1 = this._x * (1 - this._t) + x * this._t; + this._context.lineTo(x1, this._y); + this._context.lineTo(x1, y); + } + break; + } + } + this._x = x, this._y = y; + } +}; + +function step(context) { + return new Step(context, 0.5); +} + +function stepBefore(context) { + return new Step(context, 0); +} + +function stepAfter(context) { + return new Step(context, 1); +} + +function none$1(series, order) { + if (!((n = series.length) > 1)) return; + for (var i = 1, j, s0, s1 = series[order[0]], n, m = s1.length; i < n; ++i) { + s0 = s1, s1 = series[order[i]]; + for (j = 0; j < m; ++j) { + s1[j][1] += s1[j][0] = isNaN(s0[j][1]) ? s0[j][0] : s0[j][1]; + } + } +} + +function none$2(series) { + var n = series.length, o = new Array(n); + while (--n >= 0) o[n] = n; + return o; +} + +function stackValue(d, key) { + return d[key]; +} + +function stack() { + var keys = constant$11([]), + order = none$2, + offset = none$1, + value = stackValue; + + function stack(data) { + var kz = keys.apply(this, arguments), + i, + m = data.length, + n = kz.length, + sz = new Array(n), + oz; + + for (i = 0; i < n; ++i) { + for (var ki = kz[i], si = sz[i] = new Array(m), j = 0, sij; j < m; ++j) { + si[j] = sij = [0, +value(data[j], ki, j, data)]; + sij.data = data[j]; + } + si.key = ki; + } + + for (i = 0, oz = order(sz); i < n; ++i) { + sz[oz[i]].index = i; + } + + offset(sz, oz); + return sz; + } + + stack.keys = function(_) { + return arguments.length ? (keys = typeof _ === "function" ? _ : constant$11(slice$6.call(_)), stack) : keys; + }; + + stack.value = function(_) { + return arguments.length ? (value = typeof _ === "function" ? _ : constant$11(+_), stack) : value; + }; + + stack.order = function(_) { + return arguments.length ? (order = _ == null ? none$2 : typeof _ === "function" ? _ : constant$11(slice$6.call(_)), stack) : order; + }; + + stack.offset = function(_) { + return arguments.length ? (offset = _ == null ? none$1 : _, stack) : offset; + }; + + return stack; +} + +function expand(series, order) { + if (!((n = series.length) > 0)) return; + for (var i, n, j = 0, m = series[0].length, y; j < m; ++j) { + for (y = i = 0; i < n; ++i) y += series[i][j][1] || 0; + if (y) for (i = 0; i < n; ++i) series[i][j][1] /= y; + } + none$1(series, order); +} + +function diverging(series, order) { + if (!((n = series.length) > 1)) return; + for (var i, j = 0, d, dy, yp, yn, n, m = series[order[0]].length; j < m; ++j) { + for (yp = yn = 0, i = 0; i < n; ++i) { + if ((dy = (d = series[order[i]][j])[1] - d[0]) >= 0) { + d[0] = yp, d[1] = yp += dy; + } else if (dy < 0) { + d[1] = yn, d[0] = yn += dy; + } else { + d[0] = yp; + } + } + } +} + +function silhouette(series, order) { + if (!((n = series.length) > 0)) return; + for (var j = 0, s0 = series[order[0]], n, m = s0.length; j < m; ++j) { + for (var i = 0, y = 0; i < n; ++i) y += series[i][j][1] || 0; + s0[j][1] += s0[j][0] = -y / 2; + } + none$1(series, order); +} + +function wiggle(series, order) { + if (!((n = series.length) > 0) || !((m = (s0 = series[order[0]]).length) > 0)) return; + for (var y = 0, j = 1, s0, m, n; j < m; ++j) { + for (var i = 0, s1 = 0, s2 = 0; i < n; ++i) { + var si = series[order[i]], + sij0 = si[j][1] || 0, + sij1 = si[j - 1][1] || 0, + s3 = (sij0 - sij1) / 2; + for (var k = 0; k < i; ++k) { + var sk = series[order[k]], + skj0 = sk[j][1] || 0, + skj1 = sk[j - 1][1] || 0; + s3 += skj0 - skj1; + } + s1 += sij0, s2 += s3 * sij0; + } + s0[j - 1][1] += s0[j - 1][0] = y; + if (s1) y -= s2 / s1; + } + s0[j - 1][1] += s0[j - 1][0] = y; + none$1(series, order); +} + +function ascending$3(series) { + var sums = series.map(sum$2); + return none$2(series).sort(function(a, b) { return sums[a] - sums[b]; }); +} + +function sum$2(series) { + var s = 0, i = -1, n = series.length, v; + while (++i < n) if (v = +series[i][1]) s += v; + return s; +} + +function descending$2(series) { + return ascending$3(series).reverse(); +} + +function insideOut(series) { + var n = series.length, + i, + j, + sums = series.map(sum$2), + order = none$2(series).sort(function(a, b) { return sums[b] - sums[a]; }), + top = 0, + bottom = 0, + tops = [], + bottoms = []; + + for (i = 0; i < n; ++i) { + j = order[i]; + if (top < bottom) { + top += sums[j]; + tops.push(j); + } else { + bottom += sums[j]; + bottoms.push(j); + } + } + + return bottoms.reverse().concat(tops); +} + +function reverse(series) { + return none$2(series).reverse(); +} + +function constant$12(x) { + return function() { + return x; + }; +} + +function x$4(d) { + return d[0]; +} + +function y$4(d) { + return d[1]; +} + +function RedBlackTree() { + this._ = null; // root node +} + +function RedBlackNode(node) { + node.U = // parent node + node.C = // color - true for red, false for black + node.L = // left node + node.R = // right node + node.P = // previous node + node.N = null; // next node +} + +RedBlackTree.prototype = { + constructor: RedBlackTree, + + insert: function(after, node) { + var parent, grandpa, uncle; + + if (after) { + node.P = after; + node.N = after.N; + if (after.N) after.N.P = node; + after.N = node; + if (after.R) { + after = after.R; + while (after.L) after = after.L; + after.L = node; + } else { + after.R = node; + } + parent = after; + } else if (this._) { + after = RedBlackFirst(this._); + node.P = null; + node.N = after; + after.P = after.L = node; + parent = after; + } else { + node.P = node.N = null; + this._ = node; + parent = null; + } + node.L = node.R = null; + node.U = parent; + node.C = true; + + after = node; + while (parent && parent.C) { + grandpa = parent.U; + if (parent === grandpa.L) { + uncle = grandpa.R; + if (uncle && uncle.C) { + parent.C = uncle.C = false; + grandpa.C = true; + after = grandpa; + } else { + if (after === parent.R) { + RedBlackRotateLeft(this, parent); + after = parent; + parent = after.U; + } + parent.C = false; + grandpa.C = true; + RedBlackRotateRight(this, grandpa); + } + } else { + uncle = grandpa.L; + if (uncle && uncle.C) { + parent.C = uncle.C = false; + grandpa.C = true; + after = grandpa; + } else { + if (after === parent.L) { + RedBlackRotateRight(this, parent); + after = parent; + parent = after.U; + } + parent.C = false; + grandpa.C = true; + RedBlackRotateLeft(this, grandpa); + } + } + parent = after.U; + } + this._.C = false; + }, + + remove: function(node) { + if (node.N) node.N.P = node.P; + if (node.P) node.P.N = node.N; + node.N = node.P = null; + + var parent = node.U, + sibling, + left = node.L, + right = node.R, + next, + red; + + if (!left) next = right; + else if (!right) next = left; + else next = RedBlackFirst(right); + + if (parent) { + if (parent.L === node) parent.L = next; + else parent.R = next; + } else { + this._ = next; + } + + if (left && right) { + red = next.C; + next.C = node.C; + next.L = left; + left.U = next; + if (next !== right) { + parent = next.U; + next.U = node.U; + node = next.R; + parent.L = node; + next.R = right; + right.U = next; + } else { + next.U = parent; + parent = next; + node = next.R; + } + } else { + red = node.C; + node = next; + } + + if (node) node.U = parent; + if (red) return; + if (node && node.C) { node.C = false; return; } + + do { + if (node === this._) break; + if (node === parent.L) { + sibling = parent.R; + if (sibling.C) { + sibling.C = false; + parent.C = true; + RedBlackRotateLeft(this, parent); + sibling = parent.R; + } + if ((sibling.L && sibling.L.C) + || (sibling.R && sibling.R.C)) { + if (!sibling.R || !sibling.R.C) { + sibling.L.C = false; + sibling.C = true; + RedBlackRotateRight(this, sibling); + sibling = parent.R; + } + sibling.C = parent.C; + parent.C = sibling.R.C = false; + RedBlackRotateLeft(this, parent); + node = this._; + break; + } + } else { + sibling = parent.L; + if (sibling.C) { + sibling.C = false; + parent.C = true; + RedBlackRotateRight(this, parent); + sibling = parent.L; + } + if ((sibling.L && sibling.L.C) + || (sibling.R && sibling.R.C)) { + if (!sibling.L || !sibling.L.C) { + sibling.R.C = false; + sibling.C = true; + RedBlackRotateLeft(this, sibling); + sibling = parent.L; + } + sibling.C = parent.C; + parent.C = sibling.L.C = false; + RedBlackRotateRight(this, parent); + node = this._; + break; + } + } + sibling.C = true; + node = parent; + parent = parent.U; + } while (!node.C); + + if (node) node.C = false; + } +}; + +function RedBlackRotateLeft(tree, node) { + var p = node, + q = node.R, + parent = p.U; + + if (parent) { + if (parent.L === p) parent.L = q; + else parent.R = q; + } else { + tree._ = q; + } + + q.U = parent; + p.U = q; + p.R = q.L; + if (p.R) p.R.U = p; + q.L = p; +} + +function RedBlackRotateRight(tree, node) { + var p = node, + q = node.L, + parent = p.U; + + if (parent) { + if (parent.L === p) parent.L = q; + else parent.R = q; + } else { + tree._ = q; + } + + q.U = parent; + p.U = q; + p.L = q.R; + if (p.L) p.L.U = p; + q.R = p; +} + +function RedBlackFirst(node) { + while (node.L) node = node.L; + return node; +} + +function createEdge(left, right, v0, v1) { + var edge = [null, null], + index = edges.push(edge) - 1; + edge.left = left; + edge.right = right; + if (v0) setEdgeEnd(edge, left, right, v0); + if (v1) setEdgeEnd(edge, right, left, v1); + cells[left.index].halfedges.push(index); + cells[right.index].halfedges.push(index); + return edge; +} + +function createBorderEdge(left, v0, v1) { + var edge = [v0, v1]; + edge.left = left; + return edge; +} + +function setEdgeEnd(edge, left, right, vertex) { + if (!edge[0] && !edge[1]) { + edge[0] = vertex; + edge.left = left; + edge.right = right; + } else if (edge.left === right) { + edge[1] = vertex; + } else { + edge[0] = vertex; + } +} + +// Liang–Barsky line clipping. +function clipEdge(edge, x0, y0, x1, y1) { + var a = edge[0], + b = edge[1], + ax = a[0], + ay = a[1], + bx = b[0], + by = b[1], + t0 = 0, + t1 = 1, + dx = bx - ax, + dy = by - ay, + r; + + r = x0 - ax; + if (!dx && r > 0) return; + r /= dx; + if (dx < 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } else if (dx > 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } + + r = x1 - ax; + if (!dx && r < 0) return; + r /= dx; + if (dx < 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } else if (dx > 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } + + r = y0 - ay; + if (!dy && r > 0) return; + r /= dy; + if (dy < 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } else if (dy > 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } + + r = y1 - ay; + if (!dy && r < 0) return; + r /= dy; + if (dy < 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } else if (dy > 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } + + if (!(t0 > 0) && !(t1 < 1)) return true; // TODO Better check? + + if (t0 > 0) edge[0] = [ax + t0 * dx, ay + t0 * dy]; + if (t1 < 1) edge[1] = [ax + t1 * dx, ay + t1 * dy]; + return true; +} + +function connectEdge(edge, x0, y0, x1, y1) { + var v1 = edge[1]; + if (v1) return true; + + var v0 = edge[0], + left = edge.left, + right = edge.right, + lx = left[0], + ly = left[1], + rx = right[0], + ry = right[1], + fx = (lx + rx) / 2, + fy = (ly + ry) / 2, + fm, + fb; + + if (ry === ly) { + if (fx < x0 || fx >= x1) return; + if (lx > rx) { + if (!v0) v0 = [fx, y0]; + else if (v0[1] >= y1) return; + v1 = [fx, y1]; + } else { + if (!v0) v0 = [fx, y1]; + else if (v0[1] < y0) return; + v1 = [fx, y0]; + } + } else { + fm = (lx - rx) / (ry - ly); + fb = fy - fm * fx; + if (fm < -1 || fm > 1) { + if (lx > rx) { + if (!v0) v0 = [(y0 - fb) / fm, y0]; + else if (v0[1] >= y1) return; + v1 = [(y1 - fb) / fm, y1]; + } else { + if (!v0) v0 = [(y1 - fb) / fm, y1]; + else if (v0[1] < y0) return; + v1 = [(y0 - fb) / fm, y0]; + } + } else { + if (ly < ry) { + if (!v0) v0 = [x0, fm * x0 + fb]; + else if (v0[0] >= x1) return; + v1 = [x1, fm * x1 + fb]; + } else { + if (!v0) v0 = [x1, fm * x1 + fb]; + else if (v0[0] < x0) return; + v1 = [x0, fm * x0 + fb]; + } + } + } + + edge[0] = v0; + edge[1] = v1; + return true; +} + +function clipEdges(x0, y0, x1, y1) { + var i = edges.length, + edge; + + while (i--) { + if (!connectEdge(edge = edges[i], x0, y0, x1, y1) + || !clipEdge(edge, x0, y0, x1, y1) + || !(Math.abs(edge[0][0] - edge[1][0]) > epsilon$4 + || Math.abs(edge[0][1] - edge[1][1]) > epsilon$4)) { + delete edges[i]; + } + } +} + +function createCell(site) { + return cells[site.index] = { + site: site, + halfedges: [] + }; +} + +function cellHalfedgeAngle(cell, edge) { + var site = cell.site, + va = edge.left, + vb = edge.right; + if (site === vb) vb = va, va = site; + if (vb) return Math.atan2(vb[1] - va[1], vb[0] - va[0]); + if (site === va) va = edge[1], vb = edge[0]; + else va = edge[0], vb = edge[1]; + return Math.atan2(va[0] - vb[0], vb[1] - va[1]); +} + +function cellHalfedgeStart(cell, edge) { + return edge[+(edge.left !== cell.site)]; +} + +function cellHalfedgeEnd(cell, edge) { + return edge[+(edge.left === cell.site)]; +} + +function sortCellHalfedges() { + for (var i = 0, n = cells.length, cell, halfedges, j, m; i < n; ++i) { + if ((cell = cells[i]) && (m = (halfedges = cell.halfedges).length)) { + var index = new Array(m), + array = new Array(m); + for (j = 0; j < m; ++j) index[j] = j, array[j] = cellHalfedgeAngle(cell, edges[halfedges[j]]); + index.sort(function(i, j) { return array[j] - array[i]; }); + for (j = 0; j < m; ++j) array[j] = halfedges[index[j]]; + for (j = 0; j < m; ++j) halfedges[j] = array[j]; + } + } +} + +function clipCells(x0, y0, x1, y1) { + var nCells = cells.length, + iCell, + cell, + site, + iHalfedge, + halfedges, + nHalfedges, + start, + startX, + startY, + end, + endX, + endY, + cover = true; + + for (iCell = 0; iCell < nCells; ++iCell) { + if (cell = cells[iCell]) { + site = cell.site; + halfedges = cell.halfedges; + iHalfedge = halfedges.length; + + // Remove any dangling clipped edges. + while (iHalfedge--) { + if (!edges[halfedges[iHalfedge]]) { + halfedges.splice(iHalfedge, 1); + } + } + + // Insert any border edges as necessary. + iHalfedge = 0, nHalfedges = halfedges.length; + while (iHalfedge < nHalfedges) { + end = cellHalfedgeEnd(cell, edges[halfedges[iHalfedge]]), endX = end[0], endY = end[1]; + start = cellHalfedgeStart(cell, edges[halfedges[++iHalfedge % nHalfedges]]), startX = start[0], startY = start[1]; + if (Math.abs(endX - startX) > epsilon$4 || Math.abs(endY - startY) > epsilon$4) { + halfedges.splice(iHalfedge, 0, edges.push(createBorderEdge(site, end, + Math.abs(endX - x0) < epsilon$4 && y1 - endY > epsilon$4 ? [x0, Math.abs(startX - x0) < epsilon$4 ? startY : y1] + : Math.abs(endY - y1) < epsilon$4 && x1 - endX > epsilon$4 ? [Math.abs(startY - y1) < epsilon$4 ? startX : x1, y1] + : Math.abs(endX - x1) < epsilon$4 && endY - y0 > epsilon$4 ? [x1, Math.abs(startX - x1) < epsilon$4 ? startY : y0] + : Math.abs(endY - y0) < epsilon$4 && endX - x0 > epsilon$4 ? [Math.abs(startY - y0) < epsilon$4 ? startX : x0, y0] + : null)) - 1); + ++nHalfedges; + } + } + + if (nHalfedges) cover = false; + } + } + + // If there weren’t any edges, have the closest site cover the extent. + // It doesn’t matter which corner of the extent we measure! + if (cover) { + var dx, dy, d2, dc = Infinity; + + for (iCell = 0, cover = null; iCell < nCells; ++iCell) { + if (cell = cells[iCell]) { + site = cell.site; + dx = site[0] - x0; + dy = site[1] - y0; + d2 = dx * dx + dy * dy; + if (d2 < dc) dc = d2, cover = cell; + } + } + + if (cover) { + var v00 = [x0, y0], v01 = [x0, y1], v11 = [x1, y1], v10 = [x1, y0]; + cover.halfedges.push( + edges.push(createBorderEdge(site = cover.site, v00, v01)) - 1, + edges.push(createBorderEdge(site, v01, v11)) - 1, + edges.push(createBorderEdge(site, v11, v10)) - 1, + edges.push(createBorderEdge(site, v10, v00)) - 1 + ); + } + } + + // Lastly delete any cells with no edges; these were entirely clipped. + for (iCell = 0; iCell < nCells; ++iCell) { + if (cell = cells[iCell]) { + if (!cell.halfedges.length) { + delete cells[iCell]; + } + } + } +} + +var circlePool = []; + +var firstCircle; + +function Circle() { + RedBlackNode(this); + this.x = + this.y = + this.arc = + this.site = + this.cy = null; +} + +function attachCircle(arc) { + var lArc = arc.P, + rArc = arc.N; + + if (!lArc || !rArc) return; + + var lSite = lArc.site, + cSite = arc.site, + rSite = rArc.site; + + if (lSite === rSite) return; + + var bx = cSite[0], + by = cSite[1], + ax = lSite[0] - bx, + ay = lSite[1] - by, + cx = rSite[0] - bx, + cy = rSite[1] - by; + + var d = 2 * (ax * cy - ay * cx); + if (d >= -epsilon2$2) return; + + var ha = ax * ax + ay * ay, + hc = cx * cx + cy * cy, + x = (cy * ha - ay * hc) / d, + y = (ax * hc - cx * ha) / d; + + var circle = circlePool.pop() || new Circle; + circle.arc = arc; + circle.site = cSite; + circle.x = x + bx; + circle.y = (circle.cy = y + by) + Math.sqrt(x * x + y * y); // y bottom + + arc.circle = circle; + + var before = null, + node = circles._; + + while (node) { + if (circle.y < node.y || (circle.y === node.y && circle.x <= node.x)) { + if (node.L) node = node.L; + else { before = node.P; break; } + } else { + if (node.R) node = node.R; + else { before = node; break; } + } + } + + circles.insert(before, circle); + if (!before) firstCircle = circle; +} + +function detachCircle(arc) { + var circle = arc.circle; + if (circle) { + if (!circle.P) firstCircle = circle.N; + circles.remove(circle); + circlePool.push(circle); + RedBlackNode(circle); + arc.circle = null; + } +} + +var beachPool = []; + +function Beach() { + RedBlackNode(this); + this.edge = + this.site = + this.circle = null; +} + +function createBeach(site) { + var beach = beachPool.pop() || new Beach; + beach.site = site; + return beach; +} + +function detachBeach(beach) { + detachCircle(beach); + beaches.remove(beach); + beachPool.push(beach); + RedBlackNode(beach); +} + +function removeBeach(beach) { + var circle = beach.circle, + x = circle.x, + y = circle.cy, + vertex = [x, y], + previous = beach.P, + next = beach.N, + disappearing = [beach]; + + detachBeach(beach); + + var lArc = previous; + while (lArc.circle + && Math.abs(x - lArc.circle.x) < epsilon$4 + && Math.abs(y - lArc.circle.cy) < epsilon$4) { + previous = lArc.P; + disappearing.unshift(lArc); + detachBeach(lArc); + lArc = previous; + } + + disappearing.unshift(lArc); + detachCircle(lArc); + + var rArc = next; + while (rArc.circle + && Math.abs(x - rArc.circle.x) < epsilon$4 + && Math.abs(y - rArc.circle.cy) < epsilon$4) { + next = rArc.N; + disappearing.push(rArc); + detachBeach(rArc); + rArc = next; + } + + disappearing.push(rArc); + detachCircle(rArc); + + var nArcs = disappearing.length, + iArc; + for (iArc = 1; iArc < nArcs; ++iArc) { + rArc = disappearing[iArc]; + lArc = disappearing[iArc - 1]; + setEdgeEnd(rArc.edge, lArc.site, rArc.site, vertex); + } + + lArc = disappearing[0]; + rArc = disappearing[nArcs - 1]; + rArc.edge = createEdge(lArc.site, rArc.site, null, vertex); + + attachCircle(lArc); + attachCircle(rArc); +} + +function addBeach(site) { + var x = site[0], + directrix = site[1], + lArc, + rArc, + dxl, + dxr, + node = beaches._; + + while (node) { + dxl = leftBreakPoint(node, directrix) - x; + if (dxl > epsilon$4) node = node.L; else { + dxr = x - rightBreakPoint(node, directrix); + if (dxr > epsilon$4) { + if (!node.R) { + lArc = node; + break; + } + node = node.R; + } else { + if (dxl > -epsilon$4) { + lArc = node.P; + rArc = node; + } else if (dxr > -epsilon$4) { + lArc = node; + rArc = node.N; + } else { + lArc = rArc = node; + } + break; + } + } + } + + createCell(site); + var newArc = createBeach(site); + beaches.insert(lArc, newArc); + + if (!lArc && !rArc) return; + + if (lArc === rArc) { + detachCircle(lArc); + rArc = createBeach(lArc.site); + beaches.insert(newArc, rArc); + newArc.edge = rArc.edge = createEdge(lArc.site, newArc.site); + attachCircle(lArc); + attachCircle(rArc); + return; + } + + if (!rArc) { // && lArc + newArc.edge = createEdge(lArc.site, newArc.site); + return; + } + + // else lArc !== rArc + detachCircle(lArc); + detachCircle(rArc); + + var lSite = lArc.site, + ax = lSite[0], + ay = lSite[1], + bx = site[0] - ax, + by = site[1] - ay, + rSite = rArc.site, + cx = rSite[0] - ax, + cy = rSite[1] - ay, + d = 2 * (bx * cy - by * cx), + hb = bx * bx + by * by, + hc = cx * cx + cy * cy, + vertex = [(cy * hb - by * hc) / d + ax, (bx * hc - cx * hb) / d + ay]; + + setEdgeEnd(rArc.edge, lSite, rSite, vertex); + newArc.edge = createEdge(lSite, site, null, vertex); + rArc.edge = createEdge(site, rSite, null, vertex); + attachCircle(lArc); + attachCircle(rArc); +} + +function leftBreakPoint(arc, directrix) { + var site = arc.site, + rfocx = site[0], + rfocy = site[1], + pby2 = rfocy - directrix; + + if (!pby2) return rfocx; + + var lArc = arc.P; + if (!lArc) return -Infinity; + + site = lArc.site; + var lfocx = site[0], + lfocy = site[1], + plby2 = lfocy - directrix; + + if (!plby2) return lfocx; + + var hl = lfocx - rfocx, + aby2 = 1 / pby2 - 1 / plby2, + b = hl / plby2; + + if (aby2) return (-b + Math.sqrt(b * b - 2 * aby2 * (hl * hl / (-2 * plby2) - lfocy + plby2 / 2 + rfocy - pby2 / 2))) / aby2 + rfocx; + + return (rfocx + lfocx) / 2; +} + +function rightBreakPoint(arc, directrix) { + var rArc = arc.N; + if (rArc) return leftBreakPoint(rArc, directrix); + var site = arc.site; + return site[1] === directrix ? site[0] : Infinity; +} + +var epsilon$4 = 1e-6; +var epsilon2$2 = 1e-12; +var beaches; +var cells; +var circles; +var edges; + +function triangleArea(a, b, c) { + return (a[0] - c[0]) * (b[1] - a[1]) - (a[0] - b[0]) * (c[1] - a[1]); +} + +function lexicographic(a, b) { + return b[1] - a[1] + || b[0] - a[0]; +} + +function Diagram(sites, extent) { + var site = sites.sort(lexicographic).pop(), + x, + y, + circle; + + edges = []; + cells = new Array(sites.length); + beaches = new RedBlackTree; + circles = new RedBlackTree; + + while (true) { + circle = firstCircle; + if (site && (!circle || site[1] < circle.y || (site[1] === circle.y && site[0] < circle.x))) { + if (site[0] !== x || site[1] !== y) { + addBeach(site); + x = site[0], y = site[1]; + } + site = sites.pop(); + } else if (circle) { + removeBeach(circle.arc); + } else { + break; + } + } + + sortCellHalfedges(); + + if (extent) { + var x0 = +extent[0][0], + y0 = +extent[0][1], + x1 = +extent[1][0], + y1 = +extent[1][1]; + clipEdges(x0, y0, x1, y1); + clipCells(x0, y0, x1, y1); + } + + this.edges = edges; + this.cells = cells; + + beaches = + circles = + edges = + cells = null; +} + +Diagram.prototype = { + constructor: Diagram, + + polygons: function() { + var edges = this.edges; + + return this.cells.map(function(cell) { + var polygon = cell.halfedges.map(function(i) { return cellHalfedgeStart(cell, edges[i]); }); + polygon.data = cell.site.data; + return polygon; + }); + }, + + triangles: function() { + var triangles = [], + edges = this.edges; + + this.cells.forEach(function(cell, i) { + if (!(m = (halfedges = cell.halfedges).length)) return; + var site = cell.site, + halfedges, + j = -1, + m, + s0, + e1 = edges[halfedges[m - 1]], + s1 = e1.left === site ? e1.right : e1.left; + + while (++j < m) { + s0 = s1; + e1 = edges[halfedges[j]]; + s1 = e1.left === site ? e1.right : e1.left; + if (s0 && s1 && i < s0.index && i < s1.index && triangleArea(site, s0, s1) < 0) { + triangles.push([site.data, s0.data, s1.data]); + } + } + }); + + return triangles; + }, + + links: function() { + return this.edges.filter(function(edge) { + return edge.right; + }).map(function(edge) { + return { + source: edge.left.data, + target: edge.right.data + }; + }); + }, + + find: function(x, y, radius) { + var that = this, i0, i1 = that._found || 0, n = that.cells.length, cell; + + // Use the previously-found cell, or start with an arbitrary one. + while (!(cell = that.cells[i1])) if (++i1 >= n) return null; + var dx = x - cell.site[0], dy = y - cell.site[1], d2 = dx * dx + dy * dy; + + // Traverse the half-edges to find a closer cell, if any. + do { + cell = that.cells[i0 = i1], i1 = null; + cell.halfedges.forEach(function(e) { + var edge = that.edges[e], v = edge.left; + if ((v === cell.site || !v) && !(v = edge.right)) return; + var vx = x - v[0], vy = y - v[1], v2 = vx * vx + vy * vy; + if (v2 < d2) d2 = v2, i1 = v.index; + }); + } while (i1 !== null); + + that._found = i0; + + return radius == null || d2 <= radius * radius ? cell.site : null; + } +}; + +function voronoi() { + var x$$1 = x$4, + y$$1 = y$4, + extent = null; + + function voronoi(data) { + return new Diagram(data.map(function(d, i) { + var s = [Math.round(x$$1(d, i, data) / epsilon$4) * epsilon$4, Math.round(y$$1(d, i, data) / epsilon$4) * epsilon$4]; + s.index = i; + s.data = d; + return s; + }), extent); + } + + voronoi.polygons = function(data) { + return voronoi(data).polygons(); + }; + + voronoi.links = function(data) { + return voronoi(data).links(); + }; + + voronoi.triangles = function(data) { + return voronoi(data).triangles(); + }; + + voronoi.x = function(_) { + return arguments.length ? (x$$1 = typeof _ === "function" ? _ : constant$12(+_), voronoi) : x$$1; + }; + + voronoi.y = function(_) { + return arguments.length ? (y$$1 = typeof _ === "function" ? _ : constant$12(+_), voronoi) : y$$1; + }; + + voronoi.extent = function(_) { + return arguments.length ? (extent = _ == null ? null : [[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]], voronoi) : extent && [[extent[0][0], extent[0][1]], [extent[1][0], extent[1][1]]]; + }; + + voronoi.size = function(_) { + return arguments.length ? (extent = _ == null ? null : [[0, 0], [+_[0], +_[1]]], voronoi) : extent && [extent[1][0] - extent[0][0], extent[1][1] - extent[0][1]]; + }; + + return voronoi; +} + +function constant$13(x) { + return function() { + return x; + }; +} + +function ZoomEvent(target, type, transform) { + this.target = target; + this.type = type; + this.transform = transform; +} + +function Transform(k, x, y) { + this.k = k; + this.x = x; + this.y = y; +} + +Transform.prototype = { + constructor: Transform, + scale: function(k) { + return k === 1 ? this : new Transform(this.k * k, this.x, this.y); + }, + translate: function(x, y) { + return x === 0 & y === 0 ? this : new Transform(this.k, this.x + this.k * x, this.y + this.k * y); + }, + apply: function(point) { + return [point[0] * this.k + this.x, point[1] * this.k + this.y]; + }, + applyX: function(x) { + return x * this.k + this.x; + }, + applyY: function(y) { + return y * this.k + this.y; + }, + invert: function(location) { + return [(location[0] - this.x) / this.k, (location[1] - this.y) / this.k]; + }, + invertX: function(x) { + return (x - this.x) / this.k; + }, + invertY: function(y) { + return (y - this.y) / this.k; + }, + rescaleX: function(x) { + return x.copy().domain(x.range().map(this.invertX, this).map(x.invert, x)); + }, + rescaleY: function(y) { + return y.copy().domain(y.range().map(this.invertY, this).map(y.invert, y)); + }, + toString: function() { + return "translate(" + this.x + "," + this.y + ") scale(" + this.k + ")"; + } +}; + +var identity$8 = new Transform(1, 0, 0); + +transform$1.prototype = Transform.prototype; + +function transform$1(node) { + return node.__zoom || identity$8; +} + +function nopropagation$2() { + exports.event.stopImmediatePropagation(); +} + +function noevent$2() { + exports.event.preventDefault(); + exports.event.stopImmediatePropagation(); +} + +// Ignore right-click, since that should open the context menu. +function defaultFilter$2() { + return !exports.event.button; +} + +function defaultExtent$1() { + var e = this, w, h; + if (e instanceof SVGElement) { + e = e.ownerSVGElement || e; + w = e.width.baseVal.value; + h = e.height.baseVal.value; + } else { + w = e.clientWidth; + h = e.clientHeight; + } + return [[0, 0], [w, h]]; +} + +function defaultTransform() { + return this.__zoom || identity$8; +} + +function defaultWheelDelta() { + return -exports.event.deltaY * (exports.event.deltaMode ? 120 : 1) / 500; +} + +function defaultTouchable$1() { + return "ontouchstart" in this; +} + +function defaultConstrain(transform, extent, translateExtent) { + var dx0 = transform.invertX(extent[0][0]) - translateExtent[0][0], + dx1 = transform.invertX(extent[1][0]) - translateExtent[1][0], + dy0 = transform.invertY(extent[0][1]) - translateExtent[0][1], + dy1 = transform.invertY(extent[1][1]) - translateExtent[1][1]; + return transform.translate( + dx1 > dx0 ? (dx0 + dx1) / 2 : Math.min(0, dx0) || Math.max(0, dx1), + dy1 > dy0 ? (dy0 + dy1) / 2 : Math.min(0, dy0) || Math.max(0, dy1) + ); +} + +function zoom() { + var filter = defaultFilter$2, + extent = defaultExtent$1, + constrain = defaultConstrain, + wheelDelta = defaultWheelDelta, + touchable = defaultTouchable$1, + scaleExtent = [0, Infinity], + translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]], + duration = 250, + interpolate = interpolateZoom, + gestures = [], + listeners = dispatch("start", "zoom", "end"), + touchstarting, + touchending, + touchDelay = 500, + wheelDelay = 150, + clickDistance2 = 0; + + function zoom(selection$$1) { + selection$$1 + .property("__zoom", defaultTransform) + .on("wheel.zoom", wheeled) + .on("mousedown.zoom", mousedowned) + .on("dblclick.zoom", dblclicked) + .filter(touchable) + .on("touchstart.zoom", touchstarted) + .on("touchmove.zoom", touchmoved) + .on("touchend.zoom touchcancel.zoom", touchended) + .style("touch-action", "none") + .style("-webkit-tap-highlight-color", "rgba(0,0,0,0)"); + } + + zoom.transform = function(collection, transform) { + var selection$$1 = collection.selection ? collection.selection() : collection; + selection$$1.property("__zoom", defaultTransform); + if (collection !== selection$$1) { + schedule(collection, transform); + } else { + selection$$1.interrupt().each(function() { + gesture(this, arguments) + .start() + .zoom(null, typeof transform === "function" ? transform.apply(this, arguments) : transform) + .end(); + }); + } + }; + + zoom.scaleBy = function(selection$$1, k) { + zoom.scaleTo(selection$$1, function() { + var k0 = this.__zoom.k, + k1 = typeof k === "function" ? k.apply(this, arguments) : k; + return k0 * k1; + }); + }; + + zoom.scaleTo = function(selection$$1, k) { + zoom.transform(selection$$1, function() { + var e = extent.apply(this, arguments), + t0 = this.__zoom, + p0 = centroid(e), + p1 = t0.invert(p0), + k1 = typeof k === "function" ? k.apply(this, arguments) : k; + return constrain(translate(scale(t0, k1), p0, p1), e, translateExtent); + }); + }; + + zoom.translateBy = function(selection$$1, x, y) { + zoom.transform(selection$$1, function() { + return constrain(this.__zoom.translate( + typeof x === "function" ? x.apply(this, arguments) : x, + typeof y === "function" ? y.apply(this, arguments) : y + ), extent.apply(this, arguments), translateExtent); + }); + }; + + zoom.translateTo = function(selection$$1, x, y) { + zoom.transform(selection$$1, function() { + var e = extent.apply(this, arguments), + t = this.__zoom, + p = centroid(e); + return constrain(identity$8.translate(p[0], p[1]).scale(t.k).translate( + typeof x === "function" ? -x.apply(this, arguments) : -x, + typeof y === "function" ? -y.apply(this, arguments) : -y + ), e, translateExtent); + }); + }; + + function scale(transform, k) { + k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], k)); + return k === transform.k ? transform : new Transform(k, transform.x, transform.y); + } + + function translate(transform, p0, p1) { + var x = p0[0] - p1[0] * transform.k, y = p0[1] - p1[1] * transform.k; + return x === transform.x && y === transform.y ? transform : new Transform(transform.k, x, y); + } + + function centroid(extent) { + return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2]; + } + + function schedule(transition$$1, transform, center) { + transition$$1 + .on("start.zoom", function() { gesture(this, arguments).start(); }) + .on("interrupt.zoom end.zoom", function() { gesture(this, arguments).end(); }) + .tween("zoom", function() { + var that = this, + args = arguments, + g = gesture(that, args), + e = extent.apply(that, args), + p = center || centroid(e), + w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]), + a = that.__zoom, + b = typeof transform === "function" ? transform.apply(that, args) : transform, + i = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k)); + return function(t) { + if (t === 1) t = b; // Avoid rounding error on end. + else { var l = i(t), k = w / l[2]; t = new Transform(k, p[0] - l[0] * k, p[1] - l[1] * k); } + g.zoom(null, t); + }; + }); + } + + function gesture(that, args) { + for (var i = 0, n = gestures.length, g; i < n; ++i) { + if ((g = gestures[i]).that === that) { + return g; + } + } + return new Gesture(that, args); + } + + function Gesture(that, args) { + this.that = that; + this.args = args; + this.index = -1; + this.active = 0; + this.extent = extent.apply(that, args); + } + + Gesture.prototype = { + start: function() { + if (++this.active === 1) { + this.index = gestures.push(this) - 1; + this.emit("start"); + } + return this; + }, + zoom: function(key, transform) { + if (this.mouse && key !== "mouse") this.mouse[1] = transform.invert(this.mouse[0]); + if (this.touch0 && key !== "touch") this.touch0[1] = transform.invert(this.touch0[0]); + if (this.touch1 && key !== "touch") this.touch1[1] = transform.invert(this.touch1[0]); + this.that.__zoom = transform; + this.emit("zoom"); + return this; + }, + end: function() { + if (--this.active === 0) { + gestures.splice(this.index, 1); + this.index = -1; + this.emit("end"); + } + return this; + }, + emit: function(type) { + customEvent(new ZoomEvent(zoom, type, this.that.__zoom), listeners.apply, listeners, [type, this.that, this.args]); + } + }; + + function wheeled() { + if (!filter.apply(this, arguments)) return; + var g = gesture(this, arguments), + t = this.__zoom, + k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k * Math.pow(2, wheelDelta.apply(this, arguments)))), + p = mouse(this); + + // If the mouse is in the same location as before, reuse it. + // If there were recent wheel events, reset the wheel idle timeout. + if (g.wheel) { + if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) { + g.mouse[1] = t.invert(g.mouse[0] = p); + } + clearTimeout(g.wheel); + } + + // If this wheel event won’t trigger a transform change, ignore it. + else if (t.k === k) return; + + // Otherwise, capture the mouse point and location at the start. + else { + g.mouse = [p, t.invert(p)]; + interrupt(this); + g.start(); + } + + noevent$2(); + g.wheel = setTimeout(wheelidled, wheelDelay); + g.zoom("mouse", constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent, translateExtent)); + + function wheelidled() { + g.wheel = null; + g.end(); + } + } + + function mousedowned() { + if (touchending || !filter.apply(this, arguments)) return; + var g = gesture(this, arguments), + v = select(exports.event.view).on("mousemove.zoom", mousemoved, true).on("mouseup.zoom", mouseupped, true), + p = mouse(this), + x0 = exports.event.clientX, + y0 = exports.event.clientY; + + dragDisable(exports.event.view); + nopropagation$2(); + g.mouse = [p, this.__zoom.invert(p)]; + interrupt(this); + g.start(); + + function mousemoved() { + noevent$2(); + if (!g.moved) { + var dx = exports.event.clientX - x0, dy = exports.event.clientY - y0; + g.moved = dx * dx + dy * dy > clickDistance2; + } + g.zoom("mouse", constrain(translate(g.that.__zoom, g.mouse[0] = mouse(g.that), g.mouse[1]), g.extent, translateExtent)); + } + + function mouseupped() { + v.on("mousemove.zoom mouseup.zoom", null); + yesdrag(exports.event.view, g.moved); + noevent$2(); + g.end(); + } + } + + function dblclicked() { + if (!filter.apply(this, arguments)) return; + var t0 = this.__zoom, + p0 = mouse(this), + p1 = t0.invert(p0), + k1 = t0.k * (exports.event.shiftKey ? 0.5 : 2), + t1 = constrain(translate(scale(t0, k1), p0, p1), extent.apply(this, arguments), translateExtent); + + noevent$2(); + if (duration > 0) select(this).transition().duration(duration).call(schedule, t1, p0); + else select(this).call(zoom.transform, t1); + } + + function touchstarted() { + if (!filter.apply(this, arguments)) return; + var g = gesture(this, arguments), + touches$$1 = exports.event.changedTouches, + started, + n = touches$$1.length, i, t, p; + + nopropagation$2(); + for (i = 0; i < n; ++i) { + t = touches$$1[i], p = touch(this, touches$$1, t.identifier); + p = [p, this.__zoom.invert(p), t.identifier]; + if (!g.touch0) g.touch0 = p, started = true; + else if (!g.touch1) g.touch1 = p; + } + + // If this is a dbltap, reroute to the (optional) dblclick.zoom handler. + if (touchstarting) { + touchstarting = clearTimeout(touchstarting); + if (!g.touch1) { + g.end(); + p = select(this).on("dblclick.zoom"); + if (p) p.apply(this, arguments); + return; + } + } + + if (started) { + touchstarting = setTimeout(function() { touchstarting = null; }, touchDelay); + interrupt(this); + g.start(); + } + } + + function touchmoved() { + var g = gesture(this, arguments), + touches$$1 = exports.event.changedTouches, + n = touches$$1.length, i, t, p, l; + + noevent$2(); + if (touchstarting) touchstarting = clearTimeout(touchstarting); + for (i = 0; i < n; ++i) { + t = touches$$1[i], p = touch(this, touches$$1, t.identifier); + if (g.touch0 && g.touch0[2] === t.identifier) g.touch0[0] = p; + else if (g.touch1 && g.touch1[2] === t.identifier) g.touch1[0] = p; + } + t = g.that.__zoom; + if (g.touch1) { + var p0 = g.touch0[0], l0 = g.touch0[1], + p1 = g.touch1[0], l1 = g.touch1[1], + dp = (dp = p1[0] - p0[0]) * dp + (dp = p1[1] - p0[1]) * dp, + dl = (dl = l1[0] - l0[0]) * dl + (dl = l1[1] - l0[1]) * dl; + t = scale(t, Math.sqrt(dp / dl)); + p = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2]; + l = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2]; + } + else if (g.touch0) p = g.touch0[0], l = g.touch0[1]; + else return; + g.zoom("touch", constrain(translate(t, p, l), g.extent, translateExtent)); + } + + function touchended() { + var g = gesture(this, arguments), + touches$$1 = exports.event.changedTouches, + n = touches$$1.length, i, t; + + nopropagation$2(); + if (touchending) clearTimeout(touchending); + touchending = setTimeout(function() { touchending = null; }, touchDelay); + for (i = 0; i < n; ++i) { + t = touches$$1[i]; + if (g.touch0 && g.touch0[2] === t.identifier) delete g.touch0; + else if (g.touch1 && g.touch1[2] === t.identifier) delete g.touch1; + } + if (g.touch1 && !g.touch0) g.touch0 = g.touch1, delete g.touch1; + if (g.touch0) g.touch0[1] = this.__zoom.invert(g.touch0[0]); + else g.end(); + } + + zoom.wheelDelta = function(_) { + return arguments.length ? (wheelDelta = typeof _ === "function" ? _ : constant$13(+_), zoom) : wheelDelta; + }; + + zoom.filter = function(_) { + return arguments.length ? (filter = typeof _ === "function" ? _ : constant$13(!!_), zoom) : filter; + }; + + zoom.touchable = function(_) { + return arguments.length ? (touchable = typeof _ === "function" ? _ : constant$13(!!_), zoom) : touchable; + }; + + zoom.extent = function(_) { + return arguments.length ? (extent = typeof _ === "function" ? _ : constant$13([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent; + }; + + zoom.scaleExtent = function(_) { + return arguments.length ? (scaleExtent[0] = +_[0], scaleExtent[1] = +_[1], zoom) : [scaleExtent[0], scaleExtent[1]]; + }; + + zoom.translateExtent = function(_) { + return arguments.length ? (translateExtent[0][0] = +_[0][0], translateExtent[1][0] = +_[1][0], translateExtent[0][1] = +_[0][1], translateExtent[1][1] = +_[1][1], zoom) : [[translateExtent[0][0], translateExtent[0][1]], [translateExtent[1][0], translateExtent[1][1]]]; + }; + + zoom.constrain = function(_) { + return arguments.length ? (constrain = _, zoom) : constrain; + }; + + zoom.duration = function(_) { + return arguments.length ? (duration = +_, zoom) : duration; + }; + + zoom.interpolate = function(_) { + return arguments.length ? (interpolate = _, zoom) : interpolate; + }; + + zoom.on = function() { + var value = listeners.on.apply(listeners, arguments); + return value === listeners ? zoom : value; + }; + + zoom.clickDistance = function(_) { + return arguments.length ? (clickDistance2 = (_ = +_) * _, zoom) : Math.sqrt(clickDistance2); + }; + + return zoom; +} + +exports.version = version; +exports.bisect = bisectRight; +exports.bisectRight = bisectRight; +exports.bisectLeft = bisectLeft; +exports.ascending = ascending; +exports.bisector = bisector; +exports.cross = cross; +exports.descending = descending; +exports.deviation = deviation; +exports.extent = extent; +exports.histogram = histogram; +exports.thresholdFreedmanDiaconis = freedmanDiaconis; +exports.thresholdScott = scott; +exports.thresholdSturges = thresholdSturges; +exports.max = max; +exports.mean = mean; +exports.median = median; +exports.merge = merge; +exports.min = min; +exports.pairs = pairs; +exports.permute = permute; +exports.quantile = threshold; +exports.range = sequence; +exports.scan = scan; +exports.shuffle = shuffle; +exports.sum = sum; +exports.ticks = ticks; +exports.tickIncrement = tickIncrement; +exports.tickStep = tickStep; +exports.transpose = transpose; +exports.variance = variance; +exports.zip = zip; +exports.axisTop = axisTop; +exports.axisRight = axisRight; +exports.axisBottom = axisBottom; +exports.axisLeft = axisLeft; +exports.brush = brush; +exports.brushX = brushX; +exports.brushY = brushY; +exports.brushSelection = brushSelection; +exports.chord = chord; +exports.ribbon = ribbon; +exports.nest = nest; +exports.set = set$2; +exports.map = map$1; +exports.keys = keys; +exports.values = values; +exports.entries = entries; +exports.color = color; +exports.rgb = rgb; +exports.hsl = hsl; +exports.lab = lab; +exports.hcl = hcl; +exports.cubehelix = cubehelix; +exports.contours = contours; +exports.contourDensity = density; +exports.dispatch = dispatch; +exports.drag = drag; +exports.dragDisable = dragDisable; +exports.dragEnable = yesdrag; +exports.dsvFormat = dsvFormat; +exports.csvParse = csvParse; +exports.csvParseRows = csvParseRows; +exports.csvFormat = csvFormat; +exports.csvFormatRows = csvFormatRows; +exports.tsvParse = tsvParse; +exports.tsvParseRows = tsvParseRows; +exports.tsvFormat = tsvFormat; +exports.tsvFormatRows = tsvFormatRows; +exports.easeLinear = linear$1; +exports.easeQuad = quadInOut; +exports.easeQuadIn = quadIn; +exports.easeQuadOut = quadOut; +exports.easeQuadInOut = quadInOut; +exports.easeCubic = cubicInOut; +exports.easeCubicIn = cubicIn; +exports.easeCubicOut = cubicOut; +exports.easeCubicInOut = cubicInOut; +exports.easePoly = polyInOut; +exports.easePolyIn = polyIn; +exports.easePolyOut = polyOut; +exports.easePolyInOut = polyInOut; +exports.easeSin = sinInOut; +exports.easeSinIn = sinIn; +exports.easeSinOut = sinOut; +exports.easeSinInOut = sinInOut; +exports.easeExp = expInOut; +exports.easeExpIn = expIn; +exports.easeExpOut = expOut; +exports.easeExpInOut = expInOut; +exports.easeCircle = circleInOut; +exports.easeCircleIn = circleIn; +exports.easeCircleOut = circleOut; +exports.easeCircleInOut = circleInOut; +exports.easeBounce = bounceOut; +exports.easeBounceIn = bounceIn; +exports.easeBounceOut = bounceOut; +exports.easeBounceInOut = bounceInOut; +exports.easeBack = backInOut; +exports.easeBackIn = backIn; +exports.easeBackOut = backOut; +exports.easeBackInOut = backInOut; +exports.easeElastic = elasticOut; +exports.easeElasticIn = elasticIn; +exports.easeElasticOut = elasticOut; +exports.easeElasticInOut = elasticInOut; +exports.blob = blob; +exports.buffer = buffer; +exports.dsv = dsv; +exports.csv = csv$1; +exports.tsv = tsv$1; +exports.image = image; +exports.json = json; +exports.text = text; +exports.xml = xml; +exports.html = html; +exports.svg = svg; +exports.forceCenter = center$1; +exports.forceCollide = collide; +exports.forceLink = link; +exports.forceManyBody = manyBody; +exports.forceRadial = radial; +exports.forceSimulation = simulation; +exports.forceX = x$2; +exports.forceY = y$2; +exports.formatDefaultLocale = defaultLocale; +exports.formatLocale = formatLocale; +exports.formatSpecifier = formatSpecifier; +exports.precisionFixed = precisionFixed; +exports.precisionPrefix = precisionPrefix; +exports.precisionRound = precisionRound; +exports.geoArea = area$1; +exports.geoBounds = bounds; +exports.geoCentroid = centroid; +exports.geoCircle = circle; +exports.geoClipAntimeridian = clipAntimeridian; +exports.geoClipCircle = clipCircle; +exports.geoClipExtent = extent$1; +exports.geoClipRectangle = clipRectangle; +exports.geoContains = contains$1; +exports.geoDistance = distance; +exports.geoGraticule = graticule; +exports.geoGraticule10 = graticule10; +exports.geoInterpolate = interpolate$1; +exports.geoLength = length$1; +exports.geoPath = index$1; +exports.geoAlbers = albers; +exports.geoAlbersUsa = albersUsa; +exports.geoAzimuthalEqualArea = azimuthalEqualArea; +exports.geoAzimuthalEqualAreaRaw = azimuthalEqualAreaRaw; +exports.geoAzimuthalEquidistant = azimuthalEquidistant; +exports.geoAzimuthalEquidistantRaw = azimuthalEquidistantRaw; +exports.geoConicConformal = conicConformal; +exports.geoConicConformalRaw = conicConformalRaw; +exports.geoConicEqualArea = conicEqualArea; +exports.geoConicEqualAreaRaw = conicEqualAreaRaw; +exports.geoConicEquidistant = conicEquidistant; +exports.geoConicEquidistantRaw = conicEquidistantRaw; +exports.geoEquirectangular = equirectangular; +exports.geoEquirectangularRaw = equirectangularRaw; +exports.geoGnomonic = gnomonic; +exports.geoGnomonicRaw = gnomonicRaw; +exports.geoIdentity = identity$5; +exports.geoProjection = projection; +exports.geoProjectionMutator = projectionMutator; +exports.geoMercator = mercator; +exports.geoMercatorRaw = mercatorRaw; +exports.geoNaturalEarth1 = naturalEarth1; +exports.geoNaturalEarth1Raw = naturalEarth1Raw; +exports.geoOrthographic = orthographic; +exports.geoOrthographicRaw = orthographicRaw; +exports.geoStereographic = stereographic; +exports.geoStereographicRaw = stereographicRaw; +exports.geoTransverseMercator = transverseMercator; +exports.geoTransverseMercatorRaw = transverseMercatorRaw; +exports.geoRotation = rotation; +exports.geoStream = geoStream; +exports.geoTransform = transform; +exports.cluster = cluster; +exports.hierarchy = hierarchy; +exports.pack = index$2; +exports.packSiblings = siblings; +exports.packEnclose = enclose; +exports.partition = partition; +exports.stratify = stratify; +exports.tree = tree; +exports.treemap = index$3; +exports.treemapBinary = binary; +exports.treemapDice = treemapDice; +exports.treemapSlice = treemapSlice; +exports.treemapSliceDice = sliceDice; +exports.treemapSquarify = squarify; +exports.treemapResquarify = resquarify; +exports.interpolate = interpolateValue; +exports.interpolateArray = array$1; +exports.interpolateBasis = basis$1; +exports.interpolateBasisClosed = basisClosed; +exports.interpolateDate = date; +exports.interpolateNumber = reinterpolate; +exports.interpolateObject = object; +exports.interpolateRound = interpolateRound; +exports.interpolateString = interpolateString; +exports.interpolateTransformCss = interpolateTransformCss; +exports.interpolateTransformSvg = interpolateTransformSvg; +exports.interpolateZoom = interpolateZoom; +exports.interpolateRgb = interpolateRgb; +exports.interpolateRgbBasis = rgbBasis; +exports.interpolateRgbBasisClosed = rgbBasisClosed; +exports.interpolateHsl = hsl$2; +exports.interpolateHslLong = hslLong; +exports.interpolateLab = lab$1; +exports.interpolateHcl = hcl$2; +exports.interpolateHclLong = hclLong; +exports.interpolateCubehelix = cubehelix$2; +exports.interpolateCubehelixLong = cubehelixLong; +exports.quantize = quantize; +exports.path = path; +exports.polygonArea = area$2; +exports.polygonCentroid = centroid$1; +exports.polygonHull = hull; +exports.polygonContains = contains$2; +exports.polygonLength = length$2; +exports.quadtree = quadtree; +exports.randomUniform = uniform; +exports.randomNormal = normal; +exports.randomLogNormal = logNormal; +exports.randomBates = bates; +exports.randomIrwinHall = irwinHall; +exports.randomExponential = exponential$1; +exports.scaleBand = band; +exports.scalePoint = point$1; +exports.scaleIdentity = identity$6; +exports.scaleLinear = linear$2; +exports.scaleLog = log$1; +exports.scaleOrdinal = ordinal; +exports.scaleImplicit = implicit; +exports.scalePow = pow$1; +exports.scaleSqrt = sqrt$1; +exports.scaleQuantile = quantile$$1; +exports.scaleQuantize = quantize$1; +exports.scaleThreshold = threshold$1; +exports.scaleTime = time; +exports.scaleUtc = utcTime; +exports.scaleSequential = sequential; +exports.schemeCategory10 = category10; +exports.schemeAccent = Accent; +exports.schemeDark2 = Dark2; +exports.schemePaired = Paired; +exports.schemePastel1 = Pastel1; +exports.schemePastel2 = Pastel2; +exports.schemeSet1 = Set1; +exports.schemeSet2 = Set2; +exports.schemeSet3 = Set3; +exports.interpolateBrBG = BrBG; +exports.schemeBrBG = scheme; +exports.interpolatePRGn = PRGn; +exports.schemePRGn = scheme$1; +exports.interpolatePiYG = PiYG; +exports.schemePiYG = scheme$2; +exports.interpolatePuOr = PuOr; +exports.schemePuOr = scheme$3; +exports.interpolateRdBu = RdBu; +exports.schemeRdBu = scheme$4; +exports.interpolateRdGy = RdGy; +exports.schemeRdGy = scheme$5; +exports.interpolateRdYlBu = RdYlBu; +exports.schemeRdYlBu = scheme$6; +exports.interpolateRdYlGn = RdYlGn; +exports.schemeRdYlGn = scheme$7; +exports.interpolateSpectral = Spectral; +exports.schemeSpectral = scheme$8; +exports.interpolateBuGn = BuGn; +exports.schemeBuGn = scheme$9; +exports.interpolateBuPu = BuPu; +exports.schemeBuPu = scheme$10; +exports.interpolateGnBu = GnBu; +exports.schemeGnBu = scheme$11; +exports.interpolateOrRd = OrRd; +exports.schemeOrRd = scheme$12; +exports.interpolatePuBuGn = PuBuGn; +exports.schemePuBuGn = scheme$13; +exports.interpolatePuBu = PuBu; +exports.schemePuBu = scheme$14; +exports.interpolatePuRd = PuRd; +exports.schemePuRd = scheme$15; +exports.interpolateRdPu = RdPu; +exports.schemeRdPu = scheme$16; +exports.interpolateYlGnBu = YlGnBu; +exports.schemeYlGnBu = scheme$17; +exports.interpolateYlGn = YlGn; +exports.schemeYlGn = scheme$18; +exports.interpolateYlOrBr = YlOrBr; +exports.schemeYlOrBr = scheme$19; +exports.interpolateYlOrRd = YlOrRd; +exports.schemeYlOrRd = scheme$20; +exports.interpolateBlues = Blues; +exports.schemeBlues = scheme$21; +exports.interpolateGreens = Greens; +exports.schemeGreens = scheme$22; +exports.interpolateGreys = Greys; +exports.schemeGreys = scheme$23; +exports.interpolatePurples = Purples; +exports.schemePurples = scheme$24; +exports.interpolateReds = Reds; +exports.schemeReds = scheme$25; +exports.interpolateOranges = Oranges; +exports.schemeOranges = scheme$26; +exports.interpolateCubehelixDefault = cubehelix$3; +exports.interpolateRainbow = rainbow$1; +exports.interpolateWarm = warm; +exports.interpolateCool = cool; +exports.interpolateViridis = viridis; +exports.interpolateMagma = magma; +exports.interpolateInferno = inferno; +exports.interpolatePlasma = plasma; +exports.create = create; +exports.creator = creator; +exports.local = local; +exports.matcher = matcher$1; +exports.mouse = mouse; +exports.namespace = namespace; +exports.namespaces = namespaces; +exports.clientPoint = point; +exports.select = select; +exports.selectAll = selectAll; +exports.selection = selection; +exports.selector = selector; +exports.selectorAll = selectorAll; +exports.style = styleValue; +exports.touch = touch; +exports.touches = touches; +exports.window = defaultView; +exports.customEvent = customEvent; +exports.arc = arc; +exports.area = area$3; +exports.line = line; +exports.pie = pie; +exports.areaRadial = areaRadial; +exports.radialArea = areaRadial; +exports.lineRadial = lineRadial$1; +exports.radialLine = lineRadial$1; +exports.pointRadial = pointRadial; +exports.linkHorizontal = linkHorizontal; +exports.linkVertical = linkVertical; +exports.linkRadial = linkRadial; +exports.symbol = symbol; +exports.symbols = symbols; +exports.symbolCircle = circle$2; +exports.symbolCross = cross$2; +exports.symbolDiamond = diamond; +exports.symbolSquare = square; +exports.symbolStar = star; +exports.symbolTriangle = triangle; +exports.symbolWye = wye; +exports.curveBasisClosed = basisClosed$1; +exports.curveBasisOpen = basisOpen; +exports.curveBasis = basis$2; +exports.curveBundle = bundle; +exports.curveCardinalClosed = cardinalClosed; +exports.curveCardinalOpen = cardinalOpen; +exports.curveCardinal = cardinal; +exports.curveCatmullRomClosed = catmullRomClosed; +exports.curveCatmullRomOpen = catmullRomOpen; +exports.curveCatmullRom = catmullRom; +exports.curveLinearClosed = linearClosed; +exports.curveLinear = curveLinear; +exports.curveMonotoneX = monotoneX; +exports.curveMonotoneY = monotoneY; +exports.curveNatural = natural; +exports.curveStep = step; +exports.curveStepAfter = stepAfter; +exports.curveStepBefore = stepBefore; +exports.stack = stack; +exports.stackOffsetExpand = expand; +exports.stackOffsetDiverging = diverging; +exports.stackOffsetNone = none$1; +exports.stackOffsetSilhouette = silhouette; +exports.stackOffsetWiggle = wiggle; +exports.stackOrderAscending = ascending$3; +exports.stackOrderDescending = descending$2; +exports.stackOrderInsideOut = insideOut; +exports.stackOrderNone = none$2; +exports.stackOrderReverse = reverse; +exports.timeInterval = newInterval; +exports.timeMillisecond = millisecond; +exports.timeMilliseconds = milliseconds; +exports.utcMillisecond = millisecond; +exports.utcMilliseconds = milliseconds; +exports.timeSecond = second; +exports.timeSeconds = seconds; +exports.utcSecond = second; +exports.utcSeconds = seconds; +exports.timeMinute = minute; +exports.timeMinutes = minutes; +exports.timeHour = hour; +exports.timeHours = hours; +exports.timeDay = day; +exports.timeDays = days; +exports.timeWeek = sunday; +exports.timeWeeks = sundays; +exports.timeSunday = sunday; +exports.timeSundays = sundays; +exports.timeMonday = monday; +exports.timeMondays = mondays; +exports.timeTuesday = tuesday; +exports.timeTuesdays = tuesdays; +exports.timeWednesday = wednesday; +exports.timeWednesdays = wednesdays; +exports.timeThursday = thursday; +exports.timeThursdays = thursdays; +exports.timeFriday = friday; +exports.timeFridays = fridays; +exports.timeSaturday = saturday; +exports.timeSaturdays = saturdays; +exports.timeMonth = month; +exports.timeMonths = months; +exports.timeYear = year; +exports.timeYears = years; +exports.utcMinute = utcMinute; +exports.utcMinutes = utcMinutes; +exports.utcHour = utcHour; +exports.utcHours = utcHours; +exports.utcDay = utcDay; +exports.utcDays = utcDays; +exports.utcWeek = utcSunday; +exports.utcWeeks = utcSundays; +exports.utcSunday = utcSunday; +exports.utcSundays = utcSundays; +exports.utcMonday = utcMonday; +exports.utcMondays = utcMondays; +exports.utcTuesday = utcTuesday; +exports.utcTuesdays = utcTuesdays; +exports.utcWednesday = utcWednesday; +exports.utcWednesdays = utcWednesdays; +exports.utcThursday = utcThursday; +exports.utcThursdays = utcThursdays; +exports.utcFriday = utcFriday; +exports.utcFridays = utcFridays; +exports.utcSaturday = utcSaturday; +exports.utcSaturdays = utcSaturdays; +exports.utcMonth = utcMonth; +exports.utcMonths = utcMonths; +exports.utcYear = utcYear; +exports.utcYears = utcYears; +exports.timeFormatDefaultLocale = defaultLocale$1; +exports.timeFormatLocale = formatLocale$1; +exports.isoFormat = formatIso; +exports.isoParse = parseIso; +exports.now = now; +exports.timer = timer; +exports.timerFlush = timerFlush; +exports.timeout = timeout$1; +exports.interval = interval$1; +exports.transition = transition; +exports.active = active; +exports.interrupt = interrupt; +exports.voronoi = voronoi; +exports.zoom = zoom; +exports.zoomTransform = transform$1; +exports.zoomIdentity = identity$8; + +Object.defineProperty(exports, '__esModule', { value: true }); + +}))); diff --git a/debian/missing-sources/d3evolution.js b/debian/missing-sources/d3evolution.js new file mode 100644 index 0000000..521caa5 --- /dev/null +++ b/debian/missing-sources/d3evolution.js @@ -0,0 +1,562 @@ +/*! + * D3Evolution 1.1.0 (https://github.com/moisseev/D3Evolution) + * Copyright (c) 2016-2017, Alexander Moisseev, BSD 2-Clause + */ + +function D3Evolution(id, options) { + "use strict"; + + var opts = $.extend(true, { + title: "", + width: 800, + height: 400, + margin: {top: 80, right: 60, bottom: 40, left: 60}, + yAxisLabel: "", + + type: "line", // area|line + yScale: "lin", // lin|log + + duration: 1250, + interpolate: "curveLinear", + + // convert: "percentage", + + legend: { + buttonRadius: 7, + space: 130, + + entries: [ + // , + // , + // {label: "Greylisted", color: "#436EEE"}, + // {label: "Clean", color: "#66cc00"}, + ] + } + }, options); + + const curves = { + "curveLinear": d3.curveLinear, + "curveStep": d3.curveStep, + "curveStepBefore": d3.curveStepBefore, + "curveStepAfter": d3.curveStepAfter, + "curveMonotoneX": d3.curveMonotoneX, + "curveBasis": d3.curveBasis, + "curveBasisOpen": d3.curveBasisOpen, + "curveBundle": d3.curveBundle, + "curveCardinal": d3.curveCardinal, + "curveCardinalOpen": d3.curveCardinalOpen, + "curveCatmullRom": d3.curveCatmullRom, + "curveCatmullRomOpen": d3.curveCatmullRomOpen, + "curveNatural": d3.curveNatural, + }; + + var data; + var srcData; + var legendX; + + var width = opts.width - opts.margin.left - opts.margin.right; + var height = opts.height - opts.margin.top - opts.margin.bottom; + + var xScale = d3.scaleTime().range([0, width]); + + var yScale; + var yAxisScale; + + const setYScale = function () { + if (opts.yScale === "log") { + yScale = d3.scaleLog().clamp(true).range([height, 0]); + yAxisScale = d3.scaleLog().range([height - 30, 0]); + } else { + yScale = d3.scaleLinear().range([height, 0]); + yAxisScale = yScale.copy(); + } + }; + + setYScale(); + + var xAxis = d3.axisBottom().scale(xScale); + var yAxis = d3.axisLeft().scale(yAxisScale).ticks(5); + + var xAxisGrid = d3.axisBottom().tickFormat("").scale(xScale) + .tickSize(-height, 0); + var yAxisGrid = d3.axisLeft().tickFormat("").scale(yAxisScale) + .tickSize(-width, 0); + + var yScaleBoolean = d3.scaleQuantize().range([height, 0]); + var areaNull = d3.area() + .x(function (d) { return xScale(d.x); }) + .y0(function (d) { return height; }) + .y1(function (d) { return yScaleBoolean(d.y == null); }) + .curve(d3.curveStep); + + var line = d3.line() + .defined(function(d) { return d.y != null; }) + .x(function (d) { return xScale(d.x); }) + .y(function (d) { return yScale(d.y); }) + .curve(curves[opts.interpolate]); + + var area = d3.area() + .defined(function(d) { return d.y != null; }) + .x(function (d) { return xScale(d.x); }) + .y0(function (d) { return yScale(d.y0); }) + .y1(function (d) { return yScale(d.y0 + d.y); }) + .curve(curves[opts.interpolate]); + + var d3v3LayoutStack = function (data) { + data.reduce(function (res, curr) { + curr.map(function (d, i) { + d.y0 = (res.length ? res[i].y + res[i].y0 : 0); + }); + return curr; + }, []); + }; + + var stack = function () { + var yExtents; + + if (opts.type === "area") { + d3v3LayoutStack(data); + yExtents = (opts.yScale === "log") + ? d3.extent(d3.merge(data), function (d) { return ((d.y0 + d.y) === 0) ? null : d.y0 + d.y; }) + : d3.extent(d3.merge(data), function (d) { return d.y0 + d.y; }); + } else { + yExtents = (opts.yScale === "log") + ? d3.extent(d3.merge(data), function (d) { return (d.y === 0) ? null : d.y; }) + : d3.extent(d3.merge(data), function (d) { return d.y; }); + } + + if (opts.yScale === "log") { + if (yExtents[0] === undefined) { + yExtents = [.0095, .0105]; + } else if (yExtents[0] === yExtents[1]) { + yExtents[0] *= .9; + } + yAxisScale.domain([yExtents[0], yExtents[1]]); + var y0 = yAxisScale.invert(height); + yScale.domain([y0, yExtents[1]]); + } else { + yScale.domain([(yExtents[0] > 0) ? 0 : yExtents[0], yExtents[1]]); + yAxisScale.domain(yScale.domain()); + } + + /** + * Hide overlapping tick labels on logarithmic Y-axis. + * @param {number} d - Tick value. + * @param {object} p - Previous unhidden label. + * @param {number} p.y - Y-position of the tick. + * @param {string} f - Tick label format. + * @returns {string} Tick label format or empty string. + */ + function logFormat(d, p, f) { + // Minimal interval of labeled ticks + const minInterval = 15; + // The nearest power of 10. + const pow10 = Math.pow(10, Math.round(Math.log(d) / Math.LN10)); + + if ( + // Never hide labels of power of 10 tick marks + !(Math.abs(pow10 - d) < 1e-6) && ( + // Hide if the next power of 10 tick mark is too close + (Math.abs(yScale(pow10) - yScale(d)) < minInterval) || + // Hide if previous label is too close + ((p.y - yScale(d)) < minInterval) + ) + ) { + return ""; + } + + p.y = yScale(d); + return f(d); + } + + if (opts.convert === "percentage") { + var prevUnhidLabel = {y: height}; // Previous unhidden tick label + const percentFormat = d3.format(".0%"); + y0Axis.tickFormat(percentFormat); + yAxis.tickFormat((opts.yScale === "log") + ? function (d) { return logFormat(d, prevUnhidLabel, percentFormat) ;} + : percentFormat); + } else { + y0Axis.tickFormat(null); + yAxis.tickFormat(null); + } + + /** + * In some cases when extent values are to close (e.g. 0.00011 and 0.00019 + * on log scale), there are no ticks generated. Possible D3 bug. + * We should set at least labels for extents if there are no any ticks. + */ + yAxis.tickValues(!yAxisScale.ticks().length ? [yExtents[0], yExtents[1]] : null); + + const t = d3.transition() + .duration(opts.duration); + + g.select(".y.grid").transition(t).call(yAxisGrid.scale(yAxisScale)); + g.select(".y.axis").transition(t).call(yAxis.scale(yAxisScale)); + g.select(".y-zero.axis").call(y0Axis); + }; + + var colorScale = d3.scaleOrdinal(d3.schemeCategory10); + + var pathColor = function (i) { + return (opts.legend.entries[i] !== undefined && + opts.legend.entries[i].color !== undefined) ? + opts.legend.entries[i].color : + colorScale(i); + }; + + var pathLabel = function (i) { + return (opts.legend.entries[i] !== undefined && + opts.legend.entries[i].label !== undefined) ? + opts.legend.entries[i].label : + "path_" + i; + }; + + var convert2Percentage = function (a) { + var total = a.reduce(function (res, curr) { + return curr.map(function (d, i) { return d.y + (res[i] ? res[i] : 0); }); + }, []); + + var dataPercentage = $.extend(true, [], a); + + dataPercentage.forEach(function (s) { + s.forEach(function (d, i) { if (total[i]) {d.y /= total[i];} }); + }); + + return dataPercentage; + } + + var yPreprocess = function () { + if (opts.convert === "percentage") { + yAxisLabel.transition().duration(opts.duration).style("opacity", 0); + data = convert2Percentage(srcData); + } else { + yAxisLabel.transition().duration(opts.duration).style("opacity", 1); + data = srcData; +// data = $.extend(true, [], srcData) + } + stack(); + }; + + /** + * Substitute real zeroes with values mapped to zero position on the graph. + */ + const substY0 = function () { + if (opts.yScale === "log") { + const y0 = yScale.invert(height); + data.forEach(function (s) { + s.forEach(function (d, i) { return d.y == 0 ? d.y : y0; }); + }); + } + }; + + var svg = d3.select("#" + id).append("svg") + .classed('d3evolution', true) + .attr("width", opts.width) + .attr("height", opts.height); + + var legend = svg.append("g").attr("class", "legend"); + + var g = svg.append("g") + .attr("width", width) + .attr("height", height) + .attr("transform", "translate(" + opts.margin.left + ", " + opts.margin.top + ")"); + + g.append("g") + .attr("class", "x grid") + .attr("transform", "translate(0," + height + ")") + .call(xAxisGrid); + + g.append("g") + .attr("class", "y grid") + .attr("transform", "translate(0,0)") + .call(yAxisGrid); + + g.append("g") + .attr("class", "x axis") + .attr("transform", "translate(0," + height + ")") + .call(xAxis); + + g.append("g") + .attr("class", "y axis") + .attr("transform", "translate(0,0)") + .call(yAxis); + + // Zero tick mark for log scale + var y0Scale = d3.scaleOrdinal().domain([0]).range([height]); + var y0Axis = d3.axisLeft().scale(y0Scale); + g.append("g") + .attr("class", "y-zero axis") + .call(y0Axis); + + var yAxisLabel = g.append("text") + .attr("class", "y label") + .attr("x", 20 - opts.margin.left) + .attr("y", -20) + .style("opacity", (opts.convert === "percentage") ? 0 : 1) + .text(opts.yAxisLabel); + + var title = svg.append("svg:text") + .attr("x", (opts.width / 2)) + .attr("y", (opts.margin.top / 3)) + .attr("text-anchor", "middle"); + + title.append("tspan") + .attr("class", "chart-title") + .text(opts.title + " "); + + title.timeRange = title.append("tspan"); + + this.data = function (a) { + srcData = $.extend(true, [], a); + + var opacity = []; + + legendX = opts.width - opts.margin.right - opts.legend.space * srcData.length; + + // Convert time in seconds to milliseconds + srcData.forEach(function (s) { + s.forEach(function (d) { d.x *= 1000; }); + }); + + var xExtents = d3.extent(d3.merge(srcData), function (d) { return d.x; }); + xScale.domain([xExtents[0], xExtents[1]]); + + const iso = function (a) { + return d3.timeFormat("%Y-%m-%d %H:%M:%S")(new Date(a)); + }; + title.timeRange + .text("[ " + iso(xExtents[0]) + " / " + iso(xExtents[1]) + " ]"); + + var pathNull = g.selectAll("path.path-null").data(srcData); + + pathNull.enter() + .append("path") + .attr("class", "path-null"); + + g.selectAll("path.path-null") + .transition().duration(opts.duration / 2) + .style("opacity", 0) + .on("end", function () { + g.selectAll("path.path-null") + .attr("d", areaNull) + .transition().duration(opts.duration / 2) + .style("opacity", 1); + }); + + pathNull.exit() + .remove(); + + yPreprocess(); + substY0(); + + var path = g.selectAll("path.path").data(data); + + path.enter() + .append("path") + .merge(path) + .attr("class", "path") + .attr("id", function (d, i) { return "path_" + i; }) + .on("click", function (d, i) { onClick(i); }) + .on("mouseover", function (d, i) { highlight(i); }) + .on("mouseout", function (d, i) { highlight(i, false); }); + + path.exit() + .remove(); + + path = g.selectAll("path.path"); + + if (opts.type === "area") { + path + .style("fill", function (d, i) { return pathColor(i); }) + .style("stroke", "none") + .style("fill-opacity", function (d, i) { return opacity[i]; }); + } else { + path + .style("fill", "none") + .style("stroke", function (d, i) { return pathColor(i); }) + .style("opacity", function (d, i) { return opacity[i]; }); + } + + path + .transition().duration(opts.duration) + .attr("d", (opts.type === "area") ? area : line); + + const t = d3.transition() + .duration(opts.duration); + + g.select(".x.grid").transition(t).call(xAxisGrid.scale(xScale)); + g.select(".x.axis").transition(t).call(xAxis.scale(xScale)); + + var onClick = function (i) { + opacity[i] = (opacity[i] != 0) ? 0 : 1; + + d3.select("#circle_" + i) + .transition().duration(opts.duration) + .style("fill-opacity", opacity[i] + 0.2); + + d3.select("#path_" + i) + .transition().duration(opts.duration) + .style("opacity", opacity[i]); + }; + + /** + * Highlight selected path and legend circle. + * @param {number} s - Selected path index. + * @param {boolean} [h] - If false, restore previous state. + */ + const highlight = function (s, h) { + d3.select("#circle_" + s) + .attr("r", opts.legend.buttonRadius * (h === false ? 1 : 1.3)); + + const op = function (i) { + if (h === false) + return opacity[i]; + return (i === s) ? 1 : (opacity[i] == 0) ? 0 : 0.4; + }; + + g.selectAll("path.path") + .style("opacity", function (d, i) { return op(i); }) + .style("fill-opacity", function (d, i) { return op(i); }); + }; + + var buttons = legend.selectAll("circle").data(data); + + buttons.enter().append("circle") + .attr("id", function (d, i) { return "circle_" + i; }) + .attr("cy", opts.margin.top * 2 / 3) + .attr("r", opts.legend.buttonRadius) + .style("fill", function (d, i) { return pathColor(i); }) + .style("stroke", function (d, i) { return pathColor(i); }) + .style("fill-opacity", function (d, i) { return opacity[i] + 0.2; }) + .on("click", function (d, i) { onClick(i); }) + .on("mouseover", function (d, i) { highlight(i); }) + .on("mouseout", function (d, i) { highlight(i, false); }); + + buttons.exit() + .remove(); + + legend.selectAll("circle") + .transition().duration(opts.duration) + .attr("cx", function (d, i) { return legendX + opts.legend.space * i; }); + + var labels = legend.selectAll("text").data(data); + + labels.enter() + .append("text") + .attr("y", opts.margin.top * 2 / 3) + .attr("dy", "0.3em") + .text(function (d, i) { return pathLabel(i); }) + .on("click", function (d, i) { onClick(i); }) + .on("mouseover", function (d, i) { highlight(i); }) + .on("mouseout", function (d, i) { highlight(i, false); }); + + labels.exit() + .remove(); + + legend.selectAll("text") + .transition().duration(opts.duration) + .attr("x", function (d, i) { + return legendX + opts.legend.space * i + 2 * opts.legend.buttonRadius; + }); + + return this; + }; + + this.legend = function (a) { + $.extend(true, opts.legend, a); + + legend.selectAll("circle") + .transition().duration(opts.duration) + .attr("cx", function (d, i) { return legendX + opts.legend.space * i; }) + .attr("r", opts.legend.buttonRadius) + .style("fill", function (d, i) { return pathColor(i); }) + .style("stroke", function (d, i) { return pathColor(i); }); + + legend.selectAll("text") + .text(function (d, i) { return pathLabel(i); }) + .transition().duration(opts.duration) + .attr("x", function (d, i) { + return legendX + opts.legend.space * i + 2 * opts.legend.buttonRadius; + }); + + g.selectAll("path.path") + .transition().duration(opts.duration) + .style("fill", (opts.type === "area") ? function (d, i) { return pathColor(i); } : "none") + .style("stroke", (opts.type !== "area") ? function (d, i) { return pathColor(i); } : "none"); + + return this; + }; + + this.convert = function (a) { + opts.convert = a; + + yPreprocess(); + + g.selectAll("path.path") + .data(data) + .transition().duration(opts.duration) + .attr("d", (opts.type === "area") ? area : line); + + return this; + }; + + this.interpolate = function (a) { + opts.interpolate = a; + + area.curve(curves[opts.interpolate]); + line.curve(curves[opts.interpolate]); + + g.selectAll("path.path") + .attr("d", (opts.type === "area") ? area : line); + + return this; + }; + + this.type = function (a) { + opts.type = a; + + stack(); + + g.selectAll("path.path") + .style("stroke", (opts.type !== "area") ? function (d, i) { return pathColor(i); } : "none") + .style("fill", (opts.type === "area") ? function (d, i) { return pathColor(i); } : "none") + .transition().duration(opts.duration) + .attr("d", (opts.type === "area") ? area : line); + + return this; + }; + + this.yAxisLabel = function (a) { + opts.yAxisLabel = a; + yAxisLabel + .transition().duration(opts.duration / 2) + .style("opacity", 0) + .on("end", function () { + yAxisLabel + .text(opts.yAxisLabel) + .transition().duration(opts.duration / 2) + .style("opacity", 1); + }); + + return this; + }; + + this.yScale = function (a) { + opts.yScale = a; + + setYScale(); + substY0(); + stack(); + + g.selectAll("path.path") + .transition().duration(opts.duration) + .attr("d", (opts.type === "area") ? area : line); + + return this; + }; + + this.destroy = function () { + d3.select("svg").remove(); + }; +} diff --git a/debian/missing-sources/d3pie.js b/debian/missing-sources/d3pie.js new file mode 100644 index 0000000..b171df3 --- /dev/null +++ b/debian/missing-sources/d3pie.js @@ -0,0 +1,2178 @@ +/*! + * d3pie + * @author Ben Keen + * @version 0.2.1.20170923 (patched by Alexander Moisseev) + * @date 2017-09-23 + * @repo https://github.com/moisseev/d3pie/tree/patched + * @license MIT + */ + +// UMD pattern from https://github.com/umdjs/umd/blob/master/returnExports.js +(function(root, factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module + define([], factory); + } else if (typeof exports === 'object') { + // Node. Does not work with strict CommonJS, but only CommonJS-like environments that support module.exports, + // like Node + module.exports = factory(); + } else { + // browser globals (root is window) + root.d3pie = factory(root); + } +}(this, function() { + + var _scriptName = "d3pie"; + var _version = "0.2.1"; + + // used to uniquely generate IDs and classes, ensuring no conflict between multiple pies on the same page + var _uniqueIDCounter = 0; + + + // this section includes all helper libs on the d3pie object. They're populated via grunt-template. Note: to keep + // the syntax highlighting from getting all messed up, I commented out each line. That REQUIRES each of the files + // to have an empty first line. Crumby, yes, but acceptable. + //// --------- _default-settings.js -----------/** +/** + * Contains the out-the-box settings for the script. Any of these settings that aren't explicitly overridden for the + * d3pie instance will inherit from these. This is also included on the main website for use in the generation script. + */ +var defaultSettings = { + header: { + title: { + text: "", + color: "#333333", + fontSize: 18, + font: "arial" + }, + subtitle: { + text: "", + color: "#666666", + fontSize: 14, + font: "arial" + }, + location: "top-center", + titleSubtitlePadding: 8 + }, + footer: { + text: "", + color: "#666666", + fontSize: 14, + font: "arial", + location: "left" + }, + size: { + canvasHeight: 500, + canvasWidth: 500, + pieInnerRadius: "0%", + pieOuterRadius: null + }, + data: { + sortOrder: "none", + ignoreSmallSegments: { + enabled: false, + valueType: "percentage", + value: null + }, + smallSegmentGrouping: { + enabled: false, + value: 1, + valueType: "percentage", + label: "Other", + color: "#cccccc" + }, + content: [] + }, + labels: { + outer: { + format: "label", + hideWhenLessThanPercentage: null, + pieDistance: 30 + }, + inner: { + format: "percentage", + hideWhenLessThanPercentage: null + }, + mainLabel: { + color: "#333333", + font: "arial", + fontSize: 10 + }, + percentage: { + color: "#dddddd", + font: "arial", + fontSize: 10, + decimalPlaces: 0 + }, + value: { + color: "#cccc44", + font: "arial", + fontSize: 10 + }, + lines: { + enabled: true, + style: "curved", + color: "segment" + }, + truncation: { + enabled: false, + truncateLength: 30 + }, + formatter: null + }, + effects: { + load: { + effect: "default", + speed: 1000 + }, + pullOutSegmentOnClick: { + effect: "bounce", + speed: 300, + size: 10 + }, + highlightSegmentOnMouseover: true, + highlightLuminosity: -0.2 + }, + tooltips: { + enabled: false, + type: "placeholder", // caption|placeholder + string: "", + placeholderParser: null, + styles: { + fadeInSpeed: 250, + backgroundColor: "#000000", + backgroundOpacity: 0.5, + color: "#efefef", + borderRadius: 2, + font: "arial", + fontSize: 10, + padding: 4 + } + }, + misc: { + colors: { + background: null, + segments: [ + "#2484c1", "#65a620", "#7b6888", "#a05d56", "#961a1a", "#d8d23a", "#e98125", "#d0743c", "#635222", "#6ada6a", + "#0c6197", "#7d9058", "#207f33", "#44b9b0", "#bca44a", "#e4a14b", "#a3acb2", "#8cc3e9", "#69a6f9", "#5b388f", + "#546e91", "#8bde95", "#d2ab58", "#273c71", "#98bf6e", "#4daa4b", "#98abc5", "#cc1010", "#31383b", "#006391", + "#c2643f", "#b0a474", "#a5a39c", "#a9c2bc", "#22af8c", "#7fcecf", "#987ac6", "#3d3b87", "#b77b1c", "#c9c2b6", + "#807ece", "#8db27c", "#be66a2", "#9ed3c6", "#00644b", "#005064", "#77979f", "#77e079", "#9c73ab", "#1f79a7" + ], + segmentStroke: "#ffffff" + }, + gradient: { + enabled: false, + percentage: 95, + color: "#000000" + }, + canvasPadding: { + top: 5, + right: 5, + bottom: 5, + left: 5 + }, + pieCenterOffset: { + x: 0, + y: 0 + }, + cssPrefix: null + }, + callbacks: { + onload: null, + onMouseoverSegment: null, + onMouseoutSegment: null, + onClickSegment: null + } +}; + + //// --------- validate.js ----------- +var validate = { + + // called whenever a new pie chart is created + initialCheck: function(pie) { + var cssPrefix = pie.cssPrefix; + var element = pie.element; + var options = pie.options; + + // confirm d3 is available [check minimum version] + if (!window.d3 || !window.d3.hasOwnProperty("version")) { + console.error("d3pie error: d3 is not available"); + return false; + } + + // confirm element is either a DOM element or a valid string for a DOM element + if (!(element instanceof HTMLElement || element instanceof SVGElement)) { + console.error("d3pie error: the first d3pie() param must be a valid DOM element (not jQuery) or a ID string."); + return false; + } + + // confirm the CSS prefix is valid. It has to start with a-Z and contain nothing but a-Z0-9_- + if (!(/[a-zA-Z][a-zA-Z0-9_-]*$/.test(cssPrefix))) { + console.error("d3pie error: invalid options.misc.cssPrefix"); + return false; + } + + // confirm some data has been supplied + if (!helpers.isArray(options.data.content)) { + console.error("d3pie error: invalid config structure: missing data.content property."); + return false; + } + if (options.data.content.length === 0) { + console.error("d3pie error: no data supplied."); + return false; + } + + // clear out any invalid data. Each data row needs a valid positive number and a label + var data = []; + for (var i=0; i giveupIterationCount) { + clearInterval(interval); + } + inc++; + }, 1); + }, + + whenElementsExist: function(els, callback) { + var inc = 1; + var giveupIterationCount = 1000; + + var interval = setInterval(function() { + var allExist = true; + for (var i=0; i giveupIterationCount) { + clearInterval(interval); + } + inc++; + }, 1); + }, + + shuffleArray: function(array) { + var currentIndex = array.length, tmpVal, randomIndex; + + while (0 !== currentIndex) { + randomIndex = Math.floor(Math.random() * currentIndex); + currentIndex -= 1; + + // and swap it with the current element + tmpVal = array[currentIndex]; + array[currentIndex] = array[randomIndex]; + array[randomIndex] = tmpVal; + } + return array; + }, + + processObj: function(obj, is, value) { + if (typeof is === 'string') { + return helpers.processObj(obj, is.split('.'), value); + } else if (is.length === 1 && value !== undefined) { + obj[is[0]] = value; + return obj[is[0]]; + } else if (is.length === 0) { + return obj; + } else { + return helpers.processObj(obj[is[0]], is.slice(1), value); + } + }, + + getDimensions: function(id) { + var el = document.getElementById(id); + var w = 0, h = 0; + if (el) { + var dimensions = el.getBBox(); + w = dimensions.width; + h = dimensions.height; + } else { + console.log("error: getDimensions() " + id + " not found."); + } + return { w: w, h: h }; + }, + + /** + * This is based on the SVG coordinate system, where top-left is 0,0 and bottom right is n-n. + * @param r1 + * @param r2 + * @returns {boolean} + */ + rectIntersect: function(r1, r2) { + var returnVal = ( + // r2.left > r1.right + (r2.x > (r1.x + r1.w)) || + + // r2.right < r1.left + ((r2.x + r2.w) < r1.x) || + + // r2.top < r1.bottom + ((r2.y + r2.h) < r1.y) || + + // r2.bottom > r1.top + (r2.y > (r1.y + r1.h)) + ); + + return !returnVal; + }, + + /** + * Returns a lighter/darker shade of a hex value, based on a luminance value passed. + * @param hex a hex color value such as “#abc” or “#123456″ (the hash is optional) + * @param lum the luminosity factor: -0.1 is 10% darker, 0.2 is 20% lighter, etc. + * @returns {string} + */ + getColorShade: function(hex, lum) { + + // validate hex string + hex = String(hex).replace(/[^0-9a-f]/gi, ''); + if (hex.length < 6) { + hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2]; + } + lum = lum || 0; + + // convert to decimal and change luminosity + var newHex = "#"; + for (var i=0; i<3; i++) { + var c = parseInt(hex.substr(i * 2, 2), 16); + c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16); + newHex += ("00" + c).substr(c.length); + } + + return newHex; + }, + + /** + * Users can choose to specify segment colors in three ways (in order of precedence): + * 1. include a "color" attribute for each row in data.content + * 2. include a misc.colors.segments property which contains an array of hex codes + * 3. specify nothing at all and rely on this lib provide some reasonable defaults + * + * This function sees what's included and populates this.options.colors with whatever's required + * for this pie chart. + * @param data + */ + initSegmentColors: function(pie) { + var data = pie.options.data.content; + var colors = pie.options.misc.colors.segments; + + // TODO this needs a ton of error handling + + var finalColors = []; + for (var i=0; i 99) ? 99 : percent; + percent = (percent < 0) ? 0 : percent; + + var smallestDimension = (w < h) ? w : h; + + // now factor in the label line size + if (pie.options.labels.outer.format !== "none") { + var pieDistanceSpace = parseInt(pie.options.labels.outer.pieDistance, 10) * 2; + if (smallestDimension - pieDistanceSpace > 0) { + smallestDimension -= pieDistanceSpace; + } + } + + outerRadius = Math.floor((smallestDimension / 100) * percent) / 2; + } else { + outerRadius = parseInt(size.pieOuterRadius, 10); + } + } + + // inner radius + if (/%/.test(size.pieInnerRadius)) { + percent = parseInt(size.pieInnerRadius.replace(/[\D]/, ""), 10); + percent = (percent > 99) ? 99 : percent; + percent = (percent < 0) ? 0 : percent; + innerRadius = Math.floor((outerRadius / 100) * percent); + } else { + innerRadius = parseInt(size.pieInnerRadius, 10); + } + + pie.innerRadius = innerRadius; + pie.outerRadius = outerRadius; + }, + + getTotalPieSize: function(data) { + var totalSize = 0; + for (var i=0; i b.label.toLowerCase()) ? 1 : -1; }); + break; + case "label-desc": + data.sort(function(a, b) { return (a.label.toLowerCase() < b.label.toLowerCase()) ? 1 : -1; }); + break; + } + + return data; + }, + + // var pieCenter = math.getPieCenter(); + getPieTranslateCenter: function(pieCenter) { + return "translate(" + pieCenter.x + "," + pieCenter.y + ")"; + }, + + /** + * Used to determine where on the canvas the center of the pie chart should be. It takes into account the + * height and position of the title, subtitle and footer, and the various paddings. + * @private + */ + calculatePieCenter: function(pie) { + var pieCenterOffset = pie.options.misc.pieCenterOffset; + var hasTopTitle = (pie.textComponents.title.exists && pie.options.header.location !== "pie-center"); + var hasTopSubtitle = (pie.textComponents.subtitle.exists && pie.options.header.location !== "pie-center"); + + var headerOffset = pie.options.misc.canvasPadding.top; + if (hasTopTitle && hasTopSubtitle) { + headerOffset += pie.textComponents.title.h + pie.options.header.titleSubtitlePadding + pie.textComponents.subtitle.h; + } else if (hasTopTitle) { + headerOffset += pie.textComponents.title.h; + } else if (hasTopSubtitle) { + headerOffset += pie.textComponents.subtitle.h; + } + + var footerOffset = 0; + if (pie.textComponents.footer.exists) { + footerOffset = pie.textComponents.footer.h + pie.options.misc.canvasPadding.bottom; + } + + var x = ((pie.options.size.canvasWidth - pie.options.misc.canvasPadding.left - pie.options.misc.canvasPadding.right) / 2) + pie.options.misc.canvasPadding.left; + var y = ((pie.options.size.canvasHeight - footerOffset - headerOffset) / 2) + headerOffset; + + x += pieCenterOffset.x; + y += pieCenterOffset.y; + + pie.pieCenter = { x: x, y: y }; + }, + + + /** + * Rotates a point (x, y) around an axis (xm, ym) by degrees (a). + * @param x + * @param y + * @param xm + * @param ym + * @param a angle in degrees + * @returns {Array} + */ + rotate: function(x, y, xm, ym, a) { + + a = a * Math.PI / 180; // convert to radians + + var cos = Math.cos, + sin = Math.sin, + // subtract midpoints, so that midpoint is translated to origin and add it in the end again + xr = (x - xm) * cos(a) - (y - ym) * sin(a) + xm, + yr = (x - xm) * sin(a) + (y - ym) * cos(a) + ym; + + return { x: xr, y: yr }; + }, + + /** + * Translates a point x, y by distance d, and by angle a. + * @param x + * @param y + * @param dist + * @param a angle in degrees + */ + translate: function(x, y, d, a) { + var rads = math.toRadians(a); + return { + x: x + d * Math.sin(rads), + y: y - d * Math.cos(rads) + }; + }, + + // from: http://stackoverflow.com/questions/19792552/d3-put-arc-labels-in-a-pie-chart-if-there-is-enough-space + pointIsInArc: function(pt, ptData, d3Arc) { + // Center of the arc is assumed to be 0,0 + // (pt.x, pt.y) are assumed to be relative to the center + var r1 = d3Arc.innerRadius()(ptData), // Note: Using the innerRadius + r2 = d3Arc.outerRadius()(ptData), + theta1 = d3Arc.startAngle()(ptData), + theta2 = d3Arc.endAngle()(ptData); + + var dist = pt.x * pt.x + pt.y * pt.y, + angle = Math.atan2(pt.x, -pt.y); // Note: different coordinate system + + angle = (angle < 0) ? (angle + Math.PI * 2) : angle; + + return (r1 * r1 <= dist) && (dist <= r2 * r2) && + (theta1 <= angle) && (angle <= theta2); + } +}; + + //// --------- labels.js ----------- +var labels = { + + /** + * Adds the labels to the pie chart, but doesn't position them. There are two locations for the + * labels: inside (center) of the segments, or outside the segments on the edge. + * @param section "inner" or "outer" + * @param sectionDisplayType "percentage", "value", "label", "label-value1", etc. + * @param pie + */ + add: function(pie, section, sectionDisplayType) { + var include = labels.getIncludes(sectionDisplayType); + var settings = pie.options.labels; + + // group the label groups (label, percentage, value) into a single element for simpler positioning + var outerLabel = pie.svg.insert("g", "." + pie.cssPrefix + "labels-" + section) + .attr("class", pie.cssPrefix + "labels-" + section); + + var labelGroup = outerLabel.selectAll("." + pie.cssPrefix + "labelGroup-" + section) + .data(pie.options.data.content) + .enter() + .append("g") + .attr("id", function(d, i) { return pie.cssPrefix + "labelGroup" + i + "-" + section; }) + .attr("data-index", function(d, i) { return i; }) + .attr("class", pie.cssPrefix + "labelGroup-" + section) + .style("opacity", 0); + + var formatterContext = { section: section, sectionDisplayType: sectionDisplayType }; + + // 1. Add the main label + if (include.mainLabel) { + labelGroup.append("text") + .attr("id", function(d, i) { return pie.cssPrefix + "segmentMainLabel" + i + "-" + section; }) + .attr("class", pie.cssPrefix + "segmentMainLabel-" + section) + .text(function(d, i) { + var str = d.label; + + // if a custom formatter has been defined, pass it the raw label string - it can do whatever it wants with it. + // we only apply truncation if it's not defined + if (settings.formatter) { + formatterContext.index = i; + formatterContext.part = 'mainLabel'; + formatterContext.value = d.value; + formatterContext.label = str; + str = settings.formatter(formatterContext); + } else if (settings.truncation.enabled && d.label.length > settings.truncation.truncateLength) { + str = d.label.substring(0, settings.truncation.truncateLength) + "..."; + } + return str; + }) + .style("font-size", settings.mainLabel.fontSize + "px") + .style("font-family", settings.mainLabel.font) + .style("fill", settings.mainLabel.color); + } + + // 2. Add the percentage label + if (include.percentage) { + labelGroup.append("text") + .attr("id", function(d, i) { return pie.cssPrefix + "segmentPercentage" + i + "-" + section; }) + .attr("class", pie.cssPrefix + "segmentPercentage-" + section) + .text(function(d, i) { + var percentage = d.percentage; + if (settings.formatter) { + formatterContext.index = i; + formatterContext.part = "percentage"; + formatterContext.value = d.value; + formatterContext.label = d.percentage; + percentage = settings.formatter(formatterContext); + } else { + percentage += "%"; + } + return percentage; + }) + .style("font-size", settings.percentage.fontSize + "px") + .style("font-family", settings.percentage.font) + .style("fill", settings.percentage.color); + } + + // 3. Add the value label + if (include.value) { + labelGroup.append("text") + .attr("id", function(d, i) { return pie.cssPrefix + "segmentValue" + i + "-" + section; }) + .attr("class", pie.cssPrefix + "segmentValue-" + section) + .text(function(d, i) { + formatterContext.index = i; + formatterContext.part = "value"; + formatterContext.value = d.value; + formatterContext.label = d.value; + return settings.formatter ? settings.formatter(formatterContext, d.value) : d.value; + }) + .style("font-size", settings.value.fontSize + "px") + .style("font-family", settings.value.font) + .style("fill", settings.value.color); + } + }, + + /** + * @param section "inner" / "outer" + */ + positionLabelElements: function(pie, section, sectionDisplayType) { + labels["dimensions-" + section] = []; + + // get the latest widths, heights + var labelGroups = d3.selectAll("." + pie.cssPrefix + "labelGroup-" + section); + labelGroups.each(function(d, i) { + var mainLabel = d3.select(this).selectAll("." + pie.cssPrefix + "segmentMainLabel-" + section); + var percentage = d3.select(this).selectAll("." + pie.cssPrefix + "segmentPercentage-" + section); + var value = d3.select(this).selectAll("." + pie.cssPrefix + "segmentValue-" + section); + + labels["dimensions-" + section].push({ + mainLabel: (mainLabel.node() !== null) ? mainLabel.node().getBBox() : null, + percentage: (percentage.node() !== null) ? percentage.node().getBBox() : null, + value: (value.node() !== null) ? value.node().getBBox() : null + }); + }); + + var singleLinePad = 5; + var dims = labels["dimensions-" + section]; + switch (sectionDisplayType) { + case "label-value1": + d3.selectAll("." + pie.cssPrefix + "segmentValue-" + section) + .attr("dx", function(d, i) { return dims[i].mainLabel.width + singleLinePad; }); + break; + case "label-value2": + d3.selectAll("." + pie.cssPrefix + "segmentValue-" + section) + .attr("dy", function(d, i) { return dims[i].mainLabel.height; }); + break; + case "label-percentage1": + d3.selectAll("." + pie.cssPrefix + "segmentPercentage-" + section) + .attr("dx", function(d, i) { return dims[i].mainLabel.width + singleLinePad; }); + break; + case "label-percentage2": + d3.selectAll("." + pie.cssPrefix + "segmentPercentage-" + section) + .attr("dx", function(d, i) { return (dims[i].mainLabel.width / 2) - (dims[i].percentage.width / 2); }) + .attr("dy", function(d, i) { return dims[i].mainLabel.height; }); + break; + } + }, + + computeLabelLinePositions: function(pie) { + pie.lineCoordGroups = []; + d3.selectAll("." + pie.cssPrefix + "labelGroup-outer") + .each(function(d, i) { return labels.computeLinePosition(pie, i); }); + }, + + computeLinePosition: function(pie, i) { + var angle = segments.getSegmentAngle(i, pie.options.data.content, pie.totalSize, { midpoint: true }); + var originCoords = math.rotate(pie.pieCenter.x, pie.pieCenter.y - pie.outerRadius, pie.pieCenter.x, pie.pieCenter.y, angle); + var heightOffset = pie.outerLabelGroupData[i].h / 5; // TODO check + var labelXMargin = 6; // the x-distance of the label from the end of the line [TODO configurable] + + var quarter = Math.floor(angle / 90); + var midPoint = 4; + var x2, y2, x3, y3; + + // this resolves an issue when the + if (quarter === 2 && angle === 180) { + quarter = 1; + } + + switch (quarter) { + case 0: + x2 = pie.outerLabelGroupData[i].x - labelXMargin - ((pie.outerLabelGroupData[i].x - labelXMargin - originCoords.x) / 2); + y2 = pie.outerLabelGroupData[i].y + ((originCoords.y - pie.outerLabelGroupData[i].y) / midPoint); + x3 = pie.outerLabelGroupData[i].x - labelXMargin; + y3 = pie.outerLabelGroupData[i].y - heightOffset; + break; + case 1: + x2 = originCoords.x + (pie.outerLabelGroupData[i].x - originCoords.x) / midPoint; + y2 = originCoords.y + (pie.outerLabelGroupData[i].y - originCoords.y) / midPoint; + x3 = pie.outerLabelGroupData[i].x - labelXMargin; + y3 = pie.outerLabelGroupData[i].y - heightOffset; + break; + case 2: + var startOfLabelX = pie.outerLabelGroupData[i].x + pie.outerLabelGroupData[i].w + labelXMargin; + x2 = originCoords.x - (originCoords.x - startOfLabelX) / midPoint; + y2 = originCoords.y + (pie.outerLabelGroupData[i].y - originCoords.y) / midPoint; + x3 = pie.outerLabelGroupData[i].x + pie.outerLabelGroupData[i].w + labelXMargin; + y3 = pie.outerLabelGroupData[i].y - heightOffset; + break; + case 3: + var startOfLabel = pie.outerLabelGroupData[i].x + pie.outerLabelGroupData[i].w + labelXMargin; + x2 = startOfLabel + ((originCoords.x - startOfLabel) / midPoint); + y2 = pie.outerLabelGroupData[i].y + (originCoords.y - pie.outerLabelGroupData[i].y) / midPoint; + x3 = pie.outerLabelGroupData[i].x + pie.outerLabelGroupData[i].w + labelXMargin; + y3 = pie.outerLabelGroupData[i].y - heightOffset; + break; + } + + /* + * x1 / y1: the x/y coords of the start of the line, at the mid point of the segments arc on the pie circumference + * x2 / y2: if "curved" line style is being used, this is the midpoint of the line. Other + * x3 / y3: the end of the line; closest point to the label + */ + if (pie.options.labels.lines.style === "straight") { + pie.lineCoordGroups[i] = [ + { x: originCoords.x, y: originCoords.y }, + { x: x3, y: y3 } + ]; + } else { + pie.lineCoordGroups[i] = [ + { x: originCoords.x, y: originCoords.y }, + { x: x2, y: y2 }, + { x: x3, y: y3 } + ]; + } + }, + + addLabelLines: function(pie) { + var lineGroups = pie.svg.insert("g", "." + pie.cssPrefix + "pieChart") // meaning, BEFORE .pieChart + .attr("class", pie.cssPrefix + "lineGroups") + .style("opacity", 0); + + var lineGroup = lineGroups.selectAll("." + pie.cssPrefix + "lineGroup") + .data(pie.lineCoordGroups) + .enter() + .append("g") + .attr("class", pie.cssPrefix + "lineGroup"); + + var lineFunction = d3.line() + .curve(d3.curveBasis) + .x(function(d) { return d.x; }) + .y(function(d) { return d.y; }); + + lineGroup.append("path") + .attr("d", lineFunction) + .attr("stroke", function(d, i) { + return (pie.options.labels.lines.color === "segment") ? pie.options.colors[i] : pie.options.labels.lines.color; + }) + .attr("stroke-width", 1) + .attr("fill", "none") + .style("opacity", function(d, i) { + var percentage = pie.options.labels.outer.hideWhenLessThanPercentage; + var isHidden = (percentage !== null && pie.options.data.content[i].percentage < percentage) || pie.options.data.content[i].label === ""; + return isHidden ? 0 : 1; + }); + }, + + positionLabelGroups: function(pie, section) { + if (pie.options.labels[section].format === "none") { + return; + } + + d3.selectAll("." + pie.cssPrefix + "labelGroup-" + section) + .style("opacity", 0) + .attr("transform", function(d, i) { + var x, y; + if (section === "outer") { + x = pie.outerLabelGroupData[i].x; + y = pie.outerLabelGroupData[i].y; + } else { + var pieCenterCopy = extend(true, {}, pie.pieCenter); + + // now recompute the "center" based on the current _innerRadius + if (pie.innerRadius > 0) { + var angle = segments.getSegmentAngle(i, pie.options.data.content, pie.totalSize, { midpoint: true }); + var newCoords = math.translate(pie.pieCenter.x, pie.pieCenter.y, pie.innerRadius, angle); + pieCenterCopy.x = newCoords.x; + pieCenterCopy.y = newCoords.y; + } + + var dims = helpers.getDimensions(pie.cssPrefix + "labelGroup" + i + "-inner"); + var xOffset = dims.w / 2; + var yOffset = dims.h / 4; // confusing! Why 4? should be 2, but it doesn't look right + + x = pieCenterCopy.x + (pie.lineCoordGroups[i][0].x - pieCenterCopy.x) / 1.8; + y = pieCenterCopy.y + (pie.lineCoordGroups[i][0].y - pieCenterCopy.y) / 1.8; + + x = x - xOffset; + y = y + yOffset; + } + + return "translate(" + x + "," + y + ")"; + }); + }, + + + fadeInLabelsAndLines: function(pie) { + + // fade in the labels when the load effect is complete - or immediately if there's no load effect + var loadSpeed = (pie.options.effects.load.effect === "default") ? pie.options.effects.load.speed : 1; + setTimeout(function() { + var labelFadeInTime = (pie.options.effects.load.effect === "default") ? 400 : 1; // 400 is hardcoded for the present + + d3.selectAll("." + pie.cssPrefix + "labelGroup-outer") + .transition() + .duration(labelFadeInTime) + .style("opacity", function(d, i) { + var percentage = pie.options.labels.outer.hideWhenLessThanPercentage; + return (percentage !== null && d.percentage < percentage) ? 0 : 1; + }); + + d3.selectAll("." + pie.cssPrefix + "labelGroup-inner") + .transition() + .duration(labelFadeInTime) + .style("opacity", function(d, i) { + var percentage = pie.options.labels.inner.hideWhenLessThanPercentage; + return (percentage !== null && d.percentage < percentage) ? 0 : 1; + }); + + d3.selectAll("g." + pie.cssPrefix + "lineGroups") + .transition() + .duration(labelFadeInTime) + .style("opacity", 1); + + // once everything's done loading, trigger the onload callback if defined + if (helpers.isFunction(pie.options.callbacks.onload)) { + setTimeout(function() { + try { + pie.options.callbacks.onload(); + } catch (e) { } + }, labelFadeInTime); + } + }, loadSpeed); + }, + + getIncludes: function(val) { + var addMainLabel = false; + var addValue = false; + var addPercentage = false; + + switch (val) { + case "label": + addMainLabel = true; + break; + case "value": + addValue = true; + break; + case "percentage": + addPercentage = true; + break; + case "label-value1": + case "label-value2": + addMainLabel = true; + addValue = true; + break; + case "label-percentage1": + case "label-percentage2": + addMainLabel = true; + addPercentage = true; + break; + } + return { + mainLabel: addMainLabel, + value: addValue, + percentage: addPercentage + }; + }, + + + /** + * This does the heavy-lifting to compute the actual coordinates for the outer label groups. It does two things: + * 1. Make a first pass and position them in the ideal positions, based on the pie sizes + * 2. Do some basic collision avoidance. + */ + computeOuterLabelCoords: function(pie) { + + // 1. figure out the ideal positions for the outer labels + pie.svg.selectAll("." + pie.cssPrefix + "labelGroup-outer") + .each(function(d, i) { + return labels.getIdealOuterLabelPositions(pie, i); + }); + + // 2. now adjust those positions to try to accommodate conflicts + labels.resolveOuterLabelCollisions(pie); + }, + + /** + * This attempts to resolve label positioning collisions. + */ + resolveOuterLabelCollisions: function(pie) { + if (pie.options.labels.outer.format === "none") { + return; + } + + var size = pie.options.data.content.length; + labels.checkConflict(pie, 0, "clockwise", size); + labels.checkConflict(pie, size-1, "anticlockwise", size); + }, + + checkConflict: function(pie, currIndex, direction, size) { + var i, curr; + + if (size <= 1) { + return; + } + + var currIndexHemisphere = pie.outerLabelGroupData[currIndex].hs; + if (direction === "clockwise" && currIndexHemisphere !== "right") { + return; + } + if (direction === "anticlockwise" && currIndexHemisphere !== "left") { + return; + } + var nextIndex = (direction === "clockwise") ? currIndex+1 : currIndex-1; + + // this is the current label group being looked at. We KNOW it's positioned properly (the first item + // is always correct) + var currLabelGroup = pie.outerLabelGroupData[currIndex]; + + // this one we don't know about. That's the one we're going to look at and move if necessary + var examinedLabelGroup = pie.outerLabelGroupData[nextIndex]; + + var info = { + labelHeights: pie.outerLabelGroupData[0].h, + center: pie.pieCenter, + lineLength: (pie.outerRadius + pie.options.labels.outer.pieDistance), + heightChange: pie.outerLabelGroupData[0].h + 1 // 1 = padding + }; + + // loop through *ALL* label groups examined so far to check for conflicts. This is because when they're + // very tightly fitted, a later label group may still appear high up on the page + if (direction === "clockwise") { + i = 0; + for (; i<=currIndex; i++) { + curr = pie.outerLabelGroupData[i]; + + // if there's a conflict with this label group, shift the label to be AFTER the last known + // one that's been properly placed + if (!labels.isLabelHidden(pie, i) && helpers.rectIntersect(curr, examinedLabelGroup)) { + labels.adjustLabelPos(pie, nextIndex, currLabelGroup, info); + break; + } + } + } else { + i = size - 1; + for (; i >= currIndex; i--) { + curr = pie.outerLabelGroupData[i]; + + // if there's a conflict with this label group, shift the label to be AFTER the last known + // one that's been properly placed + if (!labels.isLabelHidden(pie, i) && helpers.rectIntersect(curr, examinedLabelGroup)) { + labels.adjustLabelPos(pie, nextIndex, currLabelGroup, info); + break; + } + } + } + labels.checkConflict(pie, nextIndex, direction, size); + }, + + isLabelHidden: function(pie, index) { + var percentage = pie.options.labels.outer.hideWhenLessThanPercentage; + return (percentage !== null && pie.options.data.content[index].percentage < percentage) || pie.options.data.content[index].label === ""; + }, + + // does a little math to shift a label into a new position based on the last properly placed one + adjustLabelPos: function(pie, nextIndex, lastCorrectlyPositionedLabel, info) { + var xDiff, yDiff, newXPos, newYPos; + newYPos = lastCorrectlyPositionedLabel.y + info.heightChange; + yDiff = info.center.y - newYPos; + + if (Math.abs(info.lineLength) > Math.abs(yDiff)) { + xDiff = Math.sqrt((info.lineLength * info.lineLength) - (yDiff * yDiff)); + } else { + xDiff = Math.sqrt((yDiff * yDiff) - (info.lineLength * info.lineLength)); + } + + if (lastCorrectlyPositionedLabel.hs === "right") { + newXPos = info.center.x + xDiff; + } else { + newXPos = info.center.x - xDiff - pie.outerLabelGroupData[nextIndex].w; + } + + pie.outerLabelGroupData[nextIndex].x = newXPos; + pie.outerLabelGroupData[nextIndex].y = newYPos; + }, + + /** + * @param i 0-N where N is the dataset size - 1. + */ + getIdealOuterLabelPositions: function(pie, i) { + var labelGroupNode = d3.select("#" + pie.cssPrefix + "labelGroup" + i + "-outer").node(); + if (!labelGroupNode) { + return; + } + var labelGroupDims = labelGroupNode.getBBox(); + var angle = segments.getSegmentAngle(i, pie.options.data.content, pie.totalSize, { midpoint: true }); + + var originalX = pie.pieCenter.x; + var originalY = pie.pieCenter.y - (pie.outerRadius + pie.options.labels.outer.pieDistance); + var newCoords = math.rotate(originalX, originalY, pie.pieCenter.x, pie.pieCenter.y, angle); + + // if the label is on the left half of the pie, adjust the values + var hemisphere = "right"; // hemisphere + if (angle > 180) { + newCoords.x -= (labelGroupDims.width + 8); + hemisphere = "left"; + } else { + newCoords.x += 8; + } + + pie.outerLabelGroupData[i] = { + x: newCoords.x, + y: newCoords.y, + w: labelGroupDims.width, + h: labelGroupDims.height, + hs: hemisphere + }; + } +}; + + //// --------- segments.js ----------- +var segments = { + + effectMap: { + "none": d3.easeLinear, + "bounce": d3.easeBounce, + "linear": d3.easeLinear, + "sin": d3.easeSin, + "elastic": d3.easeElastic, + "back": d3.easeBack, + "quad": d3.easeQuad, + "circle": d3.easeCircle, + "exp": d3.easeExp + }, + + /** + * Creates the pie chart segments and displays them according to the desired load effect. + * @private + */ + create: function(pie) { + var pieCenter = pie.pieCenter; + var colors = pie.options.colors; + var loadEffects = pie.options.effects.load; + var segmentStroke = pie.options.misc.colors.segmentStroke; + + // we insert the pie chart BEFORE the title, to ensure the title overlaps the pie + var pieChartElement = pie.svg.insert("g", "#" + pie.cssPrefix + "title") + .attr("transform", function() { return math.getPieTranslateCenter(pieCenter); }) + .attr("class", pie.cssPrefix + "pieChart"); + + var arc = d3.arc() + .innerRadius(pie.innerRadius) + .outerRadius(pie.outerRadius) + .startAngle(0) + .endAngle(function(d) { + return (d.value / pie.totalSize) * 2 * Math.PI; + }); + + var g = pieChartElement.selectAll("." + pie.cssPrefix + "arc") + .data(pie.options.data.content) + .enter() + .append("g") + .attr("class", pie.cssPrefix + "arc"); + + // if we're not fading in the pie, just set the load speed to 0 + var loadSpeed = loadEffects.speed; + if (loadEffects.effect === "none") { + loadSpeed = 0; + } + + g.append("path") + .attr("id", function(d, i) { return pie.cssPrefix + "segment" + i; }) + .attr("fill", function(d, i) { + var color = colors[i]; + if (pie.options.misc.gradient.enabled) { + color = "url(#" + pie.cssPrefix + "grad" + i + ")"; + } + return color; + }) + .attr("data-index", function(d, i) { return i; }) + .style("stroke", segmentStroke) + .style("stroke-width", 1) + .transition() + .ease(d3.easeCubicInOut) + .duration(loadSpeed) + .attrTween("d", function(b) { + var i = d3.interpolate({ value: 0 }, b); + return function(t) { + return pie.arc(i(t)); + }; + }); + + pie.svg.selectAll("g." + pie.cssPrefix + "arc") + .attr("transform", + function(d, i) { + var angle = 0; + if (i > 0) { + angle = segments.getSegmentAngle(i-1, pie.options.data.content, pie.totalSize); + } + return "rotate(" + angle + ")"; + } + ); + pie.arc = arc; + }, + + addGradients: function(pie) { + var grads = pie.svg.append("defs") + .selectAll("radialGradient") + .data(pie.options.data.content) + .enter().append("radialGradient") + .attr("gradientUnits", "userSpaceOnUse") + .attr("cx", 0) + .attr("cy", 0) + .attr("r", "120%") + .attr("id", function(d, i) { return pie.cssPrefix + "grad" + i; }); + + grads.append("stop").attr("offset", "0%").style("stop-color", function(d, i) { return pie.options.colors[i]; }); + grads.append("stop").attr("offset", pie.options.misc.gradient.percentage + "%").style("stop-color", pie.options.misc.gradient.color); + }, + + addSegmentEventHandlers: function(pie) { + var arc = d3.selectAll("." + pie.cssPrefix + "arc,." + pie.cssPrefix + "labelGroup-inner,." + pie.cssPrefix + "labelGroup-outer"); + + arc.on("click", function() { + var currentEl = d3.select(this); + var segment; + + // mouseover works on both the segments AND the segment labels, hence the following + if (currentEl.attr("class") === pie.cssPrefix + "arc") { + segment = currentEl.select("path"); + } else { + var index = currentEl.attr("data-index"); + segment = d3.select("#" + pie.cssPrefix + "segment" + index); + } + + var isExpanded = segment.attr("class") === pie.cssPrefix + "expanded"; + segments.onSegmentEvent(pie, pie.options.callbacks.onClickSegment, segment, isExpanded); + if (pie.options.effects.pullOutSegmentOnClick.effect !== "none") { + if (isExpanded) { + segments.closeSegment(pie, segment.node()); + } else { + segments.openSegment(pie, segment.node()); + } + } + }); + + arc.on("mouseover", function() { + var currentEl = d3.select(this); + var segment, index; + + if (currentEl.attr("class") === pie.cssPrefix + "arc") { + segment = currentEl.select("path"); + } else { + index = currentEl.attr("data-index"); + segment = d3.select("#" + pie.cssPrefix + "segment" + index); + } + + if (pie.options.effects.highlightSegmentOnMouseover) { + index = segment.attr("data-index"); + var segColor = pie.options.colors[index]; + segment.style("fill", helpers.getColorShade(segColor, pie.options.effects.highlightLuminosity)); + } + + if (pie.options.tooltips.enabled) { + index = segment.attr("data-index"); + tt.showTooltip(pie, index); + } + + var isExpanded = segment.attr("class") === pie.cssPrefix + "expanded"; + segments.onSegmentEvent(pie, pie.options.callbacks.onMouseoverSegment, segment, isExpanded); + }); + + arc.on("mousemove", function() { + tt.moveTooltip(pie); + }); + + arc.on("mouseout", function() { + var currentEl = d3.select(this); + var segment, index; + + if (currentEl.attr("class") === pie.cssPrefix + "arc") { + segment = currentEl.select("path"); + } else { + index = currentEl.attr("data-index"); + segment = d3.select("#" + pie.cssPrefix + "segment" + index); + } + + if (pie.options.effects.highlightSegmentOnMouseover) { + index = segment.attr("data-index"); + var color = pie.options.colors[index]; + if (pie.options.misc.gradient.enabled) { + color = "url(#" + pie.cssPrefix + "grad" + index + ")"; + } + segment.style("fill", color); + } + + if (pie.options.tooltips.enabled) { + index = segment.attr("data-index"); + tt.hideTooltip(pie, index); + } + + var isExpanded = segment.attr("class") === pie.cssPrefix + "expanded"; + segments.onSegmentEvent(pie, pie.options.callbacks.onMouseoutSegment, segment, isExpanded); + }); + }, + + // helper function used to call the click, mouseover, mouseout segment callback functions + onSegmentEvent: function(pie, func, segment, isExpanded) { + if (!helpers.isFunction(func)) { + return; + } + var index = parseInt(segment.attr("data-index"), 10); + func({ + segment: segment.node(), + index: index, + expanded: isExpanded, + data: pie.options.data.content[index] + }); + }, + + openSegment: function(pie, segment) { + if (pie.isOpeningSegment) { + return; + } + pie.isOpeningSegment = true; + + segments.maybeCloseOpenSegment(pie); + + d3.select(segment).transition() + .ease(segments.effectMap[pie.options.effects.pullOutSegmentOnClick.effect]) + .duration(pie.options.effects.pullOutSegmentOnClick.speed) + .attr("transform", function(d, i) { + var c = pie.arc.centroid(d), + x = c[0], + y = c[1], + h = Math.sqrt(x*x + y*y), + pullOutSize = parseInt(pie.options.effects.pullOutSegmentOnClick.size, 10); + + return "translate(" + ((x/h) * pullOutSize) + ',' + ((y/h) * pullOutSize) + ")"; + }) + .on("end", function(d, i) { + pie.currentlyOpenSegment = segment; + pie.isOpeningSegment = false; + d3.select(segment).attr("class", pie.cssPrefix + "expanded"); + }); + }, + + maybeCloseOpenSegment: function(pie) { + if (d3.selectAll("." + pie.cssPrefix + "expanded").size() > 0) { + segments.closeSegment(pie, d3.select("." + pie.cssPrefix + "expanded").node()); + } + }, + + closeSegment: function(pie, segment) { + d3.select(segment).transition() + .duration(400) + .attr("transform", "translate(0,0)") + .on("end", function(d, i) { + d3.select(segment).attr("class", ""); + pie.currentlyOpenSegment = null; + }); + }, + + getCentroid: function(el) { + var bbox = el.getBBox(); + return { + x: bbox.x + bbox.width / 2, + y: bbox.y + bbox.height / 2 + }; + }, + + /** + * General helper function to return a segment's angle, in various different ways. + * @param index + * @param opts optional object for fine-tuning exactly what you want. + */ + getSegmentAngle: function(index, data, totalSize, opts) { + var options = extend({ + // if true, this returns the full angle from the origin. Otherwise it returns the single segment angle + compounded: true, + + // optionally returns the midpoint of the angle instead of the full angle + midpoint: false + }, opts); + + var currValue = data[index].value; + var fullValue; + if (options.compounded) { + fullValue = 0; + + // get all values up to and including the specified index + for (var i=0; i<=index; i++) { + fullValue += data[i].value; + } + } + + if (typeof fullValue === 'undefined') { + fullValue = currValue; + } + + // now convert the full value to an angle + var angle = (fullValue / totalSize) * 360; + + // lastly, if we want the midpoint, factor that sucker in + if (options.midpoint) { + var currAngle = (currValue / totalSize) * 360; + angle -= (currAngle / 2); + } + + return angle; + } + +}; + + //// --------- text.js ----------- +var text = { + offscreenCoord: -10000, + + addTitle: function(pie) { + + + var title = pie.svg.selectAll("." + pie.cssPrefix + "title") + .data([pie.options.header.title]) + .enter() + .append("text") + .text(function(d) { return d.text; }) + .attr("id", pie.cssPrefix + "title") + .attr("class", pie.cssPrefix + "title") + .attr("x", text.offscreenCoord) + .attr("y", text.offscreenCoord) + .attr("text-anchor", function() { + var location; + if (pie.options.header.location === "top-center" || pie.options.header.location === "pie-center") { + location = "middle"; + } else { + location = "left"; + } + return location; + }) + .attr("fill", function(d) { return d.color; }) + .style("font-size", function(d) { return d.fontSize + "px"; }) + .style("font-family", function(d) { return d.font; }); + }, + + positionTitle: function(pie) { + var textComponents = pie.textComponents; + var headerLocation = pie.options.header.location; + var canvasPadding = pie.options.misc.canvasPadding; + var canvasWidth = pie.options.size.canvasWidth; + var titleSubtitlePadding = pie.options.header.titleSubtitlePadding; + + var x; + if (headerLocation === "top-left") { + x = canvasPadding.left; + } else { + x = ((canvasWidth - canvasPadding.right) / 2) + canvasPadding.left; + } + + // add whatever offset has been added by user + x += pie.options.misc.pieCenterOffset.x; + + var y = canvasPadding.top + textComponents.title.h; + + if (headerLocation === "pie-center") { + y = pie.pieCenter.y; + + // still not fully correct + if (textComponents.subtitle.exists) { + var totalTitleHeight = textComponents.title.h + titleSubtitlePadding + textComponents.subtitle.h; + y = y - (totalTitleHeight / 2) + textComponents.title.h; + } else { + y += (textComponents.title.h / 4); + } + } + + pie.svg.select("#" + pie.cssPrefix + "title") + .attr("x", x) + .attr("y", y); + }, + + addSubtitle: function(pie) { + var headerLocation = pie.options.header.location; + + pie.svg.selectAll("." + pie.cssPrefix + "subtitle") + .data([pie.options.header.subtitle]) + .enter() + .append("text") + .text(function(d) { return d.text; }) + .attr("x", text.offscreenCoord) + .attr("y", text.offscreenCoord) + .attr("id", pie.cssPrefix + "subtitle") + .attr("class", pie.cssPrefix + "subtitle") + .attr("text-anchor", function() { + var location; + if (headerLocation === "top-center" || headerLocation === "pie-center") { + location = "middle"; + } else { + location = "left"; + } + return location; + }) + .attr("fill", function(d) { return d.color; }) + .style("font-size", function(d) { return d.fontSize + "px"; }) + .style("font-family", function(d) { return d.font; }); + }, + + positionSubtitle: function(pie) { + var canvasPadding = pie.options.misc.canvasPadding; + var canvasWidth = pie.options.size.canvasWidth; + + var x; + if (pie.options.header.location === "top-left") { + x = canvasPadding.left; + } else { + x = ((canvasWidth - canvasPadding.right) / 2) + canvasPadding.left; + } + + // add whatever offset has been added by user + x += pie.options.misc.pieCenterOffset.x; + + var y = text.getHeaderHeight(pie); + pie.svg.select("#" + pie.cssPrefix + "subtitle") + .attr("x", x) + .attr("y", y); + }, + + addFooter: function(pie) { + pie.svg.selectAll("." + pie.cssPrefix + "footer") + .data([pie.options.footer]) + .enter() + .append("text") + .text(function(d) { return d.text; }) + .attr("x", text.offscreenCoord) + .attr("y", text.offscreenCoord) + .attr("id", pie.cssPrefix + "footer") + .attr("class", pie.cssPrefix + "footer") + .attr("text-anchor", function() { + var location = "left"; + if (pie.options.footer.location === "bottom-center") { + location = "middle"; + } else if (pie.options.footer.location === "bottom-right") { + location = "left"; // on purpose. We have to change the x-coord to make it properly right-aligned + } + return location; + }) + .attr("fill", function(d) { return d.color; }) + .style("font-size", function(d) { return d.fontSize + "px"; }) + .style("font-family", function(d) { return d.font; }); + }, + + positionFooter: function(pie) { + var footerLocation = pie.options.footer.location; + var footerWidth = pie.textComponents.footer.w; + var canvasWidth = pie.options.size.canvasWidth; + var canvasHeight = pie.options.size.canvasHeight; + var canvasPadding = pie.options.misc.canvasPadding; + + var x; + if (footerLocation === "bottom-left") { + x = canvasPadding.left; + } else if (footerLocation === "bottom-right") { + x = canvasWidth - footerWidth - canvasPadding.right; + } else { + x = canvasWidth / 2; // TODO - shouldn't this also take into account padding? + } + + pie.svg.select("#" + pie.cssPrefix + "footer") + .attr("x", x) + .attr("y", canvasHeight - canvasPadding.bottom); + }, + + getHeaderHeight: function(pie) { + var h; + if (pie.textComponents.title.exists) { + + // if the subtitle isn't defined, it'll be set to 0 + var totalTitleHeight = pie.textComponents.title.h + pie.options.header.titleSubtitlePadding + pie.textComponents.subtitle.h; + if (pie.options.header.location === "pie-center") { + h = pie.pieCenter.y - (totalTitleHeight / 2) + totalTitleHeight; + } else { + h = totalTitleHeight + pie.options.misc.canvasPadding.top; + } + } else { + if (pie.options.header.location === "pie-center") { + var footerPlusPadding = pie.options.misc.canvasPadding.bottom + pie.textComponents.footer.h; + h = ((pie.options.size.canvasHeight - footerPlusPadding) / 2) + pie.options.misc.canvasPadding.top + (pie.textComponents.subtitle.h / 2); + } else { + h = pie.options.misc.canvasPadding.top + pie.textComponents.subtitle.h; + } + } + return h; + } +}; + + //// --------- validate.js ----------- +var tt = { + addTooltips: function(pie) { + + // group the label groups (label, percentage, value) into a single element for simpler positioning + var tooltips = pie.svg.insert("g") + .attr("class", pie.cssPrefix + "tooltips"); + + tooltips.selectAll("." + pie.cssPrefix + "tooltip") + .data(pie.options.data.content) + .enter() + .append("g") + .attr("class", pie.cssPrefix + "tooltip") + .attr("id", function(d, i) { return pie.cssPrefix + "tooltip" + i; }) + .style("opacity", 0) + .append("rect") + .attr("rx", pie.options.tooltips.styles.borderRadius) + .attr("ry", pie.options.tooltips.styles.borderRadius) + .attr("x", -pie.options.tooltips.styles.padding) + .attr("opacity", pie.options.tooltips.styles.backgroundOpacity) + .style("fill", pie.options.tooltips.styles.backgroundColor); + + tooltips.selectAll("." + pie.cssPrefix + "tooltip") + .data(pie.options.data.content) + .append("text") + .attr("fill", function(d) { return pie.options.tooltips.styles.color; }) + .style("font-size", function(d) { return pie.options.tooltips.styles.fontSize; }) + .style("font-family", function(d) { return pie.options.tooltips.styles.font; }) + .text(function(d, i) { + var caption = pie.options.tooltips.string; + if (pie.options.tooltips.type === "caption") { + caption = d.caption; + } + return tt.replacePlaceholders(pie, caption, i, { + label: d.label, + value: d.value, + percentage: d.percentage + }); + }); + + tooltips.selectAll("." + pie.cssPrefix + "tooltip rect") + .attr("width", function (d, i) { + var dims = helpers.getDimensions(pie.cssPrefix + "tooltip" + i); + return dims.w + (2 * pie.options.tooltips.styles.padding); + }) + .attr("height", function (d, i) { + var dims = helpers.getDimensions(pie.cssPrefix + "tooltip" + i); + return dims.h + (2 * pie.options.tooltips.styles.padding); + }) + .attr("y", function (d, i) { + var dims = helpers.getDimensions(pie.cssPrefix + "tooltip" + i); + return -(dims.h / 2) + 1; + }); + }, + + showTooltip: function(pie, index) { + var fadeInSpeed = pie.options.tooltips.styles.fadeInSpeed; + if (tt.currentTooltip === index) { + fadeInSpeed = 1; + } + + tt.currentTooltip = index; + d3.select("#" + pie.cssPrefix + "tooltip" + index) + .transition() + .duration(fadeInSpeed) + .style("opacity", function() { return 1; }); + + tt.moveTooltip(pie); + }, + + moveTooltip: function(pie) { + d3.selectAll("#" + pie.cssPrefix + "tooltip" + tt.currentTooltip) + .attr("transform", function(d) { + var mouseCoords = d3.mouse(this.parentNode); + var x = mouseCoords[0] + pie.options.tooltips.styles.padding + 2; + var y = mouseCoords[1] - (2 * pie.options.tooltips.styles.padding) - 2; + return "translate(" + x + "," + y + ")"; + }); + }, + + hideTooltip: function(pie, index) { + d3.select("#" + pie.cssPrefix + "tooltip" + index) + .style("opacity", function() { return 0; }); + + // move the tooltip offscreen. This ensures that when the user next mouseovers the segment the hidden + // element won't interfere + d3.select("#" + pie.cssPrefix + "tooltip" + tt.currentTooltip) + .attr("transform", function(d, i) { + // klutzy, but it accounts for tooltip padding which could push it onscreen + var x = pie.options.size.canvasWidth + 1000; + var y = pie.options.size.canvasHeight + 1000; + return "translate(" + x + "," + y + ")"; + }); + }, + + replacePlaceholders: function(pie, str, index, replacements) { + + // if the user has defined a placeholderParser function, call it before doing the replacements + if (helpers.isFunction(pie.options.tooltips.placeholderParser)) { + pie.options.tooltips.placeholderParser(index, replacements); + } + + var replacer = function() { + return function(match) { + var placeholder = arguments[1]; + if (replacements.hasOwnProperty(placeholder)) { + return replacements[arguments[1]]; + } else { + return arguments[0]; + } + }; + }; + return str.replace(/\{(\w+)\}/g, replacer(replacements)); + } +}; + + + // -------------------------------------------------------------------------------------------- + + // our constructor + var d3pie = function(element, options) { + + // element can be an ID or DOM element + this.element = element; + if (typeof element === "string") { + var el = element.replace(/^#/, ""); // replace any jQuery-like ID hash char + this.element = document.getElementById(el); + } + + var opts = {}; + extend(true, opts, defaultSettings, options); + this.options = opts; + + // if the user specified a custom CSS element prefix (ID, class), use it + if (this.options.misc.cssPrefix !== null) { + this.cssPrefix = this.options.misc.cssPrefix; + } else { + this.cssPrefix = "p" + _uniqueIDCounter + "_"; + _uniqueIDCounter++; + } + + + // now run some validation on the user-defined info + if (!validate.initialCheck(this)) { + return; + } + + // add a data-role to the DOM node to let anyone know that it contains a d3pie instance, and the d3pie version + d3.select(this.element).attr(_scriptName, _version); + + // things that are done once + _setupData.call(this); + _init.call(this); + }; + + d3pie.prototype.recreate = function() { + // now run some validation on the user-defined info + if (!validate.initialCheck(this)) { + return; + } + + _setupData.call(this); + _init.call(this); + }; + + d3pie.prototype.redraw = function() { + this.element.innerHTML = ""; + _init.call(this); + }; + + d3pie.prototype.destroy = function() { + this.element.innerHTML = ""; // clear out the SVG + d3.select(this.element).attr(_scriptName, null); // remove the data attr + }; + + /** + * Returns all pertinent info about the current open info. Returns null if nothing's open, or if one is, an object of + * the following form: + * { + * element: DOM NODE, + * index: N, + * data: {} + * } + */ + d3pie.prototype.getOpenSegment = function() { + var segment = this.currentlyOpenSegment; + if (segment !== null && typeof segment !== "undefined") { + var index = parseInt(d3.select(segment).attr("data-index"), 10); + return { + element: segment, + index: index, + data: this.options.data.content[index] + }; + } else { + return null; + } + }; + + d3pie.prototype.openSegment = function(index) { + index = parseInt(index, 10); + if (index < 0 || index > this.options.data.content.length-1) { + return; + } + segments.openSegment(this, d3.select("#" + this.cssPrefix + "segment" + index).node()); + }; + + d3pie.prototype.closeSegment = function() { + segments.maybeCloseOpenSegment(); + }; + + // this let's the user dynamically update aspects of the pie chart without causing a complete redraw. It + // intelligently re-renders only the part of the pie that the user specifies. Some things cause a repaint, others + // just redraw the single element + d3pie.prototype.updateProp = function(propKey, value) { + switch (propKey) { + case "header.title.text": + var oldVal = helpers.processObj(this.options, propKey); + helpers.processObj(this.options, propKey, value); + d3.select("#" + this.cssPrefix + "title").html(value); + if ((oldVal === "" && value !== "") || (oldVal !== "" && value === "")) { + this.redraw(); + } + break; + + case "header.subtitle.text": + var oldValue = helpers.processObj(this.options, propKey); + helpers.processObj(this.options, propKey, value); + d3.select("#" + this.cssPrefix + "subtitle").html(value); + if ((oldValue === "" && value !== "") || (oldValue !== "" && value === "")) { + this.redraw(); + } + break; + + case "callbacks.onload": + case "callbacks.onMouseoverSegment": + case "callbacks.onMouseoutSegment": + case "callbacks.onClickSegment": + case "effects.pullOutSegmentOnClick.effect": + case "effects.pullOutSegmentOnClick.speed": + case "effects.pullOutSegmentOnClick.size": + case "effects.highlightSegmentOnMouseover": + case "effects.highlightLuminosity": + helpers.processObj(this.options, propKey, value); + break; + + // everything else, attempt to update it & do a repaint + default: + helpers.processObj(this.options, propKey, value); + + this.destroy(); + this.recreate(); + break; + } + }; + + + // ------------------------------------------------------------------------------------------------ + + var _setupData = function () { + this.options.data.content = math.sortPieData(this); + if (this.options.data.smallSegmentGrouping.enabled) { + this.options.data.content = helpers.applySmallSegmentGrouping(this.options.data.content, this.options.data.smallSegmentGrouping); + } + + + this.options.colors = helpers.initSegmentColors(this); + this.totalSize = math.getTotalPieSize(this.options.data.content); + + var dp = this.options.labels.percentage.decimalPlaces; + + // add in percentage data to content + for (var i=0; i + if (val === '') return true; + if (val === 'false') return false; + if (val === 'true') return true; + return val; + } + + if (DOCUMENT && typeof DOCUMENT.querySelector === 'function') { + var attrs = [['data-family-prefix', 'familyPrefix'], ['data-replacement-class', 'replacementClass'], ['data-auto-replace-svg', 'autoReplaceSvg'], ['data-auto-add-css', 'autoAddCss'], ['data-auto-a11y', 'autoA11y'], ['data-search-pseudo-elements', 'searchPseudoElements'], ['data-observe-mutations', 'observeMutations'], ['data-mutate-approach', 'mutateApproach'], ['data-keep-original-source', 'keepOriginalSource'], ['data-measure-performance', 'measurePerformance'], ['data-show-missing-icons', 'showMissingIcons']]; + attrs.forEach(function (_ref) { + var _ref2 = _slicedToArray(_ref, 2), + attr = _ref2[0], + key = _ref2[1]; + + var val = coerce(getAttrConfig(attr)); + + if (val !== undefined && val !== null) { + initial[key] = val; + } + }); + } + + var _default = { + familyPrefix: DEFAULT_FAMILY_PREFIX, + replacementClass: DEFAULT_REPLACEMENT_CLASS, + autoReplaceSvg: true, + autoAddCss: true, + autoA11y: true, + searchPseudoElements: false, + observeMutations: true, + mutateApproach: 'async', + keepOriginalSource: true, + measurePerformance: false, + showMissingIcons: true + }; + + var _config = _objectSpread({}, _default, initial); + + if (!_config.autoReplaceSvg) _config.observeMutations = false; + + var config = _objectSpread({}, _config); + + WINDOW.FontAwesomeConfig = config; + + var w = WINDOW || {}; + if (!w[NAMESPACE_IDENTIFIER]) w[NAMESPACE_IDENTIFIER] = {}; + if (!w[NAMESPACE_IDENTIFIER].styles) w[NAMESPACE_IDENTIFIER].styles = {}; + if (!w[NAMESPACE_IDENTIFIER].hooks) w[NAMESPACE_IDENTIFIER].hooks = {}; + if (!w[NAMESPACE_IDENTIFIER].shims) w[NAMESPACE_IDENTIFIER].shims = []; + var namespace = w[NAMESPACE_IDENTIFIER]; + + var functions = []; + + var listener = function listener() { + DOCUMENT.removeEventListener('DOMContentLoaded', listener); + loaded = 1; + functions.map(function (fn) { + return fn(); + }); + }; + + var loaded = false; + + if (IS_DOM) { + loaded = (DOCUMENT.documentElement.doScroll ? /^loaded|^c/ : /^loaded|^i|^c/).test(DOCUMENT.readyState); + if (!loaded) DOCUMENT.addEventListener('DOMContentLoaded', listener); + } + + function domready (fn) { + if (!IS_DOM) return; + loaded ? setTimeout(fn, 0) : functions.push(fn); + } + + var PENDING = 'pending'; + var SETTLED = 'settled'; + var FULFILLED = 'fulfilled'; + var REJECTED = 'rejected'; + + var NOOP = function NOOP() {}; + + var isNode = typeof global !== 'undefined' && typeof global.process !== 'undefined' && typeof global.process.emit === 'function'; + var asyncSetTimer = typeof setImmediate === 'undefined' ? setTimeout : setImmediate; + var asyncQueue = []; + var asyncTimer; + + function asyncFlush() { + // run promise callbacks + for (var i = 0; i < asyncQueue.length; i++) { + asyncQueue[i][0](asyncQueue[i][1]); + } // reset async asyncQueue + + + asyncQueue = []; + asyncTimer = false; + } + + function asyncCall(callback, arg) { + asyncQueue.push([callback, arg]); + + if (!asyncTimer) { + asyncTimer = true; + asyncSetTimer(asyncFlush, 0); + } + } + + function invokeResolver(resolver, promise) { + function resolvePromise(value) { + resolve(promise, value); + } + + function rejectPromise(reason) { + reject(promise, reason); + } + + try { + resolver(resolvePromise, rejectPromise); + } catch (e) { + rejectPromise(e); + } + } + + function invokeCallback(subscriber) { + var owner = subscriber.owner; + var settled = owner._state; + var value = owner._data; + var callback = subscriber[settled]; + var promise = subscriber.then; + + if (typeof callback === 'function') { + settled = FULFILLED; + + try { + value = callback(value); + } catch (e) { + reject(promise, e); + } + } + + if (!handleThenable(promise, value)) { + if (settled === FULFILLED) { + resolve(promise, value); + } + + if (settled === REJECTED) { + reject(promise, value); + } + } + } + + function handleThenable(promise, value) { + var resolved; + + try { + if (promise === value) { + throw new TypeError('A promises callback cannot return that same promise.'); + } + + if (value && (typeof value === 'function' || _typeof(value) === 'object')) { + // then should be retrieved only once + var then = value.then; + + if (typeof then === 'function') { + then.call(value, function (val) { + if (!resolved) { + resolved = true; + + if (value === val) { + fulfill(promise, val); + } else { + resolve(promise, val); + } + } + }, function (reason) { + if (!resolved) { + resolved = true; + reject(promise, reason); + } + }); + return true; + } + } + } catch (e) { + if (!resolved) { + reject(promise, e); + } + + return true; + } + + return false; + } + + function resolve(promise, value) { + if (promise === value || !handleThenable(promise, value)) { + fulfill(promise, value); + } + } + + function fulfill(promise, value) { + if (promise._state === PENDING) { + promise._state = SETTLED; + promise._data = value; + asyncCall(publishFulfillment, promise); + } + } + + function reject(promise, reason) { + if (promise._state === PENDING) { + promise._state = SETTLED; + promise._data = reason; + asyncCall(publishRejection, promise); + } + } + + function publish(promise) { + promise._then = promise._then.forEach(invokeCallback); + } + + function publishFulfillment(promise) { + promise._state = FULFILLED; + publish(promise); + } + + function publishRejection(promise) { + promise._state = REJECTED; + publish(promise); + + if (!promise._handled && isNode) { + global.process.emit('unhandledRejection', promise._data, promise); + } + } + + function notifyRejectionHandled(promise) { + global.process.emit('rejectionHandled', promise); + } + /** + * @class + */ + + + function P(resolver) { + if (typeof resolver !== 'function') { + throw new TypeError('Promise resolver ' + resolver + ' is not a function'); + } + + if (this instanceof P === false) { + throw new TypeError('Failed to construct \'Promise\': Please use the \'new\' operator, this object constructor cannot be called as a function.'); + } + + this._then = []; + invokeResolver(resolver, this); + } + + P.prototype = { + constructor: P, + _state: PENDING, + _then: null, + _data: undefined, + _handled: false, + then: function then(onFulfillment, onRejection) { + var subscriber = { + owner: this, + then: new this.constructor(NOOP), + fulfilled: onFulfillment, + rejected: onRejection + }; + + if ((onRejection || onFulfillment) && !this._handled) { + this._handled = true; + + if (this._state === REJECTED && isNode) { + asyncCall(notifyRejectionHandled, this); + } + } + + if (this._state === FULFILLED || this._state === REJECTED) { + // already resolved, call callback async + asyncCall(invokeCallback, subscriber); + } else { + // subscribe + this._then.push(subscriber); + } + + return subscriber.then; + }, + catch: function _catch(onRejection) { + return this.then(null, onRejection); + } + }; + + P.all = function (promises) { + if (!Array.isArray(promises)) { + throw new TypeError('You must pass an array to Promise.all().'); + } + + return new P(function (resolve, reject) { + var results = []; + var remaining = 0; + + function resolver(index) { + remaining++; + return function (value) { + results[index] = value; + + if (! --remaining) { + resolve(results); + } + }; + } + + for (var i = 0, promise; i < promises.length; i++) { + promise = promises[i]; + + if (promise && typeof promise.then === 'function') { + promise.then(resolver(i), reject); + } else { + results[i] = promise; + } + } + + if (!remaining) { + resolve(results); + } + }); + }; + + P.race = function (promises) { + if (!Array.isArray(promises)) { + throw new TypeError('You must pass an array to Promise.race().'); + } + + return new P(function (resolve, reject) { + for (var i = 0, promise; i < promises.length; i++) { + promise = promises[i]; + + if (promise && typeof promise.then === 'function') { + promise.then(resolve, reject); + } else { + resolve(promise); + } + } + }); + }; + + P.resolve = function (value) { + if (value && _typeof(value) === 'object' && value.constructor === P) { + return value; + } + + return new P(function (resolve) { + resolve(value); + }); + }; + + P.reject = function (reason) { + return new P(function (resolve, reject) { + reject(reason); + }); + }; + + var picked = typeof Promise === 'function' ? Promise : P; + + var d = UNITS_IN_GRID; + var meaninglessTransform = { + size: 16, + x: 0, + y: 0, + rotate: 0, + flipX: false, + flipY: false + }; + + function isReserved(name) { + return ~RESERVED_CLASSES.indexOf(name); + } + + function bunker(fn) { + try { + fn(); + } catch (e) { + if (!PRODUCTION) { + throw e; + } + } + } + function insertCss(css) { + if (!css || !IS_DOM) { + return; + } + + var style = DOCUMENT.createElement('style'); + style.setAttribute('type', 'text/css'); + style.innerHTML = css; + var headChildren = DOCUMENT.head.childNodes; + var beforeChild = null; + + for (var i = headChildren.length - 1; i > -1; i--) { + var child = headChildren[i]; + var tagName = (child.tagName || '').toUpperCase(); + + if (['STYLE', 'LINK'].indexOf(tagName) > -1) { + beforeChild = child; + } + } + + DOCUMENT.head.insertBefore(style, beforeChild); + return css; + } + var idPool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + function nextUniqueId() { + var size = 12; + var id = ''; + + while (size-- > 0) { + id += idPool[Math.random() * 62 | 0]; + } + + return id; + } + function toArray(obj) { + var array = []; + + for (var i = (obj || []).length >>> 0; i--;) { + array[i] = obj[i]; + } + + return array; + } + function classArray(node) { + if (node.classList) { + return toArray(node.classList); + } else { + return (node.getAttribute('class') || '').split(' ').filter(function (i) { + return i; + }); + } + } + function getIconName(familyPrefix, cls) { + var parts = cls.split('-'); + var prefix = parts[0]; + var iconName = parts.slice(1).join('-'); + + if (prefix === familyPrefix && iconName !== '' && !isReserved(iconName)) { + return iconName; + } else { + return null; + } + } + function htmlEscape(str) { + return "".concat(str).replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, ''').replace(//g, '>'); + } + function joinAttributes(attributes) { + return Object.keys(attributes || {}).reduce(function (acc, attributeName) { + return acc + "".concat(attributeName, "=\"").concat(htmlEscape(attributes[attributeName]), "\" "); + }, '').trim(); + } + function joinStyles(styles) { + return Object.keys(styles || {}).reduce(function (acc, styleName) { + return acc + "".concat(styleName, ": ").concat(styles[styleName], ";"); + }, ''); + } + function transformIsMeaningful(transform) { + return transform.size !== meaninglessTransform.size || transform.x !== meaninglessTransform.x || transform.y !== meaninglessTransform.y || transform.rotate !== meaninglessTransform.rotate || transform.flipX || transform.flipY; + } + function transformForSvg(_ref) { + var transform = _ref.transform, + containerWidth = _ref.containerWidth, + iconWidth = _ref.iconWidth; + var outer = { + transform: "translate(".concat(containerWidth / 2, " 256)") + }; + var innerTranslate = "translate(".concat(transform.x * 32, ", ").concat(transform.y * 32, ") "); + var innerScale = "scale(".concat(transform.size / 16 * (transform.flipX ? -1 : 1), ", ").concat(transform.size / 16 * (transform.flipY ? -1 : 1), ") "); + var innerRotate = "rotate(".concat(transform.rotate, " 0 0)"); + var inner = { + transform: "".concat(innerTranslate, " ").concat(innerScale, " ").concat(innerRotate) + }; + var path = { + transform: "translate(".concat(iconWidth / 2 * -1, " -256)") + }; + return { + outer: outer, + inner: inner, + path: path + }; + } + function transformForCss(_ref2) { + var transform = _ref2.transform, + _ref2$width = _ref2.width, + width = _ref2$width === void 0 ? UNITS_IN_GRID : _ref2$width, + _ref2$height = _ref2.height, + height = _ref2$height === void 0 ? UNITS_IN_GRID : _ref2$height, + _ref2$startCentered = _ref2.startCentered, + startCentered = _ref2$startCentered === void 0 ? false : _ref2$startCentered; + var val = ''; + + if (startCentered && IS_IE) { + val += "translate(".concat(transform.x / d - width / 2, "em, ").concat(transform.y / d - height / 2, "em) "); + } else if (startCentered) { + val += "translate(calc(-50% + ".concat(transform.x / d, "em), calc(-50% + ").concat(transform.y / d, "em)) "); + } else { + val += "translate(".concat(transform.x / d, "em, ").concat(transform.y / d, "em) "); + } + + val += "scale(".concat(transform.size / d * (transform.flipX ? -1 : 1), ", ").concat(transform.size / d * (transform.flipY ? -1 : 1), ") "); + val += "rotate(".concat(transform.rotate, "deg) "); + return val; + } + + var ALL_SPACE = { + x: 0, + y: 0, + width: '100%', + height: '100%' + }; + + function fillBlack(abstract) { + var force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; + + if (abstract.attributes && (abstract.attributes.fill || force)) { + abstract.attributes.fill = 'black'; + } + + return abstract; + } + + function deGroup(abstract) { + if (abstract.tag === 'g') { + return abstract.children; + } else { + return [abstract]; + } + } + + function makeIconMasking (_ref) { + var children = _ref.children, + attributes = _ref.attributes, + main = _ref.main, + mask = _ref.mask, + explicitMaskId = _ref.maskId, + transform = _ref.transform; + var mainWidth = main.width, + mainPath = main.icon; + var maskWidth = mask.width, + maskPath = mask.icon; + var trans = transformForSvg({ + transform: transform, + containerWidth: maskWidth, + iconWidth: mainWidth + }); + var maskRect = { + tag: 'rect', + attributes: _objectSpread({}, ALL_SPACE, { + fill: 'white' + }) + }; + var maskInnerGroupChildrenMixin = mainPath.children ? { + children: mainPath.children.map(fillBlack) + } : {}; + var maskInnerGroup = { + tag: 'g', + attributes: _objectSpread({}, trans.inner), + children: [fillBlack(_objectSpread({ + tag: mainPath.tag, + attributes: _objectSpread({}, mainPath.attributes, trans.path) + }, maskInnerGroupChildrenMixin))] + }; + var maskOuterGroup = { + tag: 'g', + attributes: _objectSpread({}, trans.outer), + children: [maskInnerGroup] + }; + var maskId = "mask-".concat(explicitMaskId || nextUniqueId()); + var clipId = "clip-".concat(explicitMaskId || nextUniqueId()); + var maskTag = { + tag: 'mask', + attributes: _objectSpread({}, ALL_SPACE, { + id: maskId, + maskUnits: 'userSpaceOnUse', + maskContentUnits: 'userSpaceOnUse' + }), + children: [maskRect, maskOuterGroup] + }; + var defs = { + tag: 'defs', + children: [{ + tag: 'clipPath', + attributes: { + id: clipId + }, + children: deGroup(maskPath) + }, maskTag] + }; + children.push(defs, { + tag: 'rect', + attributes: _objectSpread({ + fill: 'currentColor', + 'clip-path': "url(#".concat(clipId, ")"), + mask: "url(#".concat(maskId, ")") + }, ALL_SPACE) + }); + return { + children: children, + attributes: attributes + }; + } + + function makeIconStandard (_ref) { + var children = _ref.children, + attributes = _ref.attributes, + main = _ref.main, + transform = _ref.transform, + styles = _ref.styles; + var styleString = joinStyles(styles); + + if (styleString.length > 0) { + attributes['style'] = styleString; + } + + if (transformIsMeaningful(transform)) { + var trans = transformForSvg({ + transform: transform, + containerWidth: main.width, + iconWidth: main.width + }); + children.push({ + tag: 'g', + attributes: _objectSpread({}, trans.outer), + children: [{ + tag: 'g', + attributes: _objectSpread({}, trans.inner), + children: [{ + tag: main.icon.tag, + children: main.icon.children, + attributes: _objectSpread({}, main.icon.attributes, trans.path) + }] + }] + }); + } else { + children.push(main.icon); + } + + return { + children: children, + attributes: attributes + }; + } + + function asIcon (_ref) { + var children = _ref.children, + main = _ref.main, + mask = _ref.mask, + attributes = _ref.attributes, + styles = _ref.styles, + transform = _ref.transform; + + if (transformIsMeaningful(transform) && main.found && !mask.found) { + var width = main.width, + height = main.height; + var offset = { + x: width / height / 2, + y: 0.5 + }; + attributes['style'] = joinStyles(_objectSpread({}, styles, { + 'transform-origin': "".concat(offset.x + transform.x / 16, "em ").concat(offset.y + transform.y / 16, "em") + })); + } + + return [{ + tag: 'svg', + attributes: attributes, + children: children + }]; + } + + function asSymbol (_ref) { + var prefix = _ref.prefix, + iconName = _ref.iconName, + children = _ref.children, + attributes = _ref.attributes, + symbol = _ref.symbol; + var id = symbol === true ? "".concat(prefix, "-").concat(config.familyPrefix, "-").concat(iconName) : symbol; + return [{ + tag: 'svg', + attributes: { + style: 'display: none;' + }, + children: [{ + tag: 'symbol', + attributes: _objectSpread({}, attributes, { + id: id + }), + children: children + }] + }]; + } + + function makeInlineSvgAbstract(params) { + var _params$icons = params.icons, + main = _params$icons.main, + mask = _params$icons.mask, + prefix = params.prefix, + iconName = params.iconName, + transform = params.transform, + symbol = params.symbol, + title = params.title, + maskId = params.maskId, + titleId = params.titleId, + extra = params.extra, + _params$watchable = params.watchable, + watchable = _params$watchable === void 0 ? false : _params$watchable; + + var _ref = mask.found ? mask : main, + width = _ref.width, + height = _ref.height; + + var widthClass = "fa-w-".concat(Math.ceil(width / height * 16)); + var attrClass = [config.replacementClass, iconName ? "".concat(config.familyPrefix, "-").concat(iconName) : '', widthClass].filter(function (c) { + return extra.classes.indexOf(c) === -1; + }).concat(extra.classes).join(' '); + var content = { + children: [], + attributes: _objectSpread({}, extra.attributes, { + 'data-prefix': prefix, + 'data-icon': iconName, + 'class': attrClass, + 'role': extra.attributes.role || 'img', + 'xmlns': 'http://www.w3.org/2000/svg', + 'viewBox': "0 0 ".concat(width, " ").concat(height) + }) + }; + + if (watchable) { + content.attributes[DATA_FA_I2SVG] = ''; + } + + if (title) content.children.push({ + tag: 'title', + attributes: { + id: content.attributes['aria-labelledby'] || "title-".concat(titleId || nextUniqueId()) + }, + children: [title] + }); + + var args = _objectSpread({}, content, { + prefix: prefix, + iconName: iconName, + main: main, + mask: mask, + maskId: maskId, + transform: transform, + symbol: symbol, + styles: extra.styles + }); + + var _ref2 = mask.found && main.found ? makeIconMasking(args) : makeIconStandard(args), + children = _ref2.children, + attributes = _ref2.attributes; + + args.children = children; + args.attributes = attributes; + + if (symbol) { + return asSymbol(args); + } else { + return asIcon(args); + } + } + function makeLayersTextAbstract(params) { + var content = params.content, + width = params.width, + height = params.height, + transform = params.transform, + title = params.title, + extra = params.extra, + _params$watchable2 = params.watchable, + watchable = _params$watchable2 === void 0 ? false : _params$watchable2; + + var attributes = _objectSpread({}, extra.attributes, title ? { + 'title': title + } : {}, { + 'class': extra.classes.join(' ') + }); + + if (watchable) { + attributes[DATA_FA_I2SVG] = ''; + } + + var styles = _objectSpread({}, extra.styles); + + if (transformIsMeaningful(transform)) { + styles['transform'] = transformForCss({ + transform: transform, + startCentered: true, + width: width, + height: height + }); + styles['-webkit-transform'] = styles['transform']; + } + + var styleString = joinStyles(styles); + + if (styleString.length > 0) { + attributes['style'] = styleString; + } + + var val = []; + val.push({ + tag: 'span', + attributes: attributes, + children: [content] + }); + + if (title) { + val.push({ + tag: 'span', + attributes: { + class: 'sr-only' + }, + children: [title] + }); + } + + return val; + } + function makeLayersCounterAbstract(params) { + var content = params.content, + title = params.title, + extra = params.extra; + + var attributes = _objectSpread({}, extra.attributes, title ? { + 'title': title + } : {}, { + 'class': extra.classes.join(' ') + }); + + var styleString = joinStyles(extra.styles); + + if (styleString.length > 0) { + attributes['style'] = styleString; + } + + var val = []; + val.push({ + tag: 'span', + attributes: attributes, + children: [content] + }); + + if (title) { + val.push({ + tag: 'span', + attributes: { + class: 'sr-only' + }, + children: [title] + }); + } + + return val; + } + + var noop$1 = function noop() {}; + + var p = config.measurePerformance && PERFORMANCE && PERFORMANCE.mark && PERFORMANCE.measure ? PERFORMANCE : { + mark: noop$1, + measure: noop$1 + }; + var preamble = "FA \"5.13.1\""; + + var begin = function begin(name) { + p.mark("".concat(preamble, " ").concat(name, " begins")); + return function () { + return end(name); + }; + }; + + var end = function end(name) { + p.mark("".concat(preamble, " ").concat(name, " ends")); + p.measure("".concat(preamble, " ").concat(name), "".concat(preamble, " ").concat(name, " begins"), "".concat(preamble, " ").concat(name, " ends")); + }; + + var perf = { + begin: begin, + end: end + }; + + /** + * Internal helper to bind a function known to have 4 arguments + * to a given context. + */ + + var bindInternal4 = function bindInternal4(func, thisContext) { + return function (a, b, c, d) { + return func.call(thisContext, a, b, c, d); + }; + }; + + /** + * # Reduce + * + * A fast object `.reduce()` implementation. + * + * @param {Object} subject The object to reduce over. + * @param {Function} fn The reducer function. + * @param {mixed} initialValue The initial value for the reducer, defaults to subject[0]. + * @param {Object} thisContext The context for the reducer. + * @return {mixed} The final result. + */ + + + var reduce = function fastReduceObject(subject, fn, initialValue, thisContext) { + var keys = Object.keys(subject), + length = keys.length, + iterator = thisContext !== undefined ? bindInternal4(fn, thisContext) : fn, + i, + key, + result; + + if (initialValue === undefined) { + i = 1; + result = subject[keys[0]]; + } else { + i = 0; + result = initialValue; + } + + for (; i < length; i++) { + key = keys[i]; + result = iterator(result, subject[key], key, subject); + } + + return result; + }; + + function toHex(unicode) { + var result = ''; + + for (var i = 0; i < unicode.length; i++) { + var hex = unicode.charCodeAt(i).toString(16); + result += ('000' + hex).slice(-4); + } + + return result; + } + + function defineIcons(prefix, icons) { + var params = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + var _params$skipHooks = params.skipHooks, + skipHooks = _params$skipHooks === void 0 ? false : _params$skipHooks; + var normalized = Object.keys(icons).reduce(function (acc, iconName) { + var icon = icons[iconName]; + var expanded = !!icon.icon; + + if (expanded) { + acc[icon.iconName] = icon.icon; + } else { + acc[iconName] = icon; + } + + return acc; + }, {}); + + if (typeof namespace.hooks.addPack === 'function' && !skipHooks) { + namespace.hooks.addPack(prefix, normalized); + } else { + namespace.styles[prefix] = _objectSpread({}, namespace.styles[prefix] || {}, normalized); + } + /** + * Font Awesome 4 used the prefix of `fa` for all icons. With the introduction + * of new styles we needed to differentiate between them. Prefix `fa` is now an alias + * for `fas` so we'll easy the upgrade process for our users by automatically defining + * this as well. + */ + + + if (prefix === 'fas') { + defineIcons('fa', icons); + } + } + + var styles = namespace.styles, + shims = namespace.shims; + var _byUnicode = {}; + var _byLigature = {}; + var _byOldName = {}; + var build = function build() { + var lookup = function lookup(reducer) { + return reduce(styles, function (o, style, prefix) { + o[prefix] = reduce(style, reducer, {}); + return o; + }, {}); + }; + + _byUnicode = lookup(function (acc, icon, iconName) { + if (icon[3]) { + acc[icon[3]] = iconName; + } + + return acc; + }); + _byLigature = lookup(function (acc, icon, iconName) { + var ligatures = icon[2]; + acc[iconName] = iconName; + ligatures.forEach(function (ligature) { + acc[ligature] = iconName; + }); + return acc; + }); + var hasRegular = 'far' in styles; + _byOldName = reduce(shims, function (acc, shim) { + var oldName = shim[0]; + var prefix = shim[1]; + var iconName = shim[2]; + + if (prefix === 'far' && !hasRegular) { + prefix = 'fas'; + } + + acc[oldName] = { + prefix: prefix, + iconName: iconName + }; + return acc; + }, {}); + }; + build(); + function byUnicode(prefix, unicode) { + return (_byUnicode[prefix] || {})[unicode]; + } + function byLigature(prefix, ligature) { + return (_byLigature[prefix] || {})[ligature]; + } + function byOldName(name) { + return _byOldName[name] || { + prefix: null, + iconName: null + }; + } + + var styles$1 = namespace.styles; + var emptyCanonicalIcon = function emptyCanonicalIcon() { + return { + prefix: null, + iconName: null, + rest: [] + }; + }; + function getCanonicalIcon(values) { + return values.reduce(function (acc, cls) { + var iconName = getIconName(config.familyPrefix, cls); + + if (styles$1[cls]) { + acc.prefix = cls; + } else if (config.autoFetchSvg && ['fas', 'far', 'fal', 'fad', 'fab', 'fa'].indexOf(cls) > -1) { + acc.prefix = cls; + } else if (iconName) { + var shim = acc.prefix === 'fa' ? byOldName(iconName) : {}; + acc.iconName = shim.iconName || iconName; + acc.prefix = shim.prefix || acc.prefix; + } else if (cls !== config.replacementClass && cls.indexOf('fa-w-') !== 0) { + acc.rest.push(cls); + } + + return acc; + }, emptyCanonicalIcon()); + } + function iconFromMapping(mapping, prefix, iconName) { + if (mapping && mapping[prefix] && mapping[prefix][iconName]) { + return { + prefix: prefix, + iconName: iconName, + icon: mapping[prefix][iconName] + }; + } + } + + function toHtml(abstractNodes) { + var tag = abstractNodes.tag, + _abstractNodes$attrib = abstractNodes.attributes, + attributes = _abstractNodes$attrib === void 0 ? {} : _abstractNodes$attrib, + _abstractNodes$childr = abstractNodes.children, + children = _abstractNodes$childr === void 0 ? [] : _abstractNodes$childr; + + if (typeof abstractNodes === 'string') { + return htmlEscape(abstractNodes); + } else { + return "<".concat(tag, " ").concat(joinAttributes(attributes), ">").concat(children.map(toHtml).join(''), ""); + } + } + + var noop$2 = function noop() {}; + + function isWatched(node) { + var i2svg = node.getAttribute ? node.getAttribute(DATA_FA_I2SVG) : null; + return typeof i2svg === 'string'; + } + + function getMutator() { + if (config.autoReplaceSvg === true) { + return mutators.replace; + } + + var mutator = mutators[config.autoReplaceSvg]; + return mutator || mutators.replace; + } + + var mutators = { + replace: function replace(mutation) { + var node = mutation[0]; + var abstract = mutation[1]; + var newOuterHTML = abstract.map(function (a) { + return toHtml(a); + }).join('\n'); + + if (node.parentNode && node.outerHTML) { + node.outerHTML = newOuterHTML + (config.keepOriginalSource && node.tagName.toLowerCase() !== 'svg' ? "") : ''); + } else if (node.parentNode) { + var newNode = document.createElement('span'); + node.parentNode.replaceChild(newNode, node); + newNode.outerHTML = newOuterHTML; + } + }, + nest: function nest(mutation) { + var node = mutation[0]; + var abstract = mutation[1]; // If we already have a replaced node we do not want to continue nesting within it. + // Short-circuit to the standard replacement + + if (~classArray(node).indexOf(config.replacementClass)) { + return mutators.replace(mutation); + } + + var forSvg = new RegExp("".concat(config.familyPrefix, "-.*")); + delete abstract[0].attributes.style; + delete abstract[0].attributes.id; + var splitClasses = abstract[0].attributes.class.split(' ').reduce(function (acc, cls) { + if (cls === config.replacementClass || cls.match(forSvg)) { + acc.toSvg.push(cls); + } else { + acc.toNode.push(cls); + } + + return acc; + }, { + toNode: [], + toSvg: [] + }); + abstract[0].attributes.class = splitClasses.toSvg.join(' '); + var newInnerHTML = abstract.map(function (a) { + return toHtml(a); + }).join('\n'); + node.setAttribute('class', splitClasses.toNode.join(' ')); + node.setAttribute(DATA_FA_I2SVG, ''); + node.innerHTML = newInnerHTML; + } + }; + + function performOperationSync(op) { + op(); + } + + function perform(mutations, callback) { + var callbackFunction = typeof callback === 'function' ? callback : noop$2; + + if (mutations.length === 0) { + callbackFunction(); + } else { + var frame = performOperationSync; + + if (config.mutateApproach === MUTATION_APPROACH_ASYNC) { + frame = WINDOW.requestAnimationFrame || performOperationSync; + } + + frame(function () { + var mutator = getMutator(); + var mark = perf.begin('mutate'); + mutations.map(mutator); + mark(); + callbackFunction(); + }); + } + } + var disabled = false; + function disableObservation() { + disabled = true; + } + function enableObservation() { + disabled = false; + } + var mo = null; + function observe(options) { + if (!MUTATION_OBSERVER) { + return; + } + + if (!config.observeMutations) { + return; + } + + var treeCallback = options.treeCallback, + nodeCallback = options.nodeCallback, + pseudoElementsCallback = options.pseudoElementsCallback, + _options$observeMutat = options.observeMutationsRoot, + observeMutationsRoot = _options$observeMutat === void 0 ? DOCUMENT : _options$observeMutat; + mo = new MUTATION_OBSERVER(function (objects) { + if (disabled) return; + toArray(objects).forEach(function (mutationRecord) { + if (mutationRecord.type === 'childList' && mutationRecord.addedNodes.length > 0 && !isWatched(mutationRecord.addedNodes[0])) { + if (config.searchPseudoElements) { + pseudoElementsCallback(mutationRecord.target); + } + + treeCallback(mutationRecord.target); + } + + if (mutationRecord.type === 'attributes' && mutationRecord.target.parentNode && config.searchPseudoElements) { + pseudoElementsCallback(mutationRecord.target.parentNode); + } + + if (mutationRecord.type === 'attributes' && isWatched(mutationRecord.target) && ~ATTRIBUTES_WATCHED_FOR_MUTATION.indexOf(mutationRecord.attributeName)) { + if (mutationRecord.attributeName === 'class') { + var _getCanonicalIcon = getCanonicalIcon(classArray(mutationRecord.target)), + prefix = _getCanonicalIcon.prefix, + iconName = _getCanonicalIcon.iconName; + + if (prefix) mutationRecord.target.setAttribute('data-prefix', prefix); + if (iconName) mutationRecord.target.setAttribute('data-icon', iconName); + } else { + nodeCallback(mutationRecord.target); + } + } + }); + }); + if (!IS_DOM) return; + mo.observe(observeMutationsRoot, { + childList: true, + attributes: true, + characterData: true, + subtree: true + }); + } + function disconnect() { + if (!mo) return; + mo.disconnect(); + } + + function styleParser (node) { + var style = node.getAttribute('style'); + var val = []; + + if (style) { + val = style.split(';').reduce(function (acc, style) { + var styles = style.split(':'); + var prop = styles[0]; + var value = styles.slice(1); + + if (prop && value.length > 0) { + acc[prop] = value.join(':').trim(); + } + + return acc; + }, {}); + } + + return val; + } + + function classParser (node) { + var existingPrefix = node.getAttribute('data-prefix'); + var existingIconName = node.getAttribute('data-icon'); + var innerText = node.innerText !== undefined ? node.innerText.trim() : ''; + var val = getCanonicalIcon(classArray(node)); + + if (existingPrefix && existingIconName) { + val.prefix = existingPrefix; + val.iconName = existingIconName; + } + + if (val.prefix && innerText.length > 1) { + val.iconName = byLigature(val.prefix, node.innerText); + } else if (val.prefix && innerText.length === 1) { + val.iconName = byUnicode(val.prefix, toHex(node.innerText)); + } + + return val; + } + + var parseTransformString = function parseTransformString(transformString) { + var transform = { + size: 16, + x: 0, + y: 0, + flipX: false, + flipY: false, + rotate: 0 + }; + + if (!transformString) { + return transform; + } else { + return transformString.toLowerCase().split(' ').reduce(function (acc, n) { + var parts = n.toLowerCase().split('-'); + var first = parts[0]; + var rest = parts.slice(1).join('-'); + + if (first && rest === 'h') { + acc.flipX = true; + return acc; + } + + if (first && rest === 'v') { + acc.flipY = true; + return acc; + } + + rest = parseFloat(rest); + + if (isNaN(rest)) { + return acc; + } + + switch (first) { + case 'grow': + acc.size = acc.size + rest; + break; + + case 'shrink': + acc.size = acc.size - rest; + break; + + case 'left': + acc.x = acc.x - rest; + break; + + case 'right': + acc.x = acc.x + rest; + break; + + case 'up': + acc.y = acc.y - rest; + break; + + case 'down': + acc.y = acc.y + rest; + break; + + case 'rotate': + acc.rotate = acc.rotate + rest; + break; + } + + return acc; + }, transform); + } + }; + function transformParser (node) { + return parseTransformString(node.getAttribute('data-fa-transform')); + } + + function symbolParser (node) { + var symbol = node.getAttribute('data-fa-symbol'); + return symbol === null ? false : symbol === '' ? true : symbol; + } + + function attributesParser (node) { + var extraAttributes = toArray(node.attributes).reduce(function (acc, attr) { + if (acc.name !== 'class' && acc.name !== 'style') { + acc[attr.name] = attr.value; + } + + return acc; + }, {}); + var title = node.getAttribute('title'); + var titleId = node.getAttribute('data-fa-title-id'); + + if (config.autoA11y) { + if (title) { + extraAttributes['aria-labelledby'] = "".concat(config.replacementClass, "-title-").concat(titleId || nextUniqueId()); + } else { + extraAttributes['aria-hidden'] = 'true'; + extraAttributes['focusable'] = 'false'; + } + } + + return extraAttributes; + } + + function maskParser (node) { + var mask = node.getAttribute('data-fa-mask'); + + if (!mask) { + return emptyCanonicalIcon(); + } else { + return getCanonicalIcon(mask.split(' ').map(function (i) { + return i.trim(); + })); + } + } + + function blankMeta() { + return { + iconName: null, + title: null, + titleId: null, + prefix: null, + transform: meaninglessTransform, + symbol: false, + mask: null, + maskId: null, + extra: { + classes: [], + styles: {}, + attributes: {} + } + }; + } + function parseMeta(node) { + var _classParser = classParser(node), + iconName = _classParser.iconName, + prefix = _classParser.prefix, + extraClasses = _classParser.rest; + + var extraStyles = styleParser(node); + var transform = transformParser(node); + var symbol = symbolParser(node); + var extraAttributes = attributesParser(node); + var mask = maskParser(node); + return { + iconName: iconName, + title: node.getAttribute('title'), + titleId: node.getAttribute('data-fa-title-id'), + prefix: prefix, + transform: transform, + symbol: symbol, + mask: mask, + maskId: node.getAttribute('data-fa-mask-id'), + extra: { + classes: extraClasses, + styles: extraStyles, + attributes: extraAttributes + } + }; + } + + function MissingIcon(error) { + this.name = 'MissingIcon'; + this.message = error || 'Icon unavailable'; + this.stack = new Error().stack; + } + MissingIcon.prototype = Object.create(Error.prototype); + MissingIcon.prototype.constructor = MissingIcon; + + var FILL = { + fill: 'currentColor' + }; + var ANIMATION_BASE = { + attributeType: 'XML', + repeatCount: 'indefinite', + dur: '2s' + }; + var RING = { + tag: 'path', + attributes: _objectSpread({}, FILL, { + d: 'M156.5,447.7l-12.6,29.5c-18.7-9.5-35.9-21.2-51.5-34.9l22.7-22.7C127.6,430.5,141.5,440,156.5,447.7z M40.6,272H8.5 c1.4,21.2,5.4,41.7,11.7,61.1L50,321.2C45.1,305.5,41.8,289,40.6,272z M40.6,240c1.4-18.8,5.2-37,11.1-54.1l-29.5-12.6 C14.7,194.3,10,216.7,8.5,240H40.6z M64.3,156.5c7.8-14.9,17.2-28.8,28.1-41.5L69.7,92.3c-13.7,15.6-25.5,32.8-34.9,51.5 L64.3,156.5z M397,419.6c-13.9,12-29.4,22.3-46.1,30.4l11.9,29.8c20.7-9.9,39.8-22.6,56.9-37.6L397,419.6z M115,92.4 c13.9-12,29.4-22.3,46.1-30.4l-11.9-29.8c-20.7,9.9-39.8,22.6-56.8,37.6L115,92.4z M447.7,355.5c-7.8,14.9-17.2,28.8-28.1,41.5 l22.7,22.7c13.7-15.6,25.5-32.9,34.9-51.5L447.7,355.5z M471.4,272c-1.4,18.8-5.2,37-11.1,54.1l29.5,12.6 c7.5-21.1,12.2-43.5,13.6-66.8H471.4z M321.2,462c-15.7,5-32.2,8.2-49.2,9.4v32.1c21.2-1.4,41.7-5.4,61.1-11.7L321.2,462z M240,471.4c-18.8-1.4-37-5.2-54.1-11.1l-12.6,29.5c21.1,7.5,43.5,12.2,66.8,13.6V471.4z M462,190.8c5,15.7,8.2,32.2,9.4,49.2h32.1 c-1.4-21.2-5.4-41.7-11.7-61.1L462,190.8z M92.4,397c-12-13.9-22.3-29.4-30.4-46.1l-29.8,11.9c9.9,20.7,22.6,39.8,37.6,56.9 L92.4,397z M272,40.6c18.8,1.4,36.9,5.2,54.1,11.1l12.6-29.5C317.7,14.7,295.3,10,272,8.5V40.6z M190.8,50 c15.7-5,32.2-8.2,49.2-9.4V8.5c-21.2,1.4-41.7,5.4-61.1,11.7L190.8,50z M442.3,92.3L419.6,115c12,13.9,22.3,29.4,30.5,46.1 l29.8-11.9C470,128.5,457.3,109.4,442.3,92.3z M397,92.4l22.7-22.7c-15.6-13.7-32.8-25.5-51.5-34.9l-12.6,29.5 C370.4,72.1,384.4,81.5,397,92.4z' + }) + }; + + var OPACITY_ANIMATE = _objectSpread({}, ANIMATION_BASE, { + attributeName: 'opacity' + }); + + var DOT = { + tag: 'circle', + attributes: _objectSpread({}, FILL, { + cx: '256', + cy: '364', + r: '28' + }), + children: [{ + tag: 'animate', + attributes: _objectSpread({}, ANIMATION_BASE, { + attributeName: 'r', + values: '28;14;28;28;14;28;' + }) + }, { + tag: 'animate', + attributes: _objectSpread({}, OPACITY_ANIMATE, { + values: '1;0;1;1;0;1;' + }) + }] + }; + var QUESTION = { + tag: 'path', + attributes: _objectSpread({}, FILL, { + opacity: '1', + d: 'M263.7,312h-16c-6.6,0-12-5.4-12-12c0-71,77.4-63.9,77.4-107.8c0-20-17.8-40.2-57.4-40.2c-29.1,0-44.3,9.6-59.2,28.7 c-3.9,5-11.1,6-16.2,2.4l-13.1-9.2c-5.6-3.9-6.9-11.8-2.6-17.2c21.2-27.2,46.4-44.7,91.2-44.7c52.3,0,97.4,29.8,97.4,80.2 c0,67.6-77.4,63.5-77.4,107.8C275.7,306.6,270.3,312,263.7,312z' + }), + children: [{ + tag: 'animate', + attributes: _objectSpread({}, OPACITY_ANIMATE, { + values: '1;0;0;0;0;1;' + }) + }] + }; + var EXCLAMATION = { + tag: 'path', + attributes: _objectSpread({}, FILL, { + opacity: '0', + d: 'M232.5,134.5l7,168c0.3,6.4,5.6,11.5,12,11.5h9c6.4,0,11.7-5.1,12-11.5l7-168c0.3-6.8-5.2-12.5-12-12.5h-23 C237.7,122,232.2,127.7,232.5,134.5z' + }), + children: [{ + tag: 'animate', + attributes: _objectSpread({}, OPACITY_ANIMATE, { + values: '0;0;1;1;0;0;' + }) + }] + }; + var missing = { + tag: 'g', + children: [RING, DOT, QUESTION, EXCLAMATION] + }; + + var styles$2 = namespace.styles; + function asFoundIcon(icon) { + var width = icon[0]; + var height = icon[1]; + + var _icon$slice = icon.slice(4), + _icon$slice2 = _slicedToArray(_icon$slice, 1), + vectorData = _icon$slice2[0]; + + var element = null; + + if (Array.isArray(vectorData)) { + element = { + tag: 'g', + attributes: { + class: "".concat(config.familyPrefix, "-").concat(DUOTONE_CLASSES.GROUP) + }, + children: [{ + tag: 'path', + attributes: { + class: "".concat(config.familyPrefix, "-").concat(DUOTONE_CLASSES.SECONDARY), + fill: 'currentColor', + d: vectorData[0] + } + }, { + tag: 'path', + attributes: { + class: "".concat(config.familyPrefix, "-").concat(DUOTONE_CLASSES.PRIMARY), + fill: 'currentColor', + d: vectorData[1] + } + }] + }; + } else { + element = { + tag: 'path', + attributes: { + fill: 'currentColor', + d: vectorData + } + }; + } + + return { + found: true, + width: width, + height: height, + icon: element + }; + } + function findIcon(iconName, prefix) { + return new picked(function (resolve, reject) { + var val = { + found: false, + width: 512, + height: 512, + icon: missing + }; + + if (iconName && prefix && styles$2[prefix] && styles$2[prefix][iconName]) { + var icon = styles$2[prefix][iconName]; + return resolve(asFoundIcon(icon)); + } + + var headers = {}; + + if (_typeof(WINDOW.FontAwesomeKitConfig) === 'object' && typeof window.FontAwesomeKitConfig.token === 'string') { + headers['fa-kit-token'] = WINDOW.FontAwesomeKitConfig.token; + } + + if (iconName && prefix && !config.showMissingIcons) { + reject(new MissingIcon("Icon is missing for prefix ".concat(prefix, " with icon name ").concat(iconName))); + } else { + resolve(val); + } + }); + } + + var styles$3 = namespace.styles; + + function generateSvgReplacementMutation(node, nodeMeta) { + var iconName = nodeMeta.iconName, + title = nodeMeta.title, + titleId = nodeMeta.titleId, + prefix = nodeMeta.prefix, + transform = nodeMeta.transform, + symbol = nodeMeta.symbol, + mask = nodeMeta.mask, + maskId = nodeMeta.maskId, + extra = nodeMeta.extra; + return new picked(function (resolve, reject) { + picked.all([findIcon(iconName, prefix), findIcon(mask.iconName, mask.prefix)]).then(function (_ref) { + var _ref2 = _slicedToArray(_ref, 2), + main = _ref2[0], + mask = _ref2[1]; + + resolve([node, makeInlineSvgAbstract({ + icons: { + main: main, + mask: mask + }, + prefix: prefix, + iconName: iconName, + transform: transform, + symbol: symbol, + mask: mask, + maskId: maskId, + title: title, + titleId: titleId, + extra: extra, + watchable: true + })]); + }); + }); + } + + function generateLayersText(node, nodeMeta) { + var title = nodeMeta.title, + transform = nodeMeta.transform, + extra = nodeMeta.extra; + var width = null; + var height = null; + + if (IS_IE) { + var computedFontSize = parseInt(getComputedStyle(node).fontSize, 10); + var boundingClientRect = node.getBoundingClientRect(); + width = boundingClientRect.width / computedFontSize; + height = boundingClientRect.height / computedFontSize; + } + + if (config.autoA11y && !title) { + extra.attributes['aria-hidden'] = 'true'; + } + + return picked.resolve([node, makeLayersTextAbstract({ + content: node.innerHTML, + width: width, + height: height, + transform: transform, + title: title, + extra: extra, + watchable: true + })]); + } + + function generateMutation(node) { + var nodeMeta = parseMeta(node); + + if (~nodeMeta.extra.classes.indexOf(LAYERS_TEXT_CLASSNAME)) { + return generateLayersText(node, nodeMeta); + } else { + return generateSvgReplacementMutation(node, nodeMeta); + } + } + + function onTree(root) { + var callback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; + if (!IS_DOM) return; + var htmlClassList = DOCUMENT.documentElement.classList; + + var hclAdd = function hclAdd(suffix) { + return htmlClassList.add("".concat(HTML_CLASS_I2SVG_BASE_CLASS, "-").concat(suffix)); + }; + + var hclRemove = function hclRemove(suffix) { + return htmlClassList.remove("".concat(HTML_CLASS_I2SVG_BASE_CLASS, "-").concat(suffix)); + }; + + var prefixes = config.autoFetchSvg ? Object.keys(PREFIX_TO_STYLE) : Object.keys(styles$3); + var prefixesDomQuery = [".".concat(LAYERS_TEXT_CLASSNAME, ":not([").concat(DATA_FA_I2SVG, "])")].concat(prefixes.map(function (p) { + return ".".concat(p, ":not([").concat(DATA_FA_I2SVG, "])"); + })).join(', '); + + if (prefixesDomQuery.length === 0) { + return; + } + + var candidates = []; + + try { + candidates = toArray(root.querySelectorAll(prefixesDomQuery)); + } catch (e) {// noop + } + + if (candidates.length > 0) { + hclAdd('pending'); + hclRemove('complete'); + } else { + return; + } + + var mark = perf.begin('onTree'); + var mutations = candidates.reduce(function (acc, node) { + try { + var mutation = generateMutation(node); + + if (mutation) { + acc.push(mutation); + } + } catch (e) { + if (!PRODUCTION) { + if (e instanceof MissingIcon) { + console.error(e); + } + } + } + + return acc; + }, []); + return new picked(function (resolve, reject) { + picked.all(mutations).then(function (resolvedMutations) { + perform(resolvedMutations, function () { + hclAdd('active'); + hclAdd('complete'); + hclRemove('pending'); + if (typeof callback === 'function') callback(); + mark(); + resolve(); + }); + }).catch(function () { + mark(); + reject(); + }); + }); + } + function onNode(node) { + var callback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; + generateMutation(node).then(function (mutation) { + if (mutation) { + perform([mutation], callback); + } + }); + } + + function replaceForPosition(node, position) { + var pendingAttribute = "".concat(DATA_FA_PSEUDO_ELEMENT_PENDING).concat(position.replace(':', '-')); + return new picked(function (resolve, reject) { + if (node.getAttribute(pendingAttribute) !== null) { + // This node is already being processed + return resolve(); + } + + var children = toArray(node.children); + var alreadyProcessedPseudoElement = children.filter(function (c) { + return c.getAttribute(DATA_FA_PSEUDO_ELEMENT) === position; + })[0]; + var styles = WINDOW.getComputedStyle(node, position); + var fontFamily = styles.getPropertyValue('font-family').match(FONT_FAMILY_PATTERN); + var fontWeight = styles.getPropertyValue('font-weight'); + var content = styles.getPropertyValue('content'); + + if (alreadyProcessedPseudoElement && !fontFamily) { + // If we've already processed it but the current computed style does not result in a font-family, + // that probably means that a class name that was previously present to make the icon has been + // removed. So we now should delete the icon. + node.removeChild(alreadyProcessedPseudoElement); + return resolve(); + } else if (fontFamily && content !== 'none' && content !== '') { + var prefix = ~['Solid', 'Regular', 'Light', 'Duotone', 'Brands'].indexOf(fontFamily[1]) ? STYLE_TO_PREFIX[fontFamily[1].toLowerCase()] : FONT_WEIGHT_TO_PREFIX[fontWeight]; + var hexValue = toHex(content.length === 3 ? content.substr(1, 1) : content); + var iconName = byUnicode(prefix, hexValue); + var iconIdentifier = iconName; // Only convert the pseudo element in this :before/:after position into an icon if we haven't + // already done so with the same prefix and iconName + + if (iconName && (!alreadyProcessedPseudoElement || alreadyProcessedPseudoElement.getAttribute(DATA_PREFIX) !== prefix || alreadyProcessedPseudoElement.getAttribute(DATA_ICON) !== iconIdentifier)) { + node.setAttribute(pendingAttribute, iconIdentifier); + + if (alreadyProcessedPseudoElement) { + // Delete the old one, since we're replacing it with a new one + node.removeChild(alreadyProcessedPseudoElement); + } + + var meta = blankMeta(); + var extra = meta.extra; + extra.attributes[DATA_FA_PSEUDO_ELEMENT] = position; + findIcon(iconName, prefix).then(function (main) { + var abstract = makeInlineSvgAbstract(_objectSpread({}, meta, { + icons: { + main: main, + mask: emptyCanonicalIcon() + }, + prefix: prefix, + iconName: iconIdentifier, + extra: extra, + watchable: true + })); + var element = DOCUMENT.createElement('svg'); + + if (position === ':before') { + node.insertBefore(element, node.firstChild); + } else { + node.appendChild(element); + } + + element.outerHTML = abstract.map(function (a) { + return toHtml(a); + }).join('\n'); + node.removeAttribute(pendingAttribute); + resolve(); + }).catch(reject); + } else { + resolve(); + } + } else { + resolve(); + } + }); + } + + function replace(node) { + return picked.all([replaceForPosition(node, ':before'), replaceForPosition(node, ':after')]); + } + + function processable(node) { + return node.parentNode !== document.head && !~TAGNAMES_TO_SKIP_FOR_PSEUDOELEMENTS.indexOf(node.tagName.toUpperCase()) && !node.getAttribute(DATA_FA_PSEUDO_ELEMENT) && (!node.parentNode || node.parentNode.tagName !== 'svg'); + } + + function searchPseudoElements (root) { + if (!IS_DOM) return; + return new picked(function (resolve, reject) { + var operations = toArray(root.querySelectorAll('*')).filter(processable).map(replace); + var end = perf.begin('searchPseudoElements'); + disableObservation(); + picked.all(operations).then(function () { + end(); + enableObservation(); + resolve(); + }).catch(function () { + end(); + enableObservation(); + reject(); + }); + }); + } + + var baseStyles = "svg:not(:root).svg-inline--fa {\n overflow: visible;\n}\n\n.svg-inline--fa {\n display: inline-block;\n font-size: inherit;\n height: 1em;\n overflow: visible;\n vertical-align: -0.125em;\n}\n.svg-inline--fa.fa-lg {\n vertical-align: -0.225em;\n}\n.svg-inline--fa.fa-w-1 {\n width: 0.0625em;\n}\n.svg-inline--fa.fa-w-2 {\n width: 0.125em;\n}\n.svg-inline--fa.fa-w-3 {\n width: 0.1875em;\n}\n.svg-inline--fa.fa-w-4 {\n width: 0.25em;\n}\n.svg-inline--fa.fa-w-5 {\n width: 0.3125em;\n}\n.svg-inline--fa.fa-w-6 {\n width: 0.375em;\n}\n.svg-inline--fa.fa-w-7 {\n width: 0.4375em;\n}\n.svg-inline--fa.fa-w-8 {\n width: 0.5em;\n}\n.svg-inline--fa.fa-w-9 {\n width: 0.5625em;\n}\n.svg-inline--fa.fa-w-10 {\n width: 0.625em;\n}\n.svg-inline--fa.fa-w-11 {\n width: 0.6875em;\n}\n.svg-inline--fa.fa-w-12 {\n width: 0.75em;\n}\n.svg-inline--fa.fa-w-13 {\n width: 0.8125em;\n}\n.svg-inline--fa.fa-w-14 {\n width: 0.875em;\n}\n.svg-inline--fa.fa-w-15 {\n width: 0.9375em;\n}\n.svg-inline--fa.fa-w-16 {\n width: 1em;\n}\n.svg-inline--fa.fa-w-17 {\n width: 1.0625em;\n}\n.svg-inline--fa.fa-w-18 {\n width: 1.125em;\n}\n.svg-inline--fa.fa-w-19 {\n width: 1.1875em;\n}\n.svg-inline--fa.fa-w-20 {\n width: 1.25em;\n}\n.svg-inline--fa.fa-pull-left {\n margin-right: 0.3em;\n width: auto;\n}\n.svg-inline--fa.fa-pull-right {\n margin-left: 0.3em;\n width: auto;\n}\n.svg-inline--fa.fa-border {\n height: 1.5em;\n}\n.svg-inline--fa.fa-li {\n width: 2em;\n}\n.svg-inline--fa.fa-fw {\n width: 1.25em;\n}\n\n.fa-layers svg.svg-inline--fa {\n bottom: 0;\n left: 0;\n margin: auto;\n position: absolute;\n right: 0;\n top: 0;\n}\n\n.fa-layers {\n display: inline-block;\n height: 1em;\n position: relative;\n text-align: center;\n vertical-align: -0.125em;\n width: 1em;\n}\n.fa-layers svg.svg-inline--fa {\n -webkit-transform-origin: center center;\n transform-origin: center center;\n}\n\n.fa-layers-counter, .fa-layers-text {\n display: inline-block;\n position: absolute;\n text-align: center;\n}\n\n.fa-layers-text {\n left: 50%;\n top: 50%;\n -webkit-transform: translate(-50%, -50%);\n transform: translate(-50%, -50%);\n -webkit-transform-origin: center center;\n transform-origin: center center;\n}\n\n.fa-layers-counter {\n background-color: #ff253a;\n border-radius: 1em;\n -webkit-box-sizing: border-box;\n box-sizing: border-box;\n color: #fff;\n height: 1.5em;\n line-height: 1;\n max-width: 5em;\n min-width: 1.5em;\n overflow: hidden;\n padding: 0.25em;\n right: 0;\n text-overflow: ellipsis;\n top: 0;\n -webkit-transform: scale(0.25);\n transform: scale(0.25);\n -webkit-transform-origin: top right;\n transform-origin: top right;\n}\n\n.fa-layers-bottom-right {\n bottom: 0;\n right: 0;\n top: auto;\n -webkit-transform: scale(0.25);\n transform: scale(0.25);\n -webkit-transform-origin: bottom right;\n transform-origin: bottom right;\n}\n\n.fa-layers-bottom-left {\n bottom: 0;\n left: 0;\n right: auto;\n top: auto;\n -webkit-transform: scale(0.25);\n transform: scale(0.25);\n -webkit-transform-origin: bottom left;\n transform-origin: bottom left;\n}\n\n.fa-layers-top-right {\n right: 0;\n top: 0;\n -webkit-transform: scale(0.25);\n transform: scale(0.25);\n -webkit-transform-origin: top right;\n transform-origin: top right;\n}\n\n.fa-layers-top-left {\n left: 0;\n right: auto;\n top: 0;\n -webkit-transform: scale(0.25);\n transform: scale(0.25);\n -webkit-transform-origin: top left;\n transform-origin: top left;\n}\n\n.fa-lg {\n font-size: 1.3333333333em;\n line-height: 0.75em;\n vertical-align: -0.0667em;\n}\n\n.fa-xs {\n font-size: 0.75em;\n}\n\n.fa-sm {\n font-size: 0.875em;\n}\n\n.fa-1x {\n font-size: 1em;\n}\n\n.fa-2x {\n font-size: 2em;\n}\n\n.fa-3x {\n font-size: 3em;\n}\n\n.fa-4x {\n font-size: 4em;\n}\n\n.fa-5x {\n font-size: 5em;\n}\n\n.fa-6x {\n font-size: 6em;\n}\n\n.fa-7x {\n font-size: 7em;\n}\n\n.fa-8x {\n font-size: 8em;\n}\n\n.fa-9x {\n font-size: 9em;\n}\n\n.fa-10x {\n font-size: 10em;\n}\n\n.fa-fw {\n text-align: center;\n width: 1.25em;\n}\n\n.fa-ul {\n list-style-type: none;\n margin-left: 2.5em;\n padding-left: 0;\n}\n.fa-ul > li {\n position: relative;\n}\n\n.fa-li {\n left: -2em;\n position: absolute;\n text-align: center;\n width: 2em;\n line-height: inherit;\n}\n\n.fa-border {\n border: solid 0.08em #eee;\n border-radius: 0.1em;\n padding: 0.2em 0.25em 0.15em;\n}\n\n.fa-pull-left {\n float: left;\n}\n\n.fa-pull-right {\n float: right;\n}\n\n.fa.fa-pull-left,\n.fas.fa-pull-left,\n.far.fa-pull-left,\n.fal.fa-pull-left,\n.fab.fa-pull-left {\n margin-right: 0.3em;\n}\n.fa.fa-pull-right,\n.fas.fa-pull-right,\n.far.fa-pull-right,\n.fal.fa-pull-right,\n.fab.fa-pull-right {\n margin-left: 0.3em;\n}\n\n.fa-spin {\n -webkit-animation: fa-spin 2s infinite linear;\n animation: fa-spin 2s infinite linear;\n}\n\n.fa-pulse {\n -webkit-animation: fa-spin 1s infinite steps(8);\n animation: fa-spin 1s infinite steps(8);\n}\n\n@-webkit-keyframes fa-spin {\n 0% {\n -webkit-transform: rotate(0deg);\n transform: rotate(0deg);\n }\n 100% {\n -webkit-transform: rotate(360deg);\n transform: rotate(360deg);\n }\n}\n\n@keyframes fa-spin {\n 0% {\n -webkit-transform: rotate(0deg);\n transform: rotate(0deg);\n }\n 100% {\n -webkit-transform: rotate(360deg);\n transform: rotate(360deg);\n }\n}\n.fa-rotate-90 {\n -ms-filter: \"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)\";\n -webkit-transform: rotate(90deg);\n transform: rotate(90deg);\n}\n\n.fa-rotate-180 {\n -ms-filter: \"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)\";\n -webkit-transform: rotate(180deg);\n transform: rotate(180deg);\n}\n\n.fa-rotate-270 {\n -ms-filter: \"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)\";\n -webkit-transform: rotate(270deg);\n transform: rotate(270deg);\n}\n\n.fa-flip-horizontal {\n -ms-filter: \"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)\";\n -webkit-transform: scale(-1, 1);\n transform: scale(-1, 1);\n}\n\n.fa-flip-vertical {\n -ms-filter: \"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)\";\n -webkit-transform: scale(1, -1);\n transform: scale(1, -1);\n}\n\n.fa-flip-both, .fa-flip-horizontal.fa-flip-vertical {\n -ms-filter: \"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)\";\n -webkit-transform: scale(-1, -1);\n transform: scale(-1, -1);\n}\n\n:root .fa-rotate-90,\n:root .fa-rotate-180,\n:root .fa-rotate-270,\n:root .fa-flip-horizontal,\n:root .fa-flip-vertical,\n:root .fa-flip-both {\n -webkit-filter: none;\n filter: none;\n}\n\n.fa-stack {\n display: inline-block;\n height: 2em;\n position: relative;\n width: 2.5em;\n}\n\n.fa-stack-1x,\n.fa-stack-2x {\n bottom: 0;\n left: 0;\n margin: auto;\n position: absolute;\n right: 0;\n top: 0;\n}\n\n.svg-inline--fa.fa-stack-1x {\n height: 1em;\n width: 1.25em;\n}\n.svg-inline--fa.fa-stack-2x {\n height: 2em;\n width: 2.5em;\n}\n\n.fa-inverse {\n color: #fff;\n}\n\n.sr-only {\n border: 0;\n clip: rect(0, 0, 0, 0);\n height: 1px;\n margin: -1px;\n overflow: hidden;\n padding: 0;\n position: absolute;\n width: 1px;\n}\n\n.sr-only-focusable:active, .sr-only-focusable:focus {\n clip: auto;\n height: auto;\n margin: 0;\n overflow: visible;\n position: static;\n width: auto;\n}\n\n.svg-inline--fa .fa-primary {\n fill: var(--fa-primary-color, currentColor);\n opacity: 1;\n opacity: var(--fa-primary-opacity, 1);\n}\n\n.svg-inline--fa .fa-secondary {\n fill: var(--fa-secondary-color, currentColor);\n opacity: 0.4;\n opacity: var(--fa-secondary-opacity, 0.4);\n}\n\n.svg-inline--fa.fa-swap-opacity .fa-primary {\n opacity: 0.4;\n opacity: var(--fa-secondary-opacity, 0.4);\n}\n\n.svg-inline--fa.fa-swap-opacity .fa-secondary {\n opacity: 1;\n opacity: var(--fa-primary-opacity, 1);\n}\n\n.svg-inline--fa mask .fa-primary,\n.svg-inline--fa mask .fa-secondary {\n fill: black;\n}\n\n.fad.fa-inverse {\n color: #fff;\n}"; + + function css () { + var dfp = DEFAULT_FAMILY_PREFIX; + var drc = DEFAULT_REPLACEMENT_CLASS; + var fp = config.familyPrefix; + var rc = config.replacementClass; + var s = baseStyles; + + if (fp !== dfp || rc !== drc) { + var dPatt = new RegExp("\\.".concat(dfp, "\\-"), 'g'); + var customPropPatt = new RegExp("\\--".concat(dfp, "\\-"), 'g'); + var rPatt = new RegExp("\\.".concat(drc), 'g'); + s = s.replace(dPatt, ".".concat(fp, "-")).replace(customPropPatt, "--".concat(fp, "-")).replace(rPatt, ".".concat(rc)); + } + + return s; + } + + var Library = + /*#__PURE__*/ + function () { + function Library() { + _classCallCheck(this, Library); + + this.definitions = {}; + } + + _createClass(Library, [{ + key: "add", + value: function add() { + var _this = this; + + for (var _len = arguments.length, definitions = new Array(_len), _key = 0; _key < _len; _key++) { + definitions[_key] = arguments[_key]; + } + + var additions = definitions.reduce(this._pullDefinitions, {}); + Object.keys(additions).forEach(function (key) { + _this.definitions[key] = _objectSpread({}, _this.definitions[key] || {}, additions[key]); + defineIcons(key, additions[key]); + build(); + }); + } + }, { + key: "reset", + value: function reset() { + this.definitions = {}; + } + }, { + key: "_pullDefinitions", + value: function _pullDefinitions(additions, definition) { + var normalized = definition.prefix && definition.iconName && definition.icon ? { + 0: definition + } : definition; + Object.keys(normalized).map(function (key) { + var _normalized$key = normalized[key], + prefix = _normalized$key.prefix, + iconName = _normalized$key.iconName, + icon = _normalized$key.icon; + if (!additions[prefix]) additions[prefix] = {}; + additions[prefix][iconName] = icon; + }); + return additions; + } + }]); + + return Library; + }(); + + function ensureCss() { + if (config.autoAddCss && !_cssInserted) { + insertCss(css()); + + _cssInserted = true; + } + } + + function apiObject(val, abstractCreator) { + Object.defineProperty(val, 'abstract', { + get: abstractCreator + }); + Object.defineProperty(val, 'html', { + get: function get() { + return val.abstract.map(function (a) { + return toHtml(a); + }); + } + }); + Object.defineProperty(val, 'node', { + get: function get() { + if (!IS_DOM) return; + var container = DOCUMENT.createElement('div'); + container.innerHTML = val.html; + return container.children; + } + }); + return val; + } + + function findIconDefinition(iconLookup) { + var _iconLookup$prefix = iconLookup.prefix, + prefix = _iconLookup$prefix === void 0 ? 'fa' : _iconLookup$prefix, + iconName = iconLookup.iconName; + if (!iconName) return; + return iconFromMapping(library.definitions, prefix, iconName) || iconFromMapping(namespace.styles, prefix, iconName); + } + + function resolveIcons(next) { + return function (maybeIconDefinition) { + var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + var iconDefinition = (maybeIconDefinition || {}).icon ? maybeIconDefinition : findIconDefinition(maybeIconDefinition || {}); + var mask = params.mask; + + if (mask) { + mask = (mask || {}).icon ? mask : findIconDefinition(mask || {}); + } + + return next(iconDefinition, _objectSpread({}, params, { + mask: mask + })); + }; + } + + var library = new Library(); + var noAuto = function noAuto() { + config.autoReplaceSvg = false; + config.observeMutations = false; + disconnect(); + }; + var _cssInserted = false; + var dom = { + i2svg: function i2svg() { + var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + + if (IS_DOM) { + ensureCss(); + var _params$node = params.node, + node = _params$node === void 0 ? DOCUMENT : _params$node, + _params$callback = params.callback, + callback = _params$callback === void 0 ? function () {} : _params$callback; + + if (config.searchPseudoElements) { + searchPseudoElements(node); + } + + return onTree(node, callback); + } else { + return picked.reject('Operation requires a DOM of some kind.'); + } + }, + css: css, + insertCss: function insertCss$$1() { + if (!_cssInserted) { + insertCss(css()); + + _cssInserted = true; + } + }, + watch: function watch() { + var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + var autoReplaceSvgRoot = params.autoReplaceSvgRoot, + observeMutationsRoot = params.observeMutationsRoot; + + if (config.autoReplaceSvg === false) { + config.autoReplaceSvg = true; + } + + config.observeMutations = true; + domready(function () { + autoReplace({ + autoReplaceSvgRoot: autoReplaceSvgRoot + }); + observe({ + treeCallback: onTree, + nodeCallback: onNode, + pseudoElementsCallback: searchPseudoElements, + observeMutationsRoot: observeMutationsRoot + }); + }); + } + }; + var parse = { + transform: function transform(transformString) { + return parseTransformString(transformString); + } + }; + var icon = resolveIcons(function (iconDefinition) { + var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + var _params$transform = params.transform, + transform = _params$transform === void 0 ? meaninglessTransform : _params$transform, + _params$symbol = params.symbol, + symbol = _params$symbol === void 0 ? false : _params$symbol, + _params$mask = params.mask, + mask = _params$mask === void 0 ? null : _params$mask, + _params$maskId = params.maskId, + maskId = _params$maskId === void 0 ? null : _params$maskId, + _params$title = params.title, + title = _params$title === void 0 ? null : _params$title, + _params$titleId = params.titleId, + titleId = _params$titleId === void 0 ? null : _params$titleId, + _params$classes = params.classes, + classes = _params$classes === void 0 ? [] : _params$classes, + _params$attributes = params.attributes, + attributes = _params$attributes === void 0 ? {} : _params$attributes, + _params$styles = params.styles, + styles = _params$styles === void 0 ? {} : _params$styles; + if (!iconDefinition) return; + var prefix = iconDefinition.prefix, + iconName = iconDefinition.iconName, + icon = iconDefinition.icon; + return apiObject(_objectSpread({ + type: 'icon' + }, iconDefinition), function () { + ensureCss(); + + if (config.autoA11y) { + if (title) { + attributes['aria-labelledby'] = "".concat(config.replacementClass, "-title-").concat(titleId || nextUniqueId()); + } else { + attributes['aria-hidden'] = 'true'; + attributes['focusable'] = 'false'; + } + } + + return makeInlineSvgAbstract({ + icons: { + main: asFoundIcon(icon), + mask: mask ? asFoundIcon(mask.icon) : { + found: false, + width: null, + height: null, + icon: {} + } + }, + prefix: prefix, + iconName: iconName, + transform: _objectSpread({}, meaninglessTransform, transform), + symbol: symbol, + title: title, + maskId: maskId, + titleId: titleId, + extra: { + attributes: attributes, + styles: styles, + classes: classes + } + }); + }); + }); + var text = function text(content) { + var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + var _params$transform2 = params.transform, + transform = _params$transform2 === void 0 ? meaninglessTransform : _params$transform2, + _params$title2 = params.title, + title = _params$title2 === void 0 ? null : _params$title2, + _params$classes2 = params.classes, + classes = _params$classes2 === void 0 ? [] : _params$classes2, + _params$attributes2 = params.attributes, + attributes = _params$attributes2 === void 0 ? {} : _params$attributes2, + _params$styles2 = params.styles, + styles = _params$styles2 === void 0 ? {} : _params$styles2; + return apiObject({ + type: 'text', + content: content + }, function () { + ensureCss(); + return makeLayersTextAbstract({ + content: content, + transform: _objectSpread({}, meaninglessTransform, transform), + title: title, + extra: { + attributes: attributes, + styles: styles, + classes: ["".concat(config.familyPrefix, "-layers-text")].concat(_toConsumableArray(classes)) + } + }); + }); + }; + var counter = function counter(content) { + var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + var _params$title3 = params.title, + title = _params$title3 === void 0 ? null : _params$title3, + _params$classes3 = params.classes, + classes = _params$classes3 === void 0 ? [] : _params$classes3, + _params$attributes3 = params.attributes, + attributes = _params$attributes3 === void 0 ? {} : _params$attributes3, + _params$styles3 = params.styles, + styles = _params$styles3 === void 0 ? {} : _params$styles3; + return apiObject({ + type: 'counter', + content: content + }, function () { + ensureCss(); + return makeLayersCounterAbstract({ + content: content.toString(), + title: title, + extra: { + attributes: attributes, + styles: styles, + classes: ["".concat(config.familyPrefix, "-layers-counter")].concat(_toConsumableArray(classes)) + } + }); + }); + }; + var layer = function layer(assembler) { + var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + var _params$classes4 = params.classes, + classes = _params$classes4 === void 0 ? [] : _params$classes4; + return apiObject({ + type: 'layer' + }, function () { + ensureCss(); + var children = []; + assembler(function (args) { + Array.isArray(args) ? args.map(function (a) { + children = children.concat(a.abstract); + }) : children = children.concat(args.abstract); + }); + return [{ + tag: 'span', + attributes: { + class: ["".concat(config.familyPrefix, "-layers")].concat(_toConsumableArray(classes)).join(' ') + }, + children: children + }]; + }); + }; + var api = { + noAuto: noAuto, + config: config, + dom: dom, + library: library, + parse: parse, + findIconDefinition: findIconDefinition, + icon: icon, + text: text, + counter: counter, + layer: layer, + toHtml: toHtml + }; + + var autoReplace = function autoReplace() { + var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + var _params$autoReplaceSv = params.autoReplaceSvgRoot, + autoReplaceSvgRoot = _params$autoReplaceSv === void 0 ? DOCUMENT : _params$autoReplaceSv; + if ((Object.keys(namespace.styles).length > 0 || config.autoFetchSvg) && IS_DOM && config.autoReplaceSvg) api.dom.i2svg({ + node: autoReplaceSvgRoot + }); + }; + + function bootstrap() { + if (IS_BROWSER) { + if (!WINDOW.FontAwesome) { + WINDOW.FontAwesome = api; + } + + domready(function () { + autoReplace(); + observe({ + treeCallback: onTree, + nodeCallback: onNode, + pseudoElementsCallback: searchPseudoElements + }); + }); + } + + namespace.hooks = _objectSpread({}, namespace.hooks, { + addPack: function addPack(prefix, icons) { + namespace.styles[prefix] = _objectSpread({}, namespace.styles[prefix] || {}, icons); + build(); + autoReplace(); + }, + addShims: function addShims(shims) { + var _namespace$shims; + + (_namespace$shims = namespace.shims).push.apply(_namespace$shims, _toConsumableArray(shims)); + + build(); + autoReplace(); + } + }); + } + + bunker(bootstrap); + +}()); + diff --git a/debian/missing-sources/footable.js b/debian/missing-sources/footable.js new file mode 100644 index 0000000..7131ca8 --- /dev/null +++ b/debian/missing-sources/footable.js @@ -0,0 +1,7371 @@ +/* +* FooTable v3 - FooTable is a jQuery plugin that aims to make HTML tables on smaller devices look awesome. +* @version 3.1.6 +* @link http://fooplugins.com +* @copyright Steven Usher & Brad Vincent 2015 +* @license Released under the GPLv3 license. +*/ +(function($, F){ + // add in console we use in case it's missing + window.console = window.console || { log:function(){}, error:function(){} }; + + /** + * The jQuery plugin initializer. + * @function jQuery.fn.footable + * @param {(object|FooTable.Defaults)} [options] - The options to initialize the plugin with. + * @param {function} [ready] - A callback function to execute for each initialized plugin. + * @returns {jQuery} + */ + $.fn.footable = function (options, ready) { + options = options || {}; + // make sure we only work with tables + return this.filter('table').each(function (i, tbl) { + F.init(tbl, options, ready); + }); + }; + + var debug_defaults = { + events: [] + }; + F.__debug__ = JSON.parse(localStorage.getItem('footable_debug')) || false; + F.__debug_options__ = JSON.parse(localStorage.getItem('footable_debug_options')) || debug_defaults; + + /** + * Gets or sets the internal debug variable which enables some additional logging to the console. + * When enabled this value is stored in the localStorage so it can persist across page reloads. + * @param {boolean} value - Whether or not to enable additional logging. + * @param {object} [options] - Any debug specific options. + * @returns {(boolean|undefined)} + */ + F.debug = function(value, options){ + if (!F.is.boolean(value)) return F.__debug__; + F.__debug__ = value; + if (F.__debug__){ + localStorage.setItem('footable_debug', JSON.stringify(F.__debug__)); + F.__debug_options__ = $.extend(true, {}, debug_defaults, options || {}); + if (F.is.hash(options)){ + localStorage.setItem('footable_debug_options', JSON.stringify(F.__debug_options__)); + } + } else { + localStorage.removeItem('footable_debug'); + localStorage.removeItem('footable_debug_options'); + } + }; + + /** + * Gets the FooTable instance of the supplied table if one exists. + * @param {(jQuery|jQuery.selector|HTMLTableElement)} table - The jQuery table object, selector or the HTMLTableElement to retrieve FooTable from. + * @returns {(FooTable.Table|undefined)} + */ + F.get = function(table){ + return $(table).first().data('__FooTable__'); + }; + + /** + * Initializes a new instance of FooTable on the supplied table. + * @param {(jQuery|jQuery.selector|HTMLTableElement)} table - The jQuery table object, selector or the HTMLTableElement to initialize FooTable on. + * @param {object} options - The options to initialize FooTable with. + * @param {function} [ready] - A callback function to execute once the plugin is initialized. + * @returns {FooTable.Table} + */ + F.init = function(table, options, ready){ + var ft = F.get(table); + if (ft instanceof F.Table) ft.destroy(); + return new F.Table(table, options, ready); + }; + + /** + * Gets the FooTable.Row instance for the supplied element. + * @param {(jQuery|jQuery.selector|HTMLTableElement)} element - A jQuery object, selector or the HTMLElement of an element to retrieve the FooTable.Row for. + * @returns {FooTable.Row} + */ + F.getRow = function(element){ + // to get the FooTable.Row object simply walk up the DOM, find the TR and grab the __FooTableRow__ data value + var $row = $(element).closest('tr'); + // if this is a detail row get the previous row in the table to get the main TR element + if ($row.hasClass('footable-detail-row')){ + $row = $row.prev(); + } + // grab the row object + return $row.data('__FooTableRow__'); + }; + + // The below are external type definitions mainly used as pointers to jQuery docs for important information + /** + * jQuery is a fast, small, and feature-rich JavaScript library. It makes things like HTML document traversal and manipulation, event handling, animation, and Ajax much simpler with an easy-to-use API + * that works across a multitude of browsers. With a combination of versatility and extensibility, jQuery has changed the way that millions of people write JavaScript. + * @name jQuery + * @constructor + * @returns {jQuery} + * @see {@link http://api.jquery.com/} + */ + + /** + * This object provides a subset of the methods of the Deferred object (then, done, fail, always, pipe, and state) to prevent users from changing the state of the Deferred. + * @typedef {object} jQuery.Promise + * @see {@link http://api.jquery.com/Types/#Promise} + */ + + /** + * As of jQuery 1.5, the Deferred object provides a way to register multiple callbacks into self-managed callback queues, invoke callback queues as appropriate, + * and relay the success or failure state of any synchronous or asynchronous function. + * @typedef {object} jQuery.Deferred + * @see {@link http://api.jquery.com/Types/#Deferred} + */ + + /** + * jQuery's event system normalizes the event object according to W3C standards. The event object is guaranteed to be passed to the event handler. Most properties from + * the original event are copied over and normalized to the new event object. + * @typedef {object} jQuery.Event + * @see {@link http://api.jquery.com/category/events/event-object/} + */ + + /** + * Provides a way to execute callback functions based on one or more objects, usually Deferred objects that represent asynchronous events. + * @memberof jQuery + * @function when + * @param {...jQuery.Deferred} deferreds - Any number of deferred objects to wait for. + * @returns {jQuery.Promise} + * @see {@link http://api.jquery.com/jQuery.when/} + */ + + /** + * The jQuery.fn namespace used to register plugins with jQuery. + * @memberof jQuery + * @namespace fn + * @see {@link http://learn.jquery.com/plugins/basic-plugin-creation/} + */ +})( + jQuery, + /** + * The core FooTable namespace containing all the plugin code. + * @namespace + */ + FooTable = window.FooTable || {} +); +(function(F){ + var returnTrue = function(){ return true; }; + + /** + * This namespace contains commonly used array utility methods. + * @namespace {object} FooTable.arr + */ + F.arr = {}; + + /** + * Iterates over each item in the supplied array and performs the supplied function passing in the current item as the first argument. + * @memberof FooTable.arr + * @function each + * @param {Array} array - The array to iterate + * @param {function} func - The function to execute for each item. The first argument supplied to this function is the current item and the second is the current index. + */ + F.arr.each = function (array, func) { + if (!F.is.array(array) || !F.is.fn(func)) return; + for (var i = 0, len = array.length; i < len; i++) { + if (func(array[i], i) === false) break; + } + }; + + /** + * Get all items in the supplied array that optionally matches the supplied where function. If no items are found an empty array is returned. + * @memberof FooTable.arr + * @function get + * @param {Array} array - The array to get items from. + * @param {function} where - This function must return a boolean value, true includes the item in the result array. + * @returns {Array} + */ + F.arr.get = function (array, where) { + var result = []; + if (!F.is.array(array)) return result; + if (!F.is.fn(where)) return array; + for (var i = 0, len = array.length; i < len; i++) { + if (where(array[i], i)) result.push(array[i]); + } + return result; + }; + + /** + * Get a boolean value indicating if any item exists in the supplied array that optionally matches the supplied where function. + * @memberof FooTable.arr + * @function any + * @param {Array} array - The array to check. + * @param {function} [where] - [Optional] This function must return a boolean value, true indicates that the current item is a valid match. + * @returns {boolean} + */ + F.arr.any = function (array, where) { + if (!F.is.array(array)) return false; + where = F.is.fn(where) ? where : returnTrue; + for (var i = 0, len = array.length; i < len; i++) { + if (where(array[i], i)) return true; + } + return false; + }; + + /** + * Checks if the supplied value exists in the array. + * @memberof FooTable.arr + * @function contains + * @param {Array} array - The array to check. + * @param {*} value - The value to check for. + * @returns {boolean} + */ + F.arr.contains = function(array, value){ + if (!F.is.array(array) || F.is.undef(value)) return false; + for (var i = 0, len = array.length; i < len; i++) { + if (array[i] == value) return true; + } + return false; + }; + + /** + * Get the first item in the supplied array that optionally matches the supplied where function. If no item is found null is returned. + * @memberof FooTable.arr + * @function first + * @param {Array} array - The array to get the item from. + * @param {function} [where] - [Optional] This function must return a boolean value, true indicates that the current item can be returned. + * @returns {(*|null)} + */ + F.arr.first = function (array, where) { + if (!F.is.array(array)) return null; + where = F.is.fn(where) ? where : returnTrue; + for (var i = 0, len = array.length; i < len; i++) { + if (where(array[i], i)) return array[i]; + } + return null; + }; + + /** + * Creates a new array from the results of the supplied getter function. If no items are found an empty array is returned, to exclude an item from the results return null. + * @memberof FooTable.arr + * @function map + * @param {Array} array - The array to iterate. + * @param {function} getter - This function must return either a new value or null. + * The first argument is the result being returned at this point in the iteration. The second argument is the current item being iterated. + * @returns {(*|null)} + */ + F.arr.map = function (array, getter) { + var result = [], returned = null; + if (!F.is.array(array) || !F.is.fn(getter)) return result; + for (var i = 0, len = array.length; i < len; i++) { + if ((returned = getter(array[i], i)) != null) result.push(returned); + } + return result; + }; + + /** + * Removes items from the array matching the supplied where function. All removed items are returned in a new array. + * @memberof FooTable.arr + * @function remove + * @param {Array} array - The array to iterate and remove items from. + * @param {function} where - This function must return a boolean value, true includes the item in the result array. + * @returns {*} + */ + F.arr.remove = function (array, where) { + var remove = [], removed = []; + if (!F.is.array(array) || !F.is.fn(where)) return removed; + var i = 0, len = array.length; + for (; i < len; i++) { + if (where(array[i], i, removed)){ + remove.push(i); + removed.push(array[i]); + } + } + // sort the indexes to be removed from largest to smallest + remove.sort(function(a, b){ return b - a; }); + i = 0; len = remove.length; + for(; i < len; i++){ + var index = remove[i] - i; + array.splice(index, 1); + } + return removed; + }; + + /** + * Deletes a single item from the array. The item if removed is returned. + * @memberof FooTable.arr + * @function delete + * @param {Array} array - The array to iterate and delete the item from. + * @param {*} item - The item to find and delete. + * @returns {(*|null)} + */ + F.arr.delete = function(array, item){ + var remove = -1, removed = null; + if (!F.is.array(array) || F.is.undef(item)) return removed; + var i = 0, len = array.length; + for (; i < len; i++) { + if (array[i] == item){ + remove = i; + removed = array[i]; + break; + } + } + if (remove != -1) array.splice(remove, 1); + return removed; + }; + + /** + * Replaces a single item in the array with a new one. + * @memberof FooTable.arr + * @function replace + * @param {Array} array - The array to iterate and replace the item in. + * @param {*} oldItem - The item to be replaced. + * @param {*} newItem - The item to be inserted. + */ + F.arr.replace = function(array, oldItem, newItem){ + var index = array.indexOf(oldItem); + if (index !== -1) array[index] = newItem; + }; + +})(FooTable); +(function (F) { + + /** + * This namespace contains commonly used 'is' type methods that return boolean values. + * @namespace FooTable.is + */ + F.is = {}; + + /** + * Checks if the type of the value is the same as that supplied. + * @memberof FooTable.is + * @function type + * @param {*} value - The value to check the type of. + * @param {string} type - The type to check for. + * @returns {boolean} + */ + F.is.type = function (value, type) { + return typeof value === type; + }; + + /** + * Checks if the value is defined. + * @memberof FooTable.is + * @function defined + * @param {*} value - The value to check is defined. + * @returns {boolean} + */ + F.is.defined = function (value) { + return typeof value !== 'undefined'; + }; + + /** + * Checks if the value is undefined. + * @memberof FooTable.is + * @function undef + * @param {*} value - The value to check is undefined. + * @returns {boolean} + */ + F.is.undef = function (value) { + return typeof value === 'undefined'; + }; + + /** + * Checks if the value is an array. + * @memberof FooTable.is + * @function array + * @param {*} value - The value to check. + * @returns {boolean} + */ + F.is.array = function (value) { + return '[object Array]' === Object.prototype.toString.call(value); + }; + + /** + * Checks if the value is a date. + * @memberof FooTable.is + * @function date + * @param {*} value - The value to check. + * @returns {boolean} + */ + F.is.date = function (value) { + return '[object Date]' === Object.prototype.toString.call(value) && !isNaN(value.getTime()); + }; + + /** + * Checks if the value is a boolean. + * @memberof FooTable.is + * @function boolean + * @param {*} value - The value to check. + * @returns {boolean} + */ + F.is.boolean = function (value) { + return '[object Boolean]' === Object.prototype.toString.call(value); + }; + + /** + * Checks if the value is a string. + * @memberof FooTable.is + * @function string + * @param {*} value - The value to check. + * @returns {boolean} + */ + F.is.string = function (value) { + return '[object String]' === Object.prototype.toString.call(value); + }; + + /** + * Checks if the value is a number. + * @memberof FooTable.is + * @function number + * @param {*} value - The value to check. + * @returns {boolean} + */ + F.is.number = function (value) { + return '[object Number]' === Object.prototype.toString.call(value) && !isNaN(value); + }; + + /** + * Checks if the value is a function. + * @memberof FooTable.is + * @function fn + * @param {*} value - The value to check. + * @returns {boolean} + */ + F.is.fn = function (value) { + return (F.is.defined(window) && value === window.alert) || '[object Function]' === Object.prototype.toString.call(value); + }; + + /** + * Checks if the value is an error. + * @memberof FooTable.is + * @function error + * @param {*} value - The value to check. + * @returns {boolean} + */ + F.is.error = function (value) { + return '[object Error]' === Object.prototype.toString.call(value); + }; + + /** + * Checks if the value is an object. + * @memberof FooTable.is + * @function object + * @param {*} value - The value to check. + * @returns {boolean} + */ + F.is.object = function (value) { + return '[object Object]' === Object.prototype.toString.call(value); + }; + + /** + * Checks if the value is a hash. + * @memberof FooTable.is + * @function hash + * @param {*} value - The value to check. + * @returns {boolean} + */ + F.is.hash = function (value) { + return F.is.object(value) && value.constructor === Object && !value.nodeType && !value.setInterval; + }; + + /** + * Checks if the supplied object is an HTMLElement + * @memberof FooTable.is + * @function element + * @param {object} obj - The object to check. + * @returns {boolean} + */ + F.is.element = function (obj) { + return typeof HTMLElement === 'object' + ? obj instanceof HTMLElement + : obj && typeof obj === 'object' && obj !== null && obj.nodeType === 1 && typeof obj.nodeName === 'string'; + }; + + /** + * This is a simple check to determine if an object is a jQuery promise object. It simply checks the object has a "then" and "promise" function defined. + * The promise object is created as an object literal inside of jQuery.Deferred. + * It has no prototype, nor any other truly unique properties that could be used to distinguish it. + * This method should be a little more accurate than the internal jQuery one that simply checks for a "promise" method. + * @memberof FooTable.is + * @function promise + * @param {object} obj - The object to check. + * @returns {boolean} + */ + F.is.promise = function(obj){ + return F.is.object(obj) && F.is.fn(obj.then) && F.is.fn(obj.promise); + }; + + /** + * Checks if the supplied object is an instance of a jQuery object. + * @memberof FooTable.is + * @function jq + * @param {object} obj - The object to check. + * @returns {boolean} + */ + F.is.jq = function(obj){ + return F.is.defined(window.jQuery) && obj instanceof jQuery && obj.length > 0; + }; + + /** + * Checks if the supplied object is a moment.js date object. + * @memberof FooTable.is + * @function moment + * @param {object} obj - The object to check. + * @returns {boolean} + */ + F.is.moment = function(obj){ + return F.is.defined(window.moment) && F.is.object(obj) && F.is.boolean(obj._isAMomentObject) + }; + + /** + * Checks if the supplied value is an object and if it is empty. + * @memberof FooTable.is + * @function emptyObject + * @param {*} value - The value to check. + * @returns {boolean} + */ + F.is.emptyObject = function(value){ + if (!F.is.hash(value)) return false; + for(var prop in value) { + if(value.hasOwnProperty(prop)) + return false; + } + return true; + }; + + /** + * Checks if the supplied value is an array and if it is empty. + * @memberof FooTable.is + * @function emptyArray + * @param {*} value - The value to check. + * @returns {boolean} + */ + F.is.emptyArray = function(value){ + return F.is.array(value) ? value.length === 0 : true; + }; + + /** + * Checks if the supplied value is a string and if it is empty. + * @memberof FooTable.is + * @function emptyString + * @param {*} value - The value to check. + * @returns {boolean} + */ + F.is.emptyString = function(value){ + return F.is.string(value) ? value.length === 0 : true; + }; + +})(FooTable); +(function (F) { + /** + * This namespace contains commonly used string utility methods. + * @namespace FooTable.str + */ + F.str = {}; + + /** + * Checks if the supplied string contains the given substring. + * @memberof FooTable.str + * @function contains + * @param {string} str - The string to check. + * @param {string} contains - The string to check for. + * @param {boolean} [ignoreCase=false] - Whether or not to ignore casing when performing the check. + * @returns {boolean} + */ + F.str.contains = function (str, contains, ignoreCase) { + if (F.is.emptyString(str) || F.is.emptyString(contains)) return false; + return contains.length <= str.length + && (ignoreCase ? str.toUpperCase().indexOf(contains.toUpperCase()) : str.indexOf(contains)) !== -1; + }; + + /** + * Checks if the supplied string contains the exact given substring. + * @memberof FooTable.str + * @function contains + * @param {string} str - The string to check. + * @param {string} contains - The string to check for. + * @param {boolean} [ignoreCase=false] - Whether or not to ignore casing when performing the check. + * @returns {boolean} + */ + F.str.containsExact = function (str, contains, ignoreCase) { + if (F.is.emptyString(str) || F.is.emptyString(contains) || contains.length > str.length) return false; + return new RegExp('\\b'+ F.str.escapeRegExp(contains)+'\\b', ignoreCase ? 'i' : '').test(str); + }; + + /** + * Checks if the supplied string contains the given word. + * @memberof FooTable.str + * @function containsWord + * @param {string} str - The string to check. + * @param {string} word - The word to check for. + * @param {boolean} [ignoreCase=false] - Whether or not to ignore casing when performing the check. + * @returns {boolean} + */ + F.str.containsWord = function(str, word, ignoreCase){ + if (F.is.emptyString(str) || F.is.emptyString(word) || str.length < word.length) + return false; + var parts = str.split(/\W/); + for (var i = 0, len = parts.length; i < len; i++){ + if (ignoreCase ? parts[i].toUpperCase() == word.toUpperCase() : parts[i] == word) return true; + } + return false; + }; + + /** + * Returns the remainder of a string split on the first index of the given substring. + * @memberof FooTable.str + * @function from + * @param {string} str - The string to split. + * @param {string} from - The substring to split on. + * @returns {string} + */ + F.str.from = function (str, from) { + if (F.is.emptyString(str)) return str; + return F.str.contains(str, from) ? str.substring(str.indexOf(from) + 1) : str; + }; + + /** + * Checks if a string starts with the supplied prefix. + * @memberof FooTable.str + * @function startsWith + * @param {string} str - The string to check. + * @param {string} prefix - The prefix to check for. + * @returns {boolean} + */ + F.str.startsWith = function (str, prefix) { + if (F.is.emptyString(str)) return str == prefix; + return str.slice(0, prefix.length) == prefix; + }; + + /** + * Takes the supplied string and converts it to camel case. + * @memberof FooTable.str + * @function toCamelCase + * @param {string} str - The string to camel case. + * @returns {string} + */ + F.str.toCamelCase = function (str) { + if (F.is.emptyString(str)) return str; + if (str.toUpperCase() === str) return str.toLowerCase(); + return str.replace(/^([A-Z])|[-\s_](\w)/g, function (match, p1, p2) { + if (F.is.string(p2)) return p2.toUpperCase(); + return p1.toLowerCase(); + }); + }; + + /** + * Generates a random string 9 characters long using the optional prefix if supplied. + * @memberof FooTable.str + * @function random + * @param {string} [prefix] - The prefix to append to the 9 random characters. + * @returns {string} + */ + F.str.random = function(prefix){ + prefix = F.is.emptyString(prefix) ? '' : prefix; + return prefix + Math.random().toString(36).substr(2, 9); + }; + + /** + * Escapes a string for use in a regular expression. + * @memberof FooTable.str + * @function escapeRegExp + * @param {string} str - The string to escape. + * @returns {string} + */ + F.str.escapeRegExp = function(str){ + if (F.is.emptyString(str)) return str; + return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + }; + +})(FooTable); +(function (F) { + "use strict"; + + if (!Object.create) { + Object.create = (function () { + var Object = function () {}; + return function (prototype) { + if (arguments.length > 1) + throw Error('Second argument not supported'); + + if (!F.is.object(prototype)) + throw TypeError('Argument must be an object'); + + Object.prototype = prototype; + var result = new Object(); + Object.prototype = null; + return result; + }; + })(); + } + + /** + * This base implementation does nothing except provide access to the {@link FooTable.Class#extend} method. + * @constructs FooTable.Class + * @classdesc This class is based off of John Resig's [Simple JavaScript Inheritance]{@link http://ejohn.org/blog/simple-javascript-inheritance} but it has been updated to be ES 5.1 + * compatible by implementing an [Object.create polyfill]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create#Polyfill} + * for older browsers. + * @see {@link http://ejohn.org/blog/simple-javascript-inheritance} + * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create#Polyfill} + * @returns {FooTable.Class} + */ + function Class() {} + + var __extendable__ = /xyz/.test(function () {xyz;}) ? /\b_super\b/ : /.*/; + + // this._super() within the context of the new function is a pointer to the original function + // except if the hook param is specified then the this._super variable is the result of the original function + Class.__extend__ = function(proto, name, func, original){ + // to all who venture here, here be dragons! + proto[name] = F.is.fn(original) && __extendable__.test(func) ? + (function (name, fn) { + return function () { + var tmp, ret; + tmp = this._super; + this._super = original; + ret = fn.apply(this, arguments); + this._super = tmp; + return ret; + }; + })(name, func) : func; + }; + + /** + * Creates a new class that inherits from this class which in turn allows itself to be extended or if a name and function is supplied extends only that specific function on the class. + * @param {(object|string)} arg1 - An object containing any new methods/members to implement or the name of the method to extend. + * @param {function} arg2 - If the first argument is a method name then this is the new function to replace it with. + * @returns {FooTable.Class} A new class that inherits from the base class. + * @example The below shows an example of how to implement inheritance using this method. + * var Person = FooTable.Class.extend({ + * construct: function(isDancing){ + * this.dancing = isDancing; + * }, + * dance: function(){ + * return this.dancing; + * } + * }); + * + * var Ninja = Person.extend({ + * construct: function(){ + * this._super( false ); + * }, + * dance: function(){ + * // Call the inherited version of dance() + * return this._super(); + * }, + * swingSword: function(){ + * return true; + * } + * }); + * + * var p = new Person(true); + * p.dance(); // => true + * + * var n = new Ninja(); + * n.dance(); // => false + * n.swingSword(); // => true + * + * // Should all be true + * p instanceof Person && p instanceof FooTable.Class && + * n instanceof Ninja && n instanceof Person && n instanceof FooTable.Class + */ + Class.extend = function (arg1 , arg2) { + var args = Array.prototype.slice.call(arguments); + arg1 = args.shift(); + arg2 = args.shift(); + + function __extend__(proto, name, func, original){ + // to all who venture here, here be dragons! + proto[name] = F.is.fn(original) && __extendable__.test(func) ? + (function (name, fn, ofn) { + return function () { + var tmp, ret; + tmp = this._super; + this._super = ofn; + ret = fn.apply(this, arguments); + this._super = tmp; + return ret; + }; + })(name, func, original) : func; + } + + if (F.is.hash(arg1)){ + var proto = Object.create(this.prototype), + _super = this.prototype; + for (var name in arg1) { + if (name === '__ctor__') continue; + __extend__(proto, name, arg1[name], _super[name]); + } + var obj = F.is.fn(proto.__ctor__) ? proto.__ctor__ : function () { + if (!F.is.fn(this.construct)) + throw new SyntaxError('FooTable class objects must be constructed with the "new" keyword.'); + this.construct.apply(this, arguments); + }; + proto.construct = F.is.fn(proto.construct) ? proto.construct : function(){}; + obj.prototype = proto; + proto.constructor = obj; + obj.extend = Class.extend; + return obj; + } else if (F.is.string(arg1) && F.is.fn(arg2)) { + __extend__(this.prototype, arg1, arg2, this.prototype[arg1]); + } + }; + + F.Class = Class; + + F.ClassFactory = F.Class.extend(/** @lends FooTable.ClassFactory */{ + /** + * This is a simple factory for {@link FooTable.Class} objects allowing them to be registered using a friendly name + * and then new instances can be created using this friendly name. + * @constructs + * @extends FooTable.Class + * @returns {FooTable.ClassFactory} + * @this FooTable.ClassFactory + */ + construct: function(){ + /** + * An object containing all registered classes. + * @type {{}} + */ + this.registered = {}; + }, + /** + * Checks if the factory contains a class registered using the supplied name. + * @instance + * @param {string} name - The name of the class to check. + * @returns {boolean} + * @this FooTable.ClassFactory + */ + contains: function(name){ + return F.is.defined(this.registered[name]); + }, + /** + * Gets an array of all registered names. + * @instance + * @returns {Array.} + * @this FooTable.ClassFactory + */ + names: function(){ + var names = [], name; + for (name in this.registered){ + if (!this.registered.hasOwnProperty(name)) continue; + names.push(name); + } + return names; + }, + /** + * Registers a class object using the supplied friendly name and priority. The priority is only taken into account when loading all registered classes + * using the {@link FooTable.ClassFactory#load} method. + * @instance + * @param {string} name - The friendly name of the class. + * @param {function} klass - The class to register. + * @param {number} priority - This determines the order that the class is created when using the {@link FooTable.ClassFactory#load} method, higher values are loaded first. + * @this FooTable.ClassFactory + */ + register: function(name, klass, priority){ + if (!F.is.string(name) || !F.is.fn(klass)) return; + var current = this.registered[name]; + this.registered[name] = { + name: name, + klass: klass, + priority: F.is.number(priority) ? priority : (F.is.defined(current) ? current.priority : 0) + }; + }, + /** + * Creates new instances of all registered classes using there priority and the supplied arguments to return them in an array. + * @instance + * @param {object} subs - An object containing classes to substitute on load. + * @param {*} arg1 - The first argument to supply when creating new instances of all registered classes. + * @param {*} [argN...] - Any number of additional arguments to supply when creating new instances of all registered classes. + * @returns {Array.} + * @this FooTable.ClassFactory + */ + load: function(subs, arg1, argN){ + var self = this, args = Array.prototype.slice.call(arguments), reg = [], loaded = [], name, klass; + subs = args.shift() || {}; + for (name in self.registered){ + if (!self.registered.hasOwnProperty(name)) continue; + var component = self.registered[name]; + if (subs.hasOwnProperty(name)){ + klass = subs[name]; + if (F.is.string(klass)) klass = F.getFnPointer(subs[name]); + if (F.is.fn(klass)){ + component = {name: name, klass: klass, priority: self.registered[name].priority}; + } + } + reg.push(component); + } + for (name in subs){ + if (!subs.hasOwnProperty(name) || self.registered.hasOwnProperty(name)) continue; + klass = subs[name]; + if (F.is.string(klass)) klass = F.getFnPointer(subs[name]); + if (F.is.fn(klass)){ + reg.push({name: name, klass: klass, priority: 0}); + } + } + reg.sort(function(a, b){ return b.priority - a.priority; }); + F.arr.each(reg, function(r){ + if (F.is.fn(r.klass)){ + loaded.push(self._make(r.klass, args)); + } + }); + return loaded; + }, + /** + * Create a new instance of a single class using the supplied name and arguments. + * @instance + * @param {string} name - The name of the class to create. + * @param {*} arg1 - The first argument to supply to the new instance. + * @param {*} [argN...] - Any number of additional arguments to supply to the new instance. + * @returns {FooTable.Class} + * @this FooTable.ClassFactory + */ + make: function(name, arg1, argN){ + var self = this, args = Array.prototype.slice.call(arguments), reg; + name = args.shift(); + reg = self.registered[name]; + if (F.is.fn(reg.klass)){ + return self._make(reg.klass, args); + } + return null; + }, + /** + * This in effect lets us use the "apply" method on a function using the "new" keyword. + * @instance + * @private + * @param {function} klass + * @param args + * @returns {FooTable.Class} + * @this FooTable.ClassFactory + */ + _make: function(klass, args){ + function Class() { + return klass.apply(this, args); + } + Class.prototype = klass.prototype; + return new Class(); + } + }); + +})(FooTable); +(function($, F){ + + /** + * Converts the supplied cssText string into JSON object. + * @param {string} cssText - The cssText to convert to a JSON object. + * @returns {object} + */ + F.css2json = function(cssText){ + if (F.is.emptyString(cssText)) return {}; + var json = {}, props = cssText.split(';'), pair, key, value; + for (var i = 0, i_len = props.length; i < i_len; i++){ + if (F.is.emptyString(props[i])) continue; + pair = props[i].split(':'); + if (F.is.emptyString(pair[0]) || F.is.emptyString(pair[1])) continue; + key = F.str.toCamelCase($.trim(pair[0])); + value = $.trim(pair[1]); + json[key] = value; + } + return json; + }; + + /** + * Attempts to retrieve a function pointer using the given name. + * @param {string} functionName - The name of the function to fetch a pointer to. + * @returns {(function|object|null)} + */ + F.getFnPointer = function(functionName){ + if (F.is.emptyString(functionName)) return null; + var pointer = window, + parts = functionName.split('.'); + F.arr.each(parts, function(part){ + if (pointer[part]) pointer = pointer[part]; + }); + return F.is.fn(pointer) ? pointer : null; + }; + + /** + * Checks the value for function properties such as the {@link FooTable.Column#formatter} option which could also be specified using just the name + * and attempts to return the correct function pointer or null if none was found matching the value. + * @param {FooTable.Class} self - The class to use as the 'this' keyword within the context of the function. + * @param {(function|string)} value - The actual function or the name of the function for the property. + * @param {function} [def] - A default function to return if none is found. + * @returns {(function|null)} + */ + F.checkFnValue = function(self, value, def){ + def = F.is.fn(def) ? def : null; + function wrap(t, fn, d){ + if (!F.is.fn(fn)) return d; + return function(){ + return fn.apply(t, arguments); + }; + } + return F.is.fn(value) ? wrap(self, value, def) : (F.is.type(value, 'string') ? wrap(self, F.getFnPointer(value), def) : def); + }; + +})(jQuery, FooTable); +(function($, F){ + + F.Cell = F.Class.extend(/** @lends FooTable.Cell */{ + /** + * The cell class containing all the properties for cells. + * @constructs + * @extends FooTable.Class + * @param {FooTable.Table} table - The root {@link FooTable.Table} this cell belongs to. + * @param {FooTable.Row} row - The parent {@link FooTable.Row} this cell belongs to. + * @param {FooTable.Column} column - The {@link FooTable.Column} this cell falls under. + * @param {(*|HTMLElement|jQuery)} valueOrElement - Either the value or the element for the cell. + * @returns {FooTable.Cell} + * @this FooTable.Cell + */ + construct: function (table, row, column, valueOrElement) { + /** + * The root {@link FooTable.Table} for the cell. + * @instance + * @readonly + * @type {FooTable.Table} + */ + this.ft = table; + /** + * The parent {@link FooTable.Row} for the cell. + * @instance + * @readonly + * @type {FooTable.Row} + */ + this.row = row; + /** + * The {@link FooTable.Column} this cell falls under. + * @instance + * @readonly + * @type {FooTable.Column} + */ + this.column = column; + this.created = false; + this.define(valueOrElement); + }, + /** + * This is supplied either the value or the cell element/jQuery object if it exists. + * If supplied the element we need set the $el property and parse the value from it. + * @instance + * @protected + * @param {(*|jQuery)} valueOrElement - The value or element to define the cell. + * @this FooTable.Cell + */ + define: function(valueOrElement){ + /** + * The jQuery table cell object this instance wraps. + * @instance + * @type {jQuery} + */ + this.$el = F.is.element(valueOrElement) || F.is.jq(valueOrElement) ? $(valueOrElement) : null; + /** + * The jQuery row object that represents this cell in the details table. + * @type {jQuery} + */ + this.$detail = null; + + var hasOptions = F.is.hash(valueOrElement) && F.is.hash(valueOrElement.options) && F.is.defined(valueOrElement.value); + + /** + * The value of the cell. + * @instance + * @type {*} + */ + this.value = this.column.parser.call(this.column, F.is.jq(this.$el) ? this.$el : (hasOptions ? valueOrElement.value : valueOrElement), this.ft.o); + + /** + * Contains any options for the cell. These are the options supplied through the plugin constructor as part of the row object itself. + * @type {object} + */ + this.o = $.extend(true, { + classes: null, + style: null + }, hasOptions ? valueOrElement.options : {}); + /** + * An array of CSS classes for the cell. + * @instance + * @protected + * @type {Array.} + */ + this.classes = F.is.jq(this.$el) && this.$el.attr('class') ? this.$el.attr('class').match(/\S+/g) : (F.is.array(this.o.classes) ? this.o.classes : (F.is.string(this.o.classes) ? this.o.classes.match(/\S+/g) : [])); + /** + * The inline styles for the cell. + * @instance + * @protected + * @type {object} + */ + this.style = F.is.jq(this.$el) && this.$el.attr('style') ? F.css2json(this.$el.attr('style')) : (F.is.hash(this.o.style) ? this.o.style : (F.is.string(this.o.style) ? F.css2json(this.o.style) : {})); + }, + /** + * After the cell has been defined this ensures that the $el and #detail properties are jQuery objects by either creating or updating them. + * @instance + * @protected + * @this FooTable.Cell + */ + $create: function(){ + if (this.created) return; + (this.$el = F.is.jq(this.$el) ? this.$el : $('')) + .data('value', this.value) + .contents().detach().end() + .append(this.format(this.value)); + + this._setClasses(this.$el); + this._setStyle(this.$el); + + this.$detail = $('').addClass(this.row.classes.join(' ')) + .data('__FooTableCell__', this) + .append($('')) + .append($('')); + + this.created = true; + }, + /** + * Collapses this cell and displays it in the details row. + * @instance + * @protected + */ + collapse: function(){ + if (!this.created) return; + this.$detail.children('th').html(this.column.title); + this.$el.clone() + .attr('id', this.$el.attr('id') ? this.$el.attr('id') + '-detail' : undefined) + .css('display', 'table-cell') + .html('') + .append(this.$el.contents().detach()) + .replaceAll(this.$detail.children('td').first()); + + if (!F.is.jq(this.$detail.parent())) + this.$detail.appendTo(this.row.$details.find('.footable-details > tbody')); + }, + /** + * Restores this cell from a detail row back into the normal row. + * @instance + * @protected + */ + restore: function(){ + if (!this.created) return; + if (F.is.jq(this.$detail.parent())){ + var $cell = this.$detail.children('td').first(); + this.$el + .attr('class', $cell.attr('class')) + .attr('style', $cell.attr('style')) + .css('display', (this.column.hidden || !this.column.visible) ? 'none' : 'table-cell') + .append($cell.contents().detach()); + } + this.$detail.detach(); + }, + /** + * Helper method to call this cell's column parser function supplying the required parameters. + * @instance + * @protected + * @returns {*} + * @see FooTable.Column#parser + * @this FooTable.Cell + */ + parse: function(){ + return this.column.parser.call(this.column, this.$el, this.ft.o); + }, + /** + * Helper method to call this cell's column formatter function using the supplied value and any additional required parameters. + * @instance + * @protected + * @param {*} value - The value to format. + * @returns {(string|HTMLElement|jQuery)} + * @see FooTable.Column#formatter + * @this FooTable.Cell + */ + format: function(value){ + return this.column.formatter.call(this.column, value, this.ft.o, this.row.value); + }, + /** + * Allows easy access to getting or setting the cell's value. If the value is set all associated properties are also updated along with the actual element. + * Using this method also allows us to supply an object containing options and the value for the cell. + * @instance + * @param {*} [value] - The value to set for the cell. If not supplied the current value of the cell is returned. + * @param {boolean} [redraw=true] - Whether or not to redraw the row once the value has been set. + * @param {boolean} [redrawSelf=true] - Whether or not to redraw the cell itself once the value has been set, if `false` this will override the supplied `redraw` value and prevent the row from redrawing as well. + * @returns {(*|undefined)} + * @this FooTable.Cell + */ + val: function(value, redraw, redrawSelf){ + if (F.is.undef(value)){ + // get + return this.value; + } + // set + var self = this, hasOptions = F.is.hash(value) && F.is.hash(value.options) && F.is.defined(value.value); + this.o = $.extend(true, { + classes: self.classes, + style: self.style + }, hasOptions ? value.options : {}); + + this.value = hasOptions ? value.value : value; + this.classes = F.is.array(this.o.classes) ? this.o.classes : (F.is.string(this.o.classes) ? this.o.classes.match(/\S+/g) : []); + this.style = F.is.hash(this.o.style) ? this.o.style : (F.is.string(this.o.style) ? F.css2json(this.o.style) : {}); + + redrawSelf = F.is.boolean(redrawSelf) ? redrawSelf : true; + if (this.created && redrawSelf){ + this.$el.data('value', this.value).empty(); + + var $detail = this.$detail.children('td').first().empty(), + $target = F.is.jq(this.$detail.parent()) ? $detail : this.$el; + + $target.append(this.format(this.value)); + + this._setClasses($target); + this._setStyle($target); + + if (F.is.boolean(redraw) ? redraw : true) this.row.draw(); + } + }, + _setClasses: function($el){ + var hasColClasses = !F.is.emptyArray(this.column.classes), + hasClasses = !F.is.emptyArray(this.classes), + classes = null; + $el.removeAttr('class'); + if (!hasColClasses && !hasClasses) return; + if (hasColClasses && hasClasses){ + classes = this.classes.concat(this.column.classes).join(' '); + } else if (hasColClasses) { + classes = this.column.classes.join(' '); + } else if (hasClasses){ + classes = this.classes.join(' '); + } + if (!F.is.emptyString(classes)){ + $el.addClass(classes); + } + }, + _setStyle: function($el){ + var hasColStyle = !F.is.emptyObject(this.column.style), + hasStyle = !F.is.emptyObject(this.style), + style = null; + $el.removeAttr('style'); + if (!hasColStyle && !hasStyle) return; + if (hasColStyle && hasStyle){ + style = $.extend({}, this.column.style, this.style); + } else if (hasColStyle) { + style = this.column.style; + } else if (hasStyle){ + style = this.style; + } + if (F.is.hash(style)){ + $el.css(style); + } + } + }); + +})(jQuery, FooTable); +(function($, F){ + + F.Column = F.Class.extend(/** @lends FooTable.Column */{ + /** + * The column class containing all the properties for columns. All members marked as "readonly" should not be used when defining {@link FooTable.Defaults#columns}. + * @constructs + * @extends FooTable.Class + * @param {FooTable.Table} instance - The parent {@link FooTable.Table} this component belongs to. + * @param {object} definition - An object containing all the properties to set for the column. + * @param {string} [type] - The type of column, "text" by default. + * @returns {FooTable.Column} + * @this FooTable.Column + */ + construct: function(instance, definition, type){ + /** + * The root {@link FooTable.Table} for the column. + * @instance + * @readonly + * @type {FooTable.Table} + */ + this.ft = instance; + /** + * The type of data displayed by the column. + * @instance + * @readonly + * @type {string} + */ + this.type = F.is.emptyString(type) ? 'text' : type; + /** + * Whether or not the column was parsed from a standard table row containing data instead of from an actual header row. + * @instance + * @readonly + * @type {boolean} + */ + this.virtual = F.is.boolean(definition.virtual) ? definition.virtual : false; + /** + * The jQuery cell object for the column header. + * @instance + * @readonly + * @type {jQuery} + */ + this.$el = F.is.jq(definition.$el) ? definition.$el : null; + /** + * The index of the column in the table. This is set by the plugin during initialization. + * @instance + * @readonly + * @type {number} + * @default -1 + */ + this.index = F.is.number(definition.index) ? definition.index : -1; + /** + * Whether or not this in an internal only column. + * @instance + * @readonly + * @type {boolean} + * @description Internal columns or there cells will not be returned when calling methods such as `FooTable.Row#val`. + */ + this.internal = false; + this.define(definition); + this.$create(); + }, + /** + * This is supplied the column definition in the form of a simple object created by merging options supplied via the plugin constructor with those parsed from the DOM. + * @instance + * @protected + * @param {object} definition - The object containing the column definition. + * @this FooTable.Column + */ + define: function(definition){ + /** + * Whether or not this column is hidden from view and appears in the details row. + * @type {boolean} + * @default false + */ + this.hidden = F.is.boolean(definition.hidden) ? definition.hidden : false; + /** + * Whether or not this column is completely hidden from view and will not appear in the details row. + * @type {boolean} + * @default true + */ + this.visible = F.is.boolean(definition.visible) ? definition.visible : true; + + /** + * The name of the column. This name must correspond to the property name of the JSON row data. + * @type {string} + * @default null + */ + this.name = F.is.string(definition.name) ? definition.name : null; + if (this.name == null) this.name = 'col'+(definition.index+1); + /** + * The title to display in the column header, this can be HTML. + * @type {string} + * @default null + */ + this.title = F.is.string(definition.title) ? definition.title : null; + if (!this.virtual && this.title == null && F.is.jq(this.$el)) this.title = this.$el.html(); + if (this.title == null) this.title = 'Column '+(definition.index+1); + /** + * The styles to apply to all cells in this column. + * @type {object} + */ + this.style = F.is.hash(definition.style) ? definition.style : (F.is.string(definition.style) ? F.css2json(definition.style) : {}); + /** + * The classes to apply to all cells in this column. + * @type {Array.} + */ + this.classes = F.is.array(definition.classes) ? definition.classes : (F.is.string(definition.classes) ? definition.classes.match(/\S+/g) : []); + + // override any default functions ensuring when they are executed "this" within the context of the function points to the instance of this object. + this.parser = F.checkFnValue(this, definition.parser, this.parser); + this.formatter = F.checkFnValue(this, definition.formatter, this.formatter); + }, + /** + * After the column has been defined this ensures that the $el property is a jQuery object by either creating or updating the current value. + * @instance + * @protected + * @this FooTable.Column + */ + $create: function(){ + (this.$el = !this.virtual && F.is.jq(this.$el) ? this.$el : $('')).html(this.title).addClass(this.classes.join(' ')).css(this.style); + }, + /** + * This is supplied either the cell value or jQuery object to parse. Any value can be returned from this method and will be provided to the {@link FooTable.Column#format} function + * to generate the cell contents. + * @instance + * @protected + * @param {(*|jQuery)} valueOrElement - The value or jQuery cell object. + * @returns {string} + * @this FooTable.Column + */ + parser: function(valueOrElement){ + if (F.is.element(valueOrElement) || F.is.jq(valueOrElement)){ // use jQuery to get the value + var data = $(valueOrElement).data('value'); + return F.is.defined(data) ? data : $(valueOrElement).html(); + } + if (F.is.defined(valueOrElement) && valueOrElement != null) return valueOrElement+''; // use the native toString of the value + return null; // otherwise we have no value so return null + }, + /** + * This is supplied the value retrieved from the {@link FooTable.Column#parse} function and must return a string, HTMLElement or jQuery object. + * The return value from this function is what is displayed in the cell in the table. + * @instance + * @protected + * @param {string} value - The value to format. + * @param {object} options - The current plugin options. + * @param {object} rowData - An object containing the current row data. + * @returns {(string|HTMLElement|jQuery)} + * @this FooTable.Column + */ + formatter: function(value, options, rowData){ + return value == null ? '' : value; + }, + /** + * Creates a cell for this column from the supplied {@link FooTable.Row} object. This allows different column types to return different types of cells. + * @instance + * @protected + * @param {FooTable.Row} row - The row to create the cell from. + * @returns {FooTable.Cell} + * @this FooTable.Column + */ + createCell: function(row){ + var element = F.is.jq(row.$el) ? row.$el.children('td,th').get(this.index) : null, + data = F.is.hash(row.value) ? row.value[this.name] : null; + return new F.Cell(this.ft, row, this, element || data); + } + }); + + F.columns = new F.ClassFactory(); + + F.columns.register('text', F.Column); + +})(jQuery, FooTable); +(function ($, F) { + + F.Component = F.Class.extend(/** @lends FooTable.Component */{ + /** + * The base class for all FooTable components. + * @constructs + * @extends FooTable.Class + * @param {FooTable.Table} instance - The parent {@link FooTable.Table} object for the component. + * @param {boolean} enabled - Whether or not the component is enabled. + * @throws {TypeError} The instance parameter must be an instance of {@link FooTable.Table}. + * @returns {FooTable.Component} + */ + construct: function (instance, enabled) { + if (!(instance instanceof F.Table)) + throw new TypeError('The instance parameter must be an instance of FooTable.Table.'); + + /** + * The parent {@link FooTable.Table} for the component. + * @type {FooTable.Table} + */ + this.ft = instance; + /** + * Whether or not this component is enabled. Disabled components only have there preinit method called allowing for this value to be overridden. + * @type {boolean} + */ + this.enabled = F.is.boolean(enabled) ? enabled : false; + }, + /** + * The preinit method is called during the parent {@link FooTable.Table} constructor call. + * @param {object} data - The jQuery.data() object of the root table. + * @instance + * @protected + * @function + */ + preinit: function(data){}, + /** + * The init method is called during the parent {@link FooTable.Table} constructor call. + * @instance + * @protected + * @function + */ + init: function(){}, + /** + * This method is called from the {@link FooTable.Table#destroy} method. + * @instance + * @protected + * @function + */ + destroy: function(){}, + /** + * This method is called from the {@link FooTable.Table#draw} method. + * @instance + * @protected + * @function + */ + predraw: function(){}, + /** + * This method is called from the {@link FooTable.Table#draw} method. + * @instance + * @protected + * @function + */ + draw: function(){}, + /** + * This method is called from the {@link FooTable.Table#draw} method. + * @instance + * @protected + * @function + */ + postdraw: function(){} + }); + + F.components = new F.ClassFactory(); + +})(jQuery, FooTable); +(function ($, F) { + /** + * Contains all the available options for the FooTable plugin. + * @name FooTable.Defaults + * @function + * @constructor + * @returns {FooTable.Defaults} + */ + F.Defaults = function () { + /** + * Whether or not events raised using the {@link FooTable.Table#raise} method are propagated up the DOM. By default this is set to false and all events bubble up the DOM as per usual + * however the reason for this option is if we have nested tables. If false the parent table would receive all the events raised by it's children and any handlers bound to both the + * parent and child would be triggered which is not the desired behavior. + * @type {boolean} + * @default false + */ + this.stopPropagation = false; + /** + * An object in which the string keys represent one or more space-separated event types and optional namespaces, and the values represent a handler function to be called for the event(s). + * @type {object.} + * @default NULL + * @example This example shows how to pass an object containing the events and handlers. + * "on": { + * "click": function(e){ + * // bind a custom click event to do something whenever the table is clicked + * }, + * "init.ft.table": function(e, ft){ + * // bind to the FooTable initialize event to do something + * } + * } + */ + this.on = null; + }; + + /** + * Contains all the default options for the plugin. + * @type {FooTable.Defaults} + */ + F.defaults = new F.Defaults(); + +})(jQuery, FooTable); +(function($, F){ + + F.Row = F.Class.extend(/** @lends FooTable.Row */{ + /** + * The row class containing all the properties for a row and its' cells. + * @constructs + * @extends FooTable.Class + * @param {FooTable.Table} table - The parent {@link FooTable.Table} this component belongs to. + * @param {Array.} columns - The array of {@link FooTable.Column} for this row. + * @param {(*|HTMLElement|jQuery)} dataOrElement - Either the data for the row (create) or the element (parse) for the row. + * @returns {FooTable.Row} + */ + construct: function (table, columns, dataOrElement) { + /** + * The {@link FooTable.Table} for the row. + * @type {FooTable.Table} + */ + this.ft = table; + /** + * The array of {@link FooTable.Column} for this row. + * @type {Array.} + */ + this.columns = columns; + + this.created = false; + this.define(dataOrElement); + }, + /** + * This is supplied either the object containing the values for the row or the row element/jQuery object if it exists. + * If supplied the element we need to set the $el property and parse the cells from it using the column index. + * If we have an object we parse the cells from it using the column name. + * @param {(object|jQuery)} dataOrElement - The row object or element to define the row. + */ + define: function(dataOrElement){ + /** + * The jQuery table row object this instance wraps. + * @instance + * @protected + * @type {jQuery} + */ + this.$el = F.is.element(dataOrElement) || F.is.jq(dataOrElement) ? $(dataOrElement) : null; + /** + * The jQuery toggle element for the row. + * @instance + * @protected + * @type {jQuery} + */ + this.$toggle = $('', {'class': 'footable-toggle fooicon fooicon-plus'}); + + var isObj = F.is.hash(dataOrElement), + hasOptions = isObj && F.is.hash(dataOrElement.options) && F.is.hash(dataOrElement.value); + + /** + * The value of the row. + * @instance + * @protected + * @type {Object} + */ + this.value = isObj ? (hasOptions ? dataOrElement.value : dataOrElement) : null; + + /** + * Contains any options for the row. + * @type {object} + */ + this.o = $.extend(true, { + expanded: false, + classes: null, + style: null + }, hasOptions ? dataOrElement.options : {}); + + /** + * Whether or not this row is expanded and will display it's detail row when there are any hidden columns. + * @instance + * @protected + * @type {boolean} + */ + this.expanded = F.is.jq(this.$el) ? (this.$el.data('expanded') || this.o.expanded) : this.o.expanded; + /** + * An array of CSS classes for the row. + * @instance + * @protected + * @type {Array.} + */ + this.classes = F.is.jq(this.$el) && this.$el.attr('class') ? this.$el.attr('class').match(/\S+/g) : (F.is.array(this.o.classes) ? this.o.classes : (F.is.string(this.o.classes) ? this.o.classes.match(/\S+/g) : [])); + /** + * The inline styles for the row. + * @instance + * @protected + * @type {object} + */ + this.style = F.is.jq(this.$el) && this.$el.attr('style') ? F.css2json(this.$el.attr('style')) : (F.is.hash(this.o.style) ? this.o.style : (F.is.string(this.o.style) ? F.css2json(this.o.style) : {})); + + /** + * The cells array. This is populated before the call to the {@link FooTable.Row#$create} method. + * @instance + * @type {Array.} + */ + this.cells = this.createCells(); + + // this ensures the value contains the parsed cell values and not the supplied values + var self = this; + self.value = {}; + F.arr.each(self.cells, function(cell){ + self.value[cell.column.name] = cell.val(); + }); + }, + /** + * After the row has been defined this ensures that the $el property is a jQuery object by either creating or updating the current value. + * @instance + * @protected + * @this FooTable.Row + */ + $create: function(){ + if (this.created) return; + (this.$el = F.is.jq(this.$el) ? this.$el : $('')) + .data('__FooTableRow__', this); + + this._setClasses(this.$el); + this._setStyle(this.$el); + + if (this.ft.rows.toggleColumn == 'last') this.$toggle.addClass('last-column'); + + this.$details = $('', { 'class': 'footable-detail-row' }) + .append($('', { colspan: this.ft.columns.visibleColspan }) + .append($('', { 'class': 'footable-details ' + this.ft.classes.join(' ') }) + .append(''))); + + var self = this; + F.arr.each(self.cells, function(cell){ + if (!cell.created) cell.$create(); + self.$el.append(cell.$el); + }); + self.$el.off('click.ft.row').on('click.ft.row', { self: self }, self._onToggle); + this.created = true; + }, + /** + * This is called during the construct method and uses the current column definitions to create an array of {@link FooTable.Cell} objects for the row. + * @instance + * @protected + * @returns {Array.} + * @this FooTable.Row + */ + createCells: function(){ + var self = this; + return F.arr.map(self.columns, function(col){ + return col.createCell(self); + }); + }, + /** + * Allows easy access to getting or setting the row's data. If the data is set all associated properties are also updated along with the actual element. + * Using this method also allows us to supply an object containing options and the data for the row at the same time. + * @instance + * @param {object} [data] - The data to set for the row. If not supplied the current value of the row is returned. + * @param {boolean} [redraw=true] - Whether or not to redraw the table once the value has been set. + * @param {boolean} [redrawSelf=true] - Whether or not to redraw the row itself once the value has been set, if `false` this will override the supplied `redraw` value and prevent the table from redrawing as well. + * @returns {(*|undefined)} + */ + val: function(data, redraw, redrawSelf){ + var self = this; + if (!F.is.hash(data)){ + // get - check the value property and build it from the cells if required. + if (!F.is.hash(this.value) || F.is.emptyObject(this.value)){ + this.value = {}; + F.arr.each(this.cells, function(cell){ + if (!cell.column.internal){ + self.value[cell.column.name] = cell.val(); + } + }); + } + return this.value; + } + // set + this.collapse(false); + var isObj = F.is.hash(data), + hasOptions = isObj && F.is.hash(data.options) && F.is.hash(data.value); + + this.o = $.extend(true, { + expanded: self.expanded, + classes: self.classes, + style: self.style + }, hasOptions ? data.options : {}); + + this.expanded = this.o.expanded; + this.classes = F.is.array(this.o.classes) ? this.o.classes : (F.is.string(this.o.classes) ? this.o.classes.match(/\S+/g) : []); + this.style = F.is.hash(this.o.style) ? this.o.style : (F.is.string(this.o.style) ? F.css2json(this.o.style) : {}); + if (isObj) { + if ( hasOptions ) data = data.value; + if (F.is.hash(this.value)){ + for (var prop in data) { + if (!data.hasOwnProperty(prop)) continue; + this.value[prop] = data[prop]; + } + } else { + this.value = data; + } + } else { + this.value = null; + } + + redrawSelf = F.is.boolean(redrawSelf) ? redrawSelf : true; + F.arr.each(this.cells, function(cell){ + if (!cell.column.internal && F.is.defined(self.value[cell.column.name])){ + cell.val(self.value[cell.column.name], false, redrawSelf); + } + }); + + if (this.created && redrawSelf){ + this._setClasses(this.$el); + this._setStyle(this.$el); + if (F.is.boolean(redraw) ? redraw : true) this.draw(); + } + }, + _setClasses: function($el){ + var hasClasses = !F.is.emptyArray(this.classes), + classes = null; + $el.removeAttr('class'); + if (!hasClasses) return; + else classes = this.classes.join(' '); + if (!F.is.emptyString(classes)){ + $el.addClass(classes); + } + }, + _setStyle: function($el){ + var hasStyle = !F.is.emptyObject(this.style), + style = null; + $el.removeAttr('style'); + if (!hasStyle) return; + else style = this.style; + if (F.is.hash(style)){ + $el.css(style); + } + }, + /** + * Sets the current row to an expanded state displaying any hidden columns in a detail row just below it. + * @instance + * @fires FooTable.Row#"expand.ft.row" + */ + expand: function(){ + if (!this.created) return; + var self = this; + /** + * The expand.ft.row event is raised before the the row is expanded. + * Calling preventDefault on this event will stop the row being expanded. + * @event FooTable.Row#"expand.ft.row" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + * @param {FooTable.Row} row - The row about to be expanded. + */ + self.ft.raise('expand.ft.row',[self]).then(function(){ + self.__hidden__ = F.arr.map(self.cells, function(cell){ + return cell.column.hidden && cell.column.visible ? cell : null; + }); + + if (self.__hidden__.length > 0){ + self.$details.insertAfter(self.$el) + .children('td').first() + .attr('colspan', self.ft.columns.visibleColspan); + + F.arr.each(self.__hidden__, function(cell){ + cell.collapse(); + }); + } + self.$el.attr('data-expanded', true); + self.$toggle.removeClass('fooicon-plus').addClass('fooicon-minus'); + self.expanded = true; + self.ft.raise('expanded.ft.row', [self]); + }); + }, + /** + * Sets the current row to a collapsed state removing the detail row if it exists. + * @instance + * @param {boolean} [setExpanded] - Whether or not to set the {@link FooTable.Row#expanded} property to false. + * @fires FooTable.Row#"collapse.ft.row" + */ + collapse: function(setExpanded){ + if (!this.created) return; + var self = this; + /** + * The collapse.ft.row event is raised before the the row is collapsed. + * Calling preventDefault on this event will stop the row being collapsed. + * @event FooTable.Row#"collapse.ft.row" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + * @param {FooTable.Row} row - The row about to be expanded. + */ + self.ft.raise('collapse.ft.row',[self]).then(function(){ + F.arr.each(self.__hidden__, function(cell){ + cell.restore(); + }); + self.$details.detach(); + self.$el.removeAttr('data-expanded'); + self.$toggle.removeClass('fooicon-minus').addClass('fooicon-plus'); + if (F.is.boolean(setExpanded) ? setExpanded : true) self.expanded = false; + self.ft.raise('collapsed.ft.row', [self]); + }); + }, + /** + * Prior to drawing this moves the details contents back to there original cells and detaches the toggle element from the row. + * @instance + * @param {boolean} [detach] - Whether or not to detach the row. + * @this FooTable.Row + */ + predraw: function(detach){ + if (this.created){ + if (this.expanded){ + this.collapse(false); + } + this.$toggle.detach(); + detach = F.is.boolean(detach) ? detach : true; + if (detach) this.$el.detach(); + } + }, + /** + * Draws the current row and cells. + * @instance + * @this FooTable.Row + */ + draw: function($parent){ + if (!this.created) this.$create(); + if (F.is.jq($parent)) $parent.append(this.$el); + var self = this; + F.arr.each(self.cells, function(cell){ + cell.$el.css('display', (cell.column.hidden || !cell.column.visible ? 'none' : 'table-cell')); + if (self.ft.rows.showToggle && self.ft.columns.hasHidden){ + if ((self.ft.rows.toggleColumn == 'first' && cell.column.index == self.ft.columns.firstVisibleIndex) + || (self.ft.rows.toggleColumn == 'last' && cell.column.index == self.ft.columns.lastVisibleIndex)) { + cell.$el.prepend(self.$toggle); + } + } + cell.$el.add(cell.column.$el).removeClass('footable-first-visible footable-last-visible'); + if (cell.column.index == self.ft.columns.firstVisibleIndex){ + cell.$el.add(cell.column.$el).addClass('footable-first-visible'); + } + if (cell.column.index == self.ft.columns.lastVisibleIndex){ + cell.$el.add(cell.column.$el).addClass('footable-last-visible'); + } + }); + if (this.expanded){ + this.expand(); + } + }, + /** + * Toggles the row between it's expanded and collapsed state if there are hidden columns. + * @instance + * @this FooTable.Row + */ + toggle: function(){ + if (this.created && this.ft.columns.hasHidden){ + if (this.expanded) this.collapse(); + else this.expand(); + } + }, + /** + * Handles the toggle click event for rows. + * @instance + * @param {jQuery.Event} e - The jQuery.Event object for the click event. + * @private + * @this jQuery + */ + _onToggle: function (e) { + var self = e.data.self; + // only execute the toggle if the event.target is one of the approved initiators + if ($(e.target).is(self.ft.rows.toggleSelector)){ + self.toggle(); + } + } + }); + +})(jQuery, FooTable); + +(function ($, F) { + + /** + * An array of all currently loaded instances of the plugin. + * @protected + * @readonly + * @type {Array.} + */ + F.instances = []; + + F.Table = F.Class.extend(/** @lends FooTable.Table */{ + /** + * This class is the core of the plugin and drives the logic of all components. + * @constructs + * @this FooTable.Table + * @extends FooTable.Class + * @param {(HTMLTableElement|jQuery)} element - The element or jQuery table object to bind the plugin to. + * @param {object} options - The options to initialize the plugin with. + * @param {function} [ready] - A callback function to execute once the plugin is initialized. + * @returns {FooTable.Table} + */ + construct: function (element, options, ready) { + //BEGIN MEMBERS + /** + * The timeout ID for the resize event. + * @instance + * @private + * @type {?number} + */ + this._resizeTimeout = null; + /** + * The ID of the FooTable instance. + * @instance + * @type {number} + */ + this.id = F.instances.push(this); + /** + * Whether or not the plugin and all components and add-ons are fully initialized. + * @instance + * @type {boolean} + */ + this.initialized = false; + /** + * The jQuery table object the plugin is bound to. + * @instance + * @type {jQuery} + */ + this.$el = (F.is.jq(element) ? element : $(element)).first(); // ensure one table, one instance + /** + * A loader jQuery instance + * @instance + * @type {jQuery} + */ + this.$loader = $('
', { 'class': 'footable-loader' }).append($('', {'class': 'fooicon fooicon-loader'})); + /** + * The options for the plugin. This is a merge of user defined options and the default options. + * @instance + * @type {object} + */ + this.o = $.extend(true, {}, F.defaults, options); + /** + * The jQuery data object for the table at initialization. + * @instance + * @type {object} + */ + this.data = this.$el.data() || {}; + /** + * An array of all CSS classes on the table that do not start with "footable". + * @instance + * @protected + * @type {Array.} + */ + this.classes = []; + /** + * All components for this instance of the plugin. These are executed in the order they appear in the array for the initialize phase and in reverse order for the destroy phase of the plugin. + * @instance + * @protected + * @type {object} + * @prop {Array.} internal - The internal components for the plugin. These are executed either before all other components in the initialize phase or after them in the destroy phase of the plugin. + * @prop {Array.} core - The core components for the plugin. These are executed either after the internal components in the initialize phase or before them in the destroy phase of the plugin. + * @prop {Array.} custom - The custom components for the plugin. These are executed either after the core components in the initialize phase or before them in the destroy phase of the plugin. + */ + this.components = F.components.load((F.is.hash(this.data.components) ? this.data.components : this.o.components), this); + /** + * The breakpoints component for this instance of the plugin. + * @instance + * @type {FooTable.Breakpoints} + */ + this.breakpoints = this.use(FooTable.Breakpoints); + /** + * The columns component for this instance of the plugin. + * @instance + * @type {FooTable.Columns} + */ + this.columns = this.use(FooTable.Columns); + /** + * The rows component for this instance of the plugin. + * @instance + * @type {FooTable.Rows} + */ + this.rows = this.use(FooTable.Rows); + + //END MEMBERS + this._construct(ready); + }, + /** + * Once all properties are set this performs the actual initialization of the plugin calling the {@link FooTable.Table#_preinit} and + * {@link FooTable.Table#_init} methods as well as raising the {@link FooTable.Table#"ready.ft.table"} event. + * @this FooTable.Table + * @instance + * @param {function} [ready] - A callback function to execute once the plugin is initialized. + * @private + * @returns {jQuery.Promise} + * @fires FooTable.Table#"ready.ft.table" + */ + _construct: function(ready){ + var self = this; + return this._preinit().then(function(){ + return self._init().then(function(){ + /** + * The ready.ft.table event is raised after the plugin has been initialized and the table drawn. + * Calling preventDefault on this event will stop the ready callback being executed. + * @event FooTable.Table#"ready.ft.table" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + return self.raise('ready.ft.table').then(function(){ + if (F.is.fn(ready)) ready.call(self, self); + }); + }); + }).always(function(arg){ + self.$el.show(); + if (F.is.error(arg)){ + console.error('FooTable: unhandled error thrown during initialization.', arg); + } + }); + }, + /** + * The preinit method is called prior to the plugins actual initialization and provides itself and it's components an opportunity to parse any additional option values. + * @instance + * @private + * @returns {jQuery.Promise} + * @fires FooTable.Table#"preinit.ft.table" + */ + _preinit: function(){ + var self = this; + /** + * The preinit.ft.table event is raised before any components. + * Calling preventDefault on this event will disable the entire plugin. + * @event FooTable.Table#"preinit.ft.table" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + * @param {object} data - The jQuery data object from the root table element. + */ + return this.raise('preinit.ft.table', [self.data]).then(function(){ + var classes = (self.$el.attr('class') || '').match(/\S+/g) || []; + + self.o.ajax = F.checkFnValue(self, self.data.ajax, self.o.ajax); + self.o.stopPropagation = F.is.boolean(self.data.stopPropagation) + ? self.data.stopPropagation + : self.o.stopPropagation; + + for (var i = 0, len = classes.length; i < len; i++){ + if (!F.str.startsWith(classes[i], 'footable')) self.classes.push(classes[i]); + } + + self.$el.hide().after(self.$loader); + return self.execute(false, false, 'preinit', self.data); + }); + }, + /** + * Initializes this instance of the plugin and calls the callback function if one is supplied once complete. + * @this FooTable.Table + * @instance + * @private + * @return {jQuery.Promise} + * @fires FooTable.Table#"init.ft.table" + */ + _init: function(){ + var self = this; + /** + * The init.ft.table event is raised before any components are initialized. + * Calling preventDefault on this event will disable the entire plugin. + * @event FooTable.Table#"init.ft.table" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + return self.raise('init.ft.table').then(function(){ + var $thead = self.$el.children('thead'), + $tbody = self.$el.children('tbody'), + $tfoot = self.$el.children('tfoot'); + self.$el.addClass('footable footable-' + self.id); + if (F.is.hash(self.o.on)) self.$el.on(self.o.on); + if ($tfoot.length == 0) self.$el.append($tfoot = $('
')); + if ($tbody.length == 0) self.$el.append(''); + if ($thead.length == 0) self.$el.prepend($thead = $('')); + return self.execute(false, true, 'init').then(function(){ + self.$el.data('__FooTable__', self); + if ($tfoot.children('tr').length == 0) $tfoot.remove(); + if ($thead.children('tr').length == 0) $thead.remove(); + + /** + * The postinit.ft.table event is raised after any components are initialized but before the table is + * drawn for the first time. + * Calling preventDefault on this event will disable the initial drawing of the table. + * @event FooTable.Table#"postinit.ft.table" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + return self.raise('postinit.ft.table').then(function(){ + return self.draw(); + }).always(function(){ + $(window).off('resize.ft'+self.id, self._onWindowResize) + .on('resize.ft'+self.id, { self: self }, self._onWindowResize); + self.initialized = true; + }); + }); + }); + }, + /** + * Destroys this plugin removing it from the table. + * @this FooTable.Table + * @instance + * @fires FooTable.Table#"destroy.ft.table" + */ + destroy: function () { + var self = this; + /** + * The destroy.ft.table event is called before all core components. + * Calling preventDefault on this event will prevent the entire plugin from being destroyed. + * @event FooTable.Table#"destroy.ft.table" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + return self.raise('destroy.ft.table').then(function(){ + return self.execute(true, true, 'destroy').then(function () { + self.$el.removeData('__FooTable__').removeClass('footable-' + self.id); + if (F.is.hash(self.o.on)) self.$el.off(self.o.on); + $(window).off('resize.ft'+self.id, self._onWindowResize); + self.initialized = false; + F.instances[self.id] = null; + }); + }).fail(function(err){ + if (F.is.error(err)){ + console.error('FooTable: unhandled error thrown while destroying the plugin.', err); + } + }); + }, + /** + * Raises an event on this instance supplying the args array as additional parameters to the handlers. + * @this FooTable.Table + * @instance + * @param {string} eventName - The name of the event to raise, this can include namespaces. + * @param {Array} [args] - An array containing additional parameters to be passed to any bound handlers. + * @returns {jQuery.Event} + */ + raise: function(eventName, args){ + var self = this, + debug = F.__debug__ && (F.is.emptyArray(F.__debug_options__.events) || F.arr.any(F.__debug_options__.events, function(name){ return F.str.contains(eventName, name); })); + args = args || []; + args.unshift(this); + return $.Deferred(function(d){ + var evt = $.Event(eventName); + if (self.o.stopPropagation == true){ + self.$el.one(eventName, function (e) {e.stopPropagation();}); + } + if (debug) console.log('FooTable:'+eventName+': ', args); + self.$el.trigger(evt, args); + if (evt.isDefaultPrevented()){ + if (debug) console.log('FooTable: default prevented for the "'+eventName+'" event.'); + d.reject(evt); + } else d.resolve(evt); + }); + }, + /** + * Attempts to retrieve the instance of the supplied component type for this instance. + * @this FooTable.Table + * @instance + * @param {object} type - The content type to retrieve for this instance. + * @returns {(*|null)} + */ + use: function(type){ + for (var i = 0, len = this.components.length; i < len; i++){ + if (this.components[i] instanceof type) return this.components[i]; + } + return null; + }, + /** + * Performs the drawing of the table. + * @this FooTable.Table + * @instance + * @protected + * @returns {jQuery.Promise} + * @fires FooTable.Table#"predraw.ft.table" + * @fires FooTable.Table#"draw.ft.table" + * @fires FooTable.Table#"postdraw.ft.table" + */ + draw: function () { + var self = this; + + // Clone the current table and insert it into the original's place + var $elCopy = self.$el.clone().insertBefore(self.$el); + + // Detach `self.$el` from the DOM, retaining its event handlers + self.$el.detach(); + + // when drawing the order that the components are executed is important so chain the methods but use promises to retain async safety. + return self.execute(false, true, 'predraw').then(function(){ + /** + * The predraw.ft.table event is raised after all core components and add-ons have executed there predraw functions but before they execute there draw functions. + * @event FooTable.Table#"predraw.ft.table" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + return self.raise('predraw.ft.table').then(function(){ + return self.execute(false, true, 'draw').then(function(){ + /** + * The draw.ft.table event is raised after all core components and add-ons have executed there draw functions. + * @event FooTable.Table#"draw.ft.table" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + return self.raise('draw.ft.table').then(function(){ + return self.execute(false, true, 'postdraw').then(function(){ + /** + * The postdraw.ft.table event is raised after all core components and add-ons have executed there postdraw functions. + * @event FooTable.Table#"postdraw.ft.table" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + return self.raise('postdraw.ft.table'); + }); + }); + }); + }); + }).fail(function(err){ + if (F.is.error(err)){ + console.error('FooTable: unhandled error thrown during a draw operation.', err); + } + }).always(function(){ + // Replace the copy that we added above with the modified `self.$el` + $elCopy.replaceWith(self.$el); + self.$loader.remove(); + }); + }, + /** + * Executes the specified method with the optional number of parameters on all components and waits for the promise from each to be resolved before executing the next. + * @this FooTable.Table + * @instance + * @protected + * @param {boolean} reverse - Whether or not to execute the component methods in the reverse order to what they were registered in. + * @param {boolean} enabled - Whether or not to execute the method on enabled components only. + * @param {string} methodName - The name of the method to execute. + * @param {*} [param1] - The first parameter for the method. + * @param {...*} [paramN] - Any number of additional parameters for the method. + * @returns {jQuery.Promise} + */ + execute: function(reverse, enabled, methodName, param1, paramN){ + var self = this, args = Array.prototype.slice.call(arguments); + reverse = args.shift(); + enabled = args.shift(); + var components = enabled ? F.arr.get(self.components, function(c){ return c.enabled; }) : self.components.slice(0); + args.unshift(reverse ? components.reverse() : components); + return self._execute.apply(self, args); + }, + /** + * Executes the specified method with the optional number of parameters on all supplied components waiting for the result of each before executing the next. + * @this FooTable.Table + * @instance + * @private + * @param {Array.} components - The components to call the method on. + * @param {string} methodName - The name of the method to execute + * @param {*} [param1] - The first parameter for the method. + * @param {...*} [paramN] - Any additional parameters for the method. + * @returns {jQuery.Promise} + */ + _execute: function(components, methodName, param1, paramN){ + if (!components || !components.length) return $.when(); + var self = this, args = Array.prototype.slice.call(arguments), + component; + components = args.shift(); + methodName = args.shift(); + component = components.shift(); + + if (!F.is.fn(component[methodName])) + return self._execute.apply(self, [components, methodName].concat(args)); + + return $.Deferred(function(d){ + try { + var result = component[methodName].apply(component, args); + if (F.is.promise(result)){ + return result.then(d.resolve, d.reject); + } else { + d.resolve(result); + } + } catch (err) { + d.reject(err); + } + }).then(function(){ + return self._execute.apply(self, [components, methodName].concat(args)); + }); + }, + /** + * Listens to the window resize event and performs a check to see if the breakpoint has changed. + * @this window + * @instance + * @private + * @fires FooTable.Table#"resize.ft.table" + */ + _onWindowResize: function (e) { + var self = e.data.self; + if (self._resizeTimeout != null) { clearTimeout(self._resizeTimeout); } + self._resizeTimeout = setTimeout(function () { + self._resizeTimeout = null; + /** + * The resize event is raised a short time after window resize operations cease. + * @event FooTable.Table#"resize.ft.table" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + self.raise('resize.ft.table').then(function(){ + self.breakpoints.check(); + }); + }, 300); + } + }); + +})(jQuery, FooTable); +(function($, F){ + + F.ArrayColumn = F.Column.extend(/** @lends FooTable.ArrayColumn */{ + /** + * @summary A column to handle Array values. + * @constructs + * @extends FooTable.Column + * @param {FooTable.Table} instance - The parent {@link FooTable.Table} this column belongs to. + * @param {object} definition - An object containing all the properties to set for the column. + */ + construct: function(instance, definition) { + this._super(instance, definition, 'array'); + }, + /** + * @summary Parses the supplied value or element to retrieve a column value. + * @description This is supplied either the cell value or jQuery object to parse. This method will return either the Array containing the values or null. + * @instance + * @protected + * @param {(*|jQuery)} valueOrElement - The value or jQuery cell object. + * @returns {(array|null)} + */ + parser: function(valueOrElement){ + if (F.is.element(valueOrElement) || F.is.jq(valueOrElement)){ // use jQuery to get the value + var $el = $(valueOrElement), data = $el.data('value'); // .data() will automatically convert a JSON string to an array + if (F.is.array(data)) return data; + data = $el.html(); + try { + data = JSON.parse(data); + } catch(err) { + data = null; + } + return F.is.array(data) ? data : null; // if we have an array return it + } + if (F.is.array(valueOrElement)) return valueOrElement; // if we have an array return it + return null; // otherwise we have no value so return null + }, + /** + * @summary Formats the column value and creates the HTML seen within a cell. + * @description This is supplied the value retrieved from the {@link FooTable.ArrayColumn#parser} function and must return a string, HTMLElement or jQuery object. + * The return value from this function is what is displayed in the cell in the table. + * @instance + * @protected + * @param {?Array} value - The value to format. + * @param {object} options - The current plugin options. + * @param {object} rowData - An object containing the current row data. + * @returns {(string|HTMLElement|jQuery)} + */ + formatter: function(value, options, rowData){ + return F.is.array(value) ? JSON.stringify(value) : ''; + } + }); + + F.columns.register('array', F.ArrayColumn); + +})(jQuery, FooTable); +(function($, F){ + + if (F.is.undef(window.moment)){ + // The DateColumn requires moment.js to parse and format date values. Goto http://momentjs.com/ to get it. + return; + } + + F.DateColumn = F.Column.extend(/** @lends FooTable.DateColumn */{ + /** + * The date column class is used to handle date values. This column is dependent on [moment.js]{@link http://momentjs.com/} to provide date parsing and formatting functionality. + * @constructs + * @extends FooTable.Column + * @param {FooTable.Table} instance - The parent {@link FooTable.Table} this column belongs to. + * @param {object} definition - An object containing all the properties to set for the column. + * @returns {FooTable.DateColumn} + */ + construct: function(instance, definition){ + this._super(instance, definition, 'date'); + /** + * The format string to use when parsing and formatting dates. + * @instance + * @type {string} + */ + this.formatString = F.is.string(definition.formatString) ? definition.formatString : 'MM-DD-YYYY'; + }, + /** + * This is supplied either the cell value or jQuery object to parse. Any value can be returned from this method and will be provided to the {@link FooTable.DateColumn#format} function + * to generate the cell contents. + * @instance + * @protected + * @param {(*|jQuery)} valueOrElement - The value or jQuery cell object. + * @returns {(moment|null)} + * @this FooTable.DateColumn + */ + parser: function(valueOrElement){ + if (F.is.element(valueOrElement) || F.is.jq(valueOrElement)){ + var data = $(valueOrElement).data('value'); + valueOrElement = F.is.defined(data) ? data : $(valueOrElement).text(); + if (F.is.string(valueOrElement)) valueOrElement = isNaN(valueOrElement) ? valueOrElement : +valueOrElement; + } + if (F.is.date(valueOrElement)) return moment(valueOrElement); + if (F.is.object(valueOrElement) && F.is.boolean(valueOrElement._isAMomentObject)) return valueOrElement; + if (F.is.string(valueOrElement)){ + // if it looks like a number convert it and do nothing else otherwise create a new moment using the string value and formatString + if (isNaN(valueOrElement)){ + return moment(valueOrElement, this.formatString); + } else { + valueOrElement = +valueOrElement; + } + } + if (F.is.number(valueOrElement)){ + return moment(valueOrElement); + } + return null; + }, + /** + * This is supplied the value retrieved from the {@link FooTable.DateColumn#parser} function and must return a string, HTMLElement or jQuery object. + * The return value from this function is what is displayed in the cell in the table. + * @instance + * @protected + * @param {*} value - The value to format. + * @param {object} options - The current plugin options. + * @param {object} rowData - An object containing the current row data. + * @returns {(string|HTMLElement|jQuery)} + * @this FooTable.DateColumn + */ + formatter: function(value, options, rowData){ + return F.is.object(value) && F.is.boolean(value._isAMomentObject) && value.isValid() ? value.format(this.formatString) : ''; + }, + /** + * This is supplied either the cell value or jQuery object to parse. A string value must be returned from this method and will be used during filtering operations. + * @param {(*|jQuery)} valueOrElement - The value or jQuery cell object. + * @returns {string} + * @this FooTable.DateColumn + */ + filterValue: function(valueOrElement){ + // if we have an element or a jQuery object use jQuery to get the value + if (F.is.element(valueOrElement) || F.is.jq(valueOrElement)) valueOrElement = $(valueOrElement).data('filterValue') || $(valueOrElement).text(); + // if options are supplied with the value + if (F.is.hash(valueOrElement) && F.is.hash(valueOrElement.options)){ + if (F.is.string(valueOrElement.options.filterValue)) valueOrElement = valueOrElement.options.filterValue; + if (F.is.defined(valueOrElement.value)) valueOrElement = valueOrElement.value; + } + // if the value is a moment object just return the formatted value + if (F.is.object(valueOrElement) && F.is.boolean(valueOrElement._isAMomentObject)) return valueOrElement.format(this.formatString); + // if its a string + if (F.is.string(valueOrElement)){ + // if its not a number return it + if (isNaN(valueOrElement)){ + return valueOrElement; + } else { // otherwise convert it and carry on + valueOrElement = +valueOrElement; + } + } + // if the value is a number or date convert to a moment object and return the formatted result. + if (F.is.number(valueOrElement) || F.is.date(valueOrElement)){ + return moment(valueOrElement).format(this.formatString); + } + // try use the native toString of the value if its not undefined or null + if (F.is.defined(valueOrElement) && valueOrElement != null) return valueOrElement+''; + return ''; // otherwise we have no value so return an empty string + } + }); + + F.columns.register('date', F.DateColumn); + +})(jQuery, FooTable); + +(function($, F){ + + F.HTMLColumn = F.Column.extend(/** @lends FooTable.HTMLColumn */{ + /** + * The HTML column class is used to handle any raw HTML columns. + * @constructs + * @extends FooTable.Column + * @param {FooTable.Table} instance - The parent {@link FooTable.Table} this column belongs to. + * @param {object} definition - An object containing all the properties to set for the column. + * @returns {FooTable.HTMLColumn} + */ + construct: function(instance, definition){ + this._super(instance, definition, 'html'); + }, + /** + * This is supplied either the cell value or jQuery object to parse. Any value can be returned from this method and will be provided to the {@link FooTable.HTMLColumn#format} function + * to generate the cell contents. + * @instance + * @protected + * @param {(*|jQuery)} valueOrElement - The value or jQuery cell object. + * @returns {(jQuery|null)} + * @this FooTable.HTMLColumn + */ + parser: function(valueOrElement){ + if (F.is.string(valueOrElement)) valueOrElement = $($.trim(valueOrElement)); + if (F.is.element(valueOrElement)) valueOrElement = $(valueOrElement); + if (F.is.jq(valueOrElement)){ + var tagName = valueOrElement.prop('tagName').toLowerCase(); + if (tagName == 'td' || tagName == 'th'){ + var data = valueOrElement.data('value'); + return F.is.defined(data) ? data : valueOrElement.contents(); + } + return valueOrElement; + } + return null; + } + }); + + F.columns.register('html', F.HTMLColumn); + +})(jQuery, FooTable); +(function($, F){ + + F.NumberColumn = F.Column.extend(/** @lends FooTable.NumberColumn */{ + /** + * The number column class is used to handle simple number columns. + * @constructs + * @extends FooTable.Column + * @param {FooTable.Table} instance - The parent {@link FooTable.Table} this column belongs to. + * @param {object} definition - An object containing all the properties to set for the column. + * @returns {FooTable.NumberColumn} + */ + construct: function(instance, definition){ + this._super(instance, definition, 'number'); + this.decimalSeparator = F.is.string(definition.decimalSeparator) ? definition.decimalSeparator : '.'; + this.thousandSeparator = F.is.string(definition.thousandSeparator) ? definition.thousandSeparator : ','; + this.decimalSeparatorRegex = new RegExp(F.str.escapeRegExp(this.decimalSeparator), 'g'); + this.thousandSeparatorRegex = new RegExp(F.str.escapeRegExp(this.thousandSeparator), 'g'); + this.cleanRegex = new RegExp('[^\-0-9' + F.str.escapeRegExp(this.decimalSeparator) + ']', 'g'); + }, + /** + * This is supplied either the cell value or jQuery object to parse. Any value can be returned from this method and will be provided to the {@link FooTable.Column#formatter} function + * to generate the cell contents. + * @instance + * @protected + * @param {(*|jQuery)} valueOrElement - The value or jQuery cell object. + * @returns {(number|null)} + * @this FooTable.NumberColumn + */ + parser: function(valueOrElement){ + if (F.is.element(valueOrElement) || F.is.jq(valueOrElement)){ + var data = $(valueOrElement).data('value'); + valueOrElement = F.is.defined(data) ? data : $(valueOrElement).text().replace(this.cleanRegex, ''); + } + if (F.is.string(valueOrElement)){ + valueOrElement = valueOrElement.replace(this.thousandSeparatorRegex, '').replace(this.decimalSeparatorRegex, '.'); + valueOrElement = parseFloat(valueOrElement); + } + if (F.is.number(valueOrElement)) return valueOrElement; + return null; + }, + /** + * This is supplied the value retrieved from the {@link FooTable.NumberColumn#parse} function and must return a string, HTMLElement or jQuery object. + * The return value from this function is what is displayed in the cell in the table. + * @instance + * @protected + * @param {number} value - The value to format. + * @param {object} options - The current plugin options. + * @param {object} rowData - An object containing the current row data. + * @returns {(string|HTMLElement|jQuery)} + * @this FooTable.NumberColumn + */ + formatter: function(value, options, rowData){ + if (value == null) return ''; + var s = (value + '').split('.'); + if (s.length == 2 && s[0].length > 3) { + s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, this.thousandSeparator); + } + return s.join(this.decimalSeparator); + } + }); + + F.columns.register('number', F.NumberColumn); + +})(jQuery, FooTable); +(function($, F){ + + F.ObjectColumn = F.Column.extend(/** @lends FooTable.ObjectColumn */{ + /** + * @summary A column to handle Object values. + * @constructs + * @extends FooTable.Column + * @param {FooTable.Table} instance - The parent {@link FooTable.Table} this column belongs to. + * @param {object} definition - An object containing all the properties to set for the column. + */ + construct: function(instance, definition) { + this._super(instance, definition, 'object'); + }, + /** + * @summary Parses the supplied value or element to retrieve a column value. + * @description This is supplied either the cell value or jQuery object to parse. This method will return either the Object containing the values or null. + * @instance + * @protected + * @param {(*|jQuery)} valueOrElement - The value or jQuery cell object. + * @returns {(object|null)} + */ + parser: function(valueOrElement){ + if (F.is.element(valueOrElement) || F.is.jq(valueOrElement)){ // use jQuery to get the value + var $el = $(valueOrElement), data = $el.data('value'); // .data() will automatically convert a JSON string to an object + if (F.is.object(data)) return data; + data = $el.html(); + try { + data = JSON.parse(data); + } catch(err) { + data = null; + } + return F.is.object(data) ? data : null; // if we have an object return it + } + if (F.is.object(valueOrElement)) return valueOrElement; // if we have an object return it + return null; // otherwise we have no value so return null + }, + /** + * @summary Formats the column value and creates the HTML seen within a cell. + * @description This is supplied the value retrieved from the {@link FooTable.ObjectColumn#parser} function and must return a string, HTMLElement or jQuery object. + * The return value from this function is what is displayed in the cell in the table. + * @instance + * @protected + * @param {*} value - The value to format. + * @param {object} options - The current plugin options. + * @param {object} rowData - An object containing the current row data. + * @returns {(string|HTMLElement|jQuery)} + */ + formatter: function(value, options, rowData){ + return F.is.object(value) ? JSON.stringify(value) : ''; + } + }); + + F.columns.register('object', F.ObjectColumn); + +})(jQuery, FooTable); +(function($, F){ + + F.Breakpoint = F.Class.extend(/** @lends FooTable.Breakpoint */{ + /** + * The breakpoint class containing the name and maximum width for the breakpoint. + * @constructs + * @extends FooTable.Class + * @param {string} name - The name of the breakpoint. Must contain no spaces or special characters. + * @param {number} width - The width of the breakpoint in pixels. + * @returns {FooTable.Breakpoint} + */ + construct: function(name, width){ + /** + * The name of the breakpoint. + * @type {string} + */ + this.name = name; + /** + * The maximum width of the breakpoint in pixels. + * @type {number} + */ + this.width = width; + } + }); + +})(jQuery, FooTable); +(function($, F){ + F.Breakpoints = F.Component.extend(/** @lends FooTable.Breakpoints */{ + /** + * Contains the logic to calculate and apply breakpoints for the plugin. + * @constructs + * @extends FooTable.Component + * @param {FooTable.Table} table - The parent {@link FooTable.Table} this component belongs to. + * @returns {FooTable.Breakpoints} + */ + construct: function(table){ + // call the base class constructor + this._super(table, true); + + /* PROTECTED */ + /** + * This provides a shortcut to the {@link FooTable.Table#options} object. + * @protected + * @type {FooTable.Table#options} + */ + this.o = table.o; + + /* PUBLIC */ + /** + * The current breakpoint. + * @type {FooTable.Breakpoint} + */ + this.current = null; + /** + * An array of {@link FooTable.Breakpoint} objects created from parsing the options. + * @type {Array.} + */ + this.array = []; + /** + * Whether or not breakpoints cascade. When set to true all breakpoints larger than the current will be hidden along with it. + * @type {boolean} + */ + this.cascade = this.o.cascade; + /** + * Whether or not to calculate breakpoints on the width of the parent element rather than the viewport. + * @type {boolean} + */ + this.useParentWidth = this.o.useParentWidth; + /** + * This value is updated each time the current breakpoint changes and contains a space delimited string of the names of the current breakpoint and all those smaller than it. + * @type {string} + */ + this.hidden = null; + + /* PRIVATE */ + /** + * This value is set once when the {@link FooTable.Breakpoints#array} is generated and contains a space delimited string of all the breakpoint class names. + * @type {string} + * @private + */ + this._classNames = ''; + + // check if a function was supplied to override the default getWidth + this.getWidth = F.checkFnValue(this, this.o.getWidth, this.getWidth); + }, + + /* PROTECTED */ + /** + * Checks the supplied data and options for the breakpoints component. + * @instance + * @protected + * @param {object} data - The jQuery data object from the parent table. + * @fires FooTable.Breakpoints#"preinit.ft.breakpoints" + */ + preinit: function(data){ + var self = this; + /** + * The preinit.ft.breakpoints event is raised before any UI is created and provides the tables jQuery data object for additional options parsing. + * Calling preventDefault on this event will disable the entire plugin. + * @event FooTable.Breakpoints#"preinit.ft.breakpoints" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + * @param {object} data - The jQuery data object of the table raising the event. + */ + return this.ft.raise('preinit.ft.breakpoints', [data]).then(function(){ + self.cascade = F.is.boolean(data.cascade) ? data.cascade : self.cascade; + self.o.breakpoints = F.is.hash(data.breakpoints) ? data.breakpoints : self.o.breakpoints; + self.getWidth = F.checkFnValue(self, data.getWidth, self.getWidth); + if (self.o.breakpoints == null) self.o.breakpoints = { "xs": 480, "sm": 768, "md": 992, "lg": 1200 }; + // Create a nice friendly array to work with out of the breakpoints object. + for (var name in self.o.breakpoints) { + if (!self.o.breakpoints.hasOwnProperty(name)) continue; + self.array.push(new F.Breakpoint(name, self.o.breakpoints[name])); + self._classNames += 'breakpoint-' + name + ' '; + } + // Sort the breakpoints so the largest is checked first + self.array.sort(function (a, b) { + return b.width - a.width; + }); + }); + }, + /** + * Initializes the class parsing the options into a sorted array of {@link FooTable.Breakpoint} objects. + * @instance + * @protected + * @fires FooTable.Breakpoints#"init.ft.breakpoints" + */ + init: function(){ + var self = this; + /** + * The init.ft.breakpoints event is raised before any UI is generated. + * Calling preventDefault on this event will disable the entire plugin. + * @event FooTable.Breakpoints#"init.ft.breakpoints" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + return this.ft.raise('init.ft.breakpoints').then(function(){ + self.current = self.get(); + }); + }, + /** + * Whenever the table is drawn this ensures the correct breakpoint class is applied to the table. + * @instance + * @protected + */ + draw: function(){ + this.ft.$el.removeClass(this._classNames).addClass('breakpoint-' + this.current.name); + }, + + /* PUBLIC */ + /** + * Calculates the current breakpoint from the {@link FooTable.Breakpoints#array} and sets the {@link FooTable.Breakpoints#current} property. + * @instance + * @returns {FooTable.Breakpoint} + */ + calculate: function(){ + var self = this, current = null, hidden = [], breakpoint, prev = null, width = self.getWidth(); + for (var i = 0, len = self.array.length; i < len; i++) { + breakpoint = self.array[i]; + // if the width is smaller than the smallest breakpoint set the smallest as the current. + // if the width is larger than the largest breakpoint set the largest as the current. + // otherwise if the width is somewhere in between check all breakpoints testing if the width + // is greater than the current but smaller than the previous. + if ((!current && i == len -1) + || (width >= breakpoint.width && (prev instanceof F.Breakpoint ? width < prev.width : true))) { + current = breakpoint; + } + if (!current) hidden.push(breakpoint.name); + prev = breakpoint; + } + hidden.push(current.name); + self.hidden = hidden.join(' '); + return current; + }, + /** + * Supplied a columns breakpoints this returns a boolean value indicating whether or not the column is visible. + * @param {string} breakpoints - A space separated string of breakpoint names. + * @returns {boolean} + */ + visible: function(breakpoints){ + if (F.is.emptyString(breakpoints)) return true; + if (breakpoints === 'all') return false; + var parts = breakpoints.split(' '), i = 0, len = parts.length; + for (; i < len; i++){ + if (this.cascade ? F.str.containsWord(this.hidden, parts[i]) : parts[i] == this.current.name) return false; + } + return true; + }, + /** + * Performs a check between the current breakpoint and the previous breakpoint and performs a redraw if they differ. + * @instance + * @fires FooTable.Breakpoints#"before.ft.breakpoints" + * @fires FooTable.Breakpoints#"after.ft.breakpoints" + */ + check: function(){ + var self = this, bp = self.get(); + if (!(bp instanceof F.Breakpoint) + || bp == self.current) + return; + + /** + * The before.ft.breakpoints event is raised if the breakpoint has changed but before the UI is redrawn and is supplied both the current breakpoint + * and the next "new" one that is about to be applied. + * Calling preventDefault on this event will prevent the next breakpoint from being applied. + * @event FooTable.Breakpoints#"before.ft.breakpoints" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + * @param {FooTable.Breakpoint} current - The current breakpoint. + * @param {FooTable.Breakpoint} next - The breakpoint that is about to be applied. + */ + self.ft.raise('before.ft.breakpoints', [self.current, bp]).then(function(){ + var previous = self.current; + self.current = bp; + return self.ft.draw().then(function(){ + /** + * The after.ft.breakpoints event is raised after the breakpoint has changed and the UI is redrawn and is supplied both the "new" current breakpoint + * and the previous one that was replaced. + * @event FooTable.Breakpoints#"after.ft.breakpoints" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + * @param {FooTable.Breakpoint} current - The current breakpoint. + * @param {FooTable.Breakpoint} previous - The breakpoint that was just replaced. + */ + self.ft.raise('after.ft.breakpoints', [self.current, previous]); + }); + }); + }, + /** + * Attempts to return a {@link FooTable.Breakpoint} instance when passed a {@link FooTable.Breakpoint}, + * the {@link FooTable.Breakpoint#name} string or if nothing is supplied the current breakpoint. + * @instance + * @param {(FooTable.Breakpoint|string|number)} [breakpoint] - The breakpoint to retrieve. + * @returns {FooTable.Breakpoint} + */ + get: function(breakpoint){ + if (F.is.undef(breakpoint)) return this.calculate(); + if (breakpoint instanceof F.Breakpoint) return breakpoint; + if (F.is.string(breakpoint)) return F.arr.first(this.array, function (bp) { return bp.name == breakpoint; }); + if (F.is.number(breakpoint)) return breakpoint >= 0 && breakpoint < this.array.length ? this.array[breakpoint] : null; + return null; + }, + /** + * Gets the width used to determine breakpoints whether it be from the viewport, parent or a custom function. + * @instance + * @returns {number} + */ + getWidth: function(){ + if (F.is.fn(this.o.getWidth)) return this.o.getWidth(this.ft); + if (this.useParentWidth == true) return this.getParentWidth(); + return this.getViewportWidth(); + }, + /** + * Gets the tables direct parents width. + * @instance + * @returns {number} + */ + getParentWidth: function(){ + return this.ft.$el.parent().width(); + }, + /** + * Gets the current viewport width. + * @instance + * @returns {number} + */ + getViewportWidth: function(){ + return Math.max(document.documentElement.clientWidth, window.innerWidth, 0); + } + }); + + F.components.register('breakpoints', F.Breakpoints, 1000); + +})(jQuery, FooTable); +(function(F){ + /** + * A space delimited string of breakpoint names that specify when the column will be hidden. You can also specify "all" to make a column permanently display in an expandable detail row. + * @type {string} + * @default null + * @example + * breakpoints: "md" + */ + F.Column.prototype.breakpoints = null; + + F.Column.prototype.__breakpoints_define__ = function(definition){ + this.breakpoints = F.is.emptyString(definition.breakpoints) ? null : definition.breakpoints; + }; + + F.Column.extend('define', function(definition){ + this._super(definition); + this.__breakpoints_define__(definition); + }); +})(FooTable); +(function(F){ + /** + * An object containing the breakpoints for the plugin. + * @type {object.} + * @default { "xs": 480, "sm": 768, "md": 992, "lg": 1200 } + */ + F.Defaults.prototype.breakpoints = null; + + /** + * Whether or not breakpoints cascade. When set to true all breakpoints larger than the current will also be hidden along with it. + * @type {boolean} + * @default false + */ + F.Defaults.prototype.cascade = false; + + /** + * Whether or not to calculate breakpoints on the width of the parent element rather than the viewport. + * @type {boolean} + * @default false + */ + F.Defaults.prototype.useParentWidth = false; + + /** + * A function used to override the default getWidth function with a custom one. + * @type {function} + * @default null + * @example + * getWidth: function(instance){ + * if (instance.o.useParentWidth == true) return instance.$el.parent().width(); + * return instance.breakpoints.getViewportWidth(); + * } + */ + F.Defaults.prototype.getWidth = null; +})(FooTable); +(function($, F){ + F.Columns = F.Component.extend(/** @lends FooTable.Columns */{ + /** + * The columns class contains all the logic for handling columns. + * @constructs + * @extends FooTable.Component + * @param {FooTable.Table} table - The parent {@link FooTable.Table} this component belongs to. + * @returns {FooTable.Columns} + */ + construct: function(table){ + // call the base class constructor + this._super(table, true); + + /* PROTECTED */ + /** + * This provides a shortcut to the {@link FooTable.Table#options} object. + * @protected + * @type {FooTable.Table#options} + */ + this.o = table.o; + + /* PUBLIC */ + /** + * An array of {@link FooTable.Column} objects created from parsing the options and/or DOM. + * @type {Array.} + */ + this.array = []; + /** + * The jQuery header row object. + * @type {jQuery} + */ + this.$header = null; + /** + * Whether or not to display the header row. + * @type {boolean} + */ + this.showHeader = table.o.showHeader; + + this._fromHTML = F.is.emptyArray(table.o.columns) && !F.is.promise(table.o.columns); + }, + + /* PROTECTED */ + /** + * This parses the columns from either the tables rows or the supplied options. + * @instance + * @protected + * @param {object} data - The tables jQuery data object. + * @returns {jQuery.Promise} + * @this FooTable.Columns + */ + parse: function(data){ + var self = this; + return $.Deferred(function(d){ + function merge(cols1, cols2){ + var merged = []; + // check if either of the arrays is empty as it can save us having to merge them by index. + if (cols1.length == 0 || cols2.length == 0){ + merged = cols1.concat(cols2); + } else { + // at this point we have two arrays of column definitions, we now need to merge them based on there index properties + // first figure out the highest column index provided so we can loop that many times to merge all columns and provide + // defaults where nothing was specified (fill in the gaps in the array as it were). + var highest = 0; + F.arr.each(cols1.concat(cols2), function(c){ + if (c.index > highest) highest = c.index; + }); + highest++; + for (var i = 0, cols1_c, cols2_c; i < highest; i++){ + cols1_c = {}; + F.arr.each(cols1, function(c){ + if (c.index == i){ + cols1_c = c; + return false; + } + }); + cols2_c = {}; + F.arr.each(cols2, function(c){ + if (c.index == i){ + cols2_c = c; + return false; + } + }); + merged.push($.extend(true, {}, cols1_c, cols2_c)); + } + } + return merged; + } + + var json = [], html = []; + // get the column options from the content + var $header = self.ft.$el.find('tr.footable-header, thead > tr:last:has([data-breakpoints]), tbody > tr:first:has([data-breakpoints]), thead > tr:last, tbody > tr:first').first(), $cell, cdata; + if ($header.length > 0){ + var virtual = $header.parent().is('tbody') && $header.children().length == $header.children('td').length; + if (!virtual) self.$header = $header.addClass('footable-header'); + $header.children('td,th').each(function(i, cell){ + $cell = $(cell); + cdata = $cell.data(); + cdata.index = i; + cdata.$el = $cell; + cdata.virtual = virtual; + html.push(cdata); + }); + if (virtual) self.showHeader = false; + } + // get the supplied column options + if (F.is.array(self.o.columns) && !F.is.emptyArray(self.o.columns)){ + F.arr.each(self.o.columns, function(c, i){ + c.index = i; + json.push(c); + }); + self.parseFinalize(d, merge(json, html)); + } else if (F.is.promise(self.o.columns)){ + self.o.columns.then(function(cols){ + F.arr.each(cols, function(c, i){ + c.index = i; + json.push(c); + }); + self.parseFinalize(d, merge(json, html)); + }, function(xhr){ + d.reject(Error('Columns ajax request error: ' + xhr.status + ' (' + xhr.statusText + ')')); + }); + } else { + self.parseFinalize(d, merge(json, html)); + } + }); + }, + /** + * Used to finalize the parsing of columns it is supplied the parse deferred object which must be resolved with an array of {@link FooTable.Column} objects + * or rejected with an error. + * @instance + * @protected + * @param {jQuery.Deferred} deferred - The deferred object used for parsing. + * @param {Array.} cols - An array of all merged column definitions. + */ + parseFinalize: function(deferred, cols){ + // we now have a merged array of all column definitions supplied to the plugin, time to make the objects. + var self = this, columns = [], column; + F.arr.each(cols, function(def){ + // if we have a column registered using the definition type then create an instance of that column otherwise just create a default text column. + if (column = F.columns.contains(def.type) ? F.columns.make(def.type, self.ft, def) : new F.Column(self.ft, def)) + columns.push(column); + }); + if (F.is.emptyArray(columns)){ + deferred.reject(Error("No columns supplied.")); + } else { + // make sure to sort by the column index as the merge process may have mixed them up + columns.sort(function(a, b){ return a.index - b.index; }); + deferred.resolve(columns); + } + }, + /** + * The columns preinit method is used to parse and check the column options supplied from both static content and through the constructor. + * @instance + * @protected + * @param {object} data - The jQuery data object from the root table element. + * @this FooTable.Columns + */ + preinit: function(data){ + var self = this; + /** + * The preinit.ft.columns event is raised before any UI is created and provides the tables jQuery data object for additional options parsing. + * Calling preventDefault on this event will disable the entire plugin. + * @event FooTable.Columns#"preinit.ft.columns" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + * @param {object} data - The jQuery data object of the table raising the event. + */ + return self.ft.raise('preinit.ft.columns', [data]).then(function(){ + return self.parse(data).then(function(columns){ + self.array = columns; + self.showHeader = F.is.boolean(data.showHeader) ? data.showHeader : self.showHeader; + }); + }); + }, + /** + * Initializes the columns creating the table header if required. + * @instance + * @protected + * @fires FooTable.Columns#"init.ft.columns" + * @this FooTable.Columns + */ + init: function(){ + var self = this; + /** + * The init.ft.columns event is raised after the header row is created/parsed for column data. + * @event FooTable.Columns#"init.ft.columns" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} instance - The instance of the plugin raising the event. + * @param {Array.} columns - The array of {@link FooTable.Column} objects parsed from the options and/or DOM. + */ + return this.ft.raise('init.ft.columns', [ self.array ]).then(function(){ + self.$create(); + }); + }, + /** + * Destroys the columns component removing any UI generated from the table. + * @instance + * @protected + * @fires FooTable.Columns#"destroy.ft.columns" + */ + destroy: function(){ + /** + * The destroy.ft.columns event is raised before its UI is removed. + * Calling preventDefault on this event will prevent the component from being destroyed. + * @event FooTable.Columns#"destroy.ft.columns" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + var self = this; + this.ft.raise('destroy.ft.columns').then(function(){ + if (!self._fromHTML) self.$header.remove(); + }); + }, + /** + * The predraw method called from within the {@link FooTable.Table#draw} method. + * @instance + * @protected + * @this FooTable.Columns + */ + predraw: function(){ + var self = this, first = true; + self.visibleColspan = 0; + self.firstVisibleIndex = 0; + self.lastVisibleIndex = 0; + self.hasHidden = false; + F.arr.each(self.array, function(col){ + col.hidden = !self.ft.breakpoints.visible(col.breakpoints); + if (!col.hidden && col.visible){ + if (first){ + self.firstVisibleIndex = col.index; + first = false; + } + self.lastVisibleIndex = col.index; + self.visibleColspan++; + } + if (col.hidden) self.hasHidden = true; + }); + self.ft.$el.toggleClass('breakpoint', self.hasHidden); + }, + /** + * Performs the actual drawing of the columns, hiding or displaying them depending on there breakpoints. + * @instance + * @protected + * @this FooTable.Columns + */ + draw: function(){ + F.arr.each(this.array, function(col){ + col.$el.css('display', (col.hidden || !col.visible ? 'none' : 'table-cell')); + }); + if (!this.showHeader && F.is.jq(this.$header.parent())){ + this.$header.detach(); + } + }, + /** + * Creates the header row for the table from the parsed column definitions. + * @instance + * @protected + * @this FooTable.Columns + */ + $create: function(){ + var self = this; + self.$header = F.is.jq(self.$header) ? self.$header : $('', {'class': 'footable-header'}); + self.$header.children('th,td').detach(); + F.arr.each(self.array, function(col){ + self.$header.append(col.$el); + }); + if (self.showHeader && !F.is.jq(self.$header.parent())){ + self.ft.$el.children('thead').append(self.$header); + } + }, + /** + * Attempts to return a {@link FooTable.Column} instance when passed the {@link FooTable.Column} instance, the {@link FooTable.Column#name} string or the {@link FooTable.Column#index} number. + * If supplied a function this will return an array by iterating all columns passing the index and column itself to the supplied callback as arguments. + * Returning true in the callback will include the column in the result. + * @instance + * @param {(FooTable.Column|string|number|function)} column - The column to retrieve. + * @returns {(Array.|FooTable.Column|null)} The column if one is found otherwise it returns NULL. + * @example + * var column = columns.get('id'); + * if (column instanceof FooTable.Column){ + * // found the "id" column + * } else { + * // no column with a name of "id" exists + * } + * // to get an array of all hidden columns + * var columns = columns.get(function(col){ + * return col.hidden; + * }); + */ + get: function(column){ + if (column instanceof F.Column) return column; + if (F.is.string(column)) return F.arr.first(this.array, function (col) { return col.name == column; }); + if (F.is.number(column)) return F.arr.first(this.array, function (col) { return col.index == column; }); + if (F.is.fn(column)) return F.arr.get(this.array, column); + return null; + }, + /** + * Takes an array of column names, index's or actual {@link FooTable.Column} and ensures that an array of only {@link FooTable.Column} is returned. + * @instance + * @param {(Array.|Array.|Array.)} columns - The array of column names, index's or {@link FooTable.Column} to check. + * @returns {Array.} + */ + ensure: function(columns){ + var self = this, result = []; + if (!F.is.array(columns)) return result; + F.arr.each(columns, function(name){ + result.push(self.get(name)); + }); + return result; + } + }); + + F.components.register('columns', F.Columns, 900); + +})(jQuery, FooTable); +(function(F){ + /** + * An array containing the column options or a jQuery promise that resolves returning the columns. The index of the definitions must match the index of each column as it should appear in the table. For more information on the options available see the {@link FooTable.Column} object. + * @type {(Array.|jQuery.Promise)} + * @default [] + * @example + * columns: [ + * { name: 'id', title: 'ID', type: 'number' }, + * { name: 'name', title: 'Name', sorted: true, direction: 'ASC' } + * { name: 'age', title: 'Age', type: 'number', breakpoints: 'xs' } + * ] + */ + F.Defaults.prototype.columns = []; + + /** + * Specifies whether or not the column headers should be displayed. + * @type {boolean} + * @default true + */ + F.Defaults.prototype.showHeader = true; +})(FooTable); +(function ($, F) { + F.Rows = F.Component.extend(/** @lends FooTable.Rows */{ + /** + * The rows class contains all the logic for handling rows. + * @constructs + * @extends FooTable.Component + * @param {FooTable.Table} table - The parent {@link FooTable.Table} this component belongs to. + * @returns {FooTable.Rows} + */ + construct: function (table) { + // call the base class constructor + this._super(table, true); + + /** + * This provides a shortcut to the {@link FooTable.Table#options} object. + * @instance + * @protected + * @type {FooTable.Table#options} + */ + this.o = table.o; + /** + * The current working array of {@link FooTable.Row} objects. + * @instance + * @protected + * @type {Array.} + * @default [] + */ + this.array = []; + /** + * The base array of rows parsed from either the DOM or the constructor options. + * The {@link FooTable.Rows#current} member is populated with a shallow clone of this array + * during the predraw operation before any core or custom components are executed. + * @instance + * @protected + * @type {Array.} + * @default [] + */ + this.all = []; + /** + * Whether or not to display a toggle in each row when it contains hidden columns. + * @type {boolean} + * @default true + */ + this.showToggle = table.o.showToggle; + /** + * The CSS selector used to filter row click events. If the event.target property matches the selector the row will be toggled. + * @type {string} + * @default "tr,td,.footable-toggle" + */ + this.toggleSelector = table.o.toggleSelector; + /** + * Specifies which column the row toggle is appended to. Supports only two values; "first" and "last" + * @type {string} + */ + this.toggleColumn = table.o.toggleColumn; + /** + * The text to display when the table has no rows. + * @type {string} + */ + this.emptyString = table.o.empty; + /** + * Whether or not the first rows details are expanded by default when displayed on a device that hides any columns. + * @type {boolean} + */ + this.expandFirst = table.o.expandFirst; + /** + * Whether or not all row details are expanded by default when displayed on a device that hides any columns. + * @type {boolean} + */ + this.expandAll = table.o.expandAll; + /** + * The jQuery object that contains the empty row control. + * @type {jQuery} + */ + this.$empty = null; + this._fromHTML = F.is.emptyArray(table.o.rows) && !F.is.promise(table.o.rows); + }, + /** + * This parses the rows from either the tables rows or the supplied options. + * @instance + * @protected + * @returns {jQuery.Promise} + */ + parse: function(){ + var self = this; + return $.Deferred(function(d){ + var $rows = self.ft.$el.children('tbody').children('tr'); + if (F.is.array(self.o.rows) && self.o.rows.length > 0){ + self.parseFinalize(d, self.o.rows); + } else if (F.is.promise(self.o.rows)){ + self.o.rows.then(function(rows){ + self.parseFinalize(d, rows); + }, function(xhr){ + d.reject(Error('Rows ajax request error: ' + xhr.status + ' (' + xhr.statusText + ')')); + }); + } else if (F.is.jq($rows)){ + self.parseFinalize(d, $rows); + $rows.detach(); + } else { + self.parseFinalize(d, []); + } + }); + }, + /** + * Used to finalize the parsing of rows it is supplied the parse deferred object which must be resolved with an array of {@link FooTable.Row} objects + * or rejected with an error. + * @instance + * @protected + * @param {jQuery.Deferred} deferred - The deferred object used for parsing. + * @param {(Array.|jQuery)} rows - An array of row values and options or the jQuery object containing all rows. + */ + parseFinalize: function(deferred, rows){ + var self = this, result = $.map(rows, function(r){ + return new F.Row(self.ft, self.ft.columns.array, r); + }); + deferred.resolve(result); + }, + /** + * The columns preinit method is used to parse and check the column options supplied from both static content and through the constructor. + * @instance + * @protected + * @param {object} data - The jQuery data object from the root table element. + * @fires FooTable.Rows#"preinit.ft.rows" + */ + preinit: function(data){ + var self = this; + /** + * The preinit.ft.rows event is raised before any UI is created and provides the tables jQuery data object for additional options parsing. + * Calling preventDefault on this event will disable the entire plugin. + * @event FooTable.Rows#"preinit.ft.rows" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + * @param {object} data - The jQuery data object of the table raising the event. + */ + return self.ft.raise('preinit.ft.rows', [data]).then(function(){ + return self.parse().then(function(rows){ + self.all = rows; + self.array = self.all.slice(0); + self.showToggle = F.is.boolean(data.showToggle) ? data.showToggle : self.showToggle; + self.toggleSelector = F.is.string(data.toggleSelector) ? data.toggleSelector : self.toggleSelector; + self.toggleColumn = F.is.string(data.toggleColumn) ? data.toggleColumn : self.toggleColumn; + if (self.toggleColumn != "first" && self.toggleColumn != "last") self.toggleColumn = "first"; + self.emptyString = F.is.string(data.empty) ? data.empty : self.emptyString; + self.expandFirst = F.is.boolean(data.expandFirst) ? data.expandFirst : self.expandFirst; + self.expandAll = F.is.boolean(data.expandAll) ? data.expandAll : self.expandAll; + }); + }); + }, + /** + * Initializes the rows class using the supplied table and options. + * @instance + * @protected + * @fires FooTable.Rows#"init.ft.rows" + */ + init: function () { + var self = this; + /** + * The init.ft.rows event is raised after the the rows are parsed from either the DOM or the options. + * Calling preventDefault on this event will disable the entire plugin. + * @event FooTable.Rows#"init.ft.rows" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} instance - The instance of the plugin raising the event. + * @param {Array.} rows - The array of {@link FooTable.Row} objects parsed from the DOM or the options. + */ + return self.ft.raise('init.ft.rows', [self.all]).then(function(){ + self.$create(); + }); + }, + /** + * Destroys the rows component removing any UI generated from the table. + * @instance + * @protected + * @fires FooTable.Rows#"destroy.ft.rows" + */ + destroy: function(){ + /** + * The destroy.ft.rows event is raised before its UI is removed. + * Calling preventDefault on this event will prevent the component from being destroyed. + * @event FooTable.Rows#"destroy.ft.rows" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + var self = this; + this.ft.raise('destroy.ft.rows').then(function(){ + F.arr.each(self.array, function(row){ + row.predraw(!self._fromHTML); + }); + self.all = self.array = []; + }); + }, + /** + * Performs the predraw operations that are required including creating the shallow clone of the {@link FooTable.Rows#array} to work with. + * @instance + * @protected + */ + predraw: function(){ + F.arr.each(this.array, function(row){ + row.predraw(); + }); + this.array = this.all.slice(0); + }, + $create: function(){ + this.$empty = $('', { 'class': 'footable-empty' }).append($('', {'class': 'footable-filtering'}).prependTo(self.ft.$el.children('thead')); + self.$cell = $(' + * query = "Dave AND Mary" - "Dave" is the left side of the query. + * query = "Dave AND Mary OR John" - "Dave and Mary" is the left side of the query. + */ + this.left = null; + /** + * The right side of the query if one exists. OR takes precedence over AND. + * @type {FooTable.Query} + * @example + * query = "Dave AND Mary" - "Mary" is the right side of the query. + * query = "Dave AND Mary OR John" - "John" is the right side of the query. + */ + this.right = null; + /** + * The parsed parts of the query. This contains the information used to actually perform a match against a string. + * @type {Array} + */ + this.parts = []; + /** + * The type of operand to apply to the results of the individual parts of the query. + * @type {string} + */ + this.operator = null; + this.val(query); + }, + /** + * Gets or sets the value for the query. During set the value is parsed setting all properties as required. + * @param {string} [value] - If supplied the value to set for this query. + * @returns {(string|undefined)} + */ + val: function(value){ + // get + if (F.is.emptyString(value)) return this._value; + + // set + if (F.is.emptyString(this._original)) this._original = value; + else if (this._original == value) return; + + this._value = value; + this._parse(); + }, + /** + * Tests the supplied string against the query. + * @param {string} str - The string to test. + * @returns {boolean} + */ + match: function(str){ + if (F.is.emptyString(this.operator) || this.operator === 'OR') + return this._left(str, false) || this._match(str, false) || this._right(str, false); + if (this.operator === 'AND') + return this._left(str, true) && this._match(str, true) && this._right(str, true); + }, + /** + * Matches this queries parts array against the supplied string. + * @param {string} str - The string to test. + * @param {boolean} def - The default value to return based on the operand. + * @returns {boolean} + * @private + */ + _match: function(str, def){ + var self = this, result = false, empty = F.is.emptyString(str); + if (F.is.emptyArray(self.parts) && self.left instanceof F.Query) return def; + if (F.is.emptyArray(self.parts)) return result; + if (self.space === 'OR'){ + // with OR we give the str every part to test and if any match it is a success, we do exit early if a negated match occurs + F.arr.each(self.parts, function(p){ + if (p.empty && empty){ + result = true; + if (p.negate){ + result = false; + return result; + } + } else { + var match = (p.exact ? F.str.containsExact : F.str.contains)(str, p.query, self.ignoreCase); + if (match && !p.negate) result = true; + if (match && p.negate) { + result = false; + return result; + } + } + }); + } else { + // otherwise with AND we check until the first failure and then exit + result = true; + F.arr.each(self.parts, function(p){ + if (p.empty){ + if ((!empty && !p.negate) || (empty && p.negate)) result = false; + return result; + } else { + var match = (p.exact ? F.str.containsExact : F.str.contains)(str, p.query, self.ignoreCase); + if ((!match && !p.negate) || (match && p.negate)) result = false; + return result; + } + }); + } + return result; + }, + /** + * Matches the left side of the query if one exists with the supplied string. + * @param {string} str - The string to test. + * @param {boolean} def - The default value to return based on the operand. + * @returns {boolean} + * @private + */ + _left: function(str, def){ + return (this.left instanceof F.Query) ? this.left.match(str) : def; + }, + /** + * Matches the right side of the query if one exists with the supplied string. + * @param {string} str - The string to test. + * @param {boolean} def - The default value to return based on the operand. + * @returns {boolean} + * @private + */ + _right: function(str, def){ + return (this.right instanceof F.Query) ? this.right.match(str) : def; + }, + /** + * Parses the private {@link FooTable.Query#_value} property and populates the object. + * @private + */ + _parse: function(){ + if (F.is.emptyString(this._value)) return; + // OR takes precedence so test for it first + if (/\sOR\s/.test(this._value)){ + // we have an OR so split the value on the first occurrence of OR to get the left and right sides of the statement + this.operator = 'OR'; + var or = this._value.split(/(?:\sOR\s)(.*)?/); + this.left = new F.Query(or[0], this.space, this.connectors, this.ignoreCase); + this.right = new F.Query(or[1], this.space, this.connectors, this.ignoreCase); + } else if (/\sAND\s/.test(this._value)) { + // there are no more OR's so start with AND + this.operator = 'AND'; + var and = this._value.split(/(?:\sAND\s)(.*)?/); + this.left = new F.Query(and[0], this.space, this.connectors, this.ignoreCase); + this.right = new F.Query(and[1], this.space, this.connectors, this.ignoreCase); + } else { + // we have no more statements to parse so set the parts array by parsing each part of the remaining query + var self = this; + this.parts = F.arr.map(this._value.match(/(?:[^\s"]+|"[^"]*")+/g), function(str){ + return self._part(str); + }); + } + }, + /** + * Parses a single part of a query into an object to use during matching. + * @param {string} str - The string representation of the part. + * @returns {{query: string, negate: boolean, phrase: boolean, exact: boolean}} + * @private + */ + _part: function(str){ + var p = { + query: str, + negate: false, + phrase: false, + exact: false, + empty: false + }; + // support for NEGATE operand - (minus sign). Remove this first so we can get onto phrase checking + if (F.str.startsWith(p.query, '-')){ + p.query = F.str.from(p.query, '-'); + p.negate = true; + } + // support for PHRASES (exact matches) + if (/^"(.*?)"$/.test(p.query)){ // if surrounded in quotes strip them and nothing else + p.query = p.query.replace(/^"(.*?)"$/, '$1'); + p.phrase = true; + p.exact = true; + } else if (this.connectors && /(?:\w)+?([-_\+\.])(?:\w)+?/.test(p.query)) { // otherwise replace supported phrase connectors (-_+.) with spaces + p.query = p.query.replace(/(?:\w)+?([-_\+\.])(?:\w)+?/g, function(match, p1){ + return match.replace(p1, ' '); + }); + p.phrase = true; + } + p.empty = p.phrase && F.is.emptyString(p.query); + return p; + } + }); + +})(FooTable); +(function(F){ + + /** + * The value used by the filtering component during filter operations. Must be a string and can be set using the data-filter-value attribute on the cell itself. + * If this is not supplied it is set to the result of the toString method called on the value for the cell. Added by the {@link FooTable.Filtering} component. + * @type {string} + * @default null + */ + F.Cell.prototype.filterValue = null; + + // this is used to define the filtering specific properties on cell creation + F.Cell.prototype.__filtering_define__ = function(valueOrElement){ + this.filterValue = this.column.filterValue.call(this.column, valueOrElement); + }; + + // this is used to update the filterValue property whenever the cell value is changed + F.Cell.prototype.__filtering_val__ = function(value){ + if (F.is.defined(value)){ + // set only + this.filterValue = this.column.filterValue.call(this.column, value); + } + }; + + // overrides the public define method and replaces it with our own + F.Cell.extend('define', function(valueOrElement){ + this._super(valueOrElement); + this.__filtering_define__(valueOrElement); + }); + // overrides the public val method and replaces it with our own + F.Cell.extend('val', function(value, redraw, redrawSelf){ + var val = this._super(value, redraw, redrawSelf); + this.__filtering_val__(value); + return val; + }); +})(FooTable); +(function($, F){ + /** + * Whether or not the column can be used during filtering. Added by the {@link FooTable.Filtering} component. + * @type {boolean} + * @default true + */ + F.Column.prototype.filterable = true; + + /** + * This is supplied either the cell value or jQuery object to parse. A string value must be returned from this method and will be used during filtering operations. + * @param {(*|jQuery)} valueOrElement - The value or jQuery cell object. + * @returns {string} + * @this FooTable.Column + */ + F.Column.prototype.filterValue = function(valueOrElement){ + // if we have an element or a jQuery object use jQuery to get the value + if (F.is.element(valueOrElement) || F.is.jq(valueOrElement)){ + var data = $(valueOrElement).data('filterValue'); + return F.is.defined(data) ? ''+data : $(valueOrElement).text(); + } + // if options are supplied with the value + if (F.is.hash(valueOrElement) && F.is.hash(valueOrElement.options)){ + if (F.is.string(valueOrElement.options.filterValue)) return valueOrElement.options.filterValue; + if (F.is.defined(valueOrElement.value)) valueOrElement = valueOrElement.value; + } + if (F.is.defined(valueOrElement) && valueOrElement != null) return valueOrElement+''; // use the native toString of the value + return ''; // otherwise we have no value so return an empty string + }; + + // this is used to define the filtering specific properties on column creation + F.Column.prototype.__filtering_define__ = function(definition){ + this.filterable = F.is.boolean(definition.filterable) ? definition.filterable : this.filterable; + this.filterValue = F.checkFnValue(this, definition.filterValue, this.filterValue); + }; + + // overrides the public define method and replaces it with our own + F.Column.extend('define', function(definition){ + this._super(definition); // call the base so we don't have to redefine any previously set properties + this.__filtering_define__(definition); // then call our own + }); +})(jQuery, FooTable); +(function(F){ + /** + * An object containing the filtering options for the plugin. Added by the {@link FooTable.Filtering} component. + * @type {object} + * @prop {boolean} enabled=false - Whether or not to allow filtering on the table. + * @prop {({name: string, query: (string|FooTable.Query), columns: (Array.|Array.|Array.)}|Array.)} filters - The filters to apply to the current {@link FooTable.Rows#array}. + * @prop {number} delay=1200 - The delay in milliseconds before the query is auto applied after a change (any value equal to or less than zero will disable this). + * @prop {number} min=1 - The minimum number of characters allowed in the search input before it is auto applied. + * @prop {string} space="AND" - Specifies how whitespace in a filter query is handled. + * @prop {string} placeholder="Search" - The string used as the placeholder for the search input. + * @prop {string} dropdownTitle=null - The title to display at the top of the search input column select. + * @prop {string} position="right" - The string used to specify the alignment of the search input. + * @prop {string} connectors=true - Whether or not to replace phrase connectors (+.-_) with space before executing the query. + * @prop {boolean} ignoreCase=true - Whether or not ignore case when matching. + * @prop {boolean} exactMatch=false - Whether or not search queries are treated as phrases when matching. + * @prop {boolean} focus=true - Whether or not to focus the search input after the search/clear button is clicked or after auto applying the search input query. + * @prop {string} container=null - A selector specifying where to place the filtering components form, if null the form is displayed within a row in the head of the table. + */ + F.Defaults.prototype.filtering = { + enabled: false, + filters: [], + delay: 1200, + min: 1, + space: 'AND', + placeholder: 'Search', + dropdownTitle: null, + position: 'right', + connectors: true, + ignoreCase: true, + exactMatch: false, + focus: true, + container: null + }; +})(FooTable); +(function(F){ + /** + * Checks if the row is filtered using the supplied filters. + * @this FooTable.Row + * @param {Array.} filters - The filters to apply. + * @returns {boolean} + */ + F.Row.prototype.filtered = function(filters){ + var result = true, self = this; + F.arr.each(filters, function(f){ + if ((result = f.matchRow(self)) == false) return false; + }); + return result; + }; +})(FooTable); +(function($, F){ + + F.Sorter = F.Class.extend(/** @lends FooTable.Sorter */{ + /** + * The sorter object contains the column and direction to sort by. + * @constructs + * @extends FooTable.Class + * @param {FooTable.Column} column - The column to sort. + * @param {string} direction - The direction to sort by. + * @returns {FooTable.Sorter} + */ + construct: function(column, direction){ + /** + * The column to sort. + * @type {FooTable.Column} + */ + this.column = column; + /** + * The direction to sort by. + * @type {string} + */ + this.direction = direction; + } + }); + +})(jQuery, FooTable); +(function ($, F) { + F.Sorting = F.Component.extend(/** @lends FooTable.Sorting */{ + /** + * The sorting component adds a small sort button to specified column headers allowing users to sort those columns in the table. + * @constructs + * @extends FooTable.Component + * @param {FooTable.Table} table - The parent {@link FooTable.Table} object for the component. + * @returns {FooTable.Sorting} + */ + construct: function (table) { + // call the constructor of the base class + this._super(table, table.o.sorting.enabled); + + /* PROTECTED */ + /** + * This provides a shortcut to the {@link FooTable.Table#options}.[sorting]{@link FooTable.Defaults#sorting} object. + * @instance + * @protected + * @type {object} + */ + this.o = table.o.sorting; + /** + * The current sorted column. + * @instance + * @type {FooTable.Column} + */ + this.column = null; + /** + * Whether or not to allow sorting to occur, should be set using the {@link FooTable.Sorting#toggleAllowed} method. + * @instance + * @type {boolean} + */ + this.allowed = true; + /** + * The initial sort state of the table, this value is used for determining if the sorting has occurred or to reset the state to default. + * @instance + * @type {{isset: boolean, rows: Array., column: string, direction: ?string}} + */ + this.initial = null; + }, + + /* PROTECTED */ + /** + * Checks the supplied data and options for the sorting component. + * @instance + * @protected + * @param {object} data - The jQuery data object from the parent table. + * @fires FooTable.Sorting#"preinit.ft.sorting" + * @this FooTable.Sorting + */ + preinit: function(data){ + var self = this; + /** + * The preinit.ft.sorting event is raised before the UI is created and provides the tables jQuery data object for additional options parsing. + * Calling preventDefault on this event will disable the component. + * @event FooTable.Sorting#"preinit.ft.sorting" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + * @param {object} data - The jQuery data object of the table raising the event. + */ + this.ft.raise('preinit.ft.sorting', [data]).then(function(){ + if (self.ft.$el.hasClass('footable-sorting')) + self.enabled = true; + self.enabled = F.is.boolean(data.sorting) + ? data.sorting + : self.enabled; + if (!self.enabled) return; + self.column = F.arr.first(self.ft.columns.array, function(col){ return col.sorted; }); + }, function(){ + self.enabled = false; + }); + }, + /** + * Initializes the sorting component for the plugin using the supplied table and options. + * @instance + * @protected + * @fires FooTable.Sorting#"init.ft.sorting" + * @this FooTable.Sorting + */ + init: function () { + /** + * The init.ft.sorting event is raised before its UI is generated. + * Calling preventDefault on this event will disable the component. + * @event FooTable.Sorting#"init.ft.sorting" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + var self = this; + this.ft.raise('init.ft.sorting').then(function(){ + if (!self.initial){ + var isset = !!self.column; + self.initial = { + isset: isset, + // grab a shallow copy of the rows array prior to sorting - allows us to reset without an initial sort + rows: self.ft.rows.all.slice(0), + // if there is a sorted column store its name and direction + column: isset ? self.column.name : null, + direction: isset ? self.column.direction : null + } + } + F.arr.each(self.ft.columns.array, function(col){ + if (col.sortable){ + col.$el.addClass('footable-sortable').append($('', {'class': 'fooicon fooicon-sort'})); + } + }); + self.ft.$el.on('click.footable', '.footable-sortable', { self: self }, self._onSortClicked); + }, function(){ + self.enabled = false; + }); + }, + /** + * Destroys the sorting component removing any UI generated from the table. + * @instance + * @protected + * @fires FooTable.Sorting#"destroy.ft.sorting" + */ + destroy: function () { + /** + * The destroy.ft.sorting event is raised before its UI is removed. + * Calling preventDefault on this event will prevent the component from being destroyed. + * @event FooTable.Sorting#"destroy.ft.sorting" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + var self = this; + this.ft.raise('destroy.ft.paging').then(function(){ + self.ft.$el.off('click.footable', '.footable-sortable', self._onSortClicked); + self.ft.$el.children('thead').children('tr.footable-header') + .children('.footable-sortable').removeClass('footable-sortable footable-asc footable-desc') + .find('span.fooicon').remove(); + }); + }, + /** + * Performs the actual sorting against the {@link FooTable.Rows#current} array. + * @instance + * @protected + */ + predraw: function () { + if (!this.column) return; + var self = this, col = self.column; + self.ft.rows.array.sort(function (a, b) { + return col.direction == 'DESC' + ? col.sorter(b.cells[col.index].sortValue, a.cells[col.index].sortValue) + : col.sorter(a.cells[col.index].sortValue, b.cells[col.index].sortValue); + }); + }, + /** + * Updates the sorting UI setting the state of the sort buttons. + * @instance + * @protected + */ + draw: function () { + if (!this.column) return; + var self = this, + $sortable = self.ft.$el.find('thead > tr > .footable-sortable'), + $active = self.column.$el; + + $sortable.removeClass('footable-asc footable-desc').children('.fooicon').removeClass('fooicon-sort fooicon-sort-asc fooicon-sort-desc'); + $sortable.not($active).children('.fooicon').addClass('fooicon-sort'); + $active.addClass(self.column.direction == 'DESC' ? 'footable-desc' : 'footable-asc') + .children('.fooicon').addClass(self.column.direction == 'DESC' ? 'fooicon-sort-desc' : 'fooicon-sort-asc'); + }, + + /* PUBLIC */ + /** + * Sets the sorting options and calls the {@link FooTable.Table#draw} method to perform the actual sorting. + * @instance + * @param {(string|number|FooTable.Column)} column - The column name, index or the actual {@link FooTable.Column} object to sort by. + * @param {string} [direction="ASC"] - The direction to sort by, either ASC or DESC. + * @returns {jQuery.Promise} + * @fires FooTable.Sorting#"before.ft.sorting" + * @fires FooTable.Sorting#"after.ft.sorting" + */ + sort: function(column, direction){ + return this._sort(column, direction); + }, + /** + * Toggles whether or not sorting is currently allowed. + * @param {boolean} [state] - You can optionally specify the state you want it to be, if not supplied the current value is flipped. + */ + toggleAllowed: function(state){ + state = F.is.boolean(state) ? state : !this.allowed; + this.allowed = state; + this.ft.$el.toggleClass('footable-sorting-disabled', !this.allowed); + }, + /** + * Checks whether any sorting has occurred for the table. + * @returns {boolean} + */ + hasChanged: function(){ + return !(!this.initial || !this.column || + (this.column.name === this.initial.column && + (this.column.direction === this.initial.direction || (this.initial.direction === null && this.column.direction === 'ASC'))) + ); + }, + /** + * Resets the table sorting to the initial state recorded in the components init method. + */ + reset: function(){ + if (!!this.initial){ + if (this.initial.isset){ + // if the initial value specified a column, sort by it + this.sort(this.initial.column, this.initial.direction); + } else { + // if there was no initial column then we need to reset the rows to there original order + if (!!this.column){ + // if there is a currently sorted column remove the asc/desc classes and set it to null. + this.column.$el.removeClass('footable-asc footable-desc'); + this.column = null; + } + // replace the current all rows array with the one stored in the initial value + this.ft.rows.all = this.initial.rows; + // force the table to redraw itself using the updated rows array + this.ft.draw(); + } + } + }, + + /* PRIVATE */ + /** + * Performs the required steps to handle sorting including the raising of the {@link FooTable.Sorting#"before.ft.sorting"} and {@link FooTable.Sorting#"after.ft.sorting"} events. + * @instance + * @private + * @param {(string|number|FooTable.Column)} column - The column name, index or the actual {@link FooTable.Column} object to sort by. + * @param {string} [direction="ASC"] - The direction to sort by, either ASC or DESC. + * @returns {jQuery.Promise} + * @fires FooTable.Sorting#"before.ft.sorting" + * @fires FooTable.Sorting#"after.ft.sorting" + */ + _sort: function(column, direction){ + if (!this.allowed) return $.Deferred().reject('sorting disabled'); + var self = this; + var sorter = new F.Sorter(self.ft.columns.get(column), F.Sorting.dir(direction)); + /** + * The before.ft.sorting event is raised before a sort is applied and allows listeners to modify the sorter or cancel it completely by calling preventDefault on the jQuery.Event object. + * @event FooTable.Sorting#"before.ft.sorting" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + * @param {FooTable.Sorter} sorter - The sorter that is about to be applied. + */ + return self.ft.raise('before.ft.sorting', [sorter]).then(function(){ + F.arr.each(self.ft.columns.array, function(col){ + if (col != self.column) col.direction = null; + }); + self.column = self.ft.columns.get(sorter.column); + if (self.column) self.column.direction = F.Sorting.dir(sorter.direction); + return self.ft.draw().then(function(){ + /** + * The after.ft.sorting event is raised after a sorter has been applied. + * @event FooTable.Sorting#"after.ft.sorting" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + * @param {FooTable.Sorter} sorter - The sorter that has been applied. + */ + self.ft.raise('after.ft.sorting', [sorter]); + }); + }); + }, + /** + * Handles the sort button clicked event. + * @instance + * @private + * @param {jQuery.Event} e - The event object for the event. + */ + _onSortClicked: function (e) { + var self = e.data.self, $header = $(this).closest('th,td'), + direction = $header.is('.footable-asc, .footable-desc') + ? ($header.hasClass('footable-desc') ? 'ASC' : 'DESC') + : 'ASC'; + self._sort($header.index(), direction); + } + }); + + /** + * Checks the supplied string is a valid direction and if not returns ASC as default. + * @static + * @protected + * @param {string} str - The string to check. + */ + F.Sorting.dir = function(str){ + return F.is.string(str) && (str == 'ASC' || str == 'DESC') ? str : 'ASC'; + }; + + F.components.register('sorting', F.Sorting, 600); + +})(jQuery, FooTable); +(function(F){ + + /** + * The value used by the sorting component during sort operations. Can be set using the data-sort-value attribute on the cell itself. + * If this is not supplied it is set to the result of the toString method called on the value for the cell. Added by the {@link FooTable.Sorting} component. + * @type {string} + * @default null + */ + F.Cell.prototype.sortValue = null; + + // this is used to define the sorting specific properties on cell creation + F.Cell.prototype.__sorting_define__ = function(valueOrElement){ + this.sortValue = this.column.sortValue.call(this.column, valueOrElement); + }; + + // this is used to update the sortValue property whenever the cell value is changed + F.Cell.prototype.__sorting_val__ = function(value){ + if (F.is.defined(value)){ + // set only + this.sortValue = this.column.sortValue.call(this.column, value); + } + }; + + // overrides the public define method and replaces it with our own + F.Cell.extend('define', function(valueOrElement){ + this._super(valueOrElement); + this.__sorting_define__(valueOrElement); + }); + // overrides the public val method and replaces it with our own + F.Cell.extend('val', function(value, redraw, redrawSelf){ + var val = this._super(value, redraw, redrawSelf); + this.__sorting_val__(value); + return val; + }); +})(FooTable); +(function($, F){ + /** + * The direction to sort if the {@link FooTable.Column#sorted} property is set to true. Can be "ASC", "DESC" or NULL. Added by the {@link FooTable.Sorting} component. + * @type {string} + * @default null + */ + F.Column.prototype.direction = null; + /** + * Whether or not the column can be sorted. Added by the {@link FooTable.Sorting} component. + * @type {boolean} + * @default true + */ + F.Column.prototype.sortable = true; + /** + * Whether or not the column is sorted. Added by the {@link FooTable.Sorting} component. + * @type {boolean} + * @default false + */ + F.Column.prototype.sorted = false; + + /** + * This is supplied two values from the column for a comparison to be made and the result returned. Added by the {@link FooTable.Sorting} component. + * @param {*} a - The first value to be compared. + * @param {*} b - The second value to compare to the first. + * @returns {number} + * @example + * "sorter": function(a, b){ + * if (a is less than b by some ordering criterion) { + * return -1; + * } + * if (a is greater than b by the ordering criterion) { + * return 1; + * } + * // a must be equal to b + * return 0; + * } + */ + F.Column.prototype.sorter = function(a, b){ + if (typeof a === 'string') a = a.toLowerCase(); + if (typeof b === 'string') b = b.toLowerCase(); + if (a === b) return 0; + if (a < b) return -1; + return 1; + }; + + /** + * This is supplied either the cell value or jQuery object to parse. A value must be returned from this method and will be used during sorting operations. + * @param {(*|jQuery)} valueOrElement - The value or jQuery cell object. + * @returns {*} + * @this FooTable.Column + */ + F.Column.prototype.sortValue = function(valueOrElement){ + // if we have an element or a jQuery object use jQuery to get the value + if (F.is.element(valueOrElement) || F.is.jq(valueOrElement)){ + var data = $(valueOrElement).data('sortValue'); + return F.is.defined(data) ? data : this.parser(valueOrElement); + } + // if options are supplied with the value + if (F.is.hash(valueOrElement) && F.is.hash(valueOrElement.options)){ + if (F.is.string(valueOrElement.options.sortValue)) return valueOrElement.options.sortValue; + if (F.is.defined(valueOrElement.value)) valueOrElement = valueOrElement.value; + } + if (F.is.defined(valueOrElement) && valueOrElement != null) return valueOrElement; + return null; + }; + + // this is used to define the sorting specific properties on column creation + F.Column.prototype.__sorting_define__ = function(definition){ + this.sorter = F.checkFnValue(this, definition.sorter, this.sorter); + this.direction = F.is.type(definition.direction, 'string') ? F.Sorting.dir(definition.direction) : null; + this.sortable = F.is.boolean(definition.sortable) ? definition.sortable : true; + this.sorted = F.is.boolean(definition.sorted) ? definition.sorted : false; + this.sortValue = F.checkFnValue(this, definition.sortValue, this.sortValue); + }; + + // overrides the public define method and replaces it with our own + F.Column.extend('define', function(definition){ + this._super(definition); + this.__sorting_define__(definition); + }); + +})(jQuery, FooTable); +(function(F){ + /** + * An object containing the sorting options for the plugin. Added by the {@link FooTable.Sorting} component. + * @type {object} + * @prop {boolean} enabled=false - Whether or not to allow sorting on the table. + */ + F.Defaults.prototype.sorting = { + enabled: false + }; +})(FooTable); +(function($, F){ + + F.HTMLColumn.extend('__sorting_define__', function(definition){ + this._super(definition); + this.sortUse = F.is.string(definition.sortUse) && $.inArray(definition.sortUse, ['html','text']) !== -1 ? definition.sortUse : 'html'; + }); + + /** + * This is supplied either the cell value or jQuery object to parse. A value must be returned from this method and will be used during sorting operations. + * @param {(*|jQuery)} valueOrElement - The value or jQuery cell object. + * @returns {*} + * @this FooTable.HTMLColumn + */ + F.HTMLColumn.prototype.sortValue = function(valueOrElement){ + // if we have an element or a jQuery object use jQuery to get the data value or pass it off to the parser + if (F.is.element(valueOrElement) || F.is.jq(valueOrElement)){ + var data = $(valueOrElement).data('sortValue'); + return F.is.defined(data) ? data : this.parser(valueOrElement); + } + // if options are supplied with the value + if (F.is.hash(valueOrElement) && F.is.hash(valueOrElement.options)){ + if (F.is.string(valueOrElement.options.sortValue)) return valueOrElement.options.sortValue; + if (F.is.defined(valueOrElement.value)) valueOrElement = valueOrElement.value; + } + if (F.is.defined(valueOrElement) && valueOrElement != null) return valueOrElement; + return null; + }; + +})(jQuery, FooTable); +(function($, F){ + + /** + * This is supplied either the cell value or jQuery object to parse. A value must be returned from this method and will be used during sorting operations. + * @param {(*|jQuery)} valueOrElement - The value or jQuery cell object. + * @returns {*} + */ + F.NumberColumn.prototype.sortValue = function(valueOrElement){ + // if we have an element or a jQuery object use jQuery to get the data value or pass it off to the parser + if (F.is.element(valueOrElement) || F.is.jq(valueOrElement)){ + var data = $(valueOrElement).data('sortValue'); + return F.is.number(data) ? data : this.parser(valueOrElement); + } + // if options are supplied with the value + if (F.is.hash(valueOrElement) && F.is.hash(valueOrElement.options)){ + if (F.is.string(valueOrElement.options.sortValue)) return this.parser(valueOrElement); + if (F.is.number(valueOrElement.options.sortValue)) return valueOrElement.options.sortValue; + if (F.is.number(valueOrElement.value)) return valueOrElement.value; + } + if (F.is.string(valueOrElement)) return this.parser(valueOrElement); + if (F.is.number(valueOrElement)) return valueOrElement; + return null; + }; + +})(jQuery, FooTable); +(function(F){ + /** + * Sort the table using the specified column and direction. Added by the {@link FooTable.Sorting} component. + * @instance + * @param {(string|number|FooTable.Column)} column - The column name, index or the actual {@link FooTable.Column} object to sort by. + * @param {string} [direction="ASC"] - The direction to sort by, either ASC or DESC. + * @returns {jQuery.Promise} + * @fires FooTable.Sorting#"change.ft.sorting" + * @fires FooTable.Sorting#"changed.ft.sorting" + * @see FooTable.Sorting#sort + */ + F.Table.prototype.sort = function(column, direction){ + return this.use(F.Sorting).sort(column, direction); + }; +})(FooTable); +(function($, F){ + + F.Pager = F.Class.extend(/** @lends FooTable.Pager */{ + /** + * The pager object contains the page number and direction to page to. + * @constructs + * @extends FooTable.Class + * @param {number} total - The total number of pages available. + * @param {number} current - The current page number. + * @param {number} size - The number of rows per page. + * @param {number} page - The page number to goto. + * @param {boolean} forward - A boolean indicating the direction of paging, TRUE = forward, FALSE = back. + * @returns {FooTable.Pager} + */ + construct: function(total, current, size, page, forward){ + /** + * The total number of pages available. + * @type {number} + */ + this.total = total; + /** + * The current page number. + * @type {number} + */ + this.current = current; + /** + * The number of rows per page. + * @type {number} + */ + this.size = size; + /** + * The page number to goto. + * @type {number} + */ + this.page = page; + /** + * A boolean indicating the direction of paging, TRUE = forward, FALSE = back. + * @type {boolean} + */ + this.forward = forward; + } + }); + +})(jQuery, FooTable); +(function($, F){ + F.Paging = F.Component.extend(/** @lends FooTable.Paging */{ + /** + * The paging component adds a pagination control to the table allowing users to navigate table rows via pages. + * @constructs + * @extends FooTable.Component + * @param {FooTable.Table} table - The parent {@link FooTable.Table} object for the component. + * @returns {FooTable.Filtering} + */ + construct: function(table){ + // call the base constructor + this._super(table, table.o.paging.enabled); + + /* PROTECTED */ + /** + * An object containing the strings used by the paging buttons. + * @type {{ first: string, prev: string, next: string, last: string }} + */ + this.strings = table.o.paging.strings; + + /* PUBLIC */ + /** + * The current page number to display. + * @instance + * @type {number} + */ + this.current = table.o.paging.current; + /** + * The number of rows to display per page. + * @instance + * @type {number} + */ + this.size = table.o.paging.size; + /** + * The maximum number of page links to display at once. + * @instance + * @type {number} + */ + this.limit = table.o.paging.limit; + /** + * The position of the pagination control within the paging rows cell. + * @instance + * @type {string} + */ + this.position = table.o.paging.position; + /** + * The format string used to generate the text displayed under the pagination control. + * @instance + * @type {string} + */ + this.countFormat = table.o.paging.countFormat; + /** + * A selector specifying where to place the paging components UI, if null the UI is displayed within a row in the foot of the table. + * @instance + * @type {string} + */ + this.container = table.o.paging.container; + /** + * The total number of pages. + * @instance + * @type {number} + */ + this.total = -1; + /** + * The number of rows in the {@link FooTable.Rows#array} before paging is applied. + * @instance + * @type {number} + */ + this.totalRows = 0; + /** + * A number indicating the previous page displayed. + * @instance + * @type {number} + */ + this.previous = -1; + /** + * The count string generated using the {@link FooTable.Filtering#countFormat} option. This value is only set after the first call to the {@link FooTable.Filtering#predraw} method. + * @instance + * @type {string} + */ + this.formattedCount = null; + /** + * The jQuery object of the element containing the entire paging UI. + * @instance + * @type {jQuery} + */ + this.$container = null; + /** + * The jQuery object of the element wrapping all the paging UI elements. + * @instance + * @type {jQuery} + */ + this.$wrapper = null; + /** + + * The jQuery row object that contains all the paging specific elements. + * @instance + * @type {jQuery} + */ + this.$row = null; + /** + * The jQuery cell object that contains the pagination control and total count. + * @instance + * @type {jQuery} + */ + this.$cell = null; + /** + * The jQuery object that contains the links for the pagination control. + * @instance + * @type {jQuery} + */ + this.$pagination = null; + /** + * The jQuery object that contains the row count. + * @instance + * @type {jQuery} + */ + this.$count = null; + /** + * Whether or not the pagination row is detached from the table. + * @instance + * @type {boolean} + */ + this.detached = true; + + /* PRIVATE */ + /** + * Used to hold the number of page links created. + * @instance + * @type {number} + * @private + */ + this._createdLinks = 0; + }, + + /* PROTECTED */ + /** + * Checks the supplied data and options for the paging component. + * @instance + * @protected + * @param {object} data - The jQuery data object from the parent table. + * @fires FooTable.Paging#"preinit.ft.paging" + */ + preinit: function(data){ + var self = this; + /** + * The preinit.ft.paging event is raised before the UI is created and provides the tables jQuery data object for additional options parsing. + * Calling preventDefault on this event will disable the component. + * @event FooTable.Paging#"preinit.ft.paging" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + * @param {object} data - The jQuery data object of the table raising the event. + */ + this.ft.raise('preinit.ft.paging', [data]).then(function(){ + if (self.ft.$el.hasClass('footable-paging')) + self.enabled = true; + self.enabled = F.is.boolean(data.paging) + ? data.paging + : self.enabled; + + if (!self.enabled) return; + + self.size = F.is.number(data.pagingSize) + ? data.pagingSize + : self.size; + + self.current = F.is.number(data.pagingCurrent) + ? data.pagingCurrent + : self.current; + + self.limit = F.is.number(data.pagingLimit) + ? data.pagingLimit + : self.limit; + + if (self.ft.$el.hasClass('footable-paging-left')) + self.position = 'left'; + if (self.ft.$el.hasClass('footable-paging-center')) + self.position = 'center'; + if (self.ft.$el.hasClass('footable-paging-right')) + self.position = 'right'; + + self.position = F.is.string(data.pagingPosition) + ? data.pagingPosition + : self.position; + + self.countFormat = F.is.string(data.pagingCountFormat) + ? data.pagingCountFormat + : self.countFormat; + + self.container = F.is.string(data.pagingContainer) + ? data.pagingContainer + : self.container; + + self.total = Math.ceil(self.ft.rows.all.length / self.size); + }, function(){ + self.enabled = false; + }); + }, + /** + * Initializes the paging component for the plugin using the supplied table and options. + * @instance + * @protected + * @fires FooTable.Paging#"init.ft.paging" + */ + init: function(){ + /** + * The init.ft.paging event is raised before its UI is generated. + * Calling preventDefault on this event will disable the component. + * @event FooTable.Paging#"init.ft.paging" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + var self = this; + this.ft.raise('init.ft.paging').then(function(){ + self.$create(); + }, function(){ + self.enabled = false; + }); + }, + /** + * Destroys the paging component removing any UI generated from the table. + * @instance + * @protected + * @fires FooTable.Paging#"destroy.ft.paging" + */ + destroy: function () { + /** + * The destroy.ft.paging event is raised before its UI is removed. + * Calling preventDefault on this event will prevent the component from being destroyed. + * @event FooTable.Paging#"destroy.ft.paging" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + var self = this; + this.ft.raise('destroy.ft.paging').then(function(){ + self.ft.$el.removeClass('footable-paging') + .find('tfoot > tr.footable-paging').remove(); + self.detached = true; + self._createdLinks = 0; + }); + }, + /** + * Performs the actual paging against the {@link FooTable.Rows#current} array removing all rows that are not on the current visible page. + * @instance + * @protected + */ + predraw: function(){ + this.total = Math.ceil(this.ft.rows.array.length / this.size); + this.current = this.current > this.total ? this.total : (this.current < 1 ? 1 : this.current); + this.totalRows = this.ft.rows.array.length; + if (this.totalRows > this.size){ + this.ft.rows.array = this.ft.rows.array.splice((this.current - 1) * this.size, this.size); + } + this.formattedCount = this.format(this.countFormat); + }, + /** + * Updates the paging UI setting the state of the pagination control. + * @instance + * @protected + */ + draw: function(){ + if (this.total <= 1){ + if (!this.detached){ + if (this.$row){ + this.$row.detach(); + } else { + this.$wrapper.detach(); + } + this.detached = true; + } + } else { + if (this.detached){ + if (this.$row){ + var $tfoot = this.ft.$el.children('tfoot'); + if ($tfoot.length == 0){ + $tfoot = $(''); + this.ft.$el.append($tfoot); + } + this.$row.appendTo($tfoot); + } else { + this.$wrapper.appendTo(this.$container); + } + this.detached = false; + } + if (F.is.jq(this.$cell)){ + this.$cell.attr('colspan', this.ft.columns.visibleColspan); + } + this._createLinks(); + this._setVisible(this.current, this.current > this.previous); + this._setNavigation(true); + this.$count.text(this.formattedCount); + } + }, + /** + * Creates the paging UI from the current options setting the various jQuery properties of this component. + * @instance + * @protected + */ + $create: function(){ + this._createdLinks = 0; + var position = 'footable-paging-center'; + switch (this.position){ + case 'left': position = 'footable-paging-left'; break; + case 'right': position = 'footable-paging-right'; break; + } + this.ft.$el.addClass('footable-paging').addClass(position); + + this.$container = this.container === null ? null : $(this.container).first(); + if (!F.is.jq(this.$container)){ + var $tfoot = this.ft.$el.children('tfoot'); + if ($tfoot.length == 0){ + $tfoot = $(''); + this.ft.$el.append($tfoot); + } + // add it to a row and then populate it with the search input and column selector dropdown. + this.$row = $('', {'class': 'footable-paging'}).prependTo($tfoot); + this.$container = this.$cell = $(''); + self.ft.$el.append($tfoot); + } + self.$row = $('', { 'class': 'footable-editing' }).append(self.$cell).appendTo($tfoot); + }, + /** + * Creates the show button for the editing component. + * @instance + * @protected + * @returns {(string|HTMLElement|jQuery)} + */ + $buttonShow: function(){ + return ''; + }, + /** + * Creates the hide button for the editing component. + * @instance + * @protected + * @returns {(string|HTMLElement|jQuery)} + */ + $buttonHide: function(){ + return ''; + }, + /** + * Creates the add button for the editing component. + * @instance + * @protected + * @returns {(string|HTMLElement|jQuery)} + */ + $buttonAdd: function(){ + return ' '; + }, + /** + * Creates the edit button for the editing component. + * @instance + * @protected + * @returns {(string|HTMLElement|jQuery)} + */ + $buttonEdit: function(){ + return ' '; + }, + /** + * Creates the delete button for the editing component. + * @instance + * @protected + * @returns {(string|HTMLElement|jQuery)} + */ + $buttonDelete: function(){ + return ''; + }, + /** + * Creates the view button for the editing component. + * @instance + * @protected + * @returns {(string|HTMLElement|jQuery)} + */ + $buttonView: function(){ + return ' '; + }, + /** + * Creates the button group for the row buttons. + * @instance + * @protected + * @returns {(string|HTMLElement|jQuery)} + */ + $rowButtons: function(){ + if (F.is.jq(this._$buttons)) return this._$buttons.clone(); + this._$buttons = $('
'); + if (this.allowView) this._$buttons.append(this.$buttonView()); + if (this.allowEdit) this._$buttons.append(this.$buttonEdit()); + if (this.allowDelete) this._$buttons.append(this.$buttonDelete()); + return this._$buttons; + }, + /** + * Performs the drawing of the component. + */ + draw: function(){ + this.$cell.attr('colspan', this.ft.columns.visibleColspan); + }, + /** + * Handles the edit button click event. + * @instance + * @private + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @fires FooTable.Editing#"edit.ft.editing" + */ + _onEditClick: function(e){ + e.preventDefault(); + var self = e.data.self, row = $(this).closest('tr').data('__FooTableRow__'); + if (row instanceof F.Row){ + /** + * The edit.ft.editing event is raised before its callback is executed. + * Calling preventDefault on this event will prevent the callback from being executed. + * @event FooTable.Editing#"edit.ft.editing" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + * @param {FooTable.Row} row - The row to be edited. + */ + self.ft.raise('edit.ft.editing', [row]).then(function(){ + self.callbacks.editRow.call(self.ft, row); + }); + } + }, + /** + * Handles the delete button click event. + * @instance + * @private + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @fires FooTable.Editing#"delete.ft.editing" + */ + _onDeleteClick: function(e){ + e.preventDefault(); + var self = e.data.self, row = $(this).closest('tr').data('__FooTableRow__'); + if (row instanceof F.Row){ + /** + * The delete.ft.editing event is raised before its callback is executed. + * Calling preventDefault on this event will prevent the callback from being executed. + * @event FooTable.Editing#"delete.ft.editing" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + * @param {FooTable.Row} row - The row to be deleted. + */ + self.ft.raise('delete.ft.editing', [row]).then(function(){ + self.callbacks.deleteRow.call(self.ft, row); + }); + } + }, + /** + * Handles the view button click event. + * @instance + * @private + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @fires FooTable.Editing#"view.ft.editing" + */ + _onViewClick: function(e){ + e.preventDefault(); + var self = e.data.self, row = $(this).closest('tr').data('__FooTableRow__'); + if (row instanceof F.Row){ + /** + * The view.ft.editing event is raised before its callback is executed. + * Calling preventDefault on this event will prevent the callback from being executed. + * @event FooTable.Editing#"view.ft.editing" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + * @param {FooTable.Row} row - The row to be viewed. + */ + self.ft.raise('view.ft.editing', [row]).then(function(){ + self.callbacks.viewRow.call(self.ft, row); + }); + } + }, + /** + * Handles the add button click event. + * @instance + * @private + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @fires FooTable.Editing#"add.ft.editing" + */ + _onAddClick: function(e){ + e.preventDefault(); + var self = e.data.self; + /** + * The add.ft.editing event is raised before its callback is executed. + * Calling preventDefault on this event will prevent the callback from being executed. + * @event FooTable.Editing#"add.ft.editing" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + self.ft.raise('add.ft.editing').then(function(){ + self.callbacks.addRow.call(self.ft); + }); + }, + /** + * Handles the show button click event. + * @instance + * @private + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @fires FooTable.Editing#"show.ft.editing" + */ + _onShowClick: function(e){ + e.preventDefault(); + var self = e.data.self; + /** + * The show.ft.editing event is raised before its callback is executed. + * Calling preventDefault on this event will prevent the callback from being executed. + * @event FooTable.Editing#"show.ft.editing" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + self.ft.raise('show.ft.editing').then(function(){ + self.ft.$el.addClass('footable-editing-show'); + self.column.visible = true; + self.ft.draw(); + }); + }, + /** + * Handles the hide button click event. + * @instance + * @private + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @fires FooTable.Editing#"show.ft.editing" + */ + _onHideClick: function(e){ + e.preventDefault(); + var self = e.data.self; + /** + * The hide.ft.editing event is raised before its callback is executed. + * Calling preventDefault on this event will prevent the callback from being executed. + * @event FooTable.Editing#"hide.ft.editing" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + self.ft.raise('hide.ft.editing').then(function(){ + self.ft.$el.removeClass('footable-editing-show'); + self.column.visible = false; + self.ft.draw(); + }); + } + }); + + F.components.register('editing', F.Editing, 850); + +})(jQuery, FooTable); + +(function($, F){ + + F.EditingColumn = F.Column.extend(/** @lends FooTable.EditingColumn */{ + /** + * The Editing column class is used to create the column containing the editing buttons. + * @constructs + * @extends FooTable.Column + * @param {FooTable.Table} instance - The parent {@link FooTable.Table} this column belongs to. + * @param {FooTable.Editing} editing - The parent {@link FooTable.Editing} component this column is used with. + * @param {object} definition - An object containing all the properties to set for the column. + * @returns {FooTable.EditingColumn} + */ + construct: function(instance, editing, definition){ + this._super(instance, definition, 'editing'); + this.editing = editing; + this.internal = true; + }, + /** + * After the column has been defined this ensures that the $el property is a jQuery object by either creating or updating the current value. + * @instance + * @protected + * @this FooTable.Column + */ + $create: function(){ + (this.$el = !this.virtual && F.is.jq(this.$el) ? this.$el : $('
The below shows how this value would be setThe below shows what the default getWidth function would look like.
This example shows retrieving a column by name assuming a column called "id" exists. The columns object is an instance of {@link FooTable.Columns}.The below shows column definitions for a row defined as { id: Number, name: String, age: Number }. The ID column has a fixed width, the table is initially sorted on the Name column and the Age column will be hidden on phones.
').text(this.emptyString)); + }, + /** + * Performs the actual drawing of the table rows. + * @instance + * @protected + */ + draw: function(){ + var self = this, $tbody = self.ft.$el.children('tbody'), first = true; + // if we have rows + if (self.array.length > 0){ + self.$empty.detach(); + // loop through them appending to the tbody and then drawing + F.arr.each(self.array, function(row){ + if ((self.expandFirst && first) || self.expandAll){ + row.expanded = true; + first = false; + } + row.draw($tbody); + }); + } else { + // otherwise display the $empty row + self.$empty.children('td').attr('colspan', self.ft.columns.visibleColspan); + $tbody.append(self.$empty); + } + }, + /** + * Loads a JSON array of row objects into the table + * @instance + * @param {Array.} data - An array of row objects to load. + * @param {boolean} [append=false] - Whether or not to append the new rows to the current rows array or to replace them entirely. + */ + load: function(data, append){ + var self = this, rows = $.map(data, function(r){ + return new F.Row(self.ft, self.ft.columns.array, r); + }); + F.arr.each(this.array, function(row){ + row.predraw(); + }); + this.all = (F.is.boolean(append) ? append : false) ? this.all.concat(rows) : rows; + this.array = this.all.slice(0); + this.ft.draw(); + }, + /** + * Expands all visible rows. + * @instance + */ + expand: function(){ + F.arr.each(this.array, function(row){ + row.expand(); + }); + }, + /** + * Collapses all visible rows. + * @instance + */ + collapse: function(){ + F.arr.each(this.array, function(row){ + row.collapse(); + }); + } + }); + + F.components.register('rows', F.Rows, 800); + +})(jQuery, FooTable); +(function(F){ + /** + * An array of JSON objects containing the row data or a jQuery promise that resolves returning the row data. + * @type {(Array.|jQuery.Promise)} + * @default [] + */ + F.Defaults.prototype.rows = []; + + /** + * A string to display when there are no rows in the table. + * @type {string} + * @default "No results" + */ + F.Defaults.prototype.empty = 'No results'; + + /** + * Whether or not the toggle is appended to each row. + * @type {boolean} + * @default true + */ + F.Defaults.prototype.showToggle = true; + + /** + * The CSS selector used to filter row click events. If the event.target property matches the selector the row will be toggled. + * @type {string} + * @default "tr,td,.footable-toggle" + */ + F.Defaults.prototype.toggleSelector = 'tr,td,.footable-toggle'; + + /** + * Specifies which column to display the row toggle in. The only supported values are "first" or "last". + * @type {string} + * @default "first" + */ + F.Defaults.prototype.toggleColumn = 'first'; + + /** + * Whether or not the first rows details are expanded by default when displayed on a device that hides any columns. + * @type {boolean} + */ + F.Defaults.prototype.expandFirst = false; + + /** + * Whether or not all row details are expanded by default when displayed on a device that hides any columns. + * @type {boolean} + */ + F.Defaults.prototype.expandAll = false; +})(FooTable); +(function(F){ + /** + * Loads a JSON array of row objects into the table + * @param {Array.} data - An array of row objects to load. + * @param {boolean} [append=false] - Whether or not to append the new rows to the current rows array or to replace them entirely. + */ + F.Table.prototype.loadRows = function(data, append){ + this.rows.load(data, append); + }; +})(FooTable); +(function(F){ + F.Filter = F.Class.extend(/** @lends FooTable.Filter */{ + /** + * The filter object contains the query to filter by and the columns to apply it to. + * @constructs + * @extends FooTable.Class + * @param {string} name - The name for the filter. + * @param {(string|FooTable.Query)} query - The query for the filter. + * @param {Array.} columns - The columns to apply the query to. + * @param {string} [space="AND"] - How the query treats space chars. + * @param {boolean} [connectors=true] - Whether or not to replace phrase connectors (+.-_) with spaces. + * @param {boolean} [ignoreCase=true] - Whether or not ignore case when matching. + * @param {boolean} [hidden=true] - Whether or not this is a hidden filter. + * @returns {FooTable.Filter} + */ + construct: function(name, query, columns, space, connectors, ignoreCase, hidden){ + /** + * The name of the filter. + * @instance + * @type {string} + */ + this.name = name; + /** + * A string specifying how the filter treats space characters. Can be either "OR" or "AND". + * @instance + * @type {string} + */ + this.space = F.is.string(space) && (space == 'OR' || space == 'AND') ? space : 'AND'; + /** + * Whether or not to replace phrase connectors (+.-_) with spaces before executing the query. + * @instance + * @type {boolean} + */ + this.connectors = F.is.boolean(connectors) ? connectors : true; + /** + * Whether or not ignore case when matching. + * @instance + * @type {boolean} + */ + this.ignoreCase = F.is.boolean(ignoreCase) ? ignoreCase : true; + /** + * Whether or not this is a hidden filter. + * @instance + * @type {boolean} + */ + this.hidden = F.is.boolean(hidden) ? hidden : false; + /** + * The query for the filter. + * @instance + * @type {(string|FooTable.Query)} + */ + this.query = query instanceof F.Query ? query : new F.Query(query, this.space, this.connectors, this.ignoreCase); + /** + * The columns to apply the query to. + * @instance + * @type {Array.} + */ + this.columns = columns; + }, + /** + * Checks if the current filter matches the supplied string. + * If the current query property is a string it will be auto converted to a {@link FooTable.Query} object to perform the match. + * @instance + * @param {string} str - The string to check. + * @returns {boolean} + */ + match: function(str){ + if (!F.is.string(str)) return false; + if (F.is.string(this.query)){ + this.query = new F.Query(this.query, this.space, this.connectors, this.ignoreCase); + } + return this.query instanceof F.Query ? this.query.match(str) : false; + }, + /** + * Checks if the current filter matches the supplied {@link FooTable.Row}. + * @instance + * @param {FooTable.Row} row - The row to check. + * @returns {boolean} + */ + matchRow: function(row){ + var self = this, text = F.arr.map(row.cells, function(cell){ + return F.arr.contains(self.columns, cell.column) ? cell.filterValue : null; + }).join(' '); + return self.match(text); + } + }); + +})(FooTable); +(function ($, F) { + F.Filtering = F.Component.extend(/** @lends FooTable.Filtering */{ + /** + * The filtering component adds a search input and column selector dropdown to the table allowing users to filter the using space delimited queries. + * @constructs + * @extends FooTable.Component + * @param {FooTable.Table} table - The parent {@link FooTable.Table} object for the component. + * @returns {FooTable.Filtering} + */ + construct: function (table) { + // call the constructor of the base class + this._super(table, table.o.filtering.enabled); + + /* PUBLIC */ + /** + * The filters to apply to the current {@link FooTable.Rows#array}. + * @instance + * @type {Array.} + */ + this.filters = table.o.filtering.filters; + /** + * The delay in milliseconds before the query is auto applied after a change. + * @instance + * @type {number} + */ + this.delay = table.o.filtering.delay; + /** + * The minimum number of characters allowed in the search input before it is auto applied. + * @instance + * @type {number} + */ + this.min = table.o.filtering.min; + /** + * Specifies how whitespace in a filter query is handled. + * @instance + * @type {string} + */ + this.space = table.o.filtering.space; + /** + * Whether or not to replace phrase connectors (+.-_) with spaces before executing the query. + * @instance + * @type {boolean} + */ + this.connectors = table.o.filtering.connectors; + /** + * Whether or not ignore case when matching. + * @instance + * @type {boolean} + */ + this.ignoreCase = table.o.filtering.ignoreCase; + /** + * Whether or not search queries are treated as phrases when matching. + * @instance + * @type {boolean} + */ + this.exactMatch = table.o.filtering.exactMatch; + /** + * The placeholder text to display within the search $input. + * @instance + * @type {string} + */ + this.placeholder = table.o.filtering.placeholder; + /** + * The title to display at the top of the search input column select. + * @type {string} + */ + this.dropdownTitle = table.o.filtering.dropdownTitle; + /** + * The position of the $search input within the filtering rows cell. + * @type {string} + */ + this.position = table.o.filtering.position; + /** + * Whether or not to focus the search input after the search/clear button is clicked or after auto applying the search input query. + * @type {boolean} + */ + this.focus = table.o.filtering.focus; + /** + * A selector specifying where to place the filtering components form, if null the form is displayed within a row in the head of the table. + * @type {string} + */ + this.container = table.o.filtering.container; + /** + * The jQuery object of the element containing the entire filtering form. + * @instance + * @type {jQuery} + */ + this.$container = null; + /** + * The jQuery row object that contains all the filtering specific elements. + * @instance + * @type {jQuery} + */ + this.$row = null; + /** + * The jQuery cell object that contains the search input and column selector. + * @instance + * @type {jQuery} + */ + this.$cell = null; + /** + * The jQuery form object of the form that contains the search input and column selector. + * @instance + * @type {jQuery} + */ + this.$form = null; + /** + * The jQuery object of the column selector dropdown. + * @instance + * @type {jQuery} + */ + this.$dropdown = null; + /** + * The jQuery object of the search input. + * @instance + * @type {jQuery} + */ + this.$input = null; + /** + * The jQuery object of the search button. + * @instance + * @type {jQuery} + */ + this.$button = null; + + /* PRIVATE */ + /** + * The timeout ID for the filter changed event. + * @instance + * @private + * @type {?number} + */ + this._filterTimeout = null; + /** + * The regular expression used to check for encapsulating quotations. + * @instance + * @private + * @type {RegExp} + */ + this._exactRegExp = /^"(.*?)"$/; + }, + + /* PROTECTED */ + /** + * Checks the supplied data and options for the filtering component. + * @instance + * @protected + * @param {object} data - The jQuery data object from the parent table. + * @fires FooTable.Filtering#"preinit.ft.filtering" + */ + preinit: function(data){ + var self = this; + /** + * The preinit.ft.filtering event is raised before the UI is created and provides the tables jQuery data object for additional options parsing. + * Calling preventDefault on this event will disable the component. + * @event FooTable.Filtering#"preinit.ft.filtering" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + * @param {object} data - The jQuery data object of the table raising the event. + */ + return self.ft.raise('preinit.ft.filtering').then(function(){ + // first check if filtering is enabled via the class being applied + if (self.ft.$el.hasClass('footable-filtering')) + self.enabled = true; + // then check if the data-filtering-enabled attribute has been set + self.enabled = F.is.boolean(data.filtering) + ? data.filtering + : self.enabled; + + // if filtering is not enabled exit early as we don't need to do anything else + if (!self.enabled) return; + + self.space = F.is.string(data.filterSpace) + ? data.filterSpace + : self.space; + + self.min = F.is.number(data.filterMin) + ? data.filterMin + : self.min; + + self.connectors = F.is.boolean(data.filterConnectors) + ? data.filterConnectors + : self.connectors; + + self.ignoreCase = F.is.boolean(data.filterIgnoreCase) + ? data.filterIgnoreCase + : self.ignoreCase; + + self.exactMatch = F.is.boolean(data.filterExactMatch) + ? data.filterExactMatch + : self.exactMatch; + + self.focus = F.is.boolean(data.filterFocus) + ? data.filterFocus + : self.focus; + + self.delay = F.is.number(data.filterDelay) + ? data.filterDelay + : self.delay; + + self.placeholder = F.is.string(data.filterPlaceholder) + ? data.filterPlaceholder + : self.placeholder; + + self.dropdownTitle = F.is.string(data.filterDropdownTitle) + ? data.filterDropdownTitle + : self.dropdownTitle; + + self.container = F.is.string(data.filterContainer) + ? data.filterContainer + : self.container; + + self.filters = F.is.array(data.filterFilters) + ? self.ensure(data.filterFilters) + : self.ensure(self.filters); + + if (self.ft.$el.hasClass('footable-filtering-left')) + self.position = 'left'; + if (self.ft.$el.hasClass('footable-filtering-center')) + self.position = 'center'; + if (self.ft.$el.hasClass('footable-filtering-right')) + self.position = 'right'; + + self.position = F.is.string(data.filterPosition) + ? data.filterPosition + : self.position; + },function(){ + self.enabled = false; + }); + }, + /** + * Initializes the filtering component for the plugin. + * @instance + * @protected + * @fires FooTable.Filtering#"init.ft.filtering" + */ + init: function () { + var self = this; + /** + * The init.ft.filtering event is raised before its UI is generated. + * Calling preventDefault on this event will disable the component. + * @event FooTable.Filtering#"init.ft.filtering" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + return self.ft.raise('init.ft.filtering').then(function(){ + self.$create(); + }, function(){ + self.enabled = false; + }); + }, + /** + * Destroys the filtering component removing any UI from the table. + * @instance + * @protected + * @fires FooTable.Filtering#"destroy.ft.filtering" + */ + destroy: function () { + var self = this; + /** + * The destroy.ft.filtering event is raised before its UI is removed. + * Calling preventDefault on this event will prevent the component from being destroyed. + * @event FooTable.Filtering#"destroy.ft.filtering" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + */ + return self.ft.raise('destroy.ft.filtering').then(function(){ + self.ft.$el.removeClass('footable-filtering') + .find('thead > tr.footable-filtering').remove(); + }); + }, + /** + * Creates the filtering UI from the current options setting the various jQuery properties of this component. + * @instance + * @protected + * @this FooTable.Filtering + */ + $create: function () { + var self = this; + // generate the cell that actually contains all the UI. + var $form_grp = $('
', {'class': 'form-group footable-filtering-search'}) + .append($('
').attr('colspan', self.ft.columns.visibleColspan).appendTo(self.$row); + self.$container = self.$cell; + } else { + self.$container.addClass('footable-filtering-external').addClass(position); + } + self.$form = $('
', {'class': 'form-inline'}).append($form_grp).appendTo(self.$container); + + self.$input = $('', {type: 'text', 'class': 'form-control', placeholder: self.placeholder}); + + self.$button = $('
The below shows what is meant by the "left" side of a queryThe below shows what is meant by the "right" side of a queryThis example shows using pseudo code what a sort function would look like.
').attr('colspan', this.ft.columns.visibleColspan).appendTo(this.$row); + } else { + this.$container.addClass('footable-paging-external').addClass(position); + } + this.$wrapper = $('
', {'class': 'footable-pagination-wrapper'}).appendTo(this.$container); + this.$pagination = $('
').attr('colspan', self.ft.columns.visibleColspan).append(self.$buttonShow()); + if (self.allowAdd){ + self.$cell.append(self.$buttonAdd()); + } + self.$cell.append(self.$buttonHide()); + + if (self.alwaysShow){ + self.ft.$el.addClass('footable-editing-always-show'); + } + + if (!self.allowAdd) self.ft.$el.addClass('footable-editing-no-add'); + if (!self.allowEdit) self.ft.$el.addClass('footable-editing-no-edit'); + if (!self.allowDelete) self.ft.$el.addClass('footable-editing-no-delete'); + if (!self.allowView) self.ft.$el.addClass('footable-editing-no-view'); + + var $tfoot = self.ft.$el.children('tfoot'); + if ($tfoot.length == 0){ + $tfoot = $('
', {'class': 'footable-editing'})).html(this.title); + }, + /** + * This is supplied either the cell value or jQuery object to parse. Any value can be returned from this method and + * will be provided to the {@link FooTable.EditingColumn#format} function + * to generate the cell contents. + * @instance + * @protected + * @param {(*|jQuery)} valueOrElement - The value or jQuery cell object. + * @returns {(jQuery)} + */ + parser: function(valueOrElement){ + if (F.is.string(valueOrElement)) valueOrElement = $($.trim(valueOrElement)); + if (F.is.element(valueOrElement)) valueOrElement = $(valueOrElement); + if (F.is.jq(valueOrElement)){ + var tagName = valueOrElement.prop('tagName').toLowerCase(); + if (tagName == 'td' || tagName == 'th') return valueOrElement.data('value') || valueOrElement.contents(); + return valueOrElement; + } + return null; + }, + /** + * Creates a cell to be used in the supplied row for this column. + * @param {FooTable.Row} row - The row to create the cell for. + * @returns {FooTable.Cell} + */ + createCell: function(row){ + var $buttons = this.editing.$rowButtons(), $cell = $('').append($buttons); + if (F.is.jq(row.$el)){ + if (this.index === 0){ + $cell.prependTo(row.$el); + } else { + $cell.insertAfter(row.$el.children().eq(this.index-1)); + } + } + return new F.Cell(this.ft, row, this, $cell || $cell.html()); + } + }); + + F.columns.register('editing', F.EditingColumn); + +})(jQuery, FooTable); +(function($, F) { + + /** + * An object containing the editing options for the plugin. Added by the {@link FooTable.Editing} component. + * @type {object} + * @prop {boolean} enabled=false - Whether or not to allow editing on the table. + * @prop {boolean} pageToNew=true - Whether or not to automatically page to a new row when it is added to the table. + * @prop {string} position="right" - The position of the editing column in the table as well as the alignment of the buttons. + * @prop {boolean} alwaysShow=false - Whether or not the editing column and add row button are always visible. + * @prop {function} addRow - The callback function to execute when the add row button is clicked. + * @prop {function} editRow - The callback function to execute when the edit row button is clicked. + * @prop {function} deleteRow - The callback function to execute when the delete row button is clicked. + * @prop {function} viewRow - The callback function to execute when the view row button is clicked. + * @prop {string} showText - The text that appears in the show button. This can contain HTML. + * @prop {string} hideText - The text that appears in the hide button. This can contain HTML. + * @prop {string} addText - The text that appears in the add button. This can contain HTML. + * @prop {string} editText - The text that appears in the edit button. This can contain HTML. + * @prop {string} deleteText - The text that appears in the delete button. This can contain HTML. + * @prop {string} viewText - The text that appears in the view button. This can contain HTML. + * @prop {boolean} allowAdd - Whether or not to show the Add Row button. + * @prop {boolean} allowEdit - Whether or not to show the Edit Row button. + * @prop {boolean} allowDelete - Whether or not to show the Delete Row button. + * @prop {boolean} allowView - Whether or not to show the View Row button. + * @prop {object} column - The options for the editing column. @see {@link FooTable.EditingColumn} for more info. + * @prop {string} column.classes="footable-editing" - A space separated string of class names to apply to all cells in the column. + * @prop {string} column.name="editing" - The name of the column. + * @prop {string} column.title="" - The title displayed in the header row of the table for the column. + * @prop {boolean} column.filterable=false - Whether or not the column should be filterable when using the filtering component. + * @prop {boolean} column.sortable=false - Whether or not the column should be sortable when using the sorting component. + */ + F.Defaults.prototype.editing = { + enabled: false, + pageToNew: true, + position: 'right', + alwaysShow: false, + addRow: function(){}, + editRow: function(row){}, + deleteRow: function(row){}, + viewRow: function(row){}, + showText: ' Edit rows', + hideText: 'Cancel', + addText: 'New row', + editText: '', + deleteText: '', + viewText: '', + allowAdd: true, + allowEdit: true, + allowDelete: true, + allowView: false, + column: { + classes: 'footable-editing', + name: 'editing', + title: '', + filterable: false, + sortable: false + } + }; + +})(jQuery, FooTable); + +(function($, F){ + + if (F.is.defined(F.Paging)){ + /** + * Holds a shallow clone of the un-paged {@link FooTable.Rows#array} value before paging occurs and superfluous rows are removed. Added by the {@link FooTable.Editing} component. + * @instance + * @public + * @type {Array} + */ + F.Paging.prototype.unpaged = []; + + // override the default predraw method with one that sets the unpaged property. + F.Paging.extend('predraw', function(){ + this.unpaged = this.ft.rows.array.slice(0); // create a shallow clone for later use + this._super(); // call the original method + }); + } + +})(jQuery, FooTable); +(function($, F){ + + /** + * Adds the row to the table. + * @param {boolean} [redraw=true] - Whether or not to redraw the table, defaults to true but for bulk operations this + * can be set to false and then followed by a call to the {@link FooTable.Table#draw} method. + * @returns {jQuery.Deferred} + */ + F.Row.prototype.add = function(redraw){ + redraw = F.is.boolean(redraw) ? redraw : true; + var self = this; + return $.Deferred(function(d){ + var index = self.ft.rows.all.push(self) - 1; + if (redraw){ + return self.ft.draw().then(function(){ + d.resolve(index); + }); + } else { + d.resolve(index); + } + }); + }; + + /** + * Removes the row from the table. + * @param {boolean} [redraw=true] - Whether or not to redraw the table, defaults to true but for bulk operations this + * can be set to false and then followed by a call to the {@link FooTable.Table#draw} method. + * @returns {jQuery.Deferred} + */ + F.Row.prototype.delete = function(redraw){ + redraw = F.is.boolean(redraw) ? redraw : true; + var self = this; + return $.Deferred(function(d){ + var index = self.ft.rows.all.indexOf(self); + if (F.is.number(index) && index >= 0 && index < self.ft.rows.all.length){ + self.ft.rows.all.splice(index, 1); + if (redraw){ + return self.ft.draw().then(function(){ + d.resolve(self); + }); + } + } + d.resolve(self); + }); + }; + + if (F.is.defined(F.Paging)){ + // override the default add method with one that supports paging + F.Row.extend('add', function(redraw){ + redraw = F.is.boolean(redraw) ? redraw : true; + var self = this, + added = this._super(redraw), + editing = self.ft.use(F.Editing), + paging; + if (editing && editing.pageToNew && (paging = self.ft.use(F.Paging)) && redraw){ + return added.then(function(){ + var index = paging.unpaged.indexOf(self); // find this row in the unpaged array (this array will be sorted and filtered) + var page = Math.ceil((index + 1) / paging.size); // calculate the page the new row is on + if (paging.current !== page){ // goto the page if we need to + return paging.goto(page); + } + }); + } + return added; + }); + } + + if (F.is.defined(F.Sorting)){ + // override the default val method with one that supports sorting and paging + F.Row.extend('val', function(data, redraw){ + redraw = F.is.boolean(redraw) ? redraw : true; + var result = this._super(data); + if (!F.is.hash(data)){ + return result; + } + var self = this; + if (redraw){ + self.ft.draw().then(function(){ + var editing = self.ft.use(F.Editing), paging; + if (F.is.defined(F.Paging) && editing && editing.pageToNew && (paging = self.ft.use(F.Paging))){ + var index = paging.unpaged.indexOf(self); // find this row in the unpaged array (this array will be sorted and filtered) + var page = Math.ceil((index + 1) / paging.size); // calculate the page the new row is on + if (paging.current !== page){ // goto the page if we need to + return paging.goto(page); + } + } + }); + } + return result; + }); + } + +})(jQuery, FooTable); +(function(F){ + + /** + * Adds a row to the underlying {@link FooTable.Rows#all} array. + * @param {(object|FooTable.Row)} dataOrRow - A hash containing the row values or an actual {@link FooTable.Row} object. + * @param {boolean} [redraw=true] - Whether or not to redraw the table, defaults to true but for bulk operations this + * can be set to false and then followed by a call to the {@link FooTable.Table#draw} method. + */ + F.Rows.prototype.add = function(dataOrRow, redraw){ + var row = dataOrRow; + if (F.is.hash(dataOrRow)){ + row = new FooTable.Row(this.ft, this.ft.columns.array, dataOrRow); + } + if (row instanceof FooTable.Row){ + row.add(redraw); + } + }; + + /** + * Updates a row in the underlying {@link FooTable.Rows#all} array. + * @param {(number|FooTable.Row)} indexOrRow - The index to update or the actual {@link FooTable.Row} object. + * @param {object} data - A hash containing the new row values. + * @param {boolean} [redraw=true] - Whether or not to redraw the table, defaults to true but for bulk operations this + * can be set to false and then followed by a call to the {@link FooTable.Table#draw} method. + */ + F.Rows.prototype.update = function(indexOrRow, data, redraw){ + var len = this.ft.rows.all.length, + row = indexOrRow; + if (F.is.number(indexOrRow) && indexOrRow >= 0 && indexOrRow < len){ + row = this.ft.rows.all[indexOrRow]; + } + if (row instanceof FooTable.Row && F.is.hash(data)){ + row.val(data, redraw); + } + }; + + /** + * Deletes a row from the underlying {@link FooTable.Rows#all} array. + * @param {(number|FooTable.Row)} indexOrRow - The index to delete or the actual {@link FooTable.Row} object. + * @param {boolean} [redraw=true] - Whether or not to redraw the table, defaults to true but for bulk operations this + * can be set to false and then followed by a call to the {@link FooTable.Table#draw} method. + */ + F.Rows.prototype.delete = function(indexOrRow, redraw){ + var len = this.ft.rows.all.length, + row = indexOrRow; + if (F.is.number(indexOrRow) && indexOrRow >= 0 && indexOrRow < len){ + row = this.ft.rows.all[indexOrRow]; + } + if (row instanceof FooTable.Row){ + row.delete(redraw); + } + }; + +})(FooTable); + +(function($, F){ + + // global int to use if the table has no ID + var _uid = 0, + // a hash value for the current url + _url_hash = (function(str){ + var i, l, hval = 0x811c9dc5; + for (i = 0, l = str.length; i < l; i++) { + hval ^= str.charCodeAt(i); + hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24); + } + return hval >>> 0; + })(location.origin + location.pathname); + + F.State = F.Component.extend(/** @lends FooTable.State */{ + /** + * The state component adds the ability for the table to remember its basic state for filtering, paging and sorting. + * @constructs + * @extends FooTable.Component + * @param {FooTable.Table} table - The parent {@link FooTable.Table} object for the component. + * @returns {FooTable.State} + */ + construct: function(table){ + // call the constructor of the base class + this._super(table, table.o.state.enabled); + // Change this value if an update to this component requires any stored data to be reset + this._key = '1'; + /** + * The key to use to store the state for this table. + * @type {(null|string)} + */ + this.key = this._key + (F.is.string(table.o.state.key) ? table.o.state.key : this._uid()); + /** + * Whether or not to allow the filtering component to store it's state. + * @type {boolean} + */ + this.filtering = F.is.boolean(table.o.state.filtering) ? table.o.state.filtering : true; + /** + * Whether or not to allow the paging component to store it's state. + * @type {boolean} + */ + this.paging = F.is.boolean(table.o.state.paging) ? table.o.state.paging : true; + /** + * Whether or not to allow the sorting component to store it's state. + * @type {boolean} + */ + this.sorting = F.is.boolean(table.o.state.sorting) ? table.o.state.sorting : true; + }, + /* PROTECTED */ + /** + * Checks the supplied data and options for the state component. + * @instance + * @protected + * @param {object} data - The jQuery data object from the parent table. + * @fires FooTable.State#"preinit.ft.state" + * @this FooTable.State + */ + preinit: function(data){ + var self = this; + /** + * The preinit.ft.state event is raised before the UI is created and provides the tables jQuery data object for additional options parsing. + * Calling preventDefault on this event will disable the component. + * @event FooTable.State#"preinit.ft.state" + * @param {jQuery.Event} e - The jQuery.Event object for the event. + * @param {FooTable.Table} ft - The instance of the plugin raising the event. + * @param {object} data - The jQuery data object of the table raising the event. + */ + this.ft.raise('preinit.ft.state', [data]).then(function(){ + + self.enabled = F.is.boolean(data.state) + ? data.state + : self.enabled; + + if (!self.enabled) return; + + self.key = self._key + (F.is.string(data.stateKey) ? data.stateKey : self.key); + + self.filtering = F.is.boolean(data.stateFiltering) ? data.stateFiltering : self.filtering; + + self.paging = F.is.boolean(data.statePaging) ? data.statePaging : self.paging; + + self.sorting = F.is.boolean(data.stateSorting) ? data.stateSorting : self.sorting; + + }, function(){ + self.enabled = false; + }); + }, + /** + * Gets the state value for the specified key for this table. + * @instance + * @param {string} key - The key to get the value for. + * @returns {(*|null)} + */ + get: function(key){ + return JSON.parse(localStorage.getItem(this.key + ':' + key)); + }, + /** + * Sets the state value for the specified key for this table. + * @instance + * @param {string} key - The key to set the value for. + * @param {*} data - The value to store for the key. This value must be JSON.stringify friendly. + */ + set: function(key, data){ + localStorage.setItem(this.key + ':' + key, JSON.stringify(data)); + }, + /** + * Clears the state value for the specified key for this table. + * @instance + * @param {string} key - The key to clear the value for. + */ + remove: function(key){ + localStorage.removeItem(this.key + ':' + key); + }, + /** + * Executes the {@link FooTable.Component#readState} function on all components. + * @instance + */ + read: function(){ + this.ft.execute(false, true, 'readState'); + }, + /** + * Executes the {@link FooTable.Component#writeState} function on all components. + * @instance + */ + write: function(){ + this.ft.execute(false, true, 'writeState'); + }, + /** + * Executes the {@link FooTable.Component#clearState} function on all components. + * @instance + */ + clear: function(){ + this.ft.execute(false, true, 'clearState'); + }, + /** + * Generates a unique identifier for the current {@link FooTable.Table} if one is not supplied through the options. + * This value is a combination of the url hash and either the element ID or an incremented global int value. + * @instance + * @returns {*} + * @private + */ + _uid: function(){ + var id = this.ft.$el.attr('id'); + return _url_hash + '_' + (F.is.string(id) ? id : ++_uid); + } + }); + + F.components.register('state', F.State, 700); + +})(jQuery, FooTable); +(function(F){ + + /** + * This method is called from the {@link FooTable.State#read} method and allows a component to retrieve its' stored state. + * @instance + * @protected + * @function + */ + F.Component.prototype.readState = function(){}; + + /** + * This method is called from the {@link FooTable.State#write} method and allows a component to write its' current state to the store. + * @instance + * @protected + * @function + */ + F.Component.prototype.writeState = function(){}; + + /** + * This method is called from the {@link FooTable.State#clear} method and allows a component to clear any stored state. + * @instance + * @protected + * @function + */ + F.Component.prototype.clearState = function(){}; + +})(FooTable); +(function(F){ + + /** + * An object containing the state options for the plugin. Added by the {@link FooTable.State} component. + * @type {object} + * @prop {boolean} enabled=false - Whether or not to allow state to be stored for the table. This overrides the individual component enable options. + * @prop {boolean} filtering=true - Whether or not to allow the filtering state to be stored. + * @prop {boolean} paging=true - Whether or not to allow the filtering state to be stored. + * @prop {boolean} sorting=true - Whether or not to allow the filtering state to be stored. + * @prop {string} key=null - The unique key to use to store the table's data. + */ + F.Defaults.prototype.state = { + enabled: false, + filtering: true, + paging: true, + sorting: true, + key: null + }; + +})(FooTable); +(function(F){ + + if (!F.Filtering) return; + + /** + * Allows the filtering component to retrieve its' stored state. + */ + F.Filtering.prototype.readState = function(){ + if (this.ft.state.filtering){ + var state = this.ft.state.get('filtering'); + if (F.is.hash(state) && !F.is.emptyArray(state.filters)){ + this.filters = this.ensure(state.filters); + } + } + }; + + /** + * Allows the filtering component to write its' current state to the store. + */ + F.Filtering.prototype.writeState = function(){ + if (this.ft.state.filtering) { + var filters = F.arr.map(this.filters, function (f) { + return { + name: f.name, + query: f.query instanceof F.Query ? f.query.val() : f.query, + columns: F.arr.map(f.columns, function (c) { + return c.name; + }), + hidden: f.hidden, + space: f.space, + connectors: f.connectors, + ignoreCase: f.ignoreCase + }; + }); + this.ft.state.set('filtering', {filters: filters}); + } + }; + + /** + * Allows the filtering component to clear any stored state. + */ + F.Filtering.prototype.clearState = function(){ + if (this.ft.state.filtering) { + this.ft.state.remove('filtering'); + } + }; + +})(FooTable); +(function(F){ + + if (!F.Paging) return; + + /** + * Allows the paging component to retrieve its' stored state. + */ + F.Paging.prototype.readState = function(){ + if (this.ft.state.paging) { + var state = this.ft.state.get('paging'); + if (F.is.hash(state)) { + this.current = state.current; + this.size = state.size; + } + } + }; + + /** + * Allows the paging component to write its' current state to the store. + */ + F.Paging.prototype.writeState = function(){ + if (this.ft.state.paging) { + this.ft.state.set('paging', { + current: this.current, + size: this.size + }); + } + }; + + /** + * Allows the paging component to clear any stored state. + */ + F.Paging.prototype.clearState = function(){ + if (this.ft.state.paging) { + this.ft.state.remove('paging'); + } + }; + +})(FooTable); +(function(F){ + + if (!F.Sorting) return; + + /** + * Allows the sorting component to retrieve its' stored state. + */ + F.Sorting.prototype.readState = function(){ + if (this.ft.state.sorting) { + var state = this.ft.state.get('sorting'); + if (F.is.hash(state)) { + var column = this.ft.columns.get(state.column); + if (column instanceof F.Column) { + this.column = column; + this.column.direction = state.direction; + } + } + } + }; + + /** + * Allows the sorting component to write its' current state to the store. + */ + F.Sorting.prototype.writeState = function(){ + if (this.ft.state.sorting && this.column instanceof F.Column){ + this.ft.state.set('sorting', { + column: this.column.name, + direction: this.column.direction + }); + } + }; + + /** + * Allows the sorting component to clear any stored state. + */ + F.Sorting.prototype.clearState = function(){ + if (this.ft.state.sorting) { + this.ft.state.remove('sorting'); + } + }; + +})(FooTable); +(function(F){ + + // hook into the _construct method so we can add the state property to the table. + F.Table.extend('_construct', function(ready){ + this.state = this.use(FooTable.State); + return this._super(ready); + }); + + // hook into the _preinit method so we can trigger a plugin wide read state operation. + F.Table.extend('_preinit', function(){ + var self = this; + return self._super().then(function(){ + if (self.state.enabled){ + self.state.read(); + } + }); + }); + + // hook into the draw method so we can trigger a plugin wide write state operation. + F.Table.extend('draw', function(){ + var self = this; + return self._super().then(function(){ + if (self.state.enabled){ + self.state.write(); + } + }); + }); + +})(FooTable); +(function($, F){ + + F.Export = F.Component.extend(/** @lends FooTable.Export */{ + /** + * @summary This component provides some basic export functionality. + * @memberof FooTable + * @constructs Export + * @param {FooTable.Table} table - The current instance of the plugin. + */ + construct: function(table){ + // call the constructor of the base class + this._super(table, true); + /** + * @summary A snapshot of the working set of rows prior to being trimmed by the paging component. + * @memberof FooTable.Export# + * @name snapshot + * @type {FooTable.Row[]} + */ + this.snapshot = []; + }, + /** + * @summary Hooks into the predraw pipeline after sorting and filtering have taken place but prior to paging. + * @memberof FooTable.Export# + * @function predraw + * @description This method allows us to take a snapshot of the working set of rows before they are trimmed by the paging component and is called by the plugin instance. + */ + predraw: function(){ + this.snapshot = this.ft.rows.array.slice(0); + }, + /** + * @summary Return the columns as simple JavaScript objects in an array. + * @memberof FooTable.Export# + * @function columns + * @returns {Object[]} + */ + columns: function(){ + var result = []; + F.arr.each(this.ft.columns.array, function(column){ + if (!column.internal){ + result.push({ + type: column.type, + name: column.name, + title: column.title, + visible: column.visible, + hidden: column.hidden, + classes: column.classes, + style: column.style + }); + } + }); + return result; + }, + /** + * @summary Return the rows as simple JavaScript objects in an array. + * @memberof FooTable.Export# + * @function rows + * @param {boolean} [filtered=false] - Whether or not to exclude filtered rows from the result. + * @returns {Object[]} + */ + rows: function(filtered){ + filtered = F.is.boolean(filtered) ? filtered : false; + var rows = filtered ? this.ft.rows.all : this.snapshot, result = []; + F.arr.each(rows, function(row){ + result.push(row.val()); + }); + return result; + }, + /** + * @summary Return the columns and rows as a properly formatted JSON object. + * @memberof FooTable.Export# + * @function json + * @param {boolean} [filtered=false] - Whether or not to exclude filtered rows from the result. + * @returns {Object} + */ + json: function(filtered){ + return JSON.parse(JSON.stringify({columns: this.columns(),rows: this.rows(filtered)})); + }, + /** + * @summary Return the columns and rows as a properly formatted CSV value. + * @memberof FooTable.Export# + * @function csv + * @param {boolean} [filtered=false] - Whether or not to exclude filtered rows from the result. + * @returns {string} + */ + csv: function(filtered){ + var csv = "", columns = this.columns(), value, escaped; + F.arr.each(columns, function(column, i){ + escaped = '"' + column.title.replace(/"/g, '""') + '"'; + csv += (i === 0 ? escaped : "," + escaped); + }); + csv += "\n"; + + var rows = filtered ? this.ft.rows.all : this.snapshot; + F.arr.each(rows, function(row){ + F.arr.each(row.cells, function(cell, i){ + if (!cell.column.internal){ + value = cell.column.stringify.call(cell.column, cell.value, cell.ft.o, cell.row.value); + escaped = '"' + value.replace(/"/g, '""') + '"'; + csv += (i === 0 ? escaped : "," + escaped); + } + }); + csv += "\n"; + }); + return csv; + } + }); + + // register the component using a priority of 490 which falls just after filtering (500) and before paging (400). + F.components.register("export", F.Export, 490); + +})(jQuery, FooTable); +(function(F){ + // this is used to define the filtering specific properties on column creation + F.Column.prototype.__export_define__ = function(definition){ + this.stringify = F.checkFnValue(this, definition.stringify, this.stringify); + }; + + // overrides the public define method and replaces it with our own + F.Column.extend('define', function(definition){ + this._super(definition); // call the base so we don't have to redefine any previously set properties + this.__export_define__(definition); // then call our own + }); + + /** + * @summary Return the supplied value as a string. + * @memberof FooTable.Column# + * @function stringify + * @returns {string} + */ + F.Column.prototype.stringify = function(value, options, rowData){ + return value + ""; + }; + + if (F.is.defined(F.DateColumn)){ + // override the base method for DateColumns + F.DateColumn.prototype.stringify = function(value, options, rowData){ + return F.is.object(value) && F.is.boolean(value._isAMomentObject) && value.isValid() ? value.format(this.formatString) : ''; + }; + } + + // override the base method for ObjectColumns + F.ObjectColumn.prototype.stringify = function(value, options, rowData){ + return F.is.object(value) ? JSON.stringify(value) : ""; + }; + + // override the base method for ArrayColumns + F.ArrayColumn.prototype.stringify = function(value, options, rowData){ + return F.is.array(value) ? JSON.stringify(value) : ""; + }; + +})(FooTable); +(function(F){ + /** + * @summary Return the columns and rows as a properly formatted JSON object. + * @memberof FooTable.Table# + * @function toJSON + * @param {boolean} [filtered=false] - Whether or not to exclude filtered rows from the result. + * @returns {Object} + */ + F.Table.prototype.toJSON = function(filtered){ + return this.use(F.Export).json(filtered); + }; + + /** + * @summary Return the columns and rows as a properly formatted CSV value. + * @memberof FooTable.Table# + * @function toCSV + * @param {boolean} [filtered=false] - Whether or not to exclude filtered rows from the result. + * @returns {string} + */ + F.Table.prototype.toCSV = function(filtered){ + return this.use(F.Export).csv(filtered); + }; + +})(FooTable); \ No newline at end of file diff --git a/debian/missing-sources/jquery.stickytabs.js b/debian/missing-sources/jquery.stickytabs.js new file mode 100644 index 0000000..864f561 --- /dev/null +++ b/debian/missing-sources/jquery.stickytabs.js @@ -0,0 +1,61 @@ +/** + * jQuery Plugin: Sticky Tabs + * + * @author Aidan Lister + * @version 1.2.0 + */ +(function ( $ ) { + $.fn.stickyTabs = function( options ) { + var context = this + + var settings = $.extend({ + getHashCallback: function(hash, btn) { return hash }, + selectorAttribute: "href", + backToTop: false, + initialTab: $('li.active > a', context) + }, options ); + + // Show the tab corresponding with the hash in the URL, or the first tab. + var showTabFromHash = function() { + var hash = settings.selectorAttribute == "href" ? window.location.hash : window.location.hash.substring(1); + var selector = hash ? 'a[' + settings.selectorAttribute +'="' + hash + '"]' : settings.initialTab; + $(selector, context).tab('show'); + setTimeout(backToTop, 1); + } + + // We use pushState if it's available so the page won't jump, otherwise a shim. + var changeHash = function(hash) { + if (history && history.pushState) { + history.pushState(null, null, window.location.pathname + window.location.search + '#' + hash); + } else { + scrollV = document.body.scrollTop; + scrollH = document.body.scrollLeft; + window.location.hash = hash; + document.body.scrollTop = scrollV; + document.body.scrollLeft = scrollH; + } + } + + var backToTop = function() { + if (settings.backToTop === true) { + window.scrollTo(0, 0); + } + } + + // Set the correct tab when the page loads + showTabFromHash(); + + // Set the correct tab when a user uses their back/forward button + $(window).on('hashchange', showTabFromHash); + + // Change the URL when tabs are clicked + $('a', context).on('click', function(e) { + var hash = this.href.split('#')[1]; + var adjustedhash = settings.getHashCallback(hash, this); + changeHash(adjustedhash); + setTimeout(backToTop, 1); + }); + + return this; + }; +}( jQuery )); \ No newline at end of file diff --git a/debian/missing-sources/nprogress.js b/debian/missing-sources/nprogress.js new file mode 100644 index 0000000..b23b300 --- /dev/null +++ b/debian/missing-sources/nprogress.js @@ -0,0 +1,476 @@ +/* NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress + * @license MIT */ + +;(function(root, factory) { + + if (typeof define === 'function' && define.amd) { + define(factory); + } else if (typeof exports === 'object') { + module.exports = factory(); + } else { + root.NProgress = factory(); + } + +})(this, function() { + var NProgress = {}; + + NProgress.version = '0.2.0'; + + var Settings = NProgress.settings = { + minimum: 0.08, + easing: 'ease', + positionUsing: '', + speed: 200, + trickle: true, + trickleRate: 0.02, + trickleSpeed: 800, + showSpinner: true, + barSelector: '[role="bar"]', + spinnerSelector: '[role="spinner"]', + parent: 'body', + template: '
' + }; + + /** + * Updates configuration. + * + * NProgress.configure({ + * minimum: 0.1 + * }); + */ + NProgress.configure = function(options) { + var key, value; + for (key in options) { + value = options[key]; + if (value !== undefined && options.hasOwnProperty(key)) Settings[key] = value; + } + + return this; + }; + + /** + * Last number. + */ + + NProgress.status = null; + + /** + * Sets the progress bar status, where `n` is a number from `0.0` to `1.0`. + * + * NProgress.set(0.4); + * NProgress.set(1.0); + */ + + NProgress.set = function(n) { + var started = NProgress.isStarted(); + + n = clamp(n, Settings.minimum, 1); + NProgress.status = (n === 1 ? null : n); + + var progress = NProgress.render(!started), + bar = progress.querySelector(Settings.barSelector), + speed = Settings.speed, + ease = Settings.easing; + + progress.offsetWidth; /* Repaint */ + + queue(function(next) { + // Set positionUsing if it hasn't already been set + if (Settings.positionUsing === '') Settings.positionUsing = NProgress.getPositioningCSS(); + + // Add transition + css(bar, barPositionCSS(n, speed, ease)); + + if (n === 1) { + // Fade out + css(progress, { + transition: 'none', + opacity: 1 + }); + progress.offsetWidth; /* Repaint */ + + setTimeout(function() { + css(progress, { + transition: 'all ' + speed + 'ms linear', + opacity: 0 + }); + setTimeout(function() { + NProgress.remove(); + next(); + }, speed); + }, speed); + } else { + setTimeout(next, speed); + } + }); + + return this; + }; + + NProgress.isStarted = function() { + return typeof NProgress.status === 'number'; + }; + + /** + * Shows the progress bar. + * This is the same as setting the status to 0%, except that it doesn't go backwards. + * + * NProgress.start(); + * + */ + NProgress.start = function() { + if (!NProgress.status) NProgress.set(0); + + var work = function() { + setTimeout(function() { + if (!NProgress.status) return; + NProgress.trickle(); + work(); + }, Settings.trickleSpeed); + }; + + if (Settings.trickle) work(); + + return this; + }; + + /** + * Hides the progress bar. + * This is the *sort of* the same as setting the status to 100%, with the + * difference being `done()` makes some placebo effect of some realistic motion. + * + * NProgress.done(); + * + * If `true` is passed, it will show the progress bar even if its hidden. + * + * NProgress.done(true); + */ + + NProgress.done = function(force) { + if (!force && !NProgress.status) return this; + + return NProgress.inc(0.3 + 0.5 * Math.random()).set(1); + }; + + /** + * Increments by a random amount. + */ + + NProgress.inc = function(amount) { + var n = NProgress.status; + + if (!n) { + return NProgress.start(); + } else { + if (typeof amount !== 'number') { + amount = (1 - n) * clamp(Math.random() * n, 0.1, 0.95); + } + + n = clamp(n + amount, 0, 0.994); + return NProgress.set(n); + } + }; + + NProgress.trickle = function() { + return NProgress.inc(Math.random() * Settings.trickleRate); + }; + + /** + * Waits for all supplied jQuery promises and + * increases the progress as the promises resolve. + * + * @param $promise jQUery Promise + */ + (function() { + var initial = 0, current = 0; + + NProgress.promise = function($promise) { + if (!$promise || $promise.state() === "resolved") { + return this; + } + + if (current === 0) { + NProgress.start(); + } + + initial++; + current++; + + $promise.always(function() { + current--; + if (current === 0) { + initial = 0; + NProgress.done(); + } else { + NProgress.set((initial - current) / initial); + } + }); + + return this; + }; + + })(); + + /** + * (Internal) renders the progress bar markup based on the `template` + * setting. + */ + + NProgress.render = function(fromStart) { + if (NProgress.isRendered()) return document.getElementById('nprogress'); + + addClass(document.documentElement, 'nprogress-busy'); + + var progress = document.createElement('div'); + progress.id = 'nprogress'; + progress.innerHTML = Settings.template; + + var bar = progress.querySelector(Settings.barSelector), + perc = fromStart ? '-100' : toBarPerc(NProgress.status || 0), + parent = document.querySelector(Settings.parent), + spinner; + + css(bar, { + transition: 'all 0 linear', + transform: 'translate3d(' + perc + '%,0,0)' + }); + + if (!Settings.showSpinner) { + spinner = progress.querySelector(Settings.spinnerSelector); + spinner && removeElement(spinner); + } + + if (parent != document.body) { + addClass(parent, 'nprogress-custom-parent'); + } + + parent.appendChild(progress); + return progress; + }; + + /** + * Removes the element. Opposite of render(). + */ + + NProgress.remove = function() { + removeClass(document.documentElement, 'nprogress-busy'); + removeClass(document.querySelector(Settings.parent), 'nprogress-custom-parent'); + var progress = document.getElementById('nprogress'); + progress && removeElement(progress); + }; + + /** + * Checks if the progress bar is rendered. + */ + + NProgress.isRendered = function() { + return !!document.getElementById('nprogress'); + }; + + /** + * Determine which positioning CSS rule to use. + */ + + NProgress.getPositioningCSS = function() { + // Sniff on document.body.style + var bodyStyle = document.body.style; + + // Sniff prefixes + var vendorPrefix = ('WebkitTransform' in bodyStyle) ? 'Webkit' : + ('MozTransform' in bodyStyle) ? 'Moz' : + ('msTransform' in bodyStyle) ? 'ms' : + ('OTransform' in bodyStyle) ? 'O' : ''; + + if (vendorPrefix + 'Perspective' in bodyStyle) { + // Modern browsers with 3D support, e.g. Webkit, IE10 + return 'translate3d'; + } else if (vendorPrefix + 'Transform' in bodyStyle) { + // Browsers without 3D support, e.g. IE9 + return 'translate'; + } else { + // Browsers without translate() support, e.g. IE7-8 + return 'margin'; + } + }; + + /** + * Helpers + */ + + function clamp(n, min, max) { + if (n < min) return min; + if (n > max) return max; + return n; + } + + /** + * (Internal) converts a percentage (`0..1`) to a bar translateX + * percentage (`-100%..0%`). + */ + + function toBarPerc(n) { + return (-1 + n) * 100; + } + + + /** + * (Internal) returns the correct CSS for changing the bar's + * position given an n percentage, and speed and ease from Settings + */ + + function barPositionCSS(n, speed, ease) { + var barCSS; + + if (Settings.positionUsing === 'translate3d') { + barCSS = { transform: 'translate3d('+toBarPerc(n)+'%,0,0)' }; + } else if (Settings.positionUsing === 'translate') { + barCSS = { transform: 'translate('+toBarPerc(n)+'%,0)' }; + } else { + barCSS = { 'margin-left': toBarPerc(n)+'%' }; + } + + barCSS.transition = 'all '+speed+'ms '+ease; + + return barCSS; + } + + /** + * (Internal) Queues a function to be executed. + */ + + var queue = (function() { + var pending = []; + + function next() { + var fn = pending.shift(); + if (fn) { + fn(next); + } + } + + return function(fn) { + pending.push(fn); + if (pending.length == 1) next(); + }; + })(); + + /** + * (Internal) Applies css properties to an element, similar to the jQuery + * css method. + * + * While this helper does assist with vendor prefixed property names, it + * does not perform any manipulation of values prior to setting styles. + */ + + var css = (function() { + var cssPrefixes = [ 'Webkit', 'O', 'Moz', 'ms' ], + cssProps = {}; + + function camelCase(string) { + return string.replace(/^-ms-/, 'ms-').replace(/-([\da-z])/gi, function(match, letter) { + return letter.toUpperCase(); + }); + } + + function getVendorProp(name) { + var style = document.body.style; + if (name in style) return name; + + var i = cssPrefixes.length, + capName = name.charAt(0).toUpperCase() + name.slice(1), + vendorName; + while (i--) { + vendorName = cssPrefixes[i] + capName; + if (vendorName in style) return vendorName; + } + + return name; + } + + function getStyleProp(name) { + name = camelCase(name); + return cssProps[name] || (cssProps[name] = getVendorProp(name)); + } + + function applyCss(element, prop, value) { + prop = getStyleProp(prop); + element.style[prop] = value; + } + + return function(element, properties) { + var args = arguments, + prop, + value; + + if (args.length == 2) { + for (prop in properties) { + value = properties[prop]; + if (value !== undefined && properties.hasOwnProperty(prop)) applyCss(element, prop, value); + } + } else { + applyCss(element, args[1], args[2]); + } + } + })(); + + /** + * (Internal) Determines if an element or space separated list of class names contains a class name. + */ + + function hasClass(element, name) { + var list = typeof element == 'string' ? element : classList(element); + return list.indexOf(' ' + name + ' ') >= 0; + } + + /** + * (Internal) Adds a class to an element. + */ + + function addClass(element, name) { + var oldList = classList(element), + newList = oldList + name; + + if (hasClass(oldList, name)) return; + + // Trim the opening space. + element.className = newList.substring(1); + } + + /** + * (Internal) Removes a class from an element. + */ + + function removeClass(element, name) { + var oldList = classList(element), + newList; + + if (!hasClass(element, name)) return; + + // Replace the class name. + newList = oldList.replace(' ' + name + ' ', ' '); + + // Trim the opening and closing spaces. + element.className = newList.substring(1, newList.length - 1); + } + + /** + * (Internal) Gets a space separated list of the class names on the element. + * The list is wrapped with a single space on each end to facilitate finding + * matches within the list. + */ + + function classList(element) { + return (' ' + (element.className || '') + ' ').replace(/\s+/gi, ' '); + } + + /** + * (Internal) Removes an element from the DOM. + */ + + function removeElement(element) { + element && element.parentNode && element.parentNode.removeChild(element); + } + + return NProgress; +}); + diff --git a/debian/missing-sources/prism.js b/debian/missing-sources/prism.js new file mode 100644 index 0000000..6d727da --- /dev/null +++ b/debian/missing-sources/prism.js @@ -0,0 +1,1137 @@ + +/* ********************************************** + Begin prism-core.js +********************************************** */ + +var _self = (typeof window !== 'undefined') + ? window // if in browser + : ( + (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) + ? self // if in worker + : {} // if in node js + ); + +/** + * Prism: Lightweight, robust, elegant syntax highlighting + * MIT license http://www.opensource.org/licenses/mit-license.php/ + * @author Lea Verou http://lea.verou.me + */ + +var Prism = (function (_self){ + +// Private helper vars +var lang = /\blang(?:uage)?-([\w-]+)\b/i; +var uniqueId = 0; + + +var _ = { + manual: _self.Prism && _self.Prism.manual, + disableWorkerMessageHandler: _self.Prism && _self.Prism.disableWorkerMessageHandler, + util: { + encode: function encode(tokens) { + if (tokens instanceof Token) { + return new Token(tokens.type, encode(tokens.content), tokens.alias); + } else if (Array.isArray(tokens)) { + return tokens.map(encode); + } else { + return tokens.replace(/&/g, '&').replace(/' + env.content + ''; +}; + +/** + * @param {string} text + * @param {LinkedList} tokenList + * @param {any} grammar + * @param {LinkedListNode} startNode + * @param {number} startPos + * @param {boolean} [oneshot=false] + * @param {string} [target] + */ +function matchGrammar(text, tokenList, grammar, startNode, startPos, oneshot, target) { + for (var token in grammar) { + if (!grammar.hasOwnProperty(token) || !grammar[token]) { + continue; + } + + var patterns = grammar[token]; + patterns = Array.isArray(patterns) ? patterns : [patterns]; + + for (var j = 0; j < patterns.length; ++j) { + if (target && target == token + ',' + j) { + return; + } + + var pattern = patterns[j], + inside = pattern.inside, + lookbehind = !!pattern.lookbehind, + greedy = !!pattern.greedy, + lookbehindLength = 0, + alias = pattern.alias; + + if (greedy && !pattern.pattern.global) { + // Without the global flag, lastIndex won't work + var flags = pattern.pattern.toString().match(/[imsuy]*$/)[0]; + pattern.pattern = RegExp(pattern.pattern.source, flags + 'g'); + } + + pattern = pattern.pattern || pattern; + + for ( // iterate the token list and keep track of the current token/string position + var currentNode = startNode.next, pos = startPos; + currentNode !== tokenList.tail; + pos += currentNode.value.length, currentNode = currentNode.next + ) { + + var str = currentNode.value; + + if (tokenList.length > text.length) { + // Something went terribly wrong, ABORT, ABORT! + return; + } + + if (str instanceof Token) { + continue; + } + + var removeCount = 1; // this is the to parameter of removeBetween + + if (greedy && currentNode != tokenList.tail.prev) { + pattern.lastIndex = pos; + var match = pattern.exec(text); + if (!match) { + break; + } + + var from = match.index + (lookbehind && match[1] ? match[1].length : 0); + var to = match.index + match[0].length; + var p = pos; + + // find the node that contains the match + p += currentNode.value.length; + while (from >= p) { + currentNode = currentNode.next; + p += currentNode.value.length; + } + // adjust pos (and p) + p -= currentNode.value.length; + pos = p; + + // the current node is a Token, then the match starts inside another Token, which is invalid + if (currentNode.value instanceof Token) { + continue; + } + + // find the last node which is affected by this match + for ( + var k = currentNode; + k !== tokenList.tail && (p < to || (typeof k.value === 'string' && !k.prev.value.greedy)); + k = k.next + ) { + removeCount++; + p += k.value.length; + } + removeCount--; + + // replace with the new match + str = text.slice(pos, p); + match.index -= pos; + } else { + pattern.lastIndex = 0; + + var match = pattern.exec(str); + } + + if (!match) { + if (oneshot) { + break; + } + + continue; + } + + if (lookbehind) { + lookbehindLength = match[1] ? match[1].length : 0; + } + + var from = match.index + lookbehindLength, + match = match[0].slice(lookbehindLength), + to = from + match.length, + before = str.slice(0, from), + after = str.slice(to); + + var removeFrom = currentNode.prev; + + if (before) { + removeFrom = addAfter(tokenList, removeFrom, before); + pos += before.length; + } + + removeRange(tokenList, removeFrom, removeCount); + + var wrapped = new Token(token, inside ? _.tokenize(match, inside) : match, alias, match, greedy); + currentNode = addAfter(tokenList, removeFrom, wrapped); + + if (after) { + addAfter(tokenList, currentNode, after); + } + + + if (removeCount > 1) + matchGrammar(text, tokenList, grammar, currentNode.prev, pos, true, token + ',' + j); + + if (oneshot) + break; + } + } + } +} + +/** + * @typedef LinkedListNode + * @property {T} value + * @property {LinkedListNode | null} prev The previous node. + * @property {LinkedListNode | null} next The next node. + * @template T + */ + +/** + * @template T + */ +function LinkedList() { + /** @type {LinkedListNode} */ + var head = { value: null, prev: null, next: null }; + /** @type {LinkedListNode} */ + var tail = { value: null, prev: head, next: null }; + head.next = tail; + + /** @type {LinkedListNode} */ + this.head = head; + /** @type {LinkedListNode} */ + this.tail = tail; + this.length = 0; +} + +/** + * Adds a new node with the given value to the list. + * @param {LinkedList} list + * @param {LinkedListNode} node + * @param {T} value + * @returns {LinkedListNode} The added node. + * @template T + */ +function addAfter(list, node, value) { + // assumes that node != list.tail && values.length >= 0 + var next = node.next; + + var newNode = { value: value, prev: node, next: next }; + node.next = newNode; + next.prev = newNode; + list.length++; + + return newNode; +} +/** + * Removes `count` nodes after the given node. The given node will not be removed. + * @param {LinkedList} list + * @param {LinkedListNode} node + * @param {number} count + * @template T + */ +function removeRange(list, node, count) { + var next = node.next; + for (var i = 0; i < count && next !== list.tail; i++) { + next = next.next; + } + node.next = next; + next.prev = node; + list.length -= i; +} +/** + * @param {LinkedList} list + * @returns {T[]} + * @template T + */ +function toArray(list) { + var array = []; + var node = list.head.next; + while (node !== list.tail) { + array.push(node.value); + node = node.next; + } + return array; +} + + +if (!_self.document) { + if (!_self.addEventListener) { + // in Node.js + return _; + } + + if (!_.disableWorkerMessageHandler) { + // In worker + _self.addEventListener('message', function (evt) { + var message = JSON.parse(evt.data), + lang = message.language, + code = message.code, + immediateClose = message.immediateClose; + + _self.postMessage(_.highlight(code, _.languages[lang], lang)); + if (immediateClose) { + _self.close(); + } + }, false); + } + + return _; +} + +//Get current script and highlight +var script = _.util.currentScript(); + +if (script) { + _.filename = script.src; + + if (script.hasAttribute('data-manual')) { + _.manual = true; + } +} + +function highlightAutomaticallyCallback() { + if (!_.manual) { + _.highlightAll(); + } +} + +if (!_.manual) { + // If the document state is "loading", then we'll use DOMContentLoaded. + // If the document state is "interactive" and the prism.js script is deferred, then we'll also use the + // DOMContentLoaded event because there might be some plugins or languages which have also been deferred and they + // might take longer one animation frame to execute which can create a race condition where only some plugins have + // been loaded when Prism.highlightAll() is executed, depending on how fast resources are loaded. + // See https://github.com/PrismJS/prism/issues/2102 + var readyState = document.readyState; + if (readyState === 'loading' || readyState === 'interactive' && script && script.defer) { + document.addEventListener('DOMContentLoaded', highlightAutomaticallyCallback); + } else { + if (window.requestAnimationFrame) { + window.requestAnimationFrame(highlightAutomaticallyCallback); + } else { + window.setTimeout(highlightAutomaticallyCallback, 16); + } + } +} + +return _; + +})(_self); + +if (typeof module !== 'undefined' && module.exports) { + module.exports = Prism; +} + +// hack for components to work correctly in node.js +if (typeof global !== 'undefined') { + global.Prism = Prism; +} + + +/* ********************************************** + Begin prism-markup.js +********************************************** */ + +Prism.languages.markup = { + 'comment': //, + 'prolog': /<\?[\s\S]+?\?>/, + 'doctype': { + pattern: /"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:(?!)*\]\s*)?>/i, + greedy: true + }, + 'cdata': //i, + 'tag': { + pattern: /<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/i, + greedy: true, + inside: { + 'tag': { + pattern: /^<\/?[^\s>\/]+/i, + inside: { + 'punctuation': /^<\/?/, + 'namespace': /^[^\s>\/:]+:/ + } + }, + 'attr-value': { + pattern: /=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/i, + inside: { + 'punctuation': [ + /^=/, + { + pattern: /^(\s*)["']|["']$/, + lookbehind: true + } + ] + } + }, + 'punctuation': /\/?>/, + 'attr-name': { + pattern: /[^\s>\/]+/, + inside: { + 'namespace': /^[^\s>\/:]+:/ + } + } + + } + }, + 'entity': /&#?[\da-z]{1,8};/i +}; + +Prism.languages.markup['tag'].inside['attr-value'].inside['entity'] = + Prism.languages.markup['entity']; + +// Plugin to make entity title show the real entity, idea by Roman Komarov +Prism.hooks.add('wrap', function(env) { + + if (env.type === 'entity') { + env.attributes['title'] = env.content.replace(/&/, '&'); + } +}); + +Object.defineProperty(Prism.languages.markup.tag, 'addInlined', { + /** + * Adds an inlined language to markup. + * + * An example of an inlined language is CSS with `