(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); } // 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; } } 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 = $("").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 = $("
").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 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 as an output binding. // Merge defaults into the definition; don't mutate the original definition. // The base object is a Shiny output binding if we're running in Shiny mode, // or an empty object if we're not. var shinyBinding = extend(new Shiny.OutputBinding(), defaults, definition); // Wrap renderValue to handle initialization, which unfortunately isn't // supported natively by Shiny at the time of this writing. // NB: shinyBinding.initialize may be undefined, as it's optional. // Rename initialize to make sure it isn't called by a future version // of Shiny that does support initialize directly. shinyBinding._htmlwidgets_initialize = shinyBinding.initialize; delete shinyBinding.initialize; overrideMethod(shinyBinding, "find", function(superfunc) { return function(scope) { var results = superfunc(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; }; }); overrideMethod(shinyBinding, "renderValue", function(superfunc) { return function(el, data) { // 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 (!this.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 (this._htmlwidgets_initialize) { var result = this._htmlwidgets_initialize(el, el.offsetWidth, el.offsetHeight); elementData(el, "init_result", result); } } Shiny.renderDependencies(data.deps); superfunc(el, data.x, elementData(el, "init_result")); }; }); overrideMethod(shinyBinding, "resize", function(superfunc) { return 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")) { superfunc(el, width, height, elementData(el, "init_result")); } }; }); Shiny.outputBindings.register(shinyBinding, shinyBinding.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 ); } 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", resizeHandler); window.jQuery(document).on("hidden", 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); } }); }); } // 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) { 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]; } } } }; })();