Created
August 8, 2018 16:35
-
-
Save nstrayer/d94387ac7c9137fa804da8a4e5fb83a9 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8" /> | |
<script>(function() { | |
// If window.HTMLWidgets is already defined, then use it; otherwise create a | |
// new object. This allows preceding code to set options that affect the | |
// initialization process (though none currently exist). | |
window.HTMLWidgets = window.HTMLWidgets || {}; | |
// See if we're running in a viewer pane. If not, we're in a web browser. | |
var viewerMode = window.HTMLWidgets.viewerMode = | |
/\bviewer_pane=1\b/.test(window.location); | |
// See if we're running in Shiny mode. If not, it's a static document. | |
// Note that static widgets can appear in both Shiny and static modes, but | |
// obviously, Shiny widgets can only appear in Shiny apps/documents. | |
var shinyMode = window.HTMLWidgets.shinyMode = | |
typeof(window.Shiny) !== "undefined" && !!window.Shiny.outputBindings; | |
// We can't count on jQuery being available, so we implement our own | |
// version if necessary. | |
function querySelectorAll(scope, selector) { | |
if (typeof(jQuery) !== "undefined" && scope instanceof jQuery) { | |
return scope.find(selector); | |
} | |
if (scope.querySelectorAll) { | |
return scope.querySelectorAll(selector); | |
} | |
} | |
function asArray(value) { | |
if (value === null) | |
return []; | |
if ($.isArray(value)) | |
return value; | |
return [value]; | |
} | |
// Implement jQuery's extend | |
function extend(target /*, ... */) { | |
if (arguments.length == 1) { | |
return target; | |
} | |
for (var i = 1; i < arguments.length; i++) { | |
var source = arguments[i]; | |
for (var prop in source) { | |
if (source.hasOwnProperty(prop)) { | |
target[prop] = source[prop]; | |
} | |
} | |
} | |
return target; | |
} | |
// IE8 doesn't support Array.forEach. | |
function forEach(values, callback, thisArg) { | |
if (values.forEach) { | |
values.forEach(callback, thisArg); | |
} else { | |
for (var i = 0; i < values.length; i++) { | |
callback.call(thisArg, values[i], i, values); | |
} | |
} | |
} | |
// Replaces the specified method with the return value of funcSource. | |
// | |
// Note that funcSource should not BE the new method, it should be a function | |
// that RETURNS the new method. funcSource receives a single argument that is | |
// the overridden method, it can be called from the new method. The overridden | |
// method can be called like a regular function, it has the target permanently | |
// bound to it so "this" will work correctly. | |
function overrideMethod(target, methodName, funcSource) { | |
var superFunc = target[methodName] || function() {}; | |
var superFuncBound = function() { | |
return superFunc.apply(target, arguments); | |
}; | |
target[methodName] = funcSource(superFuncBound); | |
} | |
// Add a method to delegator that, when invoked, calls | |
// delegatee.methodName. If there is no such method on | |
// the delegatee, but there was one on delegator before | |
// delegateMethod was called, then the original version | |
// is invoked instead. | |
// For example: | |
// | |
// var a = { | |
// method1: function() { console.log('a1'); } | |
// method2: function() { console.log('a2'); } | |
// }; | |
// var b = { | |
// method1: function() { console.log('b1'); } | |
// }; | |
// delegateMethod(a, b, "method1"); | |
// delegateMethod(a, b, "method2"); | |
// a.method1(); | |
// a.method2(); | |
// | |
// The output would be "b1", "a2". | |
function delegateMethod(delegator, delegatee, methodName) { | |
var inherited = delegator[methodName]; | |
delegator[methodName] = function() { | |
var target = delegatee; | |
var method = delegatee[methodName]; | |
// The method doesn't exist on the delegatee. Instead, | |
// call the method on the delegator, if it exists. | |
if (!method) { | |
target = delegator; | |
method = inherited; | |
} | |
if (method) { | |
return method.apply(target, arguments); | |
} | |
}; | |
} | |
// Implement a vague facsimilie of jQuery's data method | |
function elementData(el, name, value) { | |
if (arguments.length == 2) { | |
return el["htmlwidget_data_" + name]; | |
} else if (arguments.length == 3) { | |
el["htmlwidget_data_" + name] = value; | |
return el; | |
} else { | |
throw new Error("Wrong number of arguments for elementData: " + | |
arguments.length); | |
} | |
} | |
// http://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex | |
function escapeRegExp(str) { | |
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); | |
} | |
function hasClass(el, className) { | |
var re = new RegExp("\\b" + escapeRegExp(className) + "\\b"); | |
return re.test(el.className); | |
} | |
// elements - array (or array-like object) of HTML elements | |
// className - class name to test for | |
// include - if true, only return elements with given className; | |
// if false, only return elements *without* given className | |
function filterByClass(elements, className, include) { | |
var results = []; | |
for (var i = 0; i < elements.length; i++) { | |
if (hasClass(elements[i], className) == include) | |
results.push(elements[i]); | |
} | |
return results; | |
} | |
function on(obj, eventName, func) { | |
if (obj.addEventListener) { | |
obj.addEventListener(eventName, func, false); | |
} else if (obj.attachEvent) { | |
obj.attachEvent(eventName, func); | |
} | |
} | |
function off(obj, eventName, func) { | |
if (obj.removeEventListener) | |
obj.removeEventListener(eventName, func, false); | |
else if (obj.detachEvent) { | |
obj.detachEvent(eventName, func); | |
} | |
} | |
// Translate array of values to top/right/bottom/left, as usual with | |
// the "padding" CSS property | |
// https://developer.mozilla.org/en-US/docs/Web/CSS/padding | |
function unpackPadding(value) { | |
if (typeof(value) === "number") | |
value = [value]; | |
if (value.length === 1) { | |
return {top: value[0], right: value[0], bottom: value[0], left: value[0]}; | |
} | |
if (value.length === 2) { | |
return {top: value[0], right: value[1], bottom: value[0], left: value[1]}; | |
} | |
if (value.length === 3) { | |
return {top: value[0], right: value[1], bottom: value[2], left: value[1]}; | |
} | |
if (value.length === 4) { | |
return {top: value[0], right: value[1], bottom: value[2], left: value[3]}; | |
} | |
} | |
// Convert an unpacked padding object to a CSS value | |
function paddingToCss(paddingObj) { | |
return paddingObj.top + "px " + paddingObj.right + "px " + paddingObj.bottom + "px " + paddingObj.left + "px"; | |
} | |
// Makes a number suitable for CSS | |
function px(x) { | |
if (typeof(x) === "number") | |
return x + "px"; | |
else | |
return x; | |
} | |
// Retrieves runtime widget sizing information for an element. | |
// The return value is either null, or an object with fill, padding, | |
// defaultWidth, defaultHeight fields. | |
function sizingPolicy(el) { | |
var sizingEl = document.querySelector("script[data-for='" + el.id + "'][type='application/htmlwidget-sizing']"); | |
if (!sizingEl) | |
return null; | |
var sp = JSON.parse(sizingEl.textContent || sizingEl.text || "{}"); | |
if (viewerMode) { | |
return sp.viewer; | |
} else { | |
return sp.browser; | |
} | |
} | |
// @param tasks Array of strings (or falsy value, in which case no-op). | |
// Each element must be a valid JavaScript expression that yields a | |
// function. Or, can be an array of objects with "code" and "data" | |
// properties; in this case, the "code" property should be a string | |
// of JS that's an expr that yields a function, and "data" should be | |
// an object that will be added as an additional argument when that | |
// function is called. | |
// @param target The object that will be "this" for each function | |
// execution. | |
// @param args Array of arguments to be passed to the functions. (The | |
// same arguments will be passed to all functions.) | |
function evalAndRun(tasks, target, args) { | |
if (tasks) { | |
forEach(tasks, function(task) { | |
var theseArgs = args; | |
if (typeof(task) === "object") { | |
theseArgs = theseArgs.concat([task.data]); | |
task = task.code; | |
} | |
var taskFunc = eval("(" + task + ")"); | |
if (typeof(taskFunc) !== "function") { | |
throw new Error("Task must be a function! Source:\n" + task); | |
} | |
taskFunc.apply(target, theseArgs); | |
}); | |
} | |
} | |
function initSizing(el) { | |
var sizing = sizingPolicy(el); | |
if (!sizing) | |
return; | |
var cel = document.getElementById("htmlwidget_container"); | |
if (!cel) | |
return; | |
if (typeof(sizing.padding) !== "undefined") { | |
document.body.style.margin = "0"; | |
document.body.style.padding = paddingToCss(unpackPadding(sizing.padding)); | |
} | |
if (sizing.fill) { | |
document.body.style.overflow = "hidden"; | |
document.body.style.width = "100%"; | |
document.body.style.height = "100%"; | |
document.documentElement.style.width = "100%"; | |
document.documentElement.style.height = "100%"; | |
if (cel) { | |
cel.style.position = "absolute"; | |
var pad = unpackPadding(sizing.padding); | |
cel.style.top = pad.top + "px"; | |
cel.style.right = pad.right + "px"; | |
cel.style.bottom = pad.bottom + "px"; | |
cel.style.left = pad.left + "px"; | |
el.style.width = "100%"; | |
el.style.height = "100%"; | |
} | |
return { | |
getWidth: function() { return cel.offsetWidth; }, | |
getHeight: function() { return cel.offsetHeight; } | |
}; | |
} else { | |
el.style.width = px(sizing.width); | |
el.style.height = px(sizing.height); | |
return { | |
getWidth: function() { return el.offsetWidth; }, | |
getHeight: function() { return el.offsetHeight; } | |
}; | |
} | |
} | |
// Default implementations for methods | |
var defaults = { | |
find: function(scope) { | |
return querySelectorAll(scope, "." + this.name); | |
}, | |
renderError: function(el, err) { | |
var $el = $(el); | |
this.clearError(el); | |
// Add all these error classes, as Shiny does | |
var errClass = "shiny-output-error"; | |
if (err.type !== null) { | |
// use the classes of the error condition as CSS class names | |
errClass = errClass + " " + $.map(asArray(err.type), function(type) { | |
return errClass + "-" + type; | |
}).join(" "); | |
} | |
errClass = errClass + " htmlwidgets-error"; | |
// Is el inline or block? If inline or inline-block, just display:none it | |
// and add an inline error. | |
var display = $el.css("display"); | |
$el.data("restore-display-mode", display); | |
if (display === "inline" || display === "inline-block") { | |
$el.hide(); | |
if (err.message !== "") { | |
var errorSpan = $("<span>").addClass(errClass); | |
errorSpan.text(err.message); | |
$el.after(errorSpan); | |
} | |
} else if (display === "block") { | |
// If block, add an error just after the el, set visibility:none on the | |
// el, and position the error to be on top of the el. | |
// Mark it with a unique ID and CSS class so we can remove it later. | |
$el.css("visibility", "hidden"); | |
if (err.message !== "") { | |
var errorDiv = $("<div>").addClass(errClass).css("position", "absolute") | |
.css("top", el.offsetTop) | |
.css("left", el.offsetLeft) | |
// setting width can push out the page size, forcing otherwise | |
// unnecessary scrollbars to appear and making it impossible for | |
// the element to shrink; so use max-width instead | |
.css("maxWidth", el.offsetWidth) | |
.css("height", el.offsetHeight); | |
errorDiv.text(err.message); | |
$el.after(errorDiv); | |
// Really dumb way to keep the size/position of the error in sync with | |
// the parent element as the window is resized or whatever. | |
var intId = setInterval(function() { | |
if (!errorDiv[0].parentElement) { | |
clearInterval(intId); | |
return; | |
} | |
errorDiv | |
.css("top", el.offsetTop) | |
.css("left", el.offsetLeft) | |
.css("maxWidth", el.offsetWidth) | |
.css("height", el.offsetHeight); | |
}, 500); | |
} | |
} | |
}, | |
clearError: function(el) { | |
var $el = $(el); | |
var display = $el.data("restore-display-mode"); | |
$el.data("restore-display-mode", null); | |
if (display === "inline" || display === "inline-block") { | |
if (display) | |
$el.css("display", display); | |
$(el.nextSibling).filter(".htmlwidgets-error").remove(); | |
} else if (display === "block"){ | |
$el.css("visibility", "inherit"); | |
$(el.nextSibling).filter(".htmlwidgets-error").remove(); | |
} | |
}, | |
sizing: {} | |
}; | |
// Called by widget bindings to register a new type of widget. The definition | |
// object can contain the following properties: | |
// - name (required) - A string indicating the binding name, which will be | |
// used by default as the CSS classname to look for. | |
// - initialize (optional) - A function(el) that will be called once per | |
// widget element; if a value is returned, it will be passed as the third | |
// value to renderValue. | |
// - renderValue (required) - A function(el, data, initValue) that will be | |
// called with data. Static contexts will cause this to be called once per | |
// element; Shiny apps will cause this to be called multiple times per | |
// element, as the data changes. | |
window.HTMLWidgets.widget = function(definition) { | |
if (!definition.name) { | |
throw new Error("Widget must have a name"); | |
} | |
if (!definition.type) { | |
throw new Error("Widget must have a type"); | |
} | |
// Currently we only support output widgets | |
if (definition.type !== "output") { | |
throw new Error("Unrecognized widget type '" + definition.type + "'"); | |
} | |
// TODO: Verify that .name is a valid CSS classname | |
// Support new-style instance-bound definitions. Old-style class-bound | |
// definitions have one widget "object" per widget per type/class of | |
// widget; the renderValue and resize methods on such widget objects | |
// take el and instance arguments, because the widget object can't | |
// store them. New-style instance-bound definitions have one widget | |
// object per widget instance; the definition that's passed in doesn't | |
// provide renderValue or resize methods at all, just the single method | |
// factory(el, width, height) | |
// which returns an object that has renderValue(x) and resize(w, h). | |
// This enables a far more natural programming style for the widget | |
// author, who can store per-instance state using either OO-style | |
// instance fields or functional-style closure variables (I guess this | |
// is in contrast to what can only be called C-style pseudo-OO which is | |
// what we required before). | |
if (definition.factory) { | |
definition = createLegacyDefinitionAdapter(definition); | |
} | |
if (!definition.renderValue) { | |
throw new Error("Widget must have a renderValue function"); | |
} | |
// For static rendering (non-Shiny), use a simple widget registration | |
// scheme. We also use this scheme for Shiny apps/documents that also | |
// contain static widgets. | |
window.HTMLWidgets.widgets = window.HTMLWidgets.widgets || []; | |
// Merge defaults into the definition; don't mutate the original definition. | |
var staticBinding = extend({}, defaults, definition); | |
overrideMethod(staticBinding, "find", function(superfunc) { | |
return function(scope) { | |
var results = superfunc(scope); | |
// Filter out Shiny outputs, we only want the static kind | |
return filterByClass(results, "html-widget-output", false); | |
}; | |
}); | |
window.HTMLWidgets.widgets.push(staticBinding); | |
if (shinyMode) { | |
// Shiny is running. Register the definition with an output binding. | |
// The definition itself will not be the output binding, instead | |
// we will make an output binding object that delegates to the | |
// definition. This is because we foolishly used the same method | |
// name (renderValue) for htmlwidgets definition and Shiny bindings | |
// but they actually have quite different semantics (the Shiny | |
// bindings receive data that includes lots of metadata that it | |
// strips off before calling htmlwidgets renderValue). We can't | |
// just ignore the difference because in some widgets it's helpful | |
// to call this.renderValue() from inside of resize(), and if | |
// we're not delegating, then that call will go to the Shiny | |
// version instead of the htmlwidgets version. | |
// Merge defaults with definition, without mutating either. | |
var bindingDef = extend({}, defaults, definition); | |
// This object will be our actual Shiny binding. | |
var shinyBinding = new Shiny.OutputBinding(); | |
// With a few exceptions, we'll want to simply use the bindingDef's | |
// version of methods if they are available, otherwise fall back to | |
// Shiny's defaults. NOTE: If Shiny's output bindings gain additional | |
// methods in the future, and we want them to be overrideable by | |
// HTMLWidget binding definitions, then we'll need to add them to this | |
// list. | |
delegateMethod(shinyBinding, bindingDef, "getId"); | |
delegateMethod(shinyBinding, bindingDef, "onValueChange"); | |
delegateMethod(shinyBinding, bindingDef, "onValueError"); | |
delegateMethod(shinyBinding, bindingDef, "renderError"); | |
delegateMethod(shinyBinding, bindingDef, "clearError"); | |
delegateMethod(shinyBinding, bindingDef, "showProgress"); | |
// The find, renderValue, and resize are handled differently, because we | |
// want to actually decorate the behavior of the bindingDef methods. | |
shinyBinding.find = function(scope) { | |
var results = bindingDef.find(scope); | |
// Only return elements that are Shiny outputs, not static ones | |
var dynamicResults = results.filter(".html-widget-output"); | |
// It's possible that whatever caused Shiny to think there might be | |
// new dynamic outputs, also caused there to be new static outputs. | |
// Since there might be lots of different htmlwidgets bindings, we | |
// schedule execution for later--no need to staticRender multiple | |
// times. | |
if (results.length !== dynamicResults.length) | |
scheduleStaticRender(); | |
return dynamicResults; | |
}; | |
// Wrap renderValue to handle initialization, which unfortunately isn't | |
// supported natively by Shiny at the time of this writing. | |
shinyBinding.renderValue = function(el, data) { | |
Shiny.renderDependencies(data.deps); | |
// Resolve strings marked as javascript literals to objects | |
if (!(data.evals instanceof Array)) data.evals = [data.evals]; | |
for (var i = 0; data.evals && i < data.evals.length; i++) { | |
window.HTMLWidgets.evaluateStringMember(data.x, data.evals[i]); | |
} | |
if (!bindingDef.renderOnNullValue) { | |
if (data.x === null) { | |
el.style.visibility = "hidden"; | |
return; | |
} else { | |
el.style.visibility = "inherit"; | |
} | |
} | |
if (!elementData(el, "initialized")) { | |
initSizing(el); | |
elementData(el, "initialized", true); | |
if (bindingDef.initialize) { | |
var result = bindingDef.initialize(el, el.offsetWidth, | |
el.offsetHeight); | |
elementData(el, "init_result", result); | |
} | |
} | |
bindingDef.renderValue(el, data.x, elementData(el, "init_result")); | |
evalAndRun(data.jsHooks.render, elementData(el, "init_result"), [el, data.x]); | |
}; | |
// Only override resize if bindingDef implements it | |
if (bindingDef.resize) { | |
shinyBinding.resize = function(el, width, height) { | |
// Shiny can call resize before initialize/renderValue have been | |
// called, which doesn't make sense for widgets. | |
if (elementData(el, "initialized")) { | |
bindingDef.resize(el, width, height, elementData(el, "init_result")); | |
} | |
}; | |
} | |
Shiny.outputBindings.register(shinyBinding, bindingDef.name); | |
} | |
}; | |
var scheduleStaticRenderTimerId = null; | |
function scheduleStaticRender() { | |
if (!scheduleStaticRenderTimerId) { | |
scheduleStaticRenderTimerId = setTimeout(function() { | |
scheduleStaticRenderTimerId = null; | |
window.HTMLWidgets.staticRender(); | |
}, 1); | |
} | |
} | |
// Render static widgets after the document finishes loading | |
// Statically render all elements that are of this widget's class | |
window.HTMLWidgets.staticRender = function() { | |
var bindings = window.HTMLWidgets.widgets || []; | |
forEach(bindings, function(binding) { | |
var matches = binding.find(document.documentElement); | |
forEach(matches, function(el) { | |
var sizeObj = initSizing(el, binding); | |
if (hasClass(el, "html-widget-static-bound")) | |
return; | |
el.className = el.className + " html-widget-static-bound"; | |
var initResult; | |
if (binding.initialize) { | |
initResult = binding.initialize(el, | |
sizeObj ? sizeObj.getWidth() : el.offsetWidth, | |
sizeObj ? sizeObj.getHeight() : el.offsetHeight | |
); | |
elementData(el, "init_result", initResult); | |
} | |
if (binding.resize) { | |
var lastSize = {}; | |
var resizeHandler = function(e) { | |
var size = { | |
w: sizeObj ? sizeObj.getWidth() : el.offsetWidth, | |
h: sizeObj ? sizeObj.getHeight() : el.offsetHeight | |
}; | |
if (size.w === 0 && size.h === 0) | |
return; | |
if (size.w === lastSize.w && size.h === lastSize.h) | |
return; | |
lastSize = size; | |
binding.resize(el, size.w, size.h, initResult); | |
}; | |
on(window, "resize", resizeHandler); | |
// This is needed for cases where we're running in a Shiny | |
// app, but the widget itself is not a Shiny output, but | |
// rather a simple static widget. One example of this is | |
// an rmarkdown document that has runtime:shiny and widget | |
// that isn't in a render function. Shiny only knows to | |
// call resize handlers for Shiny outputs, not for static | |
// widgets, so we do it ourselves. | |
if (window.jQuery) { | |
window.jQuery(document).on( | |
"shown.htmlwidgets shown.bs.tab.htmlwidgets shown.bs.collapse.htmlwidgets", | |
resizeHandler | |
); | |
window.jQuery(document).on( | |
"hidden.htmlwidgets hidden.bs.tab.htmlwidgets hidden.bs.collapse.htmlwidgets", | |
resizeHandler | |
); | |
} | |
// This is needed for the specific case of ioslides, which | |
// flips slides between display:none and display:block. | |
// Ideally we would not have to have ioslide-specific code | |
// here, but rather have ioslides raise a generic event, | |
// but the rmarkdown package just went to CRAN so the | |
// window to getting that fixed may be long. | |
if (window.addEventListener) { | |
// It's OK to limit this to window.addEventListener | |
// browsers because ioslides itself only supports | |
// such browsers. | |
on(document, "slideenter", resizeHandler); | |
on(document, "slideleave", resizeHandler); | |
} | |
} | |
var scriptData = document.querySelector("script[data-for='" + el.id + "'][type='application/json']"); | |
if (scriptData) { | |
var data = JSON.parse(scriptData.textContent || scriptData.text); | |
// Resolve strings marked as javascript literals to objects | |
if (!(data.evals instanceof Array)) data.evals = [data.evals]; | |
for (var k = 0; data.evals && k < data.evals.length; k++) { | |
window.HTMLWidgets.evaluateStringMember(data.x, data.evals[k]); | |
} | |
binding.renderValue(el, data.x, initResult); | |
evalAndRun(data.jsHooks.render, initResult, [el, data.x]); | |
} | |
}); | |
}); | |
invokePostRenderHandlers(); | |
} | |
// Wait until after the document has loaded to render the widgets. | |
if (document.addEventListener) { | |
document.addEventListener("DOMContentLoaded", function() { | |
document.removeEventListener("DOMContentLoaded", arguments.callee, false); | |
window.HTMLWidgets.staticRender(); | |
}, false); | |
} else if (document.attachEvent) { | |
document.attachEvent("onreadystatechange", function() { | |
if (document.readyState === "complete") { | |
document.detachEvent("onreadystatechange", arguments.callee); | |
window.HTMLWidgets.staticRender(); | |
} | |
}); | |
} | |
window.HTMLWidgets.getAttachmentUrl = function(depname, key) { | |
// If no key, default to the first item | |
if (typeof(key) === "undefined") | |
key = 1; | |
var link = document.getElementById(depname + "-" + key + "-attachment"); | |
if (!link) { | |
throw new Error("Attachment " + depname + "/" + key + " not found in document"); | |
} | |
return link.getAttribute("href"); | |
}; | |
window.HTMLWidgets.dataframeToD3 = function(df) { | |
var names = []; | |
var length; | |
for (var name in df) { | |
if (df.hasOwnProperty(name)) | |
names.push(name); | |
if (typeof(df[name]) !== "object" || typeof(df[name].length) === "undefined") { | |
throw new Error("All fields must be arrays"); | |
} else if (typeof(length) !== "undefined" && length !== df[name].length) { | |
throw new Error("All fields must be arrays of the same length"); | |
} | |
length = df[name].length; | |
} | |
var results = []; | |
var item; | |
for (var row = 0; row < length; row++) { | |
item = {}; | |
for (var col = 0; col < names.length; col++) { | |
item[names[col]] = df[names[col]][row]; | |
} | |
results.push(item); | |
} | |
return results; | |
}; | |
window.HTMLWidgets.transposeArray2D = function(array) { | |
if (array.length === 0) return array; | |
var newArray = array[0].map(function(col, i) { | |
return array.map(function(row) { | |
return row[i] | |
}) | |
}); | |
return newArray; | |
}; | |
// Split value at splitChar, but allow splitChar to be escaped | |
// using escapeChar. Any other characters escaped by escapeChar | |
// will be included as usual (including escapeChar itself). | |
function splitWithEscape(value, splitChar, escapeChar) { | |
var results = []; | |
var escapeMode = false; | |
var currentResult = ""; | |
for (var pos = 0; pos < value.length; pos++) { | |
if (!escapeMode) { | |
if (value[pos] === splitChar) { | |
results.push(currentResult); | |
currentResult = ""; | |
} else if (value[pos] === escapeChar) { | |
escapeMode = true; | |
} else { | |
currentResult += value[pos]; | |
} | |
} else { | |
currentResult += value[pos]; | |
escapeMode = false; | |
} | |
} | |
if (currentResult !== "") { | |
results.push(currentResult); | |
} | |
return results; | |
} | |
// Function authored by Yihui/JJ Allaire | |
window.HTMLWidgets.evaluateStringMember = function(o, member) { | |
var parts = splitWithEscape(member, '.', '\\'); | |
for (var i = 0, l = parts.length; i < l; i++) { | |
var part = parts[i]; | |
// part may be a character or 'numeric' member name | |
if (o !== null && typeof o === "object" && part in o) { | |
if (i == (l - 1)) { // if we are at the end of the line then evalulate | |
if (typeof o[part] === "string") | |
o[part] = eval("(" + o[part] + ")"); | |
} else { // otherwise continue to next embedded object | |
o = o[part]; | |
} | |
} | |
} | |
}; | |
// Retrieve the HTMLWidget instance (i.e. the return value of an | |
// HTMLWidget binding's initialize() or factory() function) | |
// associated with an element, or null if none. | |
window.HTMLWidgets.getInstance = function(el) { | |
return elementData(el, "init_result"); | |
}; | |
// Finds the first element in the scope that matches the selector, | |
// and returns the HTMLWidget instance (i.e. the return value of | |
// an HTMLWidget binding's initialize() or factory() function) | |
// associated with that element, if any. If no element matches the | |
// selector, or the first matching element has no HTMLWidget | |
// instance associated with it, then null is returned. | |
// | |
// The scope argument is optional, and defaults to window.document. | |
window.HTMLWidgets.find = function(scope, selector) { | |
if (arguments.length == 1) { | |
selector = scope; | |
scope = document; | |
} | |
var el = scope.querySelector(selector); | |
if (el === null) { | |
return null; | |
} else { | |
return window.HTMLWidgets.getInstance(el); | |
} | |
}; | |
// Finds all elements in the scope that match the selector, and | |
// returns the HTMLWidget instances (i.e. the return values of | |
// an HTMLWidget binding's initialize() or factory() function) | |
// associated with the elements, in an array. If elements that | |
// match the selector don't have an associated HTMLWidget | |
// instance, the returned array will contain nulls. | |
// | |
// The scope argument is optional, and defaults to window.document. | |
window.HTMLWidgets.findAll = function(scope, selector) { | |
if (arguments.length == 1) { | |
selector = scope; | |
scope = document; | |
} | |
var nodes = scope.querySelectorAll(selector); | |
var results = []; | |
for (var i = 0; i < nodes.length; i++) { | |
results.push(window.HTMLWidgets.getInstance(nodes[i])); | |
} | |
return results; | |
}; | |
var postRenderHandlers = []; | |
function invokePostRenderHandlers() { | |
while (postRenderHandlers.length) { | |
var handler = postRenderHandlers.shift(); | |
if (handler) { | |
handler(); | |
} | |
} | |
} | |
// Register the given callback function to be invoked after the | |
// next time static widgets are rendered. | |
window.HTMLWidgets.addPostRenderHandler = function(callback) { | |
postRenderHandlers.push(callback); | |
}; | |
// Takes a new-style instance-bound definition, and returns an | |
// old-style class-bound definition. This saves us from having | |
// to rewrite all the logic in this file to accomodate both | |
// types of definitions. | |
function createLegacyDefinitionAdapter(defn) { | |
var result = { | |
name: defn.name, | |
type: defn.type, | |
initialize: function(el, width, height) { | |
return defn.factory(el, width, height); | |
}, | |
renderValue: function(el, x, instance) { | |
return instance.renderValue(x); | |
}, | |
resize: function(el, width, height, instance) { | |
return instance.resize(width, height); | |
} | |
}; | |
if (defn.find) | |
result.find = defn.find; | |
if (defn.renderError) | |
result.renderError = defn.renderError; | |
if (defn.clearError) | |
result.clearError = defn.clearError; | |
return result; | |
} | |
})(); | |
</script> | |
<script>function R2D3(el, width, height) { | |
var self = this; | |
var x = null; | |
var version = null; | |
self.data = null; | |
self.root = self.svg = self.canvas = null; | |
self.width = 0; | |
self.height = 0; | |
self.options = null; | |
self.resizer = null; | |
self.renderer = null; | |
self.rendererDefaut = true; | |
self.captureErrors = null; | |
self.theme = {}; | |
self.setX = function(newX) { | |
x = newX; | |
self.data = x.data; | |
if (x.type == "data.frame") { | |
self.data = HTMLWidgets.dataframeToD3(self.data); | |
} | |
if (x.theme) { | |
self.theme = themeCapable() ? x.theme.runtime : x.theme.default; | |
} | |
self.options = x.options; | |
}; | |
self.setContainer = function(container) { | |
self.container = container; | |
}; | |
self.setRoot = function(root) { | |
self.root = self.svg = self.canvas = root; | |
}; | |
self.createRoot = function() { | |
if (self.root !== null) { | |
self.d3().select(el).select(self.container).remove(); | |
self.root = null; | |
} | |
var root = self.d3().select(el).append(self.container) | |
.attr("width", self.width) | |
.attr("height", self.height); | |
if (self.theme.background) root.style("background", self.theme.background); | |
if (self.theme.foreground) { | |
root.style("fill", self.theme.foreground); | |
root.style("color", self.theme.foreground); | |
} | |
self.setRoot(root); | |
}; | |
self.setWidth = function(width) { | |
self.width = width; | |
}; | |
self.setHeight = function(height) { | |
self.height = height; | |
}; | |
self.onRender = function(renderer) { | |
self.renderer = renderer; | |
self.rendererDefaut = false; | |
}; | |
self.onResize = function(resizer) { | |
self.resizer = resizer; | |
}; | |
self.render = function() { | |
if (self.renderer === null) return; | |
try { | |
self.renderer(self.data, self.root, self.width, self.height, self.options); | |
} | |
catch (err) { | |
self.showError(err, null, null); | |
} | |
}; | |
self.resize = function() { | |
if (self.resizer === null) return; | |
try { | |
self.resizer(self.width, self.height); | |
} | |
catch (err) { | |
self.showError(err, null, null); | |
} | |
}; | |
var simpleHash = function(data) { | |
var step = Math.max(1, Math.floor(data.length / 1000)); | |
var hash = 0; | |
for (var idx = 0; idx < data.length; idx += step) { | |
hash = ((hash << 5) - hash) + data.charCodeAt(idx); | |
hash = hash & hash; | |
} | |
return Math.abs(hash) % 1000; | |
}; | |
self.addScript = function(script) { | |
var el = document.createElement("script"); | |
el.type = "text/javascript"; | |
var debugHeader = "//# sourceURL=r2d3-script-" + simpleHash(script) + "\n"; | |
el.text = debugHeader + script; | |
self.captureErrors = function(msg, url, lineNo, columnNo, error) { | |
self.showError({ | |
message: msg, | |
stack: null | |
}, lineNo, columnNo); | |
}; | |
document.head.appendChild(el); | |
self.captureErrors = null; | |
}; | |
self.addStyle = function(style) { | |
if (!style) return; | |
var el = document.createElement("style"); | |
el.type = "text/css"; | |
if (el.styleSheet) { | |
el.styleSheet.cssText = style; | |
} else { | |
el.appendChild(document.createTextNode(style)); | |
} | |
document.head.appendChild(el); | |
}; | |
self.setVersion = function(newVersion) { | |
version = newVersion; | |
}; | |
self.d3 = function() { | |
switch(version) { | |
case 3: | |
return d3; | |
case 4: | |
return d3v4; | |
case 5: | |
return d3v5; | |
} | |
}; | |
var consoleLog = function(data) { | |
console.log(data); | |
var entry = document.getElementById("r2d3-console-entry"); | |
if (!entry) { | |
entry = document.createElement("div"); | |
entry.id = "r2d3-console-entry"; | |
entry.style.bottom = "0"; | |
entry.style.left = "0"; | |
entry.style.right = "0"; | |
entry.style.background = "rgb(244, 248, 249)"; | |
entry.style.border = "1px solid #d6dadc"; | |
entry.style.padding = "8px 15px 8px 15px"; | |
entry.style.position = "absolute"; | |
entry.style.fontFamily = "'Lucida Sans', 'DejaVu Sans', 'Lucida Grande', 'Segoe UI', Verdana, Helvetica, sans-serif, serif"; | |
entry.style.fontSize = "9pt"; | |
el.appendChild(entry); | |
entry.style.transform = "translateY(40px)"; | |
entry.style.opacity = "0"; | |
entry.style.transition = "all 0.25s"; | |
entry.onmouseenter = function() { | |
consoleHovering = true; | |
}; | |
entry.onmouseleave = function() { | |
consoleHovering = false; | |
}; | |
setTimeout(function() { | |
entry.style.transform = "translateY(0)"; | |
entry.style.opacity = "1"; | |
entry.style.transition = "all 0.5s"; | |
}, 50); | |
} | |
entry.innerText = data; | |
clearTimeout(consoleTimeout); | |
consoleTimeout = setTimeout(function() { | |
var hideConsole = function() { | |
entry.style.transform = "translateY(-60px)"; | |
entry.style.opacity = "0"; | |
entry.addEventListener("transitionend", function(event) { | |
entry = document.getElementById("r2d3-console-entry"); | |
if (entry) el.removeChild(entry); | |
consoleHovering = false; | |
}); | |
}; | |
if (consoleHovering) { | |
entry.onmouseleave = hideConsole; | |
} | |
else { | |
hideConsole(); | |
} | |
}, 3000); | |
}; | |
var consoleTimeout = null; | |
var consoleHovering = false; | |
var createConsoleOverride = function(type) { | |
return function(data) { | |
consoleLog(type + data); | |
}; | |
}; | |
self.console = { | |
assert: console.assert, | |
clear: console.clear, | |
count: console.count, | |
error: createConsoleOverride("Error: "), | |
group: console.group, | |
groupCollapsed: console.groupCollapsed, | |
groupEnd: console.groupEnd, | |
info: createConsoleOverride("Info: "), | |
log: createConsoleOverride(""), | |
table: console.table, | |
time: console.time, | |
timeEnd: console.timeEnd, | |
trace: console.trace, | |
warn: console.warn | |
}; | |
self.callD3Script = function() { | |
var d3Script = self.d3Script; | |
try { | |
d3Script(self.d3(), self, self.data, self.root, self.width, self.height, self.options, self.theme, self.console); | |
} | |
catch (err) { | |
self.showError(err, null, null); | |
} | |
}; | |
self.widgetRender = function(x) { | |
self.setX(x); | |
self.setWidth(width); | |
self.setHeight(height); | |
if (!self.root) { | |
self.setVersion(x.version); | |
self.addScript(x.script); | |
self.addStyle(x.style); | |
self.d3Script = d3Script; | |
self.setContainer(x.container); | |
self.createRoot(); | |
self.callD3Script(); | |
} | |
self.render(); | |
if (self.renderer === null) { | |
self.renderer = function(data, container, width, height, options) { | |
self.callD3Script(); | |
}; | |
} | |
if (self.resizer === null) { | |
self.resizer = function(width, height) { | |
self.createRoot(); | |
self.callD3Script(); | |
if (!self.rendererDefaut) self.render(); | |
}; | |
} | |
}; | |
self.debounce = function(f, wait) { | |
var timeout = null; | |
return function() { | |
if (timeout) window.clearTimeout(timeout); | |
timeout = window.setTimeout(f, wait); | |
}; | |
}; | |
self.resizeDebounce = self.debounce(self.resize, 100); | |
self.widgetResize = function(width, height) { | |
self.root | |
.attr("width", width) | |
.attr("height", height); | |
self.setWidth(width); | |
self.setHeight(height); | |
self.resizeDebounce(); | |
}; | |
var openSource = function(filename, line, column, domain, highlight) { | |
if (window.parent.postMessage) { | |
window.parent.postMessage({ | |
message: "openfile", | |
source: "r2d3", | |
file: filename, | |
line: line, | |
column: column, | |
highlight: highlight | |
}, domain); | |
} | |
}; | |
var themesLoaded = false; | |
var registerTheme = function(domain) { | |
domain = domain ? domain : window.location.origin; | |
if (window.parent.postMessage) { | |
window.addEventListener('message', function(event) { | |
if (typeof event.data != 'object') | |
return; | |
if (event.origin !== domain) | |
return; | |
if (event.data.message !== "ontheme") | |
return; | |
document.body.style.background = event.data.background; | |
self.theme.background = event.data.background; | |
self.theme.foreground = event.data.foreground; | |
// resize to give script chance to pick new theme | |
if (themesLoaded) self.resize(); | |
themesLoaded = true; | |
}, false); | |
window.parent.postMessage({ | |
message: "ontheme", | |
source: "r2d3" | |
}, domain); | |
} | |
}; | |
var errorObject = null; | |
var errorLine = null; | |
var errorColumn = null; | |
var errorFile = null; | |
var errorHighlightOnce = false; | |
var hostDomain = null; | |
var queryParameter = function(param) { | |
var query = window.location.search.substring(1); | |
var entries = query.split('&'); | |
for (var idxEntry = 0; idxEntry < entries.length; idxEntry++) { | |
var params = entries[idxEntry].split('='); | |
if (decodeURIComponent(params[0]) == param) { | |
return decodeURIComponent(params[1]); | |
} | |
} | |
return null; | |
}; | |
var themeCapable = function() { | |
return queryParameter("capabilities") === "1"; | |
}; | |
var registerMessageListeners = function(event) { | |
if (!themeCapable()) { | |
hostDomain = null; | |
} else { | |
hostDomain = queryParameter("host"); | |
registerTheme(hostDomain); | |
} | |
}; | |
var cleanStackTrace = function(stack) { | |
var cleaned = stack.substr(0, stack.indexOf("at d3Script")); | |
cleaned = cleaned.replace(new RegExp("\\(.*/session/view[^/]*/lib/[^/]+/", "g"), "("); | |
cleaned = cleaned.replace(new RegExp("\\(.*/session/view[^/]*/", "g"), "("); | |
return cleaned; | |
}; | |
var parseLineFileRef = function(line) { | |
var lines = x.script.split("\n"); | |
var header = "/* R2D3 Source File: "; | |
var file = null; | |
for (var maybe = line; line && maybe >= 0; maybe--) { | |
if (lines[maybe].includes(header)) { | |
var data = lines[maybe].split(header)[1]; | |
var source = data.split("*/")[0].trim(); | |
line = line - (maybe + 2); | |
file = source; | |
break; | |
} | |
} | |
return { | |
file: file, | |
line: line | |
}; | |
}; | |
var parseCallstackRef = function(callstack) { | |
var reg = new RegExp("at [^\\n]+ \\((<anonymous>|r2d3-script-[0-9]+):([0-9]+):([0-9]+)\\)"); | |
var matches = reg.exec(callstack); | |
if (matches && matches.length === 4) { | |
var line = parseInt(matches[2]); | |
var column = parseInt(matches[3]); | |
var file = null; | |
var lineRef = parseLineFileRef(line); | |
if (lineRef) { | |
file = lineRef.file; | |
line = lineRef.line; | |
} | |
return { | |
file: file, | |
line: line, | |
column: column | |
}; | |
} | |
else { | |
return null; | |
} | |
}; | |
var createSourceLink = function(path, line, column, domain) { | |
var name = baseName(path); | |
var linkEl = document.createElement("a"); | |
linkEl.innerText = "(" + name + "#" + line + ":" + column + ")"; | |
linkEl.href = "#"; | |
linkEl.color = "#4531d6"; | |
linkEl.style.display = "inline-block"; | |
linkEl.style.textDecoration = "none"; | |
linkEl.onclick = function() { | |
openSource(path, line, column, domain, false); | |
}; | |
return linkEl; | |
}; | |
var baseName = function(path) { | |
var parts = path.split(new RegExp("/|\\\\")); | |
return parts[parts.length - 1]; | |
}; | |
var showErrorImpl = function() { | |
var message = errorObject, callstack = ""; | |
if (errorObject.message) message = errorObject.message; | |
if (errorObject.stack) callstack = errorObject.stack; | |
if (errorLine === null || errorColumn === null) { | |
var parseResult = parseCallstackRef(callstack); | |
if (parseResult) { | |
errorFile = parseResult.file; | |
errorLine = parseResult.line; | |
errorColumn = parseResult.column; | |
} | |
} | |
else { | |
var parseLineResult = parseLineFileRef(errorLine); | |
if (parseLineResult) { | |
errorFile = parseLineResult.file; | |
errorLine = parseLineResult.line; | |
} | |
} | |
if (errorFile) { | |
message = message + " in "; | |
} | |
var container = document.getElementById("r2d3-error-container"); | |
if (!container) { | |
container = document.createElement("div"); | |
el.appendChild(container); | |
} | |
else { | |
container.innerHTML = ""; | |
} | |
container.id = "r2d3-error-container"; | |
container.style.fontFamily = "'Lucida Sans', 'DejaVu Sans', 'Lucida Grande', 'Segoe UI', Verdana, Helvetica, sans-serif, serif"; | |
container.style.fontSize = "9pt"; | |
container.style.color = "#444"; | |
container.style.position = "absolute"; | |
container.style.top = "0"; | |
container.style.left = "8px"; | |
container.style.right = "8px"; | |
container.style.overflow = "scroll"; | |
container.style.lineHeight = "16px"; | |
var header = document.createElement("div"); | |
header.innerText = "Error: " + message.replace("\n", ""); | |
header.style.marginTop = "8px"; | |
header.style.background = "rgb(244, 248, 249)"; | |
header.style.border = "1px solid #d6dadc"; | |
header.style.padding = "8px 15px 8px 15px"; | |
header.style.lineHeight = "24px"; | |
container.appendChild(header); | |
if (errorFile) { | |
if (hostDomain) { | |
if (!errorHighlightOnce) { | |
openSource(errorFile, errorLine, errorColumn, hostDomain, true); | |
errorHighlightOnce = true; | |
} | |
var linkEl = createSourceLink(errorFile, errorLine, errorColumn, hostDomain); | |
header.appendChild(linkEl); | |
} | |
else { | |
header.innerText = "Error: " + message.replace("\n", "") + " " + errorFile + "#" + errorLine + ":" + errorColumn; | |
} | |
} | |
if (callstack) { | |
var stack = document.createElement("div"); | |
var cleanStack = cleanStackTrace(callstack); | |
stack.style.display = "block"; | |
stack.style.border = "1px solid #d6dadc"; | |
stack.style.padding = "12px 15px 12px 15px"; | |
stack.style.background = "#FFFFFF"; | |
stack.style.borderTop = "0"; | |
var allEmpty = true; | |
var entries = cleanStack.split("\n"); | |
for (var idxEntry in entries) { | |
var entry = entries[idxEntry]; | |
var stackEl = document.createElement("div"); | |
var stackRes = parseCallstackRef(entry); | |
if (idxEntry === "0") { | |
header.appendChild(document.createElement("br")); | |
header.appendChild(document.createTextNode(entry)); | |
} else if (hostDomain && stackRes) { | |
stackEl.innerText = entry.substr( | |
0, | |
Math.max(entry.indexOf("(<anony"), entry.indexOf("(r2d3-script-")) | |
); | |
var stackLinkEl = createSourceLink(stackRes.file, stackRes.line, stackRes.column, hostDomain); | |
stackEl.appendChild(stackLinkEl); | |
} | |
else { | |
stackEl.innerText = entry; | |
} | |
if (stackEl.innerHTML.trim().length > 0) stack.appendChild(stackEl); | |
} | |
if (stack.childElementCount > 0) | |
container.appendChild(stack); | |
} | |
}; | |
self.showError = function(error, line, column) { | |
errorObject = error; | |
errorLine = line; | |
errorColumn = column; | |
showErrorImpl(); | |
}; | |
window.onerror = function (msg, url, lineNo, columnNo, error) { | |
if (self.captureErrors) { | |
self.captureErrors(msg, url, lineNo, columnNo, error); | |
} | |
return false; | |
}; | |
registerMessageListeners(); | |
}</script> | |
<script>HTMLWidgets.widget({ | |
name: 'r2d3', | |
type: 'output', | |
factory: function(el, width, height) { | |
var r2d3 = new R2D3(el, width, height); | |
return { | |
renderValue: function(x) { | |
r2d3.widgetRender(x); | |
}, | |
resize: function(width, height) { | |
r2d3.widgetResize(width, height); | |
} | |
}; | |
} | |
}); | |
</script> | |
<script>// https://d3js.org Version 5.0.0-rc.4. 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.d3v5 = global.d3v5 || {}))); | |
}(this, (function (exports) { 'use strict'; | |
var version = "5.0.0-rc.4"; | |
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); | |
var e5 = Math.sqrt(10); | |
var 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; | |
var right = 2; | |
var bottom = 3; | |
var left = 4; | |
var 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 = select(view).on("dragstart.drag", noevent, true); | |
if ("onselectstart" in root) { | |
selection.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 = select(view).on("dragstart.drag", null); | |
if (noclick) { | |
selection.on("click.drag", noevent, true); | |
setTimeout(function() { selection.on("click.drag", null); }, 0); | |
} | |
if ("onselectstart" in root) { | |
selection.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) { | |
selection | |
.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 = exports.event.changedTouches, | |
c = container.apply(this, arguments), | |
n = touches.length, i, gesture; | |
for (i = 0; i < n; ++i) { | |
if (gesture = beforestart(touches[i].identifier, c, touch, this, arguments)) { | |
nopropagation(); | |
gesture("start"); | |
} | |
} | |
} | |
function touchmoved() { | |
var touches = exports.event.changedTouches, | |
n = touches.length, i, gesture; | |
for (i = 0; i < n; ++i) { | |
if (gesture = gestures[touches[i].identifier]) { | |
noevent(); | |
gesture("drag"); | |
} | |
} | |
} | |
function touchended() { | |
var touches = exports.event.changedTouches, | |
n = touches.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[i].identifier]) { | |
nopropagation(); | |
gesture("end"); | |
} | |
} | |
} | |
function beforestart(id, container, point, that, args) { | |
var p = point(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(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*"; | |
var reN = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*"; | |
var reP = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*"; | |
var reHex3 = /^#([0-9a-f]{3})$/; | |
var reHex6 = /^#([0-9a-f]{6})$/; | |
var reRgbInteger = new RegExp("^rgb\\(" + [reI, reI, reI] + "\\)$"); | |
var reRgbPercent = new RegExp("^rgb\\(" + [reP, reP, reP] + "\\)$"); | |
var reRgbaInteger = new RegExp("^rgba\\(" + [reI, reI, reI, reN] + "\\)$"); | |
var reRgbaPercent = new RegExp("^rgba\\(" + [reP, reP, reP, reN] + "\\)$"); | |
var reHslPercent = new RegExp("^hsl\\(" + [reN, reP, reP] + "\\)$"); | |
var 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 | |
}; | |