|
- (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 = tryEval(task);
- if (typeof(taskFunc) !== "function") {
- throw new Error("Task must be a function! Source:\n" + task);
- }
- taskFunc.apply(target, theseArgs);
- });
- }
- }
-
- // Attempt eval() both with and without enclosing in parentheses.
- // Note that enclosing coerces a function declaration into
- // an expression that eval() can parse
- // (otherwise, a SyntaxError is thrown)
- function tryEval(code) {
- var result = null;
- try {
- result = eval(code);
- } catch(error) {
- if (!error instanceof SyntaxError) {
- throw error;
- }
- try {
- result = eval("(" + code + ")");
- } catch(e) {
- if (e instanceof SyntaxError) {
- throw error;
- } else {
- throw e;
- }
- }
- }
- return result;
- }
-
- 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 = {
- w: sizeObj ? sizeObj.getWidth() : el.offsetWidth,
- h: sizeObj ? sizeObj.getHeight() : el.offsetHeight
- };
- 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();
- }
-
-
- function has_jQuery3() {
- if (!window.jQuery) {
- return false;
- }
- var $version = window.jQuery.fn.jquery;
- var $major_version = parseInt($version.split(".")[0]);
- return $major_version >= 3;
- }
-
- /*
- / Shiny 1.4 bumped jQuery from 1.x to 3.x which means jQuery's
- / on-ready handler (i.e., $(fn)) is now asyncronous (i.e., it now
- / really means $(setTimeout(fn)).
- / https://jquery.com/upgrade-guide/3.0/#breaking-change-document-ready-handlers-are-now-asynchronous
- /
- / Since Shiny uses $() to schedule initShiny, shiny>=1.4 calls initShiny
- / one tick later than it did before, which means staticRender() is
- / called renderValue() earlier than (advanced) widget authors might be expecting.
- / https://github.com/rstudio/shiny/issues/2630
- /
- / For a concrete example, leaflet has some methods (e.g., updateBounds)
- / which reference Shiny methods registered in initShiny (e.g., setInputValue).
- / Since leaflet is privy to this life-cycle, it knows to use setTimeout() to
- / delay execution of those methods (until Shiny methods are ready)
- / https://github.com/rstudio/leaflet/blob/18ec981/javascript/src/index.js#L266-L268
- /
- / Ideally widget authors wouldn't need to use this setTimeout() hack that
- / leaflet uses to call Shiny methods on a staticRender(). In the long run,
- / the logic initShiny should be broken up so that method registration happens
- / right away, but binding happens later.
- */
- function maybeStaticRenderLater() {
- if (shinyMode && has_jQuery3()) {
- window.jQuery(window.HTMLWidgets.staticRender);
- } else {
- window.HTMLWidgets.staticRender();
- }
- }
-
- if (document.addEventListener) {
- document.addEventListener("DOMContentLoaded", function() {
- document.removeEventListener("DOMContentLoaded", arguments.callee, false);
- maybeStaticRenderLater();
- }, false);
- } else if (document.attachEvent) {
- document.attachEvent("onreadystatechange", function() {
- if (document.readyState === "complete") {
- document.detachEvent("onreadystatechange", arguments.callee);
- maybeStaticRenderLater();
- }
- });
- }
-
-
- 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] = tryEval(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;
- }
- })();
|