htmlwidgets/0000755000176200001440000000000013230661610012603 5ustar liggesusershtmlwidgets/inst/0000755000176200001440000000000013230461367013567 5ustar liggesusershtmlwidgets/inst/www/0000755000176200001440000000000013130653463014412 5ustar liggesusershtmlwidgets/inst/www/htmlwidgets.js0000644000176200001440000007426513130653463017321 0ustar liggesusers(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 = $("").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 // 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; } })(); htmlwidgets/inst/NEWS0000644000176200001440000000620313130653463014266 0ustar liggesusershtmlwidgets 0.9 ----------------------------------------------------------------------- * Starting with R 3.4.0, a "Calling 'structure(NULL, *)' is deprecated" warning would occur when shinyRenderWidget encountered a NULL value. (#269) * Fix edge case where using dynamic HTML dependencies from a widget binding's JS factory function would fail. htmlwidgets 0.8 ----------------------------------------------------------------------- * Export getDependency function * `onRender` hooks were firing too early when used in Shiny apps. * Widget IDs: only restore random.seed when non-NULL htmlwidgets 0.7 ----------------------------------------------------------------------- * Pass knitr options to saveWidget * Ensure that scaffoldWidget opens files correctly within RStudio * The resize handler also works for the JavaScript events `shown.bs.collapse` and `hidden.bs.collapse` now so that widgets inside the Bootstrap collapse class can be displayed * Fix references to vignettes in documentation * Add elementId parameter to widget function generated by scaffoldWidget * More robust method of generating unique widget IDs * Modify advanced and sizing vignettes to use new style widget declarations htmlwidgets 0.6 ----------------------------------------------------------------------- * Introduce new scheme for defining JavaScript bindings that will make it easier for widgets to gain access to other widget instances on the page. * Add `onRender` hook for widgets to execute custom JavaScript code after rendering. * Add `appendContent` and `prependContent` functions for adding HTML to a widget rendered in a static context (i.e. R console or Rmd) * Fix a bug where the string "" in the widget data caused `saveWidget()` to have malformed output. (#168) * Tweak pandoc conversion used in saveWidget to prevent hanging with large htmlwidget script data elements (use "markdown" rather than "markdown-strict" as input format) * Increase pandoc stack size to 512M for saveWidget (often required for e.g. larger embedded leaflet maps). Stack size can also be controlled by the pandoc.stack.size option. * Import latest version of with_pandoc_safe_environment from rmarkdown * Fix issue that prevented calling renderValue() from within resize() htmlwidgets 0.5 ----------------------------------------------------------------------- * Add background parameter to saveWidget function * Fix a bug where "" appearing in widget data would break parsing * Fix a bug where multiple widgets on a page caused all but one to miss resize events * Sync vignettes with contents of htmlwidgets website htmlwidgets 0.4 ----------------------------------------------------------------------- * Use minified files while scaffolding widget wherever available * Suppress viewing widgets in non-interactive R sessions by default * Export the HTMLWidgets.staticRender function * Add a preRenderHook for widgets * Use jsonlite rather than RJSONIO for JSON serialization * Call widget.resize in more situations htmlwidgets 0.3.2 ----------------------------------------------------------------------- * Initial release to CRAN htmlwidgets/inst/templates/0000755000176200001440000000000013130653463015564 5ustar liggesusershtmlwidgets/inst/templates/widget_js.txt0000644000176200001440000000065013130653463020305 0ustar liggesusersHTMLWidgets.widget({ name: '%s', type: 'output', factory: function(el, width, height) { // TODO: define shared variables for this instance return { renderValue: function(x) { // TODO: code to render the widget, e.g. el.innerText = x.message; }, resize: function(width, height) { // TODO: code to re-render the widget with a new size } }; } }); htmlwidgets/inst/templates/widget_r.txt0000644000176200001440000000262413130653463020135 0ustar liggesusers#' #' #' #' #' @import htmlwidgets #' #' @export %s <- function(message, width = NULL, height = NULL, elementId = NULL) { # forward options using x x = list( message = message ) # create widget htmlwidgets::createWidget( name = '%s', x, width = width, height = height, package = '%s', elementId = elementId ) } #' Shiny bindings for %s #' #' Output and render functions for using %s within Shiny #' applications and interactive Rmd documents. #' #' @param outputId output variable to read from #' @param width,height Must be a valid CSS unit (like \code{'100\%%'}, #' \code{'400px'}, \code{'auto'}) or a number, which will be coerced to a #' string and have \code{'px'} appended. #' @param expr An expression that generates a %s #' @param env The environment in which to evaluate \code{expr}. #' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This #' is useful if you want to save an expression in a variable. #' #' @name %s-shiny #' #' @export %sOutput <- function(outputId, width = '100%%', height = '400px'){ htmlwidgets::shinyWidgetOutput(outputId, '%s', width, height, package = '%s') } #' @rdname %s-shiny #' @export render%s <- function(expr, env = parent.frame(), quoted = FALSE) { if (!quoted) { expr <- substitute(expr) } # force quoted htmlwidgets::shinyRenderWidget(expr, %sOutput, env, quoted = TRUE) } htmlwidgets/inst/doc/0000755000176200001440000000000013230461367014334 5ustar liggesusershtmlwidgets/inst/doc/develop_advanced.R0000644000176200001440000000500313230461366017737 0ustar liggesusers## ----echo=FALSE, comment=''---------------------------------------------- htmlwidgets:::toJSON2(head(iris, 3), pretty = TRUE) ## ----echo=FALSE, comment=''---------------------------------------------- htmlwidgets:::toJSON2(head(iris, 3), dataframe = 'row', pretty = TRUE) ## ----echo=FALSE, comment=''---------------------------------------------- htmlwidgets:::toJSON2(unname(head(iris, 8)), dataframe = 'column', pretty = TRUE) ## ----echo=FALSE, comment=''---------------------------------------------- htmlwidgets:::toJSON2(head(iris, 8), dataframe = 'values', pretty = TRUE) ## ----eval=FALSE, code=head(capture.output(htmlwidgets:::toJSON2),-1), tidy=FALSE---- # function (x, ..., dataframe = "columns", null = "null", na = "null", # auto_unbox = TRUE, digits = getOption("shiny.json.digits", # 16), use_signif = TRUE, force = TRUE, POSIXt = "ISO8601", # UTC = TRUE, rownames = FALSE, keep_vec_names = TRUE, strict_atomic = TRUE) # { # if (strict_atomic) # x <- I(x) # jsonlite::toJSON(x, dataframe = dataframe, null = null, na = na, # auto_unbox = auto_unbox, digits = digits, use_signif = use_signif, # force = force, POSIXt = POSIXt, UTC = UTC, rownames = rownames, # keep_vec_names = keep_vec_names, json_verbatim = TRUE, # ...) # } ## ----eval=FALSE---------------------------------------------------------- # fooWidget <- function(data, name, ...) { # # ... process the data ... # params <- list(foo = data, bar = TRUE) # # customize toJSON() argument values # attr(params, 'TOJSON_ARGS') <- list(digits = 7, na = 'string') # htmlwidgets::createWidget(name, x = params, ...) # } ## ----eval=FALSE---------------------------------------------------------- # fooWidget <- function(data, name, ..., JSONArgs = list(digits = 7)) { # # ... process the data ... # params <- list(foo = data, bar = TRUE) # # customize toJSON() argument values # attr(params, 'TOJSON_ARGS') <- JSONArgs # htmlwidgets::createWidget(name, x = params, ...) # } ## ----eval=FALSE---------------------------------------------------------- # options(htmlwidgets.TOJSON_ARGS = list(digits = 7, pretty = TRUE)) ## ----eval=FALSE---------------------------------------------------------- # fooWidget <- function(data, name, ...) { # # ... process the data ... # params <- list(foo = data, bar = TRUE) # # customize the JSON serializer # attr(params, 'TOJSON_FUNC') <- MY_OWN_JSON_FUNCTION # htmlwidgets::createWidget(name, x = params, ...) # } htmlwidgets/inst/doc/develop_sizing.html0000644000176200001440000246675413230461367020273 0ustar liggesusers HTML Widget Sizing

Overview

In the spirit of HTML widgets working just like plots in R, it’s important that HTML widgets intelligently size themselves to their container, whether it be the RStudio Viewer, a figure in knitr, or a UI panel within a Shiny application. The htmlwidgets framework provides a rich mechanism for specifying the sizing behavior of widgets.

This sizing mechanism is designed to address the following constraints that affect the natural size of a widget:

  • The kind of widget it is. Some widgets may only be designed to look good at small, fixed sizes (like sparklines) while other widgets may want every pixel that can be spared (like network graphs).

  • The context into which the widget is rendered. While a given widget might look great at 960px by 480px in an R Markdown document, the same widget would look silly at that size in the RStudio Viewer pane, which is typically much smaller.

Widget sizing is handled in two steps:

  1. First, a sizing policy is specified for the widget. This is done via the sizingPolicy argument to the createWidget function. Most widgets can accept the default sizing policy (or override only one or two aspects of it) and get satisfactory sizing behavior (see details below).

  2. The sizing policy is used by the framework to compute the correct width and height for a widget given where it is being rendered. This size information is then passed to the initialize and resize methods of the widgets JavaScript binding. It’s up to the widget to then forward this size information to the underlying JavaScript library.

Specifying a sizing policy

The default HTML widget sizing policy treats the widget with the same sizing semantics as an R plot. When printed at the R console the widget is displayed within the RStudio Viewer and sized to fill the Viewer pane (modulo any padding). When rendered inside an R Markdown document the widget is sized based on the default size of figures in the document.

Note that for most widgets the default sizing behavior is fine and you won’t need to create a custom sizing policy. If you need a slightly different behavior than the default you can also selectively override the default behavior by calling the sizingPolicy function and passing the result to createWidget. For example:

htmlwidgets::createWidget(
  "sigma", 
  x, 
  width = width, 
  height = height,
  sizingPolicy = htmlwidgets::sizingPolicy(
    viewer.padding = 0,
    viewer.paneHeight = 500,
    browser.fill = TRUE
  )
)

Examples

The networkD3 package uses custom sizing policies for all of its widgets. The simpleNetwork widget eliminates padding (as d3 is already providing padding) and specifies that it wants to fill up as much space as possible when displayed in a standalone web browser:

sizingPolicy(padding = 0, browser.fill = TRUE)

The sankeyNetwork widget requires much more space than is afforded by the RStudio Viewer or a typical knitr figure so it disables those automatic sizing behaviors. It also provides a more reasonable default width and height for knitr documents:

sizingPolicy(viewer.suppress = TRUE,
             knitr.figure = FALSE,
             browser.fill = TRUE,
             browser.padding = 75,
             knitr.defaultWidth = 800,
             knitr.defaultHeight = 500)

Available options

Here are the various options that can be specified within a sizing policy:

Option Description
defaultWidth The default width used to display the widget. This parameter specifies the default width for viewing in all contexts (browser, viewer, and knitr) unless it is specifically overridden with e.g. browser.defaultWidth.
defaultHeight The default height used to display the widget. This parameter specifies the default height for viewing in all contexts (browser, viewer, and knitr) unless it is specifically overridden with e.g. browser.defaultHeight.
padding Padding around the widget (in pixels). This parameter specifies the padding for viewing in all contexts (browser and viewer) unless it is specifically overridden by e.g. browser.padding.
viewer.defaultWidth The default width used to display the widget within the RStudio Viewer.
viewer.defaultHeight The default height used to display the widget within the RStudio Viewer.
viewer.padding Padding around the widget when displayed in the RStudio Viewer (defaults to 15 pixels).
viewer.fill When displayed in the RStudio Viewer, automatically size the widget to the viewer dimensions (note that viewer.padding is still applied). Default to TRUE.
viewer.suppress Never display the widget within the RStudio Viewer (useful for widgets that require a large amount of space for rendering). Defaults to FALSE.
viewer.paneHeight Request that the RStudio Viewer be forced to a specific height when displaying this widget.
browser.defaultWidth The default width used to display the widget within a standalone web browser.
browser.defaultHeight The default height used to display the widget within a standalone web browser.
browser.padding Padding around the widget when displayed in a standalone browser (defaults to 40 pixels).
browser.fill When displayed in a standalone web browser, automatically size the widget to the browser dimensions (note that browser.padding is still applied). Defaults to FALSE.
knitr.defaultWidth The default width used to display the widget within documents generated by knitr (e.g. R Markdown).
knitr.defaultHeight The default height used to display the widget within documents generated by knitr (e.g. R Markdown).
knitr.figure Apply the default knitr fig.width and fig.height to the widget when it’s rendered within R Markdown documents. Defaults to TRUE.

JavaScript resize method

Specifying a sizing policy allows htmlwidgets to calculate the width and height of your widget based on where it’s being displayed. However, you still need to forward this sizing information on to the underlying JavaScript library you are creating a widget for.

Every JavaScript library handles dynamic sizing a bit differently. Some do it automatically, some have a resize() call to force a layout, and some require that size be set only along with data and other options. Whatever the case, the htmlwidgets framework will pass the computed sizes to both your factory function and resize function. Here’s an empty JavaScript binding that illustrates:

HTMLWidgets.widget({

  name: "demo",
  
  type: "output",
  
  factory: function(el, width, height) {
  
    return {
      renderValue: function(x) {
      
      },
      
      resize: function(width, height) {
        
      }
    };
  }
});

What you do with the passed width and height is up to you and depends on the re-sizing semantics of the underlying JavaScript library you are creating a widget for. A couple of illustrative examples are included in the next section.

Examples

dygraphs

In the dygraphs widget the implementation of re-sizing is relatively simple since the dygraphs library includes a resize() method to automatically size the graph to it’s enclosing HTML element:

resize: function(width, height) {
  if (dygraph)
    dygraph.resize();
}

forceNetwork

In the forceNetwork widget, the passed width and height are applied to the <svg> element that hosts the d3 network visualization, as well as forwarded on to the underlying d3 force simulation object:

factory: function(el, width, height) {

  // instance data
  var el = el;
  var force = d3.layout.force();

  d3.select(el).append("svg")
    .attr("width", width)
    .attr("height", height);
      
  return {
    renderValue: function(x) {
      // implementation excluded
    },
      
    resize: function(width, height) {
         
      d3.select(el).select("svg")
        .attr("width", width)
        .attr("height", height);

      force.size([width, height]).resume();
    }
  };
}

As you can see, re-sizing is handled in a wide variety of fashions in different JavaScript libraries. The resize method is intended to provide a flexible way to map the automatic sizing logic of htmlwidgets directly into the underlying library.

htmlwidgets/inst/doc/develop_intro.R0000644000176200001440000000026713230461367017335 0ustar liggesusers## ---- eval=FALSE--------------------------------------------------------- # library(sigma) # data <- system.file("examples/ediaspora.gexf.xml", package = "sigma") # sigma(data) htmlwidgets/inst/doc/develop_sizing.Rmd0000644000176200001440000002125013130653463020020 0ustar liggesusers--- title: "HTML Widget Sizing" date: "`r Sys.Date()`" output: html_document: highlight: kate toc: true toc_depth: 4 mathjax: null vignette: > %\VignetteIndexEntry{Sizing} %\VignetteEngine{knitr::rmarkdown} \usepackage[utf8]{inputenc} --- ## Overview In the spirit of HTML widgets working just like plots in R, it's important that HTML widgets intelligently size themselves to their container, whether it be the RStudio Viewer, a figure in knitr, or a UI panel within a Shiny application. The **htmlwidgets** framework provides a rich mechanism for specifying the sizing behavior of widgets. This sizing mechanism is designed to address the following constraints that affect the natural size of a widget: - **The kind of widget it is.** Some widgets may only be designed to look good at small, fixed sizes (like [sparklines](https://github.com/htmlwidgets/sparkline)) while other widgets may want every pixel that can be spared (like [network graphs](http://christophergandrud.github.io/networkD3/)). - **The context into which the widget is rendered.** While a given widget might look great at 960px by 480px in an R Markdown document, the same widget would look silly at that size in the RStudio Viewer pane, which is typically much smaller. Widget sizing is handled in two steps: 1. First, a sizing policy is specified for the widget. This is done via the `sizingPolicy` argument to the `createWidget` function. Most widgets can accept the default sizing policy (or override only one or two aspects of it) and get satisfactory sizing behavior (see details below). 2. The sizing policy is used by the framework to compute the correct width and height for a widget given where it is being rendered. This size information is then passed to the `initialize` and `resize` methods of the widgets JavaScript binding. It's up to the widget to then forward this size information to the underlying JavaScript library. ## Specifying a sizing policy The default HTML widget sizing policy treats the widget with the same sizing semantics as an R plot. When printed at the R console the widget is displayed within the RStudio Viewer and sized to fill the Viewer pane (modulo any padding). When rendered inside an R Markdown document the widget is sized based on the default size of figures in the document. Note that for most widgets the default sizing behavior is fine and you won't need to create a custom sizing policy. If you need a slightly different behavior than the default you can also selectively override the default behavior by calling the `sizingPolicy` function and passing the result to `createWidget`. For example: ```r htmlwidgets::createWidget( "sigma", x, width = width, height = height, sizingPolicy = htmlwidgets::sizingPolicy( viewer.padding = 0, viewer.paneHeight = 500, browser.fill = TRUE ) ) ``` ### Examples The [networkD3](http://christophergandrud.github.io/networkD3/) package uses custom sizing policies for all of its widgets. The `simpleNetwork` widget eliminates padding (as d3 is already providing padding) and specifies that it wants to fill up as much space as possible when displayed in a standalone web browser: ```r sizingPolicy(padding = 0, browser.fill = TRUE) ``` The `sankeyNetwork` widget requires much more space than is afforded by the RStudio Viewer or a typical knitr figure so it disables those automatic sizing behaviors. It also provides a more reasonable default width and height for knitr documents: ```r sizingPolicy(viewer.suppress = TRUE, knitr.figure = FALSE, browser.fill = TRUE, browser.padding = 75, knitr.defaultWidth = 800, knitr.defaultHeight = 500) ``` ### Available options Here are the various options that can be specified within a sizing policy: | Option | Description | |---|---| | **defaultWidth** | The default width used to display the widget. This parameter specifies the default width for viewing in all contexts (browser, viewer, and knitr) unless it is specifically overridden with e.g. browser.defaultWidth. | | **defaultHeight** | The default height used to display the widget. This parameter specifies the default height for viewing in all contexts (browser, viewer, and knitr) unless it is specifically overridden with e.g. browser.defaultHeight. | | **padding** | Padding around the widget (in pixels). This parameter specifies the padding for viewing in all contexts (browser and viewer) unless it is specifically overridden by e.g. browser.padding. | | **viewer.defaultWidth** | The default width used to display the widget within the RStudio Viewer. | | **viewer.defaultHeight** | The default height used to display the widget within the RStudio Viewer. | | **viewer.padding** | Padding around the widget when displayed in the RStudio Viewer (defaults to 15 pixels). | | **viewer.fill** | When displayed in the RStudio Viewer, automatically size the widget to the viewer dimensions (note that viewer.padding is still applied). Default to TRUE. | | **viewer.suppress** | Never display the widget within the RStudio Viewer (useful for widgets that require a large amount of space for rendering). Defaults to FALSE. | | **viewer.paneHeight** | Request that the RStudio Viewer be forced to a specific height when displaying this widget. | | **browser.defaultWidth** | The default width used to display the widget within a standalone web browser. | | **browser.defaultHeight** | The default height used to display the widget within a standalone web browser. | | **browser.padding** | Padding around the widget when displayed in a standalone browser (defaults to 40 pixels). | | **browser.fill** | When displayed in a standalone web browser, automatically size the widget to the browser dimensions (note that browser.padding is still applied). Defaults to FALSE. | | **knitr.defaultWidth** | The default width used to display the widget within documents generated by knitr (e.g. R Markdown). | | **knitr.defaultHeight** | The default height used to display the widget within documents generated by knitr (e.g. R Markdown). | | **knitr.figure** | Apply the default knitr fig.width and fig.height to the widget when it's rendered within R Markdown documents. Defaults to TRUE. | ## JavaScript resize method Specifying a sizing policy allows htmlwidgets to calculate the width and height of your widget based on where it's being displayed. However, you still need to forward this sizing information on to the underlying JavaScript library you are creating a widget for. Every JavaScript library handles dynamic sizing a bit differently. Some do it automatically, some have a resize() call to force a layout, and some require that size be set only along with data and other options. Whatever the case, the **htmlwidgets** framework will pass the computed sizes to both your `factory` function and `resize` function. Here's an empty JavaScript binding that illustrates: ```javascript HTMLWidgets.widget({ name: "demo", type: "output", factory: function(el, width, height) { return { renderValue: function(x) { }, resize: function(width, height) { } }; } }); ``` What you do with the passed width and height is up to you and depends on the re-sizing semantics of the underlying JavaScript library you are creating a widget for. A couple of illustrative examples are included in the next section. ### Examples #### dygraphs In the [dygraphs](http://rstudio.github.io/dygraphs) widget the implementation of re-sizing is relatively simple since the **dygraphs** library includes a resize() method to automatically size the graph to it's enclosing HTML element: ```javascript resize: function(width, height) { if (dygraph) dygraph.resize(); } ``` #### forceNetwork In the [forceNetwork](http://christophergandrud.github.io/networkD3/#force) widget, the passed width and height are applied to the `` element that hosts the d3 network visualization, as well as forwarded on to the underlying d3 force simulation object: ```javascript factory: function(el, width, height) { // instance data var el = el; var force = d3.layout.force(); d3.select(el).append("svg") .attr("width", width) .attr("height", height); return { renderValue: function(x) { // implementation excluded }, resize: function(width, height) { d3.select(el).select("svg") .attr("width", width) .attr("height", height); force.size([width, height]).resume(); } }; } ``` As you can see, re-sizing is handled in a wide variety of fashions in different JavaScript libraries. The `resize` method is intended to provide a flexible way to map the automatic sizing logic of **htmlwidgets** directly into the underlying library. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������htmlwidgets/inst/doc/develop_advanced.Rmd�����������������������������������������������������������0000644�0001762�0000144�00000021743�13130653463�020271� 0����������������������������������������������������������������������������������������������������ustar �ligges��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������--- title: "HTML Widgets: Advanced Topics" date: "`r Sys.Date()`" output: html_document: highlight: kate toc: true toc_depth: 4 mathjax: null vignette: > %\VignetteIndexEntry{Advanced} %\VignetteEngine{knitr::rmarkdown} \usepackage[utf8]{inputenc} --- ## Overview This article covers several aspects of creating widgets that are not required by all widgets, but are an essential part of getting bindings to certain types of JavaScript libraries to work properly. Topics covered include: * Transforming JSON representations of R objects into representations required by JavaScript libraries (e.g. an R data frame to a d3 dataset). * Tracking instance-specific widget data within JavaScript bindings. * Passing JavaScript functions from R to JavaScript (e.g. a user provided formatting or drawing function) * Generating custom HTML to enclose a widget (the default is a `
` but some libraries require a different element e.g. a ``). ## Data transformation R objects passed as part of the `x` parameter to the `createWidget()` function are transformed to JSON using the internal function `htmlwidgets:::toJSON()`^[N.B. It is not exported from **htmlwidgets**, so you are not supposed to call this function directly.], which is basically a wrapper function of `jsonlite::toJSON()` by default. However, sometimes this representation is not what is required by the JavaScript library you are interfacing with. There are two JavaScript functions that you can use to transform the JSON data. ### HTMLWidgets.dataframeToD3() R data frames are represented in "long" form (an array of named vectors) whereas d3 typically requires "wide" form (an array of objects each of which includes all names and values). Since the R representation is smaller in size and much faster to transmit over the network, we create the long-form representation of R data, and then transform the data in JavaScript using the `dataframeToD3()` helper function. Here is an example of the long-form representation of an R data frame: ```{r echo=FALSE, comment=''} htmlwidgets:::toJSON2(head(iris, 3), pretty = TRUE) ``` After we apply `HTMLWidgets.dataframeToD3()`, it will become: ```{r echo=FALSE, comment=''} htmlwidgets:::toJSON2(head(iris, 3), dataframe = 'row', pretty = TRUE) ``` As a real example, the [simpleNetwork](https://christophergandrud.github.io/networkD3/#simple) widget accepts a data frame containing network links on the R side, then transforms it to a d3 representation within the JavaScript `renderValue` function: ```javascript renderValue: function(x) { // convert links data frame to d3 friendly format var links = HTMLWidgets.dataframeToD3(x.links); // ... use the links, etc ... } ``` ### HTMLWidgets.transposeArray2D() Sometimes a 2-dimensional array requires a similar transposition. For this the `transposeArray2D()` function is provided. Here is an example array: ```{r echo=FALSE, comment=''} htmlwidgets:::toJSON2(unname(head(iris, 8)), dataframe = 'column', pretty = TRUE) ``` `HTMLWidgets.transposeArray2D()` can transpose it to: ```{r echo=FALSE, comment=''} htmlwidgets:::toJSON2(head(iris, 8), dataframe = 'values', pretty = TRUE) ``` As a real example, the [dygraphs](https://rstudio.github.io/dygraphs) widget uses this function to transpose the "file" (data) argument it gets from the R side before passing it on to the dygraphs library: ```javascript renderValue: function(x) { // ... code excluded ... // transpose array x.attrs.file = HTMLWidgets.transposeArray2D(x.attrs.file); // ... more code excluded ... } ``` ### Custom JSON serializer You may find it necessary to customize the JSON serialization of widget data when the default serializer in **htmlwidgets** does not work in the way you have expected. For widget package authors, there are two levels of customization for the JSON serialization: you can either customize the default values of arguments for `jsonlite::toJSON()`, or just customize the whole function. 1. `jsonlite::toJSON()` has a lot of arguments, and we have already changed some of its default values. Below is the JSON serializer we use in **htmlwidgets** at the moment: ```{r eval=FALSE, code=head(capture.output(htmlwidgets:::toJSON2),-1), tidy=FALSE} ``` For example, we convert data frames to JSON by columns instead of rows (the latter is `jsonlite::toJSON`'s default). If you want to change the default values of any arguments, you can attach an attribute `TOJSON_ARGS` to the widget data to be passed to `createWidget()`, e.g. ```{r eval=FALSE} fooWidget <- function(data, name, ...) { # ... process the data ... params <- list(foo = data, bar = TRUE) # customize toJSON() argument values attr(params, 'TOJSON_ARGS') <- list(digits = 7, na = 'string') htmlwidgets::createWidget(name, x = params, ...) } ``` We changed the default value of `digits` from 16 to 7, and `na` from `null` to `string` in the above example. It is up to you, the package author, whether you want to expose such customization to users. For example, you can leave an extra argument in your widget function so that users can customize the behavior of the JSON serializer: ```{r eval=FALSE} fooWidget <- function(data, name, ..., JSONArgs = list(digits = 7)) { # ... process the data ... params <- list(foo = data, bar = TRUE) # customize toJSON() argument values attr(params, 'TOJSON_ARGS') <- JSONArgs htmlwidgets::createWidget(name, x = params, ...) } ``` You can also use a global option `htmlwidgets.TOJSON_ARGS` to customize the JSON serializer arguments for all widgets in the current R session, e.g. ```{r eval=FALSE} options(htmlwidgets.TOJSON_ARGS = list(digits = 7, pretty = TRUE)) ``` 1. If you do not want to use **jsonlite**, you can completely override the serializer function by attaching an attribute `TOJSON_FUNC` to the widget data, e.g. ```{r eval=FALSE} fooWidget <- function(data, name, ...) { # ... process the data ... params <- list(foo = data, bar = TRUE) # customize the JSON serializer attr(params, 'TOJSON_FUNC') <- MY_OWN_JSON_FUNCTION htmlwidgets::createWidget(name, x = params, ...) } ``` Here `MY_OWN_JSON_FUNCTION` can be an arbitrary R function that converts R objects to JSON. If you have also specified the `TOJSON_ARGS` attribute, it will be passed to your custom JSON function as well. Note these features about custom JSON serializers require the **shiny** version to be greater than 0.11.1 if you render the widgets in Shiny apps. ## Passing JavaScript functions As you would expect, character vectors passed from R to JavaScript are converted to JavaScript strings. However, what if you want to allow users to provide custom JavaScript functions for formatting, drawing, or event handling? For this case, the **htmlwidgets** package includes a `JS()` function that allows you to request that a character value is evaluated as JavaScript when it is received on the client. For example, the [dygraphs](https://rstudio.github.io/dygraphs) widget includes a `dyCallbacks` function that allows the user to provide callback functions for a variety of contexts. These callbacks are "marked" as containing JavaScript so that they can be converted to actual JavaScript functions on the client: ```r callbacks <- list( clickCallback = JS(clickCallback) drawCallback = JS(drawCallback) highlightCallback = JS(highlightCallback) pointClickCallback = JS(pointClickCallback) underlayCallback = JS(underlayCallback) ) ``` Another example is in the [DT](https://rstudio.github.io/DT) (DataTables) widget, where users can specify an `initCallback` with JavaScript to execute after the table is loaded and initialized: ```r datatable(head(iris, 20), options = list( initComplete = JS( "function(settings, json) {", "$(this.api().table().header()).css({'background-color': '#000', 'color': '#fff'});", "}") )) ``` If multiple arguments are passed to `JS()` (as in the above example), they will be concatenated into a single string separated by `\n`. ## Custom widget HTML Typically the HTML "housing" for a widget is just a `
` element, and this is correspondingly the default behavior for new widgets that don't specify otherwise. However, sometimes you need a different element type. For example, the [sparkline](https://github.com/htmlwidgets/sparkline) widget requires a `` element so implements the following custom HTML generation function: ```r sparkline_html <- function(id, style, class, ...){ tags$span(id = id, class = class) } ``` Note that this function is looked up within the package implementing the widget by the convention `widgetname_html` so it need not be formally exported from your package or otherwise registered with **htmlwidgets**. Most widgets won't need a custom HTML function but if you need to generate custom HTML for your widget (e.g. you need an `` or a `` rather than a `
`) then you should use the **htmltools** package (as demonstrated by the code above). htmlwidgets/inst/doc/develop_advanced.html0000644000176200001440000247405013230461366020520 0ustar liggesusers HTML Widgets: Advanced Topics

Overview

This article covers several aspects of creating widgets that are not required by all widgets, but are an essential part of getting bindings to certain types of JavaScript libraries to work properly. Topics covered include:

  • Transforming JSON representations of R objects into representations required by JavaScript libraries (e.g. an R data frame to a d3 dataset).

  • Tracking instance-specific widget data within JavaScript bindings.

  • Passing JavaScript functions from R to JavaScript (e.g. a user provided formatting or drawing function)

  • Generating custom HTML to enclose a widget (the default is a <div> but some libraries require a different element e.g. a <span>).

Data transformation

R objects passed as part of the x parameter to the createWidget() function are transformed to JSON using the internal function htmlwidgets:::toJSON()1, which is basically a wrapper function of jsonlite::toJSON() by default. However, sometimes this representation is not what is required by the JavaScript library you are interfacing with. There are two JavaScript functions that you can use to transform the JSON data.

HTMLWidgets.dataframeToD3()

R data frames are represented in “long” form (an array of named vectors) whereas d3 typically requires “wide” form (an array of objects each of which includes all names and values). Since the R representation is smaller in size and much faster to transmit over the network, we create the long-form representation of R data, and then transform the data in JavaScript using the dataframeToD3() helper function.

Here is an example of the long-form representation of an R data frame:

{
  "Sepal.Length": [5.1, 4.9, 4.7],
  "Sepal.Width": [3.5, 3, 3.2],
  "Petal.Length": [1.4, 1.4, 1.3],
  "Petal.Width": [0.2, 0.2, 0.2],
  "Species": ["setosa", "setosa", "setosa"]
} 

After we apply HTMLWidgets.dataframeToD3(), it will become:

[
  {
    "Sepal.Length": 5.1,
    "Sepal.Width": 3.5,
    "Petal.Length": 1.4,
    "Petal.Width": 0.2,
    "Species": "setosa"
  },
  {
    "Sepal.Length": 4.9,
    "Sepal.Width": 3,
    "Petal.Length": 1.4,
    "Petal.Width": 0.2,
    "Species": "setosa"
  },
  {
    "Sepal.Length": 4.7,
    "Sepal.Width": 3.2,
    "Petal.Length": 1.3,
    "Petal.Width": 0.2,
    "Species": "setosa"
  }
] 

As a real example, the simpleNetwork widget accepts a data frame containing network links on the R side, then transforms it to a d3 representation within the JavaScript renderValue function:

renderValue: function(x) {

  // convert links data frame to d3 friendly format
  var links = HTMLWidgets.dataframeToD3(x.links);
  
  // ... use the links, etc ...

}

HTMLWidgets.transposeArray2D()

Sometimes a 2-dimensional array requires a similar transposition. For this the transposeArray2D() function is provided. Here is an example array:

[
  [5.1, 4.9, 4.7, 4.6, 5, 5.4, 4.6, 5],
  [3.5, 3, 3.2, 3.1, 3.6, 3.9, 3.4, 3.4],
  [1.4, 1.4, 1.3, 1.5, 1.4, 1.7, 1.4, 1.5],
  [0.2, 0.2, 0.2, 0.2, 0.2, 0.4, 0.3, 0.2],
  ["setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa"]
] 

HTMLWidgets.transposeArray2D() can transpose it to:

[
  [5.1, 3.5, 1.4, 0.2, "setosa"],
  [4.9, 3, 1.4, 0.2, "setosa"],
  [4.7, 3.2, 1.3, 0.2, "setosa"],
  [4.6, 3.1, 1.5, 0.2, "setosa"],
  [5, 3.6, 1.4, 0.2, "setosa"],
  [5.4, 3.9, 1.7, 0.4, "setosa"],
  [4.6, 3.4, 1.4, 0.3, "setosa"],
  [5, 3.4, 1.5, 0.2, "setosa"]
] 

As a real example, the dygraphs widget uses this function to transpose the “file” (data) argument it gets from the R side before passing it on to the dygraphs library:

renderValue: function(x) {
   
    // ... code excluded ...
    
    // transpose array
    x.attrs.file = HTMLWidgets.transposeArray2D(x.attrs.file);
    
    // ... more code excluded ...
}

Custom JSON serializer

You may find it necessary to customize the JSON serialization of widget data when the default serializer in htmlwidgets does not work in the way you have expected. For widget package authors, there are two levels of customization for the JSON serialization: you can either customize the default values of arguments for jsonlite::toJSON(), or just customize the whole function.

  1. jsonlite::toJSON() has a lot of arguments, and we have already changed some of its default values. Below is the JSON serializer we use in htmlwidgets at the moment:

    function (x, ..., dataframe = "columns", null = "null", na = "null", 
        auto_unbox = TRUE, digits = getOption("shiny.json.digits", 
            16), use_signif = TRUE, force = TRUE, POSIXt = "ISO8601", 
        UTC = TRUE, rownames = FALSE, keep_vec_names = TRUE, strict_atomic = TRUE) 
    {
        if (strict_atomic) 
            x <- I(x)
        jsonlite::toJSON(x, dataframe = dataframe, null = null, na = na, 
            auto_unbox = auto_unbox, digits = digits, use_signif = use_signif, 
            force = force, POSIXt = POSIXt, UTC = UTC, rownames = rownames, 
            keep_vec_names = keep_vec_names, json_verbatim = TRUE, 
            ...)
    }

    For example, we convert data frames to JSON by columns instead of rows (the latter is jsonlite::toJSON’s default). If you want to change the default values of any arguments, you can attach an attribute TOJSON_ARGS to the widget data to be passed to createWidget(), e.g.

    fooWidget <- function(data, name, ...) {
      # ... process the data ...
      params <- list(foo = data, bar = TRUE)
      # customize toJSON() argument values
      attr(params, 'TOJSON_ARGS') <- list(digits = 7, na = 'string')
      htmlwidgets::createWidget(name, x = params, ...)
    }

    We changed the default value of digits from 16 to 7, and na from null to string in the above example. It is up to you, the package author, whether you want to expose such customization to users. For example, you can leave an extra argument in your widget function so that users can customize the behavior of the JSON serializer:

    fooWidget <- function(data, name, ..., JSONArgs = list(digits = 7)) {
      # ... process the data ...
      params <- list(foo = data, bar = TRUE)
      # customize toJSON() argument values
      attr(params, 'TOJSON_ARGS') <- JSONArgs
      htmlwidgets::createWidget(name, x = params, ...)
    }

    You can also use a global option htmlwidgets.TOJSON_ARGS to customize the JSON serializer arguments for all widgets in the current R session, e.g.

    options(htmlwidgets.TOJSON_ARGS = list(digits = 7, pretty = TRUE))
  2. If you do not want to use jsonlite, you can completely override the serializer function by attaching an attribute TOJSON_FUNC to the widget data, e.g.

    fooWidget <- function(data, name, ...) {
      # ... process the data ...
      params <- list(foo = data, bar = TRUE)
      # customize the JSON serializer
      attr(params, 'TOJSON_FUNC') <- MY_OWN_JSON_FUNCTION
      htmlwidgets::createWidget(name, x = params, ...)
    }

    Here MY_OWN_JSON_FUNCTION can be an arbitrary R function that converts R objects to JSON. If you have also specified the TOJSON_ARGS attribute, it will be passed to your custom JSON function as well.

Note these features about custom JSON serializers require the shiny version to be greater than 0.11.1 if you render the widgets in Shiny apps.

Passing JavaScript functions

As you would expect, character vectors passed from R to JavaScript are converted to JavaScript strings. However, what if you want to allow users to provide custom JavaScript functions for formatting, drawing, or event handling? For this case, the htmlwidgets package includes a JS() function that allows you to request that a character value is evaluated as JavaScript when it is received on the client.

For example, the dygraphs widget includes a dyCallbacks function that allows the user to provide callback functions for a variety of contexts. These callbacks are “marked” as containing JavaScript so that they can be converted to actual JavaScript functions on the client:

callbacks <- list(
  clickCallback = JS(clickCallback)
  drawCallback = JS(drawCallback)
  highlightCallback = JS(highlightCallback)
  pointClickCallback = JS(pointClickCallback)
  underlayCallback = JS(underlayCallback)
)

Another example is in the DT (DataTables) widget, where users can specify an initCallback with JavaScript to execute after the table is loaded and initialized:

datatable(head(iris, 20), options = list(
  initComplete = JS(
    "function(settings, json) {",
    "$(this.api().table().header()).css({'background-color': '#000', 'color': '#fff'});",
    "}")
))

If multiple arguments are passed to JS() (as in the above example), they will be concatenated into a single string separated by \n.

Custom widget HTML

Typically the HTML “housing” for a widget is just a <div> element, and this is correspondingly the default behavior for new widgets that don’t specify otherwise. However, sometimes you need a different element type. For example, the sparkline widget requires a <span> element so implements the following custom HTML generation function:

sparkline_html <- function(id, style, class, ...){
  tags$span(id = id, class = class)
}

Note that this function is looked up within the package implementing the widget by the convention widgetname_html so it need not be formally exported from your package or otherwise registered with htmlwidgets.

Most widgets won’t need a custom HTML function but if you need to generate custom HTML for your widget (e.g. you need an <input> or a <span> rather than a <div>) then you should use the htmltools package (as demonstrated by the code above).


  1. N.B. It is not exported from htmlwidgets, so you are not supposed to call this function directly.

htmlwidgets/inst/doc/develop_intro.Rmd0000644000176200001440000004102013130653463017645 0ustar liggesusers--- title: "Introduction to HTML Widgets" date: "`r Sys.Date()`" output: html_document: highlight: kate toc: true toc_depth: 4 mathjax: null vignette: > %\VignetteIndexEntry{Introduction} %\VignetteEngine{knitr::rmarkdown} \usepackage[utf8]{inputenc} --- ## Overview The **[htmlwidgets](https://cran.r-project.org/package=htmlwidgets)** package provides a framework for creating R bindings to JavaScript libraries. HTML Widgets can be: * Used at the R console for data analysis just like conventional R plots. * Embedded within [R Markdown](http://rmarkdown.rstudio.com) documents * Incorporated into [Shiny](http://shiny.rstudio.com) web applications. * Saved as standalone web pages for ad-hoc sharing via email, Dropbox, etc. By following a small set of easy-to-follow conventions, it is possible to create HTML widgets with very little code. All widgets include the following components: 1. **Dependencies**. These are the JavaScript and CSS assets used by the widget (e.g. the library you are creating a wrapper for). 3. **R binding**. This is the function that end users will call to provide input data to the widget as well as specify various options for how the widget should render. This also includes some short boilerplate functions required to use the widget within Shiny applications. 3. **JavaScript binding**. This is the JavaScript code that glues everything together, passing the data and options gathered in the R binding to the underlying JavaScript library. HTML widgets are always hosted within an R package and should include all of the source code for their dependencies. This is to ensure that code which depends on widgets is fully reproducible (i.e. doesn't require an internet connection or the ongoing availability of an internet service to run). ## Example (sigma.js) To start with we'll walk through the creation of a simple widget that wraps the [sigma.js](http://sigmajs.org) graph visualization library. When we're done we'll be able to use it to display interactive visualizations of [GEXF](http://gexf.net) (Graph Exchange XML Format) data files. For example: ```{r, eval=FALSE} library(sigma) data <- system.file("examples/ediaspora.gexf.xml", package = "sigma") sigma(data) ``` sigma Note that the above is just an image of the visualization so it's not interactive. You can play with the interactive version by following the steps in the demo section below. There is remarkably little code required to create this binding. Below we'll go through all of the components step-by-step. Then we'll describe how you can create your own widgets (including automatically generating basic scaffolding for all of the core components). ### File layout Let's assume that our widget is named **sigma** and is located within an R package of the same name. Our JavaScript binding source code file is named sigma.js. Since our widget will read GEXF data files we'll also need to include both the base sigma.min.js library as well as its GEXF plugin. Here are the files that we'll add to the package: ```text R/ | sigma.R inst/ |-- htmlwidgets/ | |-- sigma.js | |-- sigma.yaml | |-- lib/ | | |-- sigma-1.0.3/ | | | |-- sigma.min.js | | | |-- plugins/ | | | | |-- sigma.parsers.gexf.min.js ``` Note the convention that the JavaScript, YAML, and other dependencies are all contained within the `inst/htmlwidgets` directory (which will subsequently be installed into a package sub-directory named `htmlwidgets`). ### Dependencies Dependencies are the JavaScript and CSS assets used by a widget. Dependencies are included within the `inst/htmlwidgets/lib` directory. Dependencies are specified using a YAML configuration file which uses the name of the widget as its base file name. Here's what our **sigma.yaml** file looks like: ```yaml dependencies: - name: sigma version: 1.0.3 src: htmlwidgets/lib/sigma-1.0.3 script: - sigma.min.js - plugins/sigma.parsers.gexf.min.js ``` The dependency `src` specification refers to the directory that contains the library and `script` refers to specific JavaScript files. If your library contains multiple JavaScript files specify each one on a line beginning with `-` as shown here. You can also add `stylesheet` entries and even `meta` or `head` entries. Multiple dependencies may be specified in one YAML file. See the documentation on the `htmlDependency` function in the [**htmltools**](https://cran.r-project.org/package=htmltools) package for additional details. ### R binding We need to provide users with an R function that invokes our widget. Typically this function will accept input data as well as various options that control the widget's display. Here's the R function for `sigma`: ```r #' @import htmlwidgets #' @export sigma <- function(gexf, drawEdges = TRUE, drawNodes = TRUE, width = NULL, height = NULL) { # read the gexf file data <- paste(readLines(gexf), collapse="\n") # create a list that contains the settings settings <- list( drawEdges = drawEdges, drawNodes = drawNodes ) # pass the data and settings using 'x' x <- list( data = data, settings = settings ) # create the widget htmlwidgets::createWidget("sigma", x, width = width, height = height) } ``` The function takes two classes of input: the GEXF data file to render and some additional settings which control how it is rendered. This input is collected into a list named `x` which is then passed on to the `htmlwidgets::createWidget` function. This `x` variable will subsequently be made available to the JavaScript binding for sigma (this is described below). Any width or height parameter specified is also forwarded to the widget (widgets size themselves automatically by default so typically don't require an explicit width or height). We want our sigma widget to also work in Shiny applications, so we add the following boilerplate Shiny output and render functions (these are always the same for all widgets): ```r #' @export sigmaOutput <- function(outputId, width = "100%", height = "400px") { htmlwidgets::shinyWidgetOutput(outputId, "sigma", width, height, package = "sigma") } #' @export renderSigma <- function(expr, env = parent.frame(), quoted = FALSE) { if (!quoted) { expr <- substitute(expr) } # force quoted htmlwidgets::shinyRenderWidget(expr, sigmaOutput, env, quoted = TRUE) } ``` ### JavaScript binding _**Note:** An older, less intuitive JavaScript binding API was used in htmlwidgets 0.5.2 and earlier, and continues to be supported in newer versions of htmlwidgets. See this [archived version](https://cdn.rawgit.com/ramnathv/htmlwidgets/f735840bf938d35d3c4143c0d16515da6ff252bd/develop_intro.html#javascript-binding) for details on the legacy binding API. New widgets are encouraged to use the newer API described below._ The third piece in the puzzle is the JavaScript required to activate the widget. By convention we'll define our JavaScript binding in the file `inst/htmlwidgets/sigma.js`. Here is the full source code of the binding: ```javascript HTMLWidgets.widget({ name: "sigma", type: "output", factory: function(el, width, height) { // create our sigma object and bind it to the element var sig = new sigma(el.id); return { renderValue: function(x) { // parse gexf data var parser = new DOMParser(); var data = parser.parseFromString(x.data, "application/xml"); // apply settings for (var name in x.settings) sig.settings(name, x.settings[name]); // update the sigma object sigma.parsers.gexf( data, // parsed gexf data sig, // sigma object function() { // need to call refresh to reflect new settings and data sig.refresh(); } ); }, resize: function(width, height) { // forward resize on to sigma renderers for (var name in sig.renderers) sig.renderers[name].resize(width, height); }, // Make the sigma object available as a property on the widget // instance we're returning from factory(). This is generally a // good idea for extensibility--it helps users of this widget // interact directly with sigma, if needed. s: sig }; } }); ``` We provide a name and type for the widget, plus a `factory` function that takes `el` (the HTML element that will host this widget), `width`, and `height` (width and height of the HTML element, in pixels--you can always use `offsetWidth` and `offsetHeight` for this). The `factory` function should prepare the HTML element to start receiving values. In this case we create a new sigma element and pass it the `id` of the DOM element that hosts the widget on the page. We're going to need access to the sigma object later (to update its data and settings) so we save it as a variable `sig`. Note that variables declared directly inside of the factory function are tied to a particular widget instance/`el`. The return value of the `factory` function is called a _widget instance object_. It is a bridge between the htmlwidgets runtime, and the JavaScript visualization that you're wrapping. As the name implies, each widget instance object is responsible for managing a single widget instance on a page. The widget instance object you create must have one required method, and may have one optional method: 1. The required `renderValue` method actually pours our dynamic data and settings into the widget's DOM element. The `x` parameter contains the widget data and settings. We parse and update the GEXF data, apply the settings to our previously-created `sig` sigma object, and finally call `refresh` to reflect the new values on-screen. This method may be called repeatedly with different data (i.e. in Shiny), so be sure to account for that possibility. If it makes sense for your widget, consider making your visualization transition smoothly from one value of `x` to another. 2. The optional `resize` method is called whenever the element containing the widget is resized. The only reason not to implement this method is if your widget naturally scales (without additional JavaScript code needing to be invoked) when its element size changes. In the case of sigma.js, we forward the sizing information on to each of the underlying sigma renderers. All JavaScript libraries handle initialization, binding to DOM elements, dynamically updating data, and resizing slightly differently. Most of the work on the JavaScript side of creating widgets is mapping these three functions---`factory`, `renderValue`, and `resize`---correctly onto the behavior of the underlying library. The sigma.js example uses a simple object literal to create its widget instance object, but you can also use [class based objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript#Custom_objects) or any other style of object, as long as `obj.renderValue(x)` and `obj.resize(width, height)` can be invoked on it. You can add additional methods and properties on the widget instance object. Although they won't be called by htmlwidgets itself, they might be useful to users of your widget that know some JavaScript and want to further customize your widget by adding custom JS code (e.g. using the `htmlwidgets::onRender` R function). In this case we add an `s` property to make the sigma object itself available. ### Demo Our widget is now complete! If you want to test drive it without reproducing all of the code locally you can install it from GitHub as follows: ```r devtools::install_github('jjallaire/sigma') ``` Here's the code to try it out with some sample data included with the package: ```r library(sigma) sigma(system.file("examples/ediaspora.gexf.xml", package = "sigma")) ``` If you execute this code in the R console you'll see the widget displayed in the RStudio Viewer (or in an external browser if you aren't running RStudio). If you include it within an R Markdown document the widget will be embedded into the document. We can also use the widget in a Shiny application: ```r library(shiny) library(sigma) gexf <- system.file("examples/ediaspora.gexf.xml", package = "sigma") ui = shinyUI(fluidPage( checkboxInput("drawEdges", "Draw Edges", value = TRUE), checkboxInput("drawNodes", "Draw Nodes", value = TRUE), sigmaOutput('sigma') )) server = function(input, output) { output$sigma <- renderSigma( sigma(gexf, drawEdges = input$drawEdges, drawNodes = input$drawNodes) ) } shinyApp(ui = ui, server = server) ``` ## Creating your own widgets ### Requirements To implement a widget you need to create a new R package that in turn depends on the **htmlwidgets** package. You can install the package from CRAN as follows: ```r install.packages("htmlwidgets") ``` While it's not strictly required, the step-by-step instructions below for getting started also make use of the **devtools** package which you can also install from CRAN: ```r install.packages("devtools") ``` ### Scaffolding To create a new widget you can call the `scaffoldWidget` function to generate the basic structure for your widget. This function will: * Create the .R, .js, and .yaml files required for your widget; * If provided, take a [Bower](https://bower.io/) package name and automatically download the JavaScript library (and its dependencies) and add the required entries to the .yaml file. This method is highly recommended as it ensures that you get started with the right file structure. Here's an example that assumes you want to create a widget named 'mywidget' in a new package of the same name: ```r devtools::create("mywidget") # create package using devtools setwd("mywidget") # navigate to package dir htmlwidgets::scaffoldWidget("mywidget") # create widget scaffolding devtools::install() # install the package so we can try it ``` This creates a simple widget that takes a single `text` argument and displays that text within the widgets HTML element. You can try it like this: ```r library(mywidget) mywidget("hello, world") ``` This is the most minimal widget possible and doesn't yet include a JavaScript library to interface to (note that `scaffoldWidget` can optionally include JavaScript library dependencies via the `bowerPkg` argument). Before getting started with development you should review the introductory example above to make sure you understand the various components and also review the additional articles and examples linked to in the next section. ### Learning more #### Additional articles There are additional articles that cover more advanced ground: * [HTML Widget Sizing](develop_sizing.html) explains custom sizing policies and when you might need to use them and describes implementing a `resize` method within JavaScript bindings. * [HTML Widgets: Advanced Topics](develop_advanced.html) describes framework features that support per-widget instance data, data transformations (e.g. converting a data frame into a d3 dataset), and providing widget options that are live JavaScript objects (e.g. function definitions). The Sizing article is particularly important as most JavaScript libraries require some additional interaction to keep their size synchronized with their containing element. #### Examples Studying the code of other packages is a great way to learn more about creating widgets: 1. The [networkD3](https://github.com/christophergandrud/networkD3) package illustrates creating a widget on top of [D3](http://d3js.org), using a custom sizing policy for a larger widget, and providing multiple widgets from a single package. 2. The [dygraphs](https://github.com/rstudio/dygraphs/) package illustrates using widget instance data, handling dynamic re-sizing, and using [magrittr](https://github.com/smbache/magrittr) to decompose a large and flat JavaScript API into a more modular and pipeable R API. 3. The [sparkline](https://github.com/htmlwidgets/sparkline) package illustrates providing a custom HTML generation function (since sparklines must be housed in `` rather than `
` elements). #### Questions and issues If you have questions about developing widgets or run into problems during development please don't hesitate to [post an issue](https://github.com/ramnathv/htmlwidgets/issues) on the project's GitHub repository. htmlwidgets/inst/doc/develop_intro.html0000644000176200001440000317340413230461367020107 0ustar liggesusers Introduction to HTML Widgets

Overview

The htmlwidgets package provides a framework for creating R bindings to JavaScript libraries. HTML Widgets can be:

  • Used at the R console for data analysis just like conventional R plots.
  • Embedded within R Markdown documents
  • Incorporated into Shiny web applications.
  • Saved as standalone web pages for ad-hoc sharing via email, Dropbox, etc.

By following a small set of easy-to-follow conventions, it is possible to create HTML widgets with very little code. All widgets include the following components:

  1. Dependencies. These are the JavaScript and CSS assets used by the widget (e.g. the library you are creating a wrapper for).

  2. R binding. This is the function that end users will call to provide input data to the widget as well as specify various options for how the widget should render. This also includes some short boilerplate functions required to use the widget within Shiny applications.

  3. JavaScript binding. This is the JavaScript code that glues everything together, passing the data and options gathered in the R binding to the underlying JavaScript library.

HTML widgets are always hosted within an R package and should include all of the source code for their dependencies. This is to ensure that code which depends on widgets is fully reproducible (i.e. doesn’t require an internet connection or the ongoing availability of an internet service to run).

Example (sigma.js)

To start with we’ll walk through the creation of a simple widget that wraps the sigma.js graph visualization library. When we’re done we’ll be able to use it to display interactive visualizations of GEXF (Graph Exchange XML Format) data files. For example:

library(sigma)
data <- system.file("examples/ediaspora.gexf.xml", package = "sigma")
sigma(data)

sigma

Note that the above is just an image of the visualization so it’s not interactive. You can play with the interactive version by following the steps in the demo section below.

There is remarkably little code required to create this binding. Below we’ll go through all of the components step-by-step. Then we’ll describe how you can create your own widgets (including automatically generating basic scaffolding for all of the core components).

File layout

Let’s assume that our widget is named sigma and is located within an R package of the same name. Our JavaScript binding source code file is named sigma.js. Since our widget will read GEXF data files we’ll also need to include both the base sigma.min.js library as well as its GEXF plugin. Here are the files that we’ll add to the package:

R/
| sigma.R

inst/
|-- htmlwidgets/
|   |-- sigma.js
|   |-- sigma.yaml
|   |-- lib/
|   |   |-- sigma-1.0.3/
|   |   |   |-- sigma.min.js
|   |   |   |-- plugins/
|   |   |   |   |-- sigma.parsers.gexf.min.js

Note the convention that the JavaScript, YAML, and other dependencies are all contained within the inst/htmlwidgets directory (which will subsequently be installed into a package sub-directory named htmlwidgets).

Dependencies

Dependencies are the JavaScript and CSS assets used by a widget. Dependencies are included within the inst/htmlwidgets/lib directory. Dependencies are specified using a YAML configuration file which uses the name of the widget as its base file name. Here’s what our sigma.yaml file looks like:

dependencies:
  - name: sigma
    version: 1.0.3
    src: htmlwidgets/lib/sigma-1.0.3
    script: 
      - sigma.min.js
      - plugins/sigma.parsers.gexf.min.js

The dependency src specification refers to the directory that contains the library and script refers to specific JavaScript files. If your library contains multiple JavaScript files specify each one on a line beginning with - as shown here. You can also add stylesheet entries and even meta or head entries. Multiple dependencies may be specified in one YAML file. See the documentation on the htmlDependency function in the htmltools package for additional details.

R binding

We need to provide users with an R function that invokes our widget. Typically this function will accept input data as well as various options that control the widget’s display. Here’s the R function for sigma:

#' @import htmlwidgets
#' @export
sigma <- function(gexf, drawEdges = TRUE, drawNodes = TRUE,
                  width = NULL, height = NULL) {
  
  # read the gexf file
  data <- paste(readLines(gexf), collapse="\n")
  
  # create a list that contains the settings
  settings <- list(
    drawEdges = drawEdges,
    drawNodes = drawNodes
  )
  
  # pass the data and settings using 'x'
  x <- list(
    data = data,
    settings = settings
  )
  
  # create the widget
  htmlwidgets::createWidget("sigma", x, width = width, height = height)
}

The function takes two classes of input: the GEXF data file to render and some additional settings which control how it is rendered. This input is collected into a list named x which is then passed on to the htmlwidgets::createWidget function. This x variable will subsequently be made available to the JavaScript binding for sigma (this is described below). Any width or height parameter specified is also forwarded to the widget (widgets size themselves automatically by default so typically don’t require an explicit width or height).

We want our sigma widget to also work in Shiny applications, so we add the following boilerplate Shiny output and render functions (these are always the same for all widgets):

#' @export
sigmaOutput <- function(outputId, width = "100%", height = "400px") {
  htmlwidgets::shinyWidgetOutput(outputId, "sigma", width, height, package = "sigma")
}
#' @export
renderSigma <- function(expr, env = parent.frame(), quoted = FALSE) {
  if (!quoted) { expr <- substitute(expr) } # force quoted
  htmlwidgets::shinyRenderWidget(expr, sigmaOutput, env, quoted = TRUE)
}

JavaScript binding

Note: An older, less intuitive JavaScript binding API was used in htmlwidgets 0.5.2 and earlier, and continues to be supported in newer versions of htmlwidgets. See this archived version for details on the legacy binding API. New widgets are encouraged to use the newer API described below.

The third piece in the puzzle is the JavaScript required to activate the widget. By convention we’ll define our JavaScript binding in the file inst/htmlwidgets/sigma.js. Here is the full source code of the binding:

HTMLWidgets.widget({

  name: "sigma",
  
  type: "output",
  
  factory: function(el, width, height) {
  
    // create our sigma object and bind it to the element
    var sig = new sigma(el.id);
    
    return {
      renderValue: function(x) {
          
        // parse gexf data
        var parser = new DOMParser();
        var data = parser.parseFromString(x.data, "application/xml");
        
        // apply settings
        for (var name in x.settings)
          sig.settings(name, x.settings[name]);
        
        // update the sigma object
        sigma.parsers.gexf(
          data,          // parsed gexf data
          sig,           // sigma object
          function() {
            // need to call refresh to reflect new settings and data
            sig.refresh();
          }
        );
      },
      
      resize: function(width, height) {
        
        // forward resize on to sigma renderers
        for (var name in sig.renderers)
          sig.renderers[name].resize(width, height);  
      },
      
      // Make the sigma object available as a property on the widget
      // instance we're returning from factory(). This is generally a
      // good idea for extensibility--it helps users of this widget
      // interact directly with sigma, if needed.
      s: sig
    };
  }
});

We provide a name and type for the widget, plus a factory function that takes el (the HTML element that will host this widget), width, and height (width and height of the HTML element, in pixels–you can always use offsetWidth and offsetHeight for this).

The factory function should prepare the HTML element to start receiving values. In this case we create a new sigma element and pass it the id of the DOM element that hosts the widget on the page.

We’re going to need access to the sigma object later (to update its data and settings) so we save it as a variable sig. Note that variables declared directly inside of the factory function are tied to a particular widget instance/el.

The return value of the factory function is called a widget instance object. It is a bridge between the htmlwidgets runtime, and the JavaScript visualization that you’re wrapping. As the name implies, each widget instance object is responsible for managing a single widget instance on a page.

The widget instance object you create must have one required method, and may have one optional method:

  1. The required renderValue method actually pours our dynamic data and settings into the widget’s DOM element. The x parameter contains the widget data and settings. We parse and update the GEXF data, apply the settings to our previously-created sig sigma object, and finally call refresh to reflect the new values on-screen. This method may be called repeatedly with different data (i.e. in Shiny), so be sure to account for that possibility. If it makes sense for your widget, consider making your visualization transition smoothly from one value of x to another.

  2. The optional resize method is called whenever the element containing the widget is resized. The only reason not to implement this method is if your widget naturally scales (without additional JavaScript code needing to be invoked) when its element size changes. In the case of sigma.js, we forward the sizing information on to each of the underlying sigma renderers.

All JavaScript libraries handle initialization, binding to DOM elements, dynamically updating data, and resizing slightly differently. Most of the work on the JavaScript side of creating widgets is mapping these three functions—factory, renderValue, and resize—correctly onto the behavior of the underlying library.

The sigma.js example uses a simple object literal to create its widget instance object, but you can also use class based objects or any other style of object, as long as obj.renderValue(x) and obj.resize(width, height) can be invoked on it.

You can add additional methods and properties on the widget instance object. Although they won’t be called by htmlwidgets itself, they might be useful to users of your widget that know some JavaScript and want to further customize your widget by adding custom JS code (e.g. using the htmlwidgets::onRender R function). In this case we add an s property to make the sigma object itself available.

Demo

Our widget is now complete! If you want to test drive it without reproducing all of the code locally you can install it from GitHub as follows:

devtools::install_github('jjallaire/sigma')

Here’s the code to try it out with some sample data included with the package:

library(sigma)
sigma(system.file("examples/ediaspora.gexf.xml", package = "sigma"))

If you execute this code in the R console you’ll see the widget displayed in the RStudio Viewer (or in an external browser if you aren’t running RStudio). If you include it within an R Markdown document the widget will be embedded into the document.

We can also use the widget in a Shiny application:

library(shiny)
library(sigma)

gexf <- system.file("examples/ediaspora.gexf.xml", package = "sigma")

ui = shinyUI(fluidPage(
  checkboxInput("drawEdges", "Draw Edges", value = TRUE),
  checkboxInput("drawNodes", "Draw Nodes", value = TRUE),
  sigmaOutput('sigma')
))

server = function(input, output) {
  output$sigma <- renderSigma(
    sigma(gexf, 
          drawEdges = input$drawEdges, 
          drawNodes = input$drawNodes)
  )
}

shinyApp(ui = ui, server = server)

Creating your own widgets

Requirements

To implement a widget you need to create a new R package that in turn depends on the htmlwidgets package. You can install the package from CRAN as follows:

install.packages("htmlwidgets")

While it’s not strictly required, the step-by-step instructions below for getting started also make use of the devtools package which you can also install from CRAN:

install.packages("devtools")

Scaffolding

To create a new widget you can call the scaffoldWidget function to generate the basic structure for your widget. This function will:

  • Create the .R, .js, and .yaml files required for your widget;

  • If provided, take a Bower package name and automatically download the JavaScript library (and its dependencies) and add the required entries to the .yaml file.

This method is highly recommended as it ensures that you get started with the right file structure. Here’s an example that assumes you want to create a widget named ‘mywidget’ in a new package of the same name:

devtools::create("mywidget")               # create package using devtools
setwd("mywidget")                          # navigate to package dir
htmlwidgets::scaffoldWidget("mywidget")    # create widget scaffolding
devtools::install()                        # install the package so we can try it

This creates a simple widget that takes a single text argument and displays that text within the widgets HTML element. You can try it like this:

library(mywidget)
mywidget("hello, world")

This is the most minimal widget possible and doesn’t yet include a JavaScript library to interface to (note that scaffoldWidget can optionally include JavaScript library dependencies via the bowerPkg argument). Before getting started with development you should review the introductory example above to make sure you understand the various components and also review the additional articles and examples linked to in the next section.

Learning more

Additional articles

There are additional articles that cover more advanced ground:

  • HTML Widget Sizing explains custom sizing policies and when you might need to use them and describes implementing a resize method within JavaScript bindings.

  • HTML Widgets: Advanced Topics describes framework features that support per-widget instance data, data transformations (e.g. converting a data frame into a d3 dataset), and providing widget options that are live JavaScript objects (e.g. function definitions).

The Sizing article is particularly important as most JavaScript libraries require some additional interaction to keep their size synchronized with their containing element.

Examples

Studying the code of other packages is a great way to learn more about creating widgets:

  1. The networkD3 package illustrates creating a widget on top of D3, using a custom sizing policy for a larger widget, and providing multiple widgets from a single package.

  2. The dygraphs package illustrates using widget instance data, handling dynamic re-sizing, and using magrittr to decompose a large and flat JavaScript API into a more modular and pipeable R API.

  3. The sparkline package illustrates providing a custom HTML generation function (since sparklines must be housed in <span> rather than <div> elements).

Questions and issues

If you have questions about developing widgets or run into problems during development please don’t hesitate to post an issue on the project’s GitHub repository.

htmlwidgets/NAMESPACE0000644000176200001440000000101713130653463014027 0ustar liggesusers# Generated by roxygen2: do not edit by hand S3method(as.tags,htmlwidget) S3method(print,htmlwidget) S3method(print,suppress_viewer) export(JS) export(appendContent) export(createWidget) export(getDependency) export(onRender) export(onStaticRenderComplete) export(prependContent) export(saveWidget) export(scaffoldWidget) export(setWidgetIdSeed) export(shinyRenderWidget) export(shinyWidgetOutput) export(sizingPolicy) import(htmltools) importFrom(utils,browseURL) importFrom(utils,file.edit) importFrom(utils,packageVersion) htmlwidgets/R/0000755000176200001440000000000013230460521013002 5ustar liggesusershtmlwidgets/R/utils.R0000644000176200001440000001730013130653463014276 0ustar liggesusers# Copied from shiny 0.14.2 toJSON2 <- function( x, ..., dataframe = "columns", null = "null", na = "null", auto_unbox = TRUE, digits = getOption("shiny.json.digits", 16), use_signif = TRUE, force = TRUE, POSIXt = "ISO8601", UTC = TRUE, rownames = FALSE, keep_vec_names = TRUE, strict_atomic = TRUE ) { if (strict_atomic) x <- I(x) jsonlite::toJSON( x, dataframe = dataframe, null = null, na = na, auto_unbox = auto_unbox, digits = digits, use_signif = use_signif, force = force, POSIXt = POSIXt, UTC = UTC, rownames = rownames, keep_vec_names = keep_vec_names, json_verbatim = TRUE, ... ) } if (requireNamespace('shiny') && packageVersion('shiny') >= '0.12.0') local({ tryCatch({ toJSON <- getFromNamespace('toJSON', 'shiny') args2 <- formals(toJSON2) args1 <- formals(toJSON) if (!identical(args1, args2)) { warning('Check shiny:::toJSON and make sure htmlwidgets:::toJSON is in sync') } }) }) toJSON <- function(x) { if (!is.list(x) || !('x' %in% names(x))) return(toJSON2(x)) func <- attr(x$x, 'TOJSON_FUNC', exact = TRUE) args <- attr(x$x, 'TOJSON_ARGS', exact = TRUE) if (length(args) == 0) args <- getOption('htmlwidgets.TOJSON_ARGS') if (!is.function(func)) func <- toJSON2 res <- if (length(args) == 0) func(x) else do.call(func, c(list(x = x), args)) # make sure shiny:::toJSON() does not encode it again structure(res, class = 'json') } #' Get js and css dependencies for a htmlwidget #' #' @param name name of the widget. #' @param package name of the package, defaults to the widget name. #' @export getDependency <- function(name, package = name){ config = sprintf("htmlwidgets/%s.yaml", name) jsfile = sprintf("htmlwidgets/%s.js", name) config = yaml::yaml.load_file( system.file(config, package = package) ) widgetDep <- lapply(config$dependencies, function(l){ l$src = system.file(l$src, package = package) do.call(htmlDependency, l) }) bindingDir <- system.file("htmlwidgets", package = package) argsDep <- NULL copyBindingDir <- getOption('htmlwidgets.copybindingdir', TRUE) # TODO: remove this trick when htmltools >= 0.3.3 is on CRAN if (copyBindingDir) { if (packageVersion('htmltools') < '0.3.3') { bindingDir <- tempfile("widgetbinding") dir.create(bindingDir, mode = "0700") file.copy(system.file(jsfile, package = package), bindingDir) } else argsDep <- list(all_files = FALSE) } bindingDep <- do.call(htmlDependency, c(list( paste0(name, "-binding"), packageVersion(package), bindingDir, script = basename(jsfile) ), argsDep)) c( list(htmlDependency("htmlwidgets", packageVersion("htmlwidgets"), src = system.file("www", package="htmlwidgets"), script = "htmlwidgets.js" )), widgetDep, list(bindingDep) ) } `%||%` <- function(x, y){ if (is.null(x)) y else x } prop <- function(x, path) { tryCatch({ for (i in strsplit(path, "$", fixed = TRUE)[[1]]) { if (is.null(x)) return(NULL) x <- x[[i]] } return(x) }, error = function(e) { return(NULL) }) } any_prop <- function(scopes, path) { for (scope in scopes) { result <- prop(scope, path) if (!is.null(result)) return(result) } return(NULL) } #' Mark character strings as literal JavaScript code #' #' This function \code{JS()} marks character vectors with a special class, so #' that it will be treated as literal JavaScript code when evaluated on the #' client-side. #' @param ... character vectors as the JavaScript source code (all arguments #' will be pasted into one character string) #' @author Yihui Xie #' @export #' @examples library(htmlwidgets) #' JS('1 + 1') #' list(x = JS('function(foo) {return foo;}'), y = 1:10) #' JS('function(x) {', 'return x + 1;', '}') JS <- function(...) { x <- c(...) if (is.null(x)) return() if (!is.character(x)) stop("The arguments for JS() must be a character vector") x <- paste(x, collapse = '\n') structure(x, class = unique(c("JS_EVAL", oldClass(x)))) } # Creates a list of keys whose values need to be evaluated on the client-side. # # It works by transforming \code{list(foo = list(1, list(bar = # I('function(){}')), 2))} to \code{list("foo.2.bar")}. Later on the JS side, we # will split foo.2.bar to ['foo', '2', 'bar'] and evaluate the JSON object # member. Note '2' (character) should have been 2 (integer) but it does not seem # to matter in JS: x[2] is the same as x['2'] when all child members of x are # unnamed, and ('2' in x) will be true even if x is an array without names. This # is a little hackish. # # @param list a list in which the elements that should be evaluated as # JavaScript are to be identified # @author Yihui Xie JSEvals <- function(list) { # the `%||% list()` part is necessary as of R 3.4.0 (April 2017) -- if `evals` # is NULL then `I(evals)` results in a warning in R 3.4.0. This is circumvented # if we let `evals` be equal to `list()` in those cases evals <- names(which(unlist(shouldEval(list)))) %||% list() I(evals) # need I() to prevent toJSON() from converting it to scalar } #' JSON elements that are character with the class JS_EVAL will be evaluated #' #' @noRd #' @keywords internal shouldEval <- function(options) { if (is.list(options)) { if ((n <- length(options)) == 0) return(FALSE) # use numeric indices as names (remember JS indexes from 0, hence -1 here) if (is.null(names(options))) names(options) <- seq_len(n) - 1L # Escape '\' and '.' by prefixing them with '\'. This allows us to tell the # difference between periods as separators and periods that are part of the # name itself. names(options) <- gsub("([\\.])", "\\\\\\1", names(options)) nms <- names(options) if (length(nms) != n || any(nms == '')) stop("'options' must be a fully named list, or have no names (NULL)") lapply(options, shouldEval) } else { is.character(options) && inherits(options, 'JS_EVAL') } } # JSEvals(list(list(foo.bar=JS("hi"), baz.qux="bye"))) == "0.foo\\.bar" #' Execute JavaScript code after static render #' #' Convenience function for wrapping a JavaScript code string with a #' \code{ to appear inside of a script tag, even if it's # inside a quoted string. Fortunately we know that in JSON, the only place # the '<' character can appear is inside a quoted string, where a Unicode # escape has the same effect, without confusing the browser's parser. The # repro for the bug this gsub fixes is to have the string "" appear # anywhere in the data/metadata of a widget--you will get a syntax error # instead of a properly rendered widget. # # Another issue is that if appears inside a quoted string, # then when pandoc coverts it with --self-contained, the escaping gets messed # up. There may be other patterns that trigger this behavior, so to be safe # we can replace all instances of "= ([0-9.]+)\\).*$' if (is.na(x) || length(grep(r, x)) == 0 || system.file(package = 'shiny') == '') return() v <- gsub(r, '\\1', x) f <- if (error) stop else packageStartupMessage if (utils::packageVersion('shiny') < v) f("Please upgrade the 'shiny' package to (at least) version ", v) } # Helper function to create payload createPayload <- function(instance){ if (!is.null(instance$preRenderHook)){ instance <- instance$preRenderHook(instance) instance$preRenderHook <- NULL } x <- .subset2(instance, "x") list(x = x, evals = JSEvals(x), jsHooks = instance$jsHooks) } # package globals .globals <- new.env(parent = emptyenv()) htmlwidgets/R/savewidget.R0000644000176200001440000000363413230460521015275 0ustar liggesusers#' Save a widget to an HTML file #' #' Save a rendered widget to an HTML file (e.g. for sharing with others). #' #' @param widget Widget to save #' @param file File to save HTML into #' @param selfcontained Whether to save the HTML as a single self-contained file #' (with external resources base64 encoded) or a file with external resources #' placed in an adjacent directory. #' @param libdir Directory to copy HTML dependencies into (defaults to #' filename_files). #' @param background Text string giving the html background color of the widget. #' Defaults to white. #' @param title Text to use as the title of the generated page. #' @param knitrOptions A list of \pkg{knitr} chunk options. #' @export saveWidget <- function(widget, file, selfcontained = TRUE, libdir = NULL, background = "white", title = class(widget)[[1]], knitrOptions = list()) { # convert to HTML tags html <- toHTML(widget, standalone = TRUE, knitrOptions = knitrOptions) # form a path for dependenent files if (is.null(libdir)){ libdir <- paste(tools::file_path_sans_ext(basename(file)), "_files", sep = "") } # make it self-contained if requested if (selfcontained) { # Save the file # Include a title; pandoc 2.0 complains if you don't have one pandoc_save_markdown(html, file = file, libdir = libdir, background = background, title = title) if (!pandoc_available()) { stop("Saving a widget with selfcontained = TRUE requires pandoc. For details see:\n", "https://github.com/rstudio/rmarkdown/blob/master/PANDOC.md") } pandoc_self_contained_html(file, file) unlink(libdir, recursive = TRUE) } else { # no pandoc needed if not selfcontained html <- tagList(tags$head(tags$title(title)), html) htmltools::save_html(html, file = file, libdir = libdir, background = background) } invisible(NULL) } htmlwidgets/R/seed.R0000644000176200001440000000000013130653463014043 0ustar liggesusershtmlwidgets/R/sizing.R0000644000176200001440000002273013130653463014444 0ustar liggesusers#' Create a widget sizing policy #' #' Define the policy by which HTML widgets will be sized in various containers #' (e.g. Browser, RStudio Viewer, R Markdown, Shiny). Note that typically #' widgets can accept the default sizing policy (or override only one or two #' aspects of it) and get satisfactory sizing behavior via the automatic sizing #' logic built into the htmlwidgets framework (see the notes below for the most #' typical exceptions to this). #' #' @param defaultWidth The default width used to display the widget. This #' parameter specifies the default width for viewing in all contexts (browser, #' viewer, and knitr) unless it is specifically overridden with e.g. #' \code{browser.defaultWidth}. #' @param viewer.defaultWidth The default width used to display the widget #' within the RStudio Viewer. #' @param browser.defaultWidth The default width used to display the widget #' within a standalone web browser. #' @param knitr.defaultWidth The default width used to display the widget within #' documents generated by knitr (e.g. R Markdown). #' @param defaultHeight The default height used to display the widget. This #' parameter specifies the default height for viewing in all contexts #' (browser, viewer, and knitr) unless it is specifically overridden with e.g. #' \code{browser.defaultHeight}. #' @param viewer.defaultHeight The default height used to display the widget #' within the RStudio Viewer. #' @param browser.defaultHeight The default height used to display the widget #' within a standalone web browser. #' @param knitr.defaultHeight The default height used to display the widget #' within documents generated by knitr (e.g. R Markdown). #' @param padding Padding around the widget (in pixels). This parameter #' specifies the padding for viewing in all contexts (browser and viewer) #' unless it is specifically overriden by e.g. \code{browser.padding}. #' @param browser.padding Padding around the widget when displayed in a #' standalone browser (defaults to 40 pixels). #' @param viewer.padding Padding around the widget when displayed in the RStudio #' Viewer (defaults to 15 pixels). #' @param viewer.fill When displayed in the RStudio Viewer, automatically size #' the widget to the viewer dimensions (note that \code{viewer.padding} is #' still applied). Default to \code{TRUE}. #' @param browser.fill When displayed in a standalone web browser, automatically #' size the widget to the browser dimensions (note that \code{browser.padding} #' is still applied). Defaults to \code{FALSE}. #' @param viewer.paneHeight Request that the RStudio Viewer be forced to a #' specific height when displaying this widget. #' @param viewer.suppress Never display the widget within the RStudio Viewer #' (useful for widgets that require a large amount of space for rendering). #' Defaults to \code{FALSE}. #' @param knitr.figure Apply the default knitr fig.width and fig.height to the #' widget when it's rendered within R Markdown documents. Defaults to #' \code{TRUE}. #' #' @return A widget sizing policy #' #' @details #' #' The default HTML widget sizing policy treats the widget with the same sizing #' semantics as an R plot. When printed at the R console the widget is displayed #' within the RStudio Viewer and sized to fill the Viewer pane (modulo any #' padding). When rendered inside an R Markdown document the widget is sized #' based on the default size of figures in the document. #' #' You might need to change the default behavior if your widget is extremely #' large. In this case you might specify \code{viewer.suppress = TRUE} and #' \code{knitr.figure = FALSE} as well provide for a larger default width and #' height for knitr. #' #' You also might need to change the default behavior if you widget already #' incorporates padding. In this case you might specify \code{viewer.padding = #' 0}. #' #' For additional details on widget sizing: #' #' \code{vignette("develop_sizing", package = "htmlwidgets")} #' #' #' @export sizingPolicy <- function( defaultWidth = NULL, defaultHeight = NULL, padding = NULL, viewer.defaultWidth = NULL, viewer.defaultHeight = NULL, viewer.padding = NULL, viewer.fill = TRUE, viewer.suppress = FALSE, viewer.paneHeight = NULL, browser.defaultWidth = NULL, browser.defaultHeight = NULL, browser.padding = NULL, browser.fill = FALSE, knitr.defaultWidth = NULL, knitr.defaultHeight = NULL, knitr.figure = TRUE) { list( defaultWidth = defaultWidth, defaultHeight = defaultHeight, padding = padding, viewer = list( defaultWidth = viewer.defaultWidth, defaultHeight = viewer.defaultHeight, padding = viewer.padding, fill = viewer.fill, suppress = viewer.suppress, paneHeight = viewer.paneHeight ), browser = list( defaultWidth = browser.defaultWidth, defaultHeight = browser.defaultHeight, padding = browser.padding, fill = browser.fill ), knitr = list( defaultWidth = knitr.defaultWidth, defaultHeight = knitr.defaultHeight, figure = knitr.figure ) ) } DEFAULT_WIDTH <- 960 DEFAULT_HEIGHT <- 500 DEFAULT_PADDING <- 40 DEFAULT_WIDTH_VIEWER <- 450 DEFAULT_HEIGHT_VIEWER <- 350 DEFAULT_PADDING_VIEWER <- 15 #' Resolve widget sizing policy #' #' Take a widget object and sizing policy, and some other contextual details, #' and figure out what width/height to use, if possible. Some decisions may need #' to be deferred until runtime; include any metadata that's needed for that #' decision in the result as well. #' #' @param x The widget object whose size is to be determined. It may have $width #' and $height directly on it, which means we should obey those. #' @param sp The sizing policy to use. #' @param standalone Logical value indicating whether the widget is being #' rendered in a standalone context (where it's the only thing on the page; #' this is usually via `print.htmlwidget()`). #' @param knitrOptions Object representing the knitr options passed to us via #' `knit_print`. If we're not doing a `knit_print` right now, then the value #' should be `NULL`. #' @return A list that is guaranteed to have `width` and `height` values, each of #' which is either a number or CSS unit string. If `standalone=TRUE` then the #' list will also have a `runtime` value that is a list, that contains two #' nested lists `viewer` and `browser`. Each of those in turn has `width`, #' `height`, `padding` (between 1 and 4 numbers), and `fill` (`TRUE`/`FALSE`). #' @keywords internal #' @examples #' x <- list( #' sizingPolicy = list( #' defaultWidth = 800, #' defaultHeight = 500, #' padding = 15, #' viewer = list( #' fill = TRUE, #' padding = 0 #' ), #' browser = list( #' fill = FALSE, #' defaultWidth = 960, #' defaultHeight = 600, #' padding = 20 #' ), #' knitr = list( #' # Actually knitr$defaultWidth and knitr$defaultHeight #' # are ignored if figure = TRUE #' defaultWidth = 800, #' defaultHeight = 600, #' figure = TRUE #' ) #' ) #' ) #' #' # Sizing for standalone mode #' str(resolveSizing(x, x$sizingPolicy, TRUE, NULL)) #' # Sizing for knitr #' str(resolveSizing(x, x$sizingPolicy, FALSE, #' list(out.width.px = 150, out.height.px = 100))) #' #' # Explicit width/height provided by user--overrides any #' # default width/height #' x$width <- 300 #' x$height <- 250 #' str(resolveSizing(x, x$sizingPolicy, FALSE, #' list(out.width.px = 150, out.height.px = 100))) #' @keywords internal #' @noRd resolveSizing <- function(x, sp, standalone, knitrOptions = NULL) { if (isTRUE(standalone)) { userSized <- !is.null(x$width) || !is.null(x$height) viewerScopes <- list(sp$viewer, sp) browserScopes <- list(sp$browser, sp) # Precompute the width, height, padding, and fill for each scenario. return(list( runtime = list( viewer = list( width = x$width %||% any_prop(viewerScopes, "defaultWidth") %||% DEFAULT_WIDTH_VIEWER, height = x$height %||% any_prop(viewerScopes, "defaultHeight") %||% DEFAULT_HEIGHT_VIEWER, padding = any_prop(viewerScopes, "padding") %||% DEFAULT_PADDING_VIEWER, fill = !userSized && any_prop(viewerScopes, "fill") %||% TRUE ), browser = list( width = x$width %||% any_prop(browserScopes, "defaultWidth") %||% DEFAULT_WIDTH, height = x$height %||% any_prop(browserScopes, "defaultHeight") %||% DEFAULT_HEIGHT, padding = any_prop(browserScopes, "padding") %||% DEFAULT_PADDING, fill = !userSized && any_prop(browserScopes, "fill") %||% FALSE ) ), width = x$width %||% prop(sp, "defaultWidth") %||% DEFAULT_WIDTH, height = x$height %||% prop(sp, "defaultHeight") %||% DEFAULT_HEIGHT )) } else if (!is.null(knitrOptions)) { knitrScopes <- list(sp$knitr, sp) isFigure <- any_prop(knitrScopes, "figure") figWidth <- if (isFigure) knitrOptions$out.width.px else NULL figHeight <- if (isFigure) knitrOptions$out.height.px else NULL # Compute the width and height return(list( width = x$width %||% figWidth %||% any_prop(knitrScopes, "defaultWidth") %||% DEFAULT_WIDTH, height = x$height %||% figHeight %||% any_prop(knitrScopes, "defaultHeight") %||% DEFAULT_HEIGHT )) } else { # Some non-knitr, non-print scenario. # Just resolve the width/height vs. defaultWidth/defaultHeight return(list( width = x$width %||% prop(sp, "defaultWidth") %||% DEFAULT_WIDTH, height = x$height %||% prop(sp, "defaultHeight") %||% DEFAULT_HEIGHT )) } } htmlwidgets/R/scaffold.R0000644000176200001440000001316213130653463014721 0ustar liggesusers#' Create implementation scaffolding for an HTML widget #' #' Add the minimal code required to implement an HTML widget to an R package. #' #' @param name Name of widget #' @param bowerPkg Optional name of \href{http://bower.io/}{Bower} package upon #' which this widget is based. If you specify this parameter then bower will #' be used to automatically download the widget's source code and dependencies #' and add them to the widget's YAML. #' @param edit Automatically open the widget's JavaScript source file after #' creating the scaffolding. #' #' @note This function must be executed from the root directory of the package #' you wish to add the widget to. #' #' @export scaffoldWidget <- function(name, bowerPkg = NULL, edit = interactive()){ if (!file.exists('DESCRIPTION')){ stop( "You need to create a package to house your widget first!", call. = F ) } if (!file.exists('inst')){ dir.create('inst') } package = read.dcf('DESCRIPTION')[[1,"Package"]] addWidgetConstructor(name, package, edit) addWidgetYAML(name, bowerPkg, edit) addWidgetJS(name, edit) } addWidgetConstructor <- function(name, package, edit){ tpl <- paste(readLines( system.file('templates/widget_r.txt', package = 'htmlwidgets') ), collapse = "\n") capName = function(name){ paste0(toupper(substring(name, 1, 1)), substring(name, 2)) } if (!file.exists(file_ <- sprintf("R/%s.R", name))){ cat( sprintf(tpl, name, name, package, name, name, name, name, name, name, package, name, capName(name), name), file = file_ ) message('Created boilerplate for widget constructor ', file_) } else { message(file_, " already exists") } if (edit) fileEdit(file_) } addWidgetYAML <- function(name, bowerPkg, edit){ tpl <- "# (uncomment to add a dependency) # dependencies: # - name: # version: # src: # script: # stylesheet: " if (!file.exists('inst/htmlwidgets')){ dir.create('inst/htmlwidgets') } if (!is.null(bowerPkg)){ installBowerPkg(bowerPkg) tpl <- getConfig(bowerPkg) } if (!file.exists(file_ <- sprintf('inst/htmlwidgets/%s.yaml', name))){ cat(tpl, file = file_) message('Created boilerplate for widget dependencies at ', sprintf('inst/htmlwidgets/%s.yaml', name) ) } else { message(file_, " already exists") } if (edit) fileEdit(file_) } addWidgetJS <- function(name, edit){ tpl <- paste(readLines( system.file('templates/widget_js.txt', package = 'htmlwidgets') ), collapse = "\n") if (!file.exists(file_ <- sprintf('inst/htmlwidgets/%s.js', name))){ cat(sprintf(tpl, name), file = file_) message('Created boilerplate for widget javascript bindings at ', sprintf('inst/htmlwidgets/%s.js', name) ) } else { message(file_, " already exists") } if (edit) fileEdit(file_) } # Install bower package to inst/htmlwidgets/lib # # This function uses bower to install a javascript package along with # its dependencies. installBowerPkg <- function(pkg){ # check if bower is installed if (findBower() == ""){ stop( "Please install bower from http://bower.io", call. = FALSE ) } #check if we are in the root directory of a package if (!file.exists('DESCRIPTION')){ stop("You need to be in a package directory to run this!", call. = F) } # set up .bowerrc to install packages to correct directory if (!file.exists('.bowerrc')){ x = '{"directory": "inst/htmlwidgets/lib"}' cat(x, file = '.bowerrc') } # Install package message("Installing ", pkg, " using bower...", "\n\n") cmd <- sprintf('%s install %s', findBower(), pkg) system(cmd) message("... Done! installing ", pkg) } # Try really hard to find bower in Windows findBower <- function(){ # a slightly more robust finder of bower for windows # which does not require PATH environment variable to be set bowerPath = if(Sys.which("bower") == "") { # if it does not find Sys.which('bower') # also check APPDATA to see if found there if(identical(.Platform$OS.type,"windows")) { Sys.which(file.path(Sys.getenv("APPDATA"),"npm","bower.")) } } else { Sys.which("bower") } return(bowerPath) } # Read the bower.json file readBower <- function(pkg, src = "inst/htmlwidgets/lib"){ bower = jsonlite::fromJSON( file.path(src, pkg, 'bower.json') ) spec = list( name = basename(bower$name), version = bower$version, src = paste0('htmlwidgets/lib/', pkg), script = getMinified( bower$main[grepl('^.*\\.js$', bower$main)], basename(bower$name) ), style = getMinified( bower$main[grepl('^.*\\.css$', bower$main)], basename(bower$name) ) ) deps = bower$dependencies spec = Filter(function(x) length(x) != 0, spec) list(spec = spec, deps = deps) } # Get YAML configuration for widget getConfig <- function(pkg, src = "inst/htmlwidgets/lib"){ deps = readBower(pkg, src)$deps all = c(names(deps),pkg) config = lapply(all, function(pkg){ readBower(pkg, src = src)$spec }) yaml::as.yaml(list(dependencies = config)) } # Replace dependency with minified version if it exists getMinified <- function(x, name, src = 'inst/htmlwidgets/lib'){ xFile = file.path(src, name, x) ext = tools::file_ext(xFile) minFile = paste0(tools::file_path_sans_ext(xFile), '.min.', ext) sapply(seq_along(x), function(i){ if (file.exists(minFile[i])) { file.path(dirname(x[i]), basename(minFile[i])) } else { x[i] } }) } # invoke file.edit in a way that will bind to the RStudio editor # when running inside RStudio fileEdit <- function(file) { fileEditFunc <- eval(parse(text = "file.edit"), envir = globalenv()) fileEditFunc(file) } htmlwidgets/R/pandoc.R0000644000176200001440000001734513230460521014403 0ustar liggesusers pandoc_available <- function(version = NULL) { # ensure we've scanned for pandoc find_pandoc() # check availability if (!is.null(.pandoc$dir)) if (!is.null(version)) .pandoc$version >= version else TRUE else FALSE } pandoc_save_markdown <- function(html, file, background = "white", title, libdir = "lib") { # Forked from htmltools::save_html to work better with pandoc_self_contained_html # ensure that the paths to dependencies are relative to the base # directory where the webpage is being built. dir <- dirname(file) oldwd <- setwd(dir) on.exit(setwd(oldwd), add = TRUE) rendered <- renderTags(html) deps <- lapply(rendered$dependencies, function(dep) { dep <- htmltools::copyDependencyToDir(dep, libdir, FALSE) dep <- htmltools::makeDependencyRelative(dep, dir, FALSE) dep }) # Build the markdown page. Anything that goes into the eventual goes in # the yaml header, and will be rendered using the pandoc template. html <- c( "---", yaml::as.yaml(list( title = htmltools::htmlEscape(title), "header-include" = renderDependencies(deps, c("href", "file")), "head" = rendered$head, "background-color" = htmltools::htmlEscape(background, attribute = TRUE) )), "---", rendered$html ) # write it writeLines(html, file, useBytes = TRUE) } # The input should be the path to a file that was created using pandoc_save_markdown pandoc_self_contained_html <- function(input, output) { # make input file path absolute input <- normalizePath(input) # ensure output file exists and make it's path absolute if (!file.exists(output)) file.create(output) output <- normalizePath(output) # create a template template <- tempfile(fileext = ".html") writeLines(c( "", "", "", "", "$title$", "$for(header-include)$", "$header-include$", "$endfor$", "$for(head)$", "$head$", "$endfor$", "", "", "$body$", "", "" ), template) # convert from markdown to html to get base64 encoding # (note there is no markdown in the source document but # we still need to do this "conversion" to get the # base64 encoding) pandoc_convert( input = input, from = "markdown", output = output, options = c( "--self-contained", "--template", template ) ) invisible(output) } pandoc_convert <- function(input, to = NULL, from = NULL, output = NULL, citeproc = FALSE, options = NULL, verbose = FALSE, wd = NULL) { # ensure we've scanned for pandoc find_pandoc() # execute in specified working directory if (is.null(wd)) { wd <- base_dir(input) } oldwd <- setwd(wd) on.exit(setwd(oldwd), add = TRUE) # input file and formats args <- c(input) if (!is.null(to)) args <- c(args, "--to", to) if (!is.null(from)) args <- c(args, "--from", from) # output file if (!is.null(output)) args <- c(args, "--output", output) # additional command line options args <- c(args, options) # set pandoc stack size stack_size <- getOption("pandoc.stack.size", default = "512m") args <- c(c("+RTS", paste0("-K", stack_size), "-RTS"), args) # build the conversion command command <- paste(quoted(pandoc()), paste(quoted(args), collapse = " ")) # show it in verbose mode if (verbose) cat(command, "\n") # run the conversion with_pandoc_safe_environment({ result <- system(command) }) if (result != 0) stop("pandoc document conversion failed with error ", result, call. = FALSE) invisible(NULL) } # get the path to the pandoc binary pandoc <- function() { find_pandoc() file.path(.pandoc$dir, "pandoc") } # Scan for a copy of pandoc and set the internal cache if it's found. find_pandoc <- function() { if (is.null(.pandoc$dir)) { # define potential sources sys_pandoc <- Sys.which("pandoc") sources <- c(Sys.getenv("RSTUDIO_PANDOC"), ifelse(nzchar(sys_pandoc), dirname(sys_pandoc), "")) if (!is_windows()) sources <- c(sources, path.expand("~/opt/pandoc")) # determine the versions of the sources versions <- lapply(sources, function(src) { if (file.exists(src)) get_pandoc_version(src) else numeric_version("0") }) # find the maximum version found_src <- NULL found_ver <- numeric_version("0") for (i in 1:length(sources)) { ver <- versions[[i]] if (ver > found_ver) { found_ver <- ver found_src <- sources[[i]] } } # did we find a version? if (!is.null(found_src)) { .pandoc$dir <- found_src .pandoc$version <- found_ver } } } # wrap a system call to pandoc so that LC_ALL is not set # see: https://github.com/rstudio/rmarkdown/issues/31 # see: https://ghc.haskell.org/trac/ghc/ticket/7344 with_pandoc_safe_environment <- function(code) { lc_all <- Sys.getenv("LC_ALL", unset = NA) if (!is.na(lc_all)) { Sys.unsetenv("LC_ALL") on.exit(Sys.setenv(LC_ALL = lc_all), add = TRUE) } lc_ctype <- Sys.getenv("LC_CTYPE", unset = NA) if (!is.na(lc_ctype)) { Sys.unsetenv("LC_CTYPE") on.exit(Sys.setenv(LC_CTYPE = lc_ctype), add = TRUE) } if (Sys.info()['sysname'] == "Linux" && is.na(Sys.getenv("HOME", unset = NA))) { stop("The 'HOME' environment variable must be set before running Pandoc.") } if (Sys.info()['sysname'] == "Linux" && is.na(Sys.getenv("LANG", unset = NA))) { # fill in a the LANG environment variable if it doesn't exist Sys.setenv(LANG=detect_generic_lang()) on.exit(Sys.unsetenv("LANG"), add = TRUE) } if (Sys.info()['sysname'] == "Linux" && identical(Sys.getenv("LANG"), "en_US")) { Sys.setenv(LANG="en_US.UTF-8") on.exit(Sys.setenv(LANG="en_US"), add = TRUE) } force(code) } # if there is no LANG environment variable set pandoc is going to hang so # we need to specify a "generic" lang setting. With glibc >= 2.13 you can # specify C.UTF-8 so we prefer that. If we can't find that then we fall back # to en_US.UTF-8. detect_generic_lang <- function() { locale_util <- Sys.which("locale") if (nzchar(locale_util)) { locales <- system(paste(locale_util, "-a"), intern = TRUE) locales <- suppressWarnings( strsplit(locales, split = "\n", fixed = TRUE) ) if ("C.UTF-8" %in% locales) return ("C.UTF-8") } # default to en_US.UTF-8 "en_US.UTF-8" } # quote args if they need it quoted <- function(args) { spaces <- grepl(' ', args, fixed=TRUE) args[spaces] <- shQuote(args[spaces]) args } # Find common base directory, throw error if it doesn't exist base_dir <- function(x) { abs <- vapply(x, tools::file_path_as_absolute, character(1)) base <- unique(dirname(abs)) if (length(base) > 1) { stop("Input files not all in same directory, please supply explicit wd", call. = FALSE) } base } # Get an S3 numeric_version for the pandoc utility at the specified path get_pandoc_version <- function(pandoc_dir) { pandoc_path <- file.path(pandoc_dir, "pandoc") with_pandoc_safe_environment({ version_info <- system(paste(shQuote(pandoc_path), "--version"), intern = TRUE) }) version <- strsplit(version_info, "\n")[[1]][1] version <- strsplit(version, " ")[[1]][2] numeric_version(version) } is_windows <- function() { identical(.Platform$OS.type, "windows") } # Environment used to cache the current pandoc directory and version .pandoc <- new.env() .pandoc$dir <- NULL .pandoc$version <- NULL htmlwidgets/R/knitr-methods.R0000644000176200001440000000315213130653463015726 0ustar liggesusers# Reusable function for registering a set of methods with S3 manually. The # methods argument is a list of character vectors, each of which has the form # c(package, genname, class). registerMethods <- function(methods) { lapply(methods, function(method) { pkg <- method[[1]] generic <- method[[2]] class <- method[[3]] func <- get(paste(generic, class, sep=".")) if (pkg %in% loadedNamespaces()) { registerS3method(generic, class, func, envir = asNamespace(pkg)) } setHook( packageEvent(pkg, "onLoad"), function(...) { registerS3method(generic, class, func, envir = asNamespace(pkg)) } ) }) } .onLoad <- function(...) { # htmlwidgets provides methods for knitr::knit_print, but knitr isn't a Depends or # Imports of htmltools, only an Enhances. Therefore, the NAMESPACE file has to # declare it as an export, not an S3method. That means that R will only know to # use our methods if htmlwidgets is actually attached, i.e., you have to use # library(htmlwidgets) in a knitr document or else you'll get escaped HTML in your # document. This code snippet manually registers our method(s) with S3 once both # htmlwidgets and knitr are loaded. registerMethods(list( # c(package, genname, class) c("knitr", "knit_print", "htmlwidget") )) } .onAttach <- function(...) { # warn if the version of shiny is lower than what was specified in DESCRIPTION checkShinyVersion(error = FALSE) } knit_print.htmlwidget <- function(x, ..., options = NULL) { knitr::knit_print(toHTML(x, standalone = FALSE, knitrOptions = options), options = options, ...) } htmlwidgets/R/widgetid.R0000644000176200001440000000247213230460521014732 0ustar liggesusers #' Set the random seed for widget element ids #' #' Set a random seed for generating widget element ids. Calling this #' function rather than relying on the default behavior ensures #' stable widget ids across sessions. #' #' @inheritParams base::set.seed #' #' @export setWidgetIdSeed <- function(seed, kind = NULL, normal.kind = NULL) { sysSeed <- .GlobalEnv$.Random.seed on.exit({ .globals$idSeed <- .GlobalEnv$.Random.seed if (!is.null(sysSeed)) .GlobalEnv$.Random.seed <- sysSeed else rm(".Random.seed", envir = .GlobalEnv) }) set.seed(seed, kind = kind, normal.kind = normal.kind) } # create a new unique widget id createWidgetId <- function(bytes = 10) { # Note what the system's random seed is before we start, so we can restore it after sysSeed <- .GlobalEnv$.Random.seed # Replace system seed with our own seed if (!is.null(.globals$idSeed)) { .GlobalEnv$.Random.seed <- .globals$idSeed } on.exit({ # Change our own seed to match the current seed .globals$idSeed <- .GlobalEnv$.Random.seed # Restore the system seed--we were never here if(!is.null(sysSeed)) .GlobalEnv$.Random.seed <- sysSeed else rm(".Random.seed", envir = .GlobalEnv) }) paste( format(as.hexmode(sample(256, bytes, replace = TRUE)-1), width=2), collapse = "") } htmlwidgets/R/imports.R0000644000176200001440000000013013130653463014624 0ustar liggesusers#' @import htmltools NULL #' @importFrom utils browseURL file.edit packageVersion NULL htmlwidgets/vignettes/0000755000176200001440000000000013230461367014622 5ustar liggesusershtmlwidgets/vignettes/images/0000755000176200001440000000000013130653463016066 5ustar liggesusershtmlwidgets/vignettes/images/sigma.png0000644000176200001440000034133713130653463017707 0ustar liggesusersPNG  IHDRl3 FiCCPICC ProfileH WgXY>S@HB D@JM^wAABH(1uqׂ+*b[]Y "vZ>EAY 6T;>y{{=gܹi+sq-"?!5xL0M=@z eHW6hB,3D< 9B֌xir w @D,#я-U>!t/5@"_$Bq8"d" U %q#<"/o*šm_d C1!ܟ 5B\ Uz3Գ$xtEuې35,2"Q)q?n(#ۄ!CHT$ `k!?,-JɊlY8j~8,8j 4ٙ(ѳwKSF:"i897$P8W%!Q()͖TS9"W*>a.xBIb$X"F$ Cݗ % !$`*_,Uo(FP9 Ǟ)T$R<q3[0H3L^GՄb BJ2`*d ][A 180#RT)Ft<Ґ'}Bzރ:aPz$=n7ȀEM࢑MS^6x'V# Z$(N.P *<PQd9CZ#d0#$4G eD;X=JrHZ}ЏRK<Ǟ>"c0+$+(_-R!{Iq'.(<9\Uū*r88u:}UJ /O/DBg(YB~Z(pS>y]7x5ZNۯ+J-8vETԉ0@ Gp@1 Q%TOY0J jXa삽p(3p F;tKIG4?#$RHs%zrYK6ȇd7Ʀh>(ZmVAA;L;ޝv[:Σ=ѻJϦϤ/o7[= Àc2BF cc*^MCTE-\-MM@BmqjOzյԭ}cE3ԗoWoPޮf0l|Z>i=k s oRyk5hxòg&Fk6md KS.g$'#Trj9W9/4544'kkVhҼ٥e%КUuDV6WY;V;O{n: k0Bm:ts 7+~=mץFfmsK֛WwLGyQ\2AMaÂ-oaKh731XaPgpߐ47o8piî} ?87#(h6KF=&ruƧLx&&&LtrMMLO>f~YlYYy-^,,-M-ZβJJb;kEu66Q6656lٶUv^v9vJaCU#n9k䍌`d,GZ1/NNN۝:8q^ERrݕ:׵mmwXEM=<=<:=-==7xZuΛ=B>:m3ZU /VD+v` uVaQeiMמ.~i38)4lYg:1i܅sE59?g /xmʷ [jJ8%[|m^}b)^(s*(D?oiҖe6-/-"`Ůr+Ǯ]_U)WUl^\\Ӷ6fm:u}Z/Y2r 7(xuS}7mEֈUUۊ=ٞ^?V0QNζ]񻚫=w^V(k:Lseo}/;̓ysb3j$umGijm8_v5;ZyLز (>(o:uqӔ&>ig _ ^@= j';09\*?alQpe@{4 `jVЙ~':C.*hk1ೢwc_h~1_!=?mi“{@IDATx|\y%~郙`{%Hhɒ,K,^;-&M];8"˶,˲,SN=!  *io7_;_TS( B`Q!FP( FP( "D@ ExԐ B@! B@!P|^45dB@!P(B@!P,B_M Y!P(J{@!P(%ESCV( P( "D@ ExԐ B@! B@!P|^45dB@!P(B@!P,B_M Y!P(J{@!P(%ESCNO5B@!/*@B. EiVXLz$'%%{ՇB@!uu !gqm^Sm@NJ/+ƣP2zoI#qpx xfc%=w59B` ҹj&D@?FO{)' F@ }yf$ȳl ݔۡh ݞ8D3j B`!#B:jlQ]UT#dL RW i < DuP(f@5 j@ʼn@~f{%'asB܌Vۋ[s"vzB@!XH(˜" #>o=S$L Fb8oT;asi:Lz8 B@@ pB"PiΦo(T :Mk_pVR((.T  Z7^<چ4 X ;SKzȕ>U P|꘩o\Df㷧کNOŭ.al(qMȰ5e\#\#η3լ/;PEL|JXhfWM!P&*l6U}/9 duZi*߁I.G R/䎥6%wc ](.+Ipa6 J\jo/x)&#LS_fr*Rt!ONƵ˳t7M 7S;ZƗ 'ߎ# SPn.B`"| ¨a-|D;=؋_g[0EiZ+e~xl( _fB_;فob7x9ns s,F_wRG@iK B4k\@Ծñ jNnTl Iyj.Q3>@{C _6[a:V(.CH}a$/ m4g[sx8:>i̘BRm&O^ I$IGg \"0m\LK!4 _exChboY}*@sU?R\ͬ] h>7L+uB@!0K( |U*fI G '-qyGxZ1z" ;_|2Hl|JztyB"l[1MuB@!0(> C@`؍7[_D|\w;\,M;Mv_;נ3Qϧn~Yۖeˋ2WWKF.H!0Cԯq+ @ԇN+FZBf`$[ -,~Y@o0Xy5ˡN.>pkT\]juP>IR/Q7xh;ҌCAkmzݏx< JZ|aWpe]㮒a[f4H4 =kTS(J/Fj!/eo;4%{Ч"mA!dV9˼)q.}I #cp 0Ē 5cD7 T+#͜yΟ= f>厕=qۉњN<1zP9frF]C Nb}sn!زFcf*iN>g6_IFTC%[S'dc:!QM!XX("C@m8/ǫ~OW]$}~b,m5[b:`rR )gGGӈn]5B`a!~ z,2:_{ۧEG<tq%41 2tm/%xi.6cБ"V5B`a!ºj4sP\jyL%1_–Y҆G#ڶ9rvfuS&kk$DcM=|~է L̴?}@@wqn?,9l0 曠Okdrw l6dbKfVOH+EO5B` 8™B`BDx{qߏ[GAG=({tJKV]Uet2ĖF8׬|6M6e^#o ֑%Mwۏ'63nX:DεIDDPV3:B`a P8ы!"hz,lZLj){%\sJm"6j[ 6\_p'4˜a)ge}ot'X76PW.L!0G(Np |9-JbvZ7M3dI+=ZxUZ8L.Jdᑏ(anjNLqq"o#@7cm|z)F}A!%'G}XodxDRk~vݹ|o |ߢQO|Ms΂?vc`SRQ ^2 }ݾP':XBA- +_Ds2<@$EWf'r-º!: P26==mZoOuíR{V{7>Oaaj3#GН:s-ȿNm6mx4sC!,~?|_@VF##ITd0m}vKKrKhj*Kxq?jxxx n[{I!.>n V=08 :'<ԏV}ŢvTdܿe~Òi'GuZ[ӟ! ǁ1⾾ۋ.P겡4ӊVe!/tnbCIuZw5 AfCVRT7+ҽ ,6zC x=lpw{xd )k S \R{ث 6r"QŽ B69-~kB(‹{$i[O rtm(L<ێW$Jn6.9yTuyX]m u涳ۗX|3qT(f%g^FK@}NtIG{7" pm\h*{d.)H1\T{rO&8H|6#.z7D#xU*7ThSPO/ʹi SuOl/zC! s*\f@+NAj}?C~ֶ:Li&TiUy(rYa&o%0᮵y I`mqQ!{t6Ug?k^ GP2G SMvjY&;>Uz-R4Ms6pgc2\6V;UEXYL)>zd^C9DŽ&kn{>CxuV+Yv k3 & r-9fzɜw !qz}o)h?}Q Pޟf,2hX{iNwSXRkS$N$4 ħ_~~kuKRwkdpT2?$5ap96w/'d? n<^xG%3\L8B Q*~ Q ロe P?ߌ(od'Vf@:'[&Z 4I'"2[rub83Ī?e#\X}ъ d}?@B[LkҰ,?z4B@!0 zB !Q&G|*k{S\Bbmnэ0zALи,fZpҼGj)v[5џ #c0[p-.m`QM9L{3RiH)M&_me,qSݜ[IvnSM!3TٜAN4h9Ҕ5թH{Bj%(BXVV.Nɼ$)%MsZB.,2X$0O:|ExgH{yu62R&çӟѨWJ_9zNg*0#ެ:5Ŕ]^^X?ohQBXZיA'G5ZkǠ?űGI(s|I=<:afs5i+m.D7Wu"H+j @P>cz(B+j1JnuPW-} 8<ڂF`Ry%/g .I\kЌƆz|z]ܟ-lC}oFdj6fBzdJi||k[PMUWM!HJ'K(${YvJjrO_HF\tf:^fiӱuPK7I.ŭ%StMb³L _Ę53b0Yt$b^j]$ c6k^iǽ w3QϬ:"wE¦&=D@IphRc;JaB: `5Xev" Rfr eb`z[`G$7:lwq⻘&E^gۇilbM.h$8@bl_7-"-s32OyX Rbj@f0_јyB @!PO(3A@G_ЇW:ۃҔ YHqY-z%Φ@D=7dWju,SRμ5| sؒH;Wd׼؅S]nWfJ@Ȳ:ҕW+l @"ٍXpiA+6J).c=-ݧ#dtuj&,&֍tkҊ`N>rܭuQفL[쥬M+2pN bFA/z^lJ/]V_H4*-ш^I?$Y P(JxC}hj&H"O2@ i`>I 3W-4ŅJkF zC!bOs6y{+ĸxoﮇ'<:N!P|1_:vy^h">&LbQW-61߈A%]k` bͷ:YxIϕ3ZŊr‚Z4-2LE˷k&[20nk9&V͢>tń?<ϋ>D E|F>:?I洲s sדuEjR )vL|VB`"|K ^i{5bsz9)Al\n9<~ƒ'=H3΂.~sJG.(̸>s/Jh;Dgy/}=H9%{j?94~MQ2f΁= q7OdaYFABjSﯸWBp81 @MS *j3F`$M+K҈ IL#k zaaSk YWA4q\G+)]bvsK~"yX%ͪAD?K#廛QJĊ d ɎqrC4KA@cÑ{FqahlԶCBq6I̒F6/s&+ԡ5.E|ے4zBkJb8dp ixɑ?H˃D5!)v,*3GqӲ"LLX")[7 ~( ˎp~]kn$4 X0SnxɎGlG:3A`v&=.j4 O"̻Bj>EE_m֙5bY\-hiC+)`L3s,nL6\Vj{&Oy˟ի+(~~$f"PV֤O'IOY Et-<8]OFEJSB`)G<#&ADj|%eٸ!kl40Xp_at,LQQtN8YtWJ6ʰ5%&@.+Vi .YKK"s؉wޅ"5ށ.4>3BK+(g =k$mEChIùt5GbuaOYMRDx-! pMr|h{n1l K7 lT0)AǞ+ѯ%171qSMFIJ|zG);oCp졾&DWkdS'BJhXv ؚK>0tfsbMKR9uqf<$1>pKL@3}lxQ!=*7[)s9q&(|۞xyã2kq"į(kQE'dB`!D(})C]U,@v:2|)%1vd--(1m Btm^ %MȹV8_+b J`9|XL%幺xC U"N-w/LI>5E[gWKP7*9>ٽ~jS@@l ]a1SLJdxCs:!SzO}!߅;؍RG%*9Hv'b_g}ntQ,6ƚ~Asa+yr_N5 ztkf!xi UZYҾEh|;ح ߹>$3^KZx&*YE=,w9"Ia&B(s.Qq['pCizy.wV'ЎP,mydPA3|b Chqp)d7mɯ-|p,_$RJ5|}C',gŸ"k@+ He3iR.cp pC!,&JftxUWkٿ[KxlE}_zdiƒDqZFVV$ωVBe a" ^:Hmۊ̻ӴԠ4M0Sq'Gf IdFl56\^8uԵ쓏_aax2}繠|1-` ^\*.wFB@ZD?B!nyb. *|q" Bw&+Җ1kiu>%@h^G`k<9ܝwkar:̈́I,$ 5?D+C~,R4I$H% {= )@X3ras"wx5Aɟ7? A_ok،fz^4?iq!Jh]n?Xt($F^a^eDXeKȬdqJ<1ꃅ2g4] nI@w'!٦TXiB[*mݔsю̃R`QB-_ލ"k-5 F5aKR0dc d,u1tR0GH9'_B; &aO/%2)y-h1O6y!#ݫe?CmFISm!0sAH0)E. T& 2m`f\٬B[X|c<݉Z/D4-i8ɬgH6#.IvbW!Ƃ%7g$Ӓ+ދƖN4gpnsGilЖ@nE95鋦(DjV kBMfjgێ" V;‘p[`ɶ"@װ\~Pق$S П;@rj5"mi( %FsoBsoQS̓uxװu'j- 8TdoEw+t蠞ˏ?'á-XCQ Č.<|(g%n]3)٘oo^.L6{ *Y_45&)5bf2}J:qwr}&d9c>N g[oe:h`jR8Ȉn 샿{25V2:o^@ΧoF9o{ r.'cp_ǁL.m3O8nM)BAS?$33޾3Et2b׍k򵰚`hbiԾGc#TG" ޣHe:@‡("| \ մm=)K+ӑrFANr;lw[t_ړr)4=p:7vX;`S XNw-# yk"F\ jyoc >|èxx YXdY>XςRIM[IF>w! Ѿ%%?Р o(qc/PJ΍yI^DLkL\Z.P&6GE%* ﹛$% I9Ӹ2G{d/B]~ؖIP Kv,d&A  rvnGc j(Ѥ5+=s#}pn$et4-eԲ3[lOXԤ0ed`(7 M{HUGWG\OTDcp[#(L5 cknkAӯhnObA:G~c0k0 \Dwǃהp3&i$Uj+w׎EXӉۈS&9EHDSXzD棖l@y|v;\+X3$I02o( u$& W"ACv6iǟ?0Cz=G3^It/enx-"p쨢{$(S*YWxhtNX ,iǚY c*wg ZWM}&A ~R4sM0\,#,-|LEUr(i`}{Nu/|lKm+7aJ}p&"ћuj /Õp6*9/|I4J/;(o6BYvܳ%χ=Y)ӱ,9d3QX~r|絳hu`e*łd8a8 6qtj۲b-GHm&nV0fp/Rp:&Wo[);Irn&;}~eI';'7WbSYiOy֢b`56 ݆gUhaOT8wG ROFamןkR+Ba;"‚ n3IOV_&)oی6`Vc&nC* }9X*(li_6kSp+Vdlu/ovfC^9 :l6|oE|Ѐ#_yK$Id dmvTg/§VnzMچdoIܮ܆[i=l̺"3?/, >(1)&<㕃WεLEPPI6|N>OiŚ4X,&-F 7Yj3r~s0XF'[eP~腑Zsֳ&`G\$FˌH; nK'y*I3<0 d?݃@s= C&8&hY݈KK):xz'Bfimc {ty{#7'4I%eW-t6=]}PJ Dfn}o=C[X 3gܵz"jg#O(w|z1mm9WeܤZpJ4`Ѣkiț qL@ CBjɸ G Pm.B'c]3"$i `:t'QoE׫/!ZSHbrr E=^(Btvxz ?]~d}Z>ߩGU }#Qˋc,EUD=l_y*ҁ{ dqmEsw^npRZ4}6]zc]~gUu'9`#jmϽԵk]!;֎=#EcO`we?ס|էF?I?2d˚܊Syנ*ME%} 9 י0Yas95~  Gk Ex=ފ<ڻׅo׊UԾѕ\ K *'2>313j  @Q2Cp*PNq.GG>xe /=j)הDܢ̑ BN}7os}D Jd['i $V٧ވXWumk|?̵:yم#=fhpM6\?P,sZ'9J>~45nvS8I%3j5E@) ɹۖҏPЇSG逃1؉9l9Ç0.s_ah,ȗe`5ٲ\O$/&*+" ]pKZ][p.]D1F\uRM.*Bavi>ȏnU,43WTyĸkYFz|(cˎTʿ~/ppי4sDO G jFxj1pt;S(tii/ܸMqCw.p25 >ގ}> qmAS5`N=nRV{C0H>C3@v97QxO7~pL& $|q2QF6bYFBL^3=B[_iL4m$4/C6ؙ>C4eamx8?3`yD3&db]wua 憯>n:pvΒfF(EditZ=i|gyN"C}A4?q={۵MKy(b@=.$_HW#c1R X΅jj2I d FºE<~BE~}-57r>iQ!tFye;QwQ6Ia2>V5& P)8>tFnwRX갵f<j=ؔ!nBL6b Xyh-v gȘ(홷~n@")rR 4ol"BrOa..n 0BY"S~c3"Ahl*%G]ZHRcY0E3Tzsy]M44-.,!xŔdjs),_i/huZG5aW:.t8/&t)t1C}ڰܑsٹ%3ݵ5ow2M*l(:7QC0] +J(Y'״ IRdl ޖV+"J2 ^$rOdAj2K5vD} =C?:MGw?LvkSVgj8y5 iS80vLZr#+cŐBwcϲ`J1r7u>~F͗řѢE@ {mudiJ[׋6)L"Ws'3IS>\ '>|GGa43Lj oMx]QJ!Ӗ_\vĻWHeF' fN-٢\皴n&YY,2ڗsm: HmKB*ZytY(],{׳#x6:-He0[ BF?r/cK˓o@V CUQ`BIᣫ>J- Wk}]6[8{g] eL-O9>E\S@=;ш(7Bv*M 35ɼI DPO(K},^?ՍEUP_]Dyg.&-9Q`m->tӧ$ hu A>{w1!h!pzL~?<@tL%Ϧl#Eiйo#L9\ Ԑ>D0wd#F,Lml/DۘBɝ{Ȅq\P->R]\}) Xf j漛 o@ݏw4Y+s?dIf2,y-/"gs!*֯뾎Fҳ.K[5v=z̓y.<2maAuȿIvmfn{%e 1ss9<-\@Kf]Gq[u?!M%7%)y${?l%{ h x)îgQXࣝp櫉IۥkS!BwV{Lڒ SUDD&I!EO>e꟢{$!e]F:Hi1 qL kGLk'p}4{koC!UG~fxCzthg1zd+}f"'ZJF՗h1ro_!Bv0QM"x'dz+I#.꯮fgzV%Lr#UM%' b8Lk 3|bY< ĉr,ngs?N]|qlrl'%ki:r6L.Ǥ02i-Ч%dc‹$( lt>#LUԖ>^D&D: D`6BsPTu?{p~hk? >)n/ 8~e*V AqϼאS2yp@5)/U3`?#oiqoE -dd4%JꅟO7X)[Z!B4jɧڐUyXpEv=#9c>z [QD@:O?|x0LILyF Xqvl$YmH$.gZ|7#Ͷw毣͇٫iN>/D`dˈĈ[DbB< sx~ֆ Th;KV̓xQ#M v:/ +N-֬),a.+AO O3ć:Gc_ow!csn˚ xf6y{E Yܬ1kty-,ڢb yRP'PRq#Aq'Mof䝎`38qbşmRìy6uDx'[ 4Uda.|j ,uE⬧5#p#wSu) 7-6ׇ9<V40`޷ոҦ2|;7eZnRsyv#)r >Ow \`ޔ#ǐu߳"p _|8w'{ [j|b瑕M=ۅz`A~هqX, v7L|w/ñh$/b5HZ:GB/;4>{yEK:݈` ^+]m>B#5aS "ך`/ca+qjLԟla"00|EJ@ň&g.b˝ǙCSV7(>L p\N|n}Ck#-`_-N/>>Akw :UL\7QXsQ'7Ola9Kqs% u2gj~ b#IL: ~w6snH[Jp+0B1ք!{Э\k@Q=d+$|1M6>'wo?]GtD+uȳDezWgE}u뮽$ÿg}fA߄c_{ɋ+hrըn$ ۫їfl8uHnknãfJ'Y( i#~ m/ÒeA[*fiDK%l}]Ѭj5C%.dj$tF2c0," Лh߽ y8(*WͯԎyh"L$;q_Z](kS #@2D|VvKͱܞwdΰ"{GfB!>dtw!:t6ffjCj~W4 1Bɖ}Hd-W %f'Z{ц; ;ӴB\R~ @7񊳐[=pY޸&@s}x1/$140sv Q,"ŠRYn0hs:2uU^tP߉ooB9c᝾s1E)P@[=%͋Ĉތ(4S"iӳ TuG)Fۀ2޽9'=ȍ7`cqg0ٱ,AWZb't_"?Q +5U(/_ ۜt9TFOYZETI1Ps?BX/AAraIY}9eEO2x2/rxr%wb{R>")'2=O">Fm \ͅ P&4oHEsU5 #2uD_fyڀ6Zh20bv%.uAT 8)8*ޖ6it{z> x>ܟ1 `w-:TXۿ~˶1]l"}L/ϏJd2q*L{ ԯ'ܲxsBM:"AH 3c`.Y,ͤh;$%@? \I@hS?ʘ wftpTP,̉8>ڋ-p^ܻsH1f"@ì#?{<.`QbKӘ<FR8DӠGfDؒVNtb{"ʓ$n!Z̧`"#%鏒@ЀI|ώ HYMBZn"=HY9hk⁂68"Z'R_PN?ky=AuvElka"承#-ۀ4sT<;ӽ(.JKyۏIZ5Q)~q2 ]"a\(MZNRcsbC29)jfX Z1f#5FT>)cNC iF]#D}n/ #h&+h˺AC!^ޔu<yoq( UqL+c$! )D)nmGb~*£͘_Ɨ^'#=AKy|3R@C?Q]nÎ*2#dF^T*ܕ ̋xL%,Po,D9H sWd7Hl5"KoFxLn2^aOc9z /4z֚gy4ahnjø@Er$09'/Fc"Ð[;)g{]Ȥ$G߇ڟ`*Jb{#>܎(g aXg㓣:ž6;:zM,~0b(.@8R.>(2/1K]W3lߓE2bǟK(% 5LԼkz<ad\ ¶l"@^i!DdfF;dr_< nJ׉"Jvh4HӠGzzkE#S%}Vw7~Y \iz{|Kq+/_ez \t^Mbcg7BLl9 z1Ky(Hu-5ysd1],?(tPc4!a!nJӂP5vc([p|hWk7ô8rY"Q,9w܎Bpo"zW1}UrQ(j3'\獊 .˵2\.GĭIF4Ʋ?ׂKrJh~ >/Lyg\cyO} 1E1Q?> 5&yv-]m 8|ԘXfB(z}۾v Lf\ܮ -+xy|qw#Y\XٓCTq'<&SylVщgsoGKdē)o(6M3|Gre KI\̱(gM+&0$/˄!{Uΰm&?5US@y~7qr"V3DgGbuI;C B,"eypFC\*-'TK8b0M4dףGfw_46c+`i쿅X ^D?K!j@D'2P޺xW7nx!!4 SQn+$ <VjؗX׽8ҁrBR"Y'"S=1}}D;8Yjy Z?}r wl#vQ=D8 w؎3&`) d2Sa @zHgG8IrɻEsD~=Pio@9Ǩ7}9cqCǔ=Gc*OC$= ,b J޸oX fQCb@T4xcvZUsr9cR#% 2=YC CƐ%ےȂLE(JapԐq-%DL^rnpbn< '^xГ&rq牜ux4Mj%I|mM2̺fr/SeXc C/ 5?VyadٻrK9UyM9P{hw΄"|*e㒜 ,5AKgpj"d],n=U-4Ÿb)|[=m0*;*Tj<8Ί?c߷&; IjY]ۘ^qs[|0xEx"}WRxē'OL=˟:_N-/N=Ǐ ,r؟* 8X: !!qwX2d>UhpA;X~Vrz;"huGJ5X5SV/ܤ#8ņ>dpԖp5J~\€d%N$ɦ;L:iU{ix\ܶ{W izѽ.z$!M,5kG]haW;/TaH WI2gG ^=k%zXįIAcʋ(}oŮVL'A.=1?2 4;r)=yѧd>dg\beBV9ɉ$)=o&z~cJŐQTg#OG;,%5Dl3IMd[&eVN*H")8C{<] -/OsvzzрӖґ<'." eu}Br-\&7Q[H!핌E:3뾭8:B60˓ZInʯCnoXʨ_(CDpj4!pt$fs@:Ⱥ`1H#/L~Liඥ4 TדULNq=a \; Iݺpoƿmyg?&s{3YY'PzaGs\FNtoo;Ic>e㨬l߽uM {I)~Hۻ&FZ@vֵfz$ߗ qr^^n Ã8w,5XWuIs.lQ{9pQ eBĪJsėYP0$D#8,a.#6>ZQ??e0zmǺ1t.&Kr$߿2~Rc뎏gϦ7gd1qL12Լ'p%^\$JTGQdq j ,*P%Wv@ FEͱ_#xÐI :[bT# J&Zihuq<>T|eNXV%R1 ({Y%GyGaO^!gz-/y9HTC2"L_u4#Qhm,Y^C(Pì~iLHʡZ4R7#<$oP (DL,&Do+Q2W<)@a#oPK(y)ikawŴ&`Jp y)"ٿ-Bbrt:D c> wQi{[T$"4 w=z̵vQf/ne^D^,Y:E!%}h˅.$q\Tq~۰_s"T]UPr/ _Q_^~?3 RK>:q>&SLe .ǐw|; yOXql /tB}UC O kZYD.&+AD/>DsQDk%R=*#ÑD1uٴ1-xw4;֣DG{tQ,Hc)BV0K$!Z9ؿ,fp[i&/iiK LT) (Yx3~2B\0޳9s 9A`scCwZd K+F]LA"paܜGL悔)DǍu܏>oO}y1Q_z{#*iboq5K0b?Q8ԾȊ?~aᆴ04M Ju VRVt␭*oS=y4R%.oeHZ x5-0h)3ѱ9 ɬV?#n`v“i%U[=mG'`6'*A`@\86Vҁe.?~,<*v&$ӍQOkGpWLE1} '("?4ģyZ܈ ,H0q' ӌݍdB bNյ+8rHgT'Lݚ*۪Ǻ|^ƇvYnIF#9g]Tf2c 3Pf>(_˿ s'\38ޜq|a>oҜjd0IB+-Y8D*M"5 , [B?G$qz;QtCX7;wx.}m&,pºi,I]APLtuH첪lƱJ¶4s]aE*x.!wl0+"]RNN _iS;p Yb1I*}jFg Ab^=Gkvwgy&z-5aDfh( E8=kZafiau~B1Bs&LD-0clfϗ*Нp. fZK_f@s%X*<ψK>XM\d ]ϊS9aiʨKAX 8A7`uםhy{ǃPf1*!ےvn'0m%V LՁ_|x׿};kP̅߄x /n sEZaty [Zpk(h!Tr;I̵~v%tDV܃ blwFg9 "j63y˚MB RA&@H)C2"U(z =S= f( J:XqVM|ߗr vO`dtfg2 wPle](\ +iXEd`G+L]t beMB6ܕ}vY@o4yHv fdt, EΟ/Tuܲrץ0P$%i qa'VX]-N×Ɉq..w)~07QՄg0X$փKݲ41Z2Lf)RG2+>A5ᩫ/# %0S.:If!0qSNu`A+k戋q{j&lJ\Wh1] 8CƸ%̅ qlb6v;.D΀.BD".s#=T9LhͰ;(5*XjF^ }g{` M8YHR~ .=2Fpɍێwgys=ʠ{Pyi.d2=Tj]UR_RD!numSHj &?f-Ro9T!0pr+v&|=L'WFlgTi%5GN*a* D 3t_RBK,̦ݺgKkq<>V6$zlΥG `/=>sM h5۩o P7t (Sp ʘgJuVƳF"~AK.y!\įkտ`?Qش֖@IDAT\3#$kH0SƺmP 'EpVF۝hZ?U~{C{ *bÈܯ,qP7O S {~{ѥ B{v}32qI1 5WDʼ~8XFўL4bo + XL Omi^p..~t, iALDqƞc51p4 dO1W2X^:i#ۓqL$Iw`k|b3nw~[|F'$C1Kbف*3kia Q`\3ފi8SHE.1Rպ=;bעHv~o_d:fc}p{`NLc=cKzaMor O36m JM eC]D8_Th}ʶ*^G-u w;֦ |85pgT.8?4^9״g32-:#޷P~>\6ms}G@d6 dڌBɈv[BOU%8йjc9\zG;v/0{'z ã4\ܜ5A!宋'Grƥ8]0|j'z^hV$!sхx3D¿]J'!gK3Ha*]ZKvwd1"gt5Fޠ'^DiLyfM#x*⼽*[2C,j})-R'zh) hMQDPr>+a?^9y%*不/a{G]KAGdtۑQ~Bt)2\4 b\o!`w%,`x= /L4> {2JJC:ɬ (D`.,wl;.LLsq1̏>E\%< E? 02A'8tTѦ r H݄CphFdFe ynqv3˚lyPyK sb`g[WxT#]B|,G>tZ )i@ Wk͇Yx!ar֕%ʧE>3G eVYdV:d[g(Fua0޲12.cx R,7f""g% %Qc$ aG-KKy.z1'OǼ/R{{0ކH׶ɟ>:Jfs OSiw4*77[2t(gBb"ٱ$ܑBjrQyL278GwӐo ML6{:ý8g3l::wQ"5 \|Ha y#0wwq<7}/$4(:ߍ>Rx cKMh(]"B{XlY iQ~rE 9p^le 1l;hcxg]9y O~2ց+Mpq Y" ?^4fY^Rj m(K!«1ƒulkPyt5Us Ci|`n'B$tncT+[}‹|N.; h|zRPGJaYʽK̡ML:!&%[2a PmyO™hrTv[<%\7#j{]#Hq|֯AV|j Rw<o\rJO=WZ$(bzY9aiY!4 )m̠>c߬QXZbe="HXRjTHFp۩Շ ,r(0=KMTe,vY:{b`Ze w41V!xUH!ҋv#2t=Gϖ4|ꪠg;g=b*C!,YĈZGR3EqEНk#wD+}*Bj\iwo/ mwWW5;<\'%q C&jK.N5(]đ󗅘;}6`81]c<6`hy\%it |nG `vI4&P. =VEμvie g}/CHH]ܞP+T閿?ʞ-JF@g}QmzRݦf9EEQةG&X28?p,6$,Jr8c;t1ãTN^ۮ*`"baoK?gA˝0 T(m :"b=ܠo hnĀ/I%xǼ,`R4;n3~S#S'sGk$j'2U3Xp K8<3[Q9,mG]}r-tDZm FV=^Tˢ dU)!py|,c~\EiD'HD15i$!ڸ:ud!^Fԟb  3kT3՛|g Q$-}CcHBsN b;[s "L*j&`3Rgfb_\x-OԹpHmi!3 b" ӅW_:;F&h .yb"Z J #8~" S =7h߯xWIBX'xY)Y[_ڊIE0}!x9сvӍ@ D2P&0D*uBh7HHs}\!R1rVJdVWS3;R4ZւJXLg8|?_\϶WC|,)zx , m9S[z/QE(-`M3sDd`H'B,`/ca7L}naܐI b> k63+li{[0+9'[6+o[ AgQɿ;=WբkF[&St}T]3xo@R1ayٞ#x"Y^ku6R&s',*o}\mИ(/QnSi>{`))AbB P֎R|l2o;޹Vu>ƪlܖr1:JJ; I7ZVz$I껱+ܛ"7b_#`m3'vSkys=G uBB{6ф%TȒ7V|bz8WBrv~[ts] b1hCC_ITLyDKG`J 2Z$2>7_!}8Oe/˘_,SԥI!HooOvw:9܎{%G>U ? ],k ,n"D] slɾf\/ T\ՙZEtQ~}eog6!6$5Iн>0ʤhwD`xWټW?e eHgVy[oN {ԐvЉ@-c(|/n _Aơ'ySlLPEģ{xl3  Qެ݅&4Yp{Z}DoH(&k&vЀ3;2J2.3ϻH-]9mɍ$zݏ-OY,s>ىe7޶L4QGӢ$v٧dF<uZP$CYxVi _&N0C=Y/.ϫ"ɑ 蔅8LxVjhK*x9QQ&vdyv6u[Koyݍ0?iZҧGnbs]dAcy(yk&MvKWPE"⺚|4>=pL54T +#p$ w5XCz`9( 2`X==(hQ&ߤrrɵh#0)˛hdM#zwWLsd?fmtL&Ob(amFBPNbfּIr#":e?նh<6jQj>r2rte͹CI%h^Ovy.MJzOurze]ص>3քA򐇙WO3CH+Q_h8-QE$w9Kbނ_,f(܍,Q\$vWw[2QJJ[bJDm?iJoc^T>Q̃… 5&&ХOa— D^"kTk{¼f5BtuTϑ:r[($%drMIt`xZAhrFt^ ܌c`+R5,oûxi} X.Ɇ,ƚč]jnh{>P3iJ"}Ai)n_G$HDu2dTZVmSiA[k`Ț^Koѕ\ԤKrv漇GzXt]GUҋOT5?Ora0jlܒz8BHg ZᾗjbwiブšNRRkۭ?BFI"C LY_[@b$?$M#&oPk)zzMG=Ȳޓ'wjƅOiݨIbض"2!(: G),n[ l܌@8QmE xH9HogţtrV4*]޿]JII(]-'VN4aܖ}p.OLu [[vXj![Y4ԝ&f("ȊCjU*=o( OG hu=!`1bMiþ&\ʰv2`;[2D8FXs.F.YEKIcƝ)w^:I/s"i=QȆ\3O(sH$^}̶&|c9x56+Lfh_ߦ*Sgp:{8=ڃc|Q/6 <޴#paLjcs1+z{ER@KD$A+}$u^o>.7*_s/LR>a#.Ug;׏6KSU/4Pij~}Av7Yު:v#h 9oK#,gzoه-߷2Mo$(3yԺ6-Rp,%ry/XîxOVE++m`8½Hw.V]}h&[c0P)JdciBLpәYp?<6B $9@5z1rϷo٪{6aWI;;d {#0j5Nxf8ZZm&?bw/.:bhMĞH[EfLƋLд1azeyrxD;;Y웇F"Cmǩz8RZ+p}lD2] ? AhBh."tu:QJnz%̍C]aEGK-D=Xe<2È m|쇁e3G4C#bOOX7q[X$Kx[8N9=$oW_=$i,k7!\1IL \cVH%E T!lFĿVSdvuك %a9.Mη{Y<<]-RUH]}fuxpA%JyF#.73n?sp9Ndi# HֵNEc-2L]=rSX-ee j"\ðVOy c?APZl6tō@U,KpG%RO-?`JztrqAܶ$ 6ERH#%o!+D_@\?Agc|tyaa*dM/S;/ 7LG8i=1+U}HH)7,J& X&Tw5cpK|rSakQŠvɲ'\zê #{+]ΙZL qL/obTM)Гޔ ,q :}Ӂs<>znN9icctCj4Bȁ_ ~;P5Ёmdz e 'k3:B THd;z^V3?[ p:aH50NjPu^a]{pN^u?h΃%ܮѳ#ajqp[GMKUmB_3MnFoY[Bg=6V!a$`IGwv r e2 uG40kg F/Q\34b}M uؔYz8@*,Γ,JՋ#6y<:uΒD`gW9V SV{^{8CH4A$ ;t2o>t )[d{)|u&@e\l ǿKY-TOj{xk?KmȨ O1?(\ln# u=;wc}[ O\xDS811æ;}pĢ?:n8zOϻq <^;7t*r0D v6(买?}5Cr!!tI;魷ʫ?c8vU# 392$.+-W}t':\riz /KC"傈Ne62Ԉھ3x|PF{ &»=(tL}&U6$ lAAX"|c)dxS3??|ƹ`S< E0РF$B^s\k<k8t),517Q3JHy 'V=9͑Ժ@ ٍ:E$4mёݱKLV Cr~e h W3St~<]hXK6kW|4D.NMUuv0ЭٍUTD +T_@N4 }Rg`eJK9]F=/ZL> UL֫-9,-k[olfi_I<|!\'CmB^B$3+tTؾD#޺;uDDGPQ~729_v| EOc"@;J3!l137&B# ZQJl(G=$%OPu3,o,[`JSa䗹y.N.lT8b&mL<@ճlmGuK^;Sb9i3;ܭІQ߁7ex w/L;Jq.5%}# Zs{xҸ0’.-Pb \r{DC_M=QdcM"y4(\})""121vZ~*)m!7w"6HDd<0 zājiܙ\oYMW5qwƻ-P~QeW~ ,koEOC<Ġ6%1&蕉rQ-kXڇ7߄6&^zz:׉7{fݔbiKYۢGo=%<<ˎd*5ɑ-\h!u_o̧p<ЎX5аeԗ IiT&s؂5U_:o9180{49IƼTdfsM1B쮃hu JJtS/HO|z ؐX[\c-eeh}̹_oy!BGeJ#>k9ǎNm#>m}:NquNg@EM ٤BI?QF&W~LVe*`N<鸍`6{'?ڍ6һnہte,`"QS|;5{PE# ' #NI} 2ԣi A{7]j;9ن/}ؔaD/i{;Ƚ61Ԟ1N,hg]9,`:ŝy?$ -SB꡹!֛_D_ ]_( c1B?6|R]ϦO?@KHxphCpw:ʾ{}6,cG츐E>v[aƑ$ecZ4w$$TnA~o =<~i9 :#  _kHu6N +WuZw|/вنhꠧ=X$9Xy7i!!nc]"@O·ʬ#t&ǙBNJJA%2<[n"{YoF"spg(C"2*j5 t!bJoRpKm;kZ뷛wUgՎX;.ћaC~ g0A64􎽽vS<~_ڭ nEP,f'& BNvC,+)6~H'ځC _WU ׶84汯{ʉ]"ϟw K\YOs013\͛if:WNt7tߨܷ>1 6d+`܆=|譫ID-eT~gପf>.b׭G| # { ch+i5[֤QaAnF~Q4ɴ7nÃueffM斓MH0-v"bYO @.>˿wTe>5ݬcw8Kr-f:j{@/dex3YSii4dIW}ÃN"̻vPxe> hkb6c-xt3 U?KaGp߼[(9ΩMC$Yŧ~fzĥxI**Iki!zEmJÝ\|a?|Z>\g?l Yʖ*"Vj|szEʭw`GKo;y7M)B[ &gX@;ݲYL??WxxCY0=x$Y̲ p؄$ؾ]i/'ڕ3+zA`-7=p亸]gOMȧ_O\xJvXNy,macZmim \>ZoNx& %NgSV*N~>RaG hI-փ.]E2hrU,5PB% g9hx0ZOy8%C擀jvu٧R p2bǞJܞ0X8<LK';7֢[c\DzV[`gѸ,Ƀ>g=ta#q1c TGX82NGo><#Qx||6”o{6ZH$Hp{!jmK<ډ޶$7m:8c;ޖeE{"y^ EdK2^(x?}<7Bz~1ѣe˧)#EH[ FkV#Zi XXZ_1b^"0>73ͥ@%%nLLDwHJ]3m' ڸ;[V3G[SPbV4z2Ճob ;5$(qQҒVC,mA/.VnRv=f*Dq|3#hIbl0$EM,!\1X`JKǖ,l9P7Tn 'MKG'v#: >):h]WǞ%yGb=p2o! <'[~ w W(Yi\UqŪ|!h31th8jTiXQ_MX?bY ʐֆt {ACҪUKw~ϕk1!I*e))nC2*$x!;/뉆(vpDl ʋMM#56f)dHKėVF,•$E3ruf7dǡP'+O) f*IcOtTۨ}wg>w=lF50q*0_[E I}z5qv;laO;O6܈M߁~Jry#-|,Qh/^׷ ]48X3~QjM7&>Eqi,Ҍ8r1-MVIou{ /R66S}ǏInE*+gkh@Sfvܢ1ބT -U~gw=ksG&rV?cn-ۿUr9Ii;MC/>/w(`3هyӑE N6F&ZаTiQ5~'F!uFNu܋e\֟fQ C.[9ڜ=k3WƗYMœ)pG8lQeDXʍ~oPImǬ]$K?L\!l*^xֿkV`d[O8=FV u:L)85OFGE9sKZAQN ϶6J.AУuAH0?|FxHKǁm5F(I%~.Lj佇#vRi`$;Jz`&ğYV ioM+FgVHfyGȖ$ngpg\CP6ǣNߟ0%Gda.-c-6 ]- 6P̅Il$ԻTDKu_X\[[D{&㇝i4`vV4JR)Uc(8ɱk=k̓Ŏ>ַO]poYإO.4s9Gqe. lۭ4IDBB/H:d_*ڳ ;J| ?0Ћ=s63n-DžK0vUlM+M!3jYJCbtvcĮ|#`/#_<.ZoDƚw\$}3kj&.Hf?3zW&Ey^Z5ETa3ve=# `b [c!!Q l+C2 55hS׾lBIl1sJ2{k4, VYzؾ˽_/L<8x7|JkÌDM7h_ 2D[?L/Ru}4XHXrͧ|K*2q=k=NuF_3-,hYa_kȲBS`M@IDATBZ((>RRŹ?liJCc Z>hfz\2$"R{q*-e8Bt %x~zVM SjP3npI2Uۋ0}`)1,IQqA!$nOljyp!"Ezy?zB`/D'XPHA~*)Y;<= nY'>W2YpQERAŵ{ƨ߃0/a%Qb{.sʮc>hx/J]N<$7dm؈ +q ~ ; IGϚj7Fj .ZVޣK7]JU]*1]|Ho&? 뤷U4x$՗kkMo<цoCqS:)]HL/!g3хV>mݳ*KHOQO@| ]T}oZH#ޙ$+GMLZhLA$'<ڴ53>wwqhSF(V Wfle^A8މf% ҟ9cI_sݘvRD0M,-] 5E-3QӮJǯLS x6Ğv"+D< <T3c<.5h˃*79@6},n&q15wB 7> کW=A$6"z:SMR`-ޯ --oTj=b-Т@O7_~j~[gcĽ}g w.51-q3z[? F2Wb=㟨OJ {c.bO^ LUЁe|5jF!c-$QăOEB YVKn$es"5@ )H6gV[[ A"|^\}*|3PǓw2=v(q/e)ճudqOEԼU@P, .i=n7@l3&[wg"I xa̚Bq1)02՞W ԅ7$o}hCzf""ScCa J> }B~ =θE7Cˡ{ûٝjw"  Vb:oSۂoyܽFhl { 8\ sVDW_CtnarIְl߰ 9xJﱟ}22T7@3ϐmBη cY\ ƕ7o+NzF"Y{W~L%?!L"I)pֲDz;wD̜*U. j7 >vy*a>DbiG+m3iQJ +w~^ Ljj][1ߞ m-R/ wrSfX}ڞf<4?V0bHuL~<4)J;ZH]=Uv>"ĉSkARO: Qit`4U*V_+(W`7ofd4'PGϊc7!DRMR*T(󾀧).3XGQnXO($8s͌)BWulYYU%>XY_!]͢F~{!f-aO"9,َѯB\;_lKnFftHX@YF_T̙E5`hynC$hTGo |n"CUވ!QiTt15Y- XR)DR_K:uJV< Z־ɅU [=w^9c +{~e_sNl5'Tfh3 <Šfm9-d#9=q=nL\ŷ;\1;<]M8s߭wzF)\hn%@j'dAyL{'3c;RӼF>RMOi19sa `34F]d]۩")H6S*Tǥ=E[xrZFXa$f Twe}G#AV%`wP3#3|;O {Gw+J"l j 2Lg0eВ,zw /aQMN7Zzp67,METR1#3b_*5]$d˘/o\=ZPG=y\3+N~V'K )C7xπB͋ǗM1u %fai-[ Il=n\>=+U*;ߑ݆n$ѴJ}aɍVʾo JEςkI?vI]KF5A[ED"QQ9?n^<5 gJlb{o0bf׋a\saaYQKׯb$ĕ%S\BIn˿#H*2)0~:zfQ'Bo[]C@tǻ-lQW56#ے޶Wѷ_*5k}p?MMR ^[Γⵠ4у3]q3+]h!z iU&3_,d3 F֯>ֆGe!7&7cr2!O5Ȁ1E ۖ~iˉ'l 3H᷆"30^nZǹ;VQ6#~{H$QvEQr*6%=m^AqZkM Ք /r^&>g=I-q qf\ -V =v<a8ֳצN;O e͸a*wݛ I<<[w E%3<:ڈ(B3q3}5$+faoLpWՇU@(k$91>E*U4ΥE;tyf '".1c"1OsWd49o <@?KZw'7XW1pBg|Gڛk7SetJT9(hMTicّ4Yg$m6a-.%q.KA" _K+`#1[YT cbj`B 7M$`"Xc8A=]',Z-[l㽐Ka_7#o{]LO;ؒEKq=G;H >hBf9dql.f,HrDq)ʳBFD>o5h) 5Z8mA7{ *~,z~Ko{3H&Ds8OЫ$ޣxoq1(K[bl 'wsۼu; xJu46Qf:6mVl}mj2`9G h PjS l_njS-r)6 7xSjSXBjj@ᓹ..ח5S2FmoF~ia]X{ZM0/F"bzv Yt \8Yz 1*>ϲdxLJX/Jն~j[&fͽA^yCg@gĈ`ց+MNDTQ*CXFT#aAL׫!7v⣛Qh1Sې`XC"[`b߬U,H {ѻHat|f4E;p`s E;,!Θg'(;BZ|Ke:qǭXuh]5DMra\{+Ts;$R򿁂\4 ^5 ŧS9'GYAqWQiy;zkX>55Di zSN Jcʳ ~0hS'Uxw/(5 A/ou30/E›@^Ӂ6[/ZzF*04uFeBdO)L'´u*rc Nxz꣣Zbr4F2!9V}yN|zd%Ib[_Z:J&};Eiw)JXh=!~TaE-c+R"sR@e5L 9\ğeyJnD£ל_*5 f\-0ӪE \\S6u7J-{C疭6G ٴPU5rHσub#NtOal3  {m.5Ϯ]~w U/LFrٲ2=ZR92U=*RbBIK\TuX>7N]CncuV ކX8lwA.Ud j,/g{SMVeW𲖭-킥ÎdDLC4*EC4`:яR{/}*l@4q wWܭ0uychѧ@d9ID LԳ|NAZ*yX ,)cqZ?6|] x~Ԥ ߞ!q p2yaJRx&^Xi9ps;|AH;4*9 :lxU1hD^T}$ZXN`eV@9]ȍfHj}c6mHUO -CovĪ۹xVLJ4o佅Dۑ32EKpav#0,)7C=u2 /#~ltލ[3ۆdy^U[q\kǎ)%CR2zKU;?jlrGm*٫i%},SI.d6¤(Wձ3m'7:/ xjM\_@%3_F$sI}JthʱκD,, 'uu):ֺv-/3>w %SM i&X8n.a#XቁS3Ng+רT,XCdC x7dbZ;50ԹtG`|j3p_v(fʝl!{ܷ0gc`̎ʄbԴ[EU BICtlkBodRGirZ"$k]. d4\M[ۖ>@4R,),sW.[ nL<MaBj'BTT. %;]âU-_ dCo#yGװ?f$kf|w"ls@>p-c{+QkvT{~"뇐HK{0te`VeZq)3U任}#AjbdK[ME!v hTXܳ+$%sbV@JՈ?OC[V߼7Ik^eH~l\6 z؆c`VPp܊gؐJ?X].7ߓ.߫[ Q_-+yxɥ)u.fIeTvto[L ZYTu1셽MIMd9}ۓ͋ڊZa7gfU}h\[uĄ4?.g{ٝӠdkhΓi4cm^kPC _{yl@΃kFA{OO ܰSbN+Wxl2$&?$R NF6?trT>sydV޷EN|š-z?Co5A6Ġ L8(cw!Nb ΉX 43A+bF9xL81kd;8߃{)ȼ\t|> ݔZuCW>ETN= eC|g.6Zs$0GdQq w$MAFVU^@Teg!իeDr6npOד k(P,0{?IJE#&Ա\1`@2}<S]˧_?Y 52v y5#b=.f}),6"`w>e.`kL(1?ъhZp~8 ν+>rd/[Nղ saEK/"Ǡaȡ,E![ާm濖1Xlg {$ 8 v[i$i}i 7HM$xۺ$1/ѝ-u=b\e}': ZNk} DxށFΏ`dZ߫@'0l}#{l:B{A LeHRú-PV0]/GU:`He0{q׽ =;w # Ĕ"!9r6[(jS3K;U`-sx\yыwDH;E|[yl6n|6:q^l C_J$F)1c+?o'xr@/IWw v|se"E2"gCb1⪧Eh:NAp%zPۻN\utj;I`vs!1J5 " Ym!roy[B9?zU`<ȨyP}l3BU=M`&ؑ0fQs$8\98,@3o iƧXfm?#Ƃdl妳߭RvmbA,Ff~Q4==,3u9󾘖0|iVФl=.: = /۲0&9l7}0ꕦD) `9u[gs$&M<1 P4">i6ޮ{ o0ZteHV:7DG[RvzY/@Zl9J 4DҪFFd hvIbBt%oisfg[> ;Hڙ24@< 6@,LVlFȇI>SuMGnLW:Hb$ A ~3pO{ 0CLuQLU/ˏe|*`e7v PItZB` ]F[ b=ALO&~F`ayW.ldxP%0&br8kfv:q:rBt d'~<'F Gu7~MQRd2"#^10UPƀxKݘfMF| _ӆ[BE.؟y3n/-FkRJTW _N|v}iAjr5pwlң lXN6BSBp6&C BOFZS |'?(|=@HDi" ӝޯ#}8ԇ^5-7?ӔІFGmCX1w'S,2gЈv+t_/~~)a(Vq| WWC#IN4}F.fd^l܍(*MSa 6>?_Hx'lt֞gòu[jAwyRۊRܓMlj~fMoND2R/n=hDYDkSػkGvaZrz1w1җwCg;{zQ*鈓gHI\$Ba|,3>pʝ\sDjy{Cuh 񙚁.G`:6' '^ė9TgaZڜ&#QP,tf-d3=ScX&1L/`hq;*~ oقDJv'zDbvU|!fDckIzFz}b .cmR<%Kօ?lŜ86p T=Y)/u=x(e Qy:HBFIlk@Жjg{WVTBk`k!ׇr? >fZtQtx"/=ƌttmˇ?3[gavW܁P4$o̳CÙa.XhizjCm C4xu=oC Zw.~|n{pg-?|_ , dĖHLgTt; 3٩X?´xp eY% pwO?׭lF.LUT3gTTc_6lFn-g1[;Xb'Sr  v(^J"f߄{o1f{,d}aϿ԰٨lTl-{E$t]~{jG0upGس-@.b*Alk۷U+r#:o^7@0=Ь446̘:ձ?dܦ  Ъ@){z,:!Y p!Dd˱ n`M5o R UDI[ωYDmow5^k<^f>qvWjsOXكp[ ZFzZPPiH54Qs wU:Ň<yWQI^\hdb+DLD7P넃 6"_cRUc N'=ޚS3W_^=r©8 $I.1r뢔?[سeʹl{5m?YQ82FqIr0C[`,Nm]|QkϨLr~ ǜ. тU?yN-Jn/ Ng`c_w~ە 9ꕺuE~N|fJ[HN!ڐ0AAnVN꡻vv_0\r- >QZ}')&3}xVNu%q>BSe5q9e?[Dx')7%‚T5@~d9푘JQI"OY05Vš?&iٶE9Ҝ<.X}{yI޷lOٿ5z|T =: 0 dv|,} kZnb,_ԛoVbRKϼ?~p/ZׯWJkwQOĕW#zRt醱*ybs2e̅~!|A~fYE\y19a͟˄wxc̀Gk 7f >16-*#eK_?)7?pD3gjDx߽ҍTVK60|fJ[#h|\B%eWh{]MZFY8dV/3z)8 HDOfiomډ[SP@fu}[G-lrd97]Ek‘>,{(2!["-ibr"MX8^*)CR'OVWB 6uHS#|H'&ywȎV:mij ;6sc)rBЗ!QB D:rUA!}x 1fTz`q#=o)$B$uGC" 'hԵ`s%t:`fbh>qTc{U#ٳ;g9sKlܶSf`M-MgNVS^צYӱ#…=+ъnjE,fa>N(ԨOlߗ>[ ?)ppzFdƆa^VEIt]uK\7#a0 ݢ˽;: H4hRuN|8++NȂBm΋Xń;݊wt*?EŧAec[& [Jkަ4뮞*Oc}<=J孕f.t09?>C*{pld~J4:R BPF.JyZ{=':z]"{.#6F/W=ZS)\#g復zE 7"Y(적+* v44""X  aI\5H.԰k>-Zt|ˢB^뱘1֠?Y^rҿEc-.~w<GcoM p/fS4"pZb6o0]Յ 2+jgJyKzwX)\dkG6ItK~Gus$D7Na6`~o0f=khBVraQtr(;"e][OpcWKw&d˜xw`_Z`{CinCEA#72{*{ x?:!&roQEmy/Nhv@l@4Я( %Vɀׯ'~zFF{KdDQsuKyF"WNM`$zSi0HcD,sW ɻ]8 \>[p|~..U)m^l9N۬(š( J]8i`Qe/dJ\rᣤ5#kIzl}G3ivR$*,[gLF~}!|ќLNǐ#=\=]|jFdAqz9tYiCX/ g pP$̅P5.brrD0'zH& KHvC={Q7@ӛ=Ռ=q;ɴ}w܊[CHXާY6b/B㟟?Y-s6w W,I|5j%s;DW5 '06*=Kp=ZSx$^Ą`)Nn=ykhA5!$X=h[[eHm;'!6E zC3>|砡dۛl55hy-yJ=T]d4Y*CNKUS =m6q%O,"l  &](D&S4lkX5Fm7nk)*b)r{8n챊ɾ4 Ͽ==0O-FMI}LÂylmA5P]YCʗn>k8攙XIT-Ht(1\8Op$ x nޮMh(:HH;Xߋnb} u,̍>6QVFRxjӎ+ *c?VҢRδaNU/mCkTSQ#Փ?933r5Dk $8 |g@RU6l-7W#*6$rmx`eG撊!`nK5%[hb:](cѽPL%ϑ'rM?B锽䵉MFލz3TrmLEqTUQ%.ldy]4$%(^楈YW`M G CG4$qوYqQ>}}||s!Y0mN[VS.VTOwxϑDKбi3썍o|h$;^O.BƮd.8~~gyy<0|G:\?-gη3A;j ۻ H."ɎO7_3 ah>)OG<&xt%_ؑ\@CTl6{eȧҠB<`D$f{\}> XGj TbMRmsCKKN; bɄߋơhH&ԒΈX NP{vļ>M&ldk.~]liy65{<DTZD\NՋhوebtYY΅.M/qgπGA`. ЊI0 BvET}X^^E7j.^׈>7^% #oa踡S] ܟ&bU]Mޤ2yNaq7Pz.eq#,%E b&?{f[s{3]Է=VD;B!kѡ/7h+$Wa)aY)_œ8uƄ@IDAT+ĆC>t@?H}q ԃo_Cte!,ta<;A|#7Qk)`%,#䶍 32Q?]Y3@Ws4NLbÿ. pc/J[u#SÆۅ.vExSisv.eXKlc$h˙ l!/|3eV>>iyaB??aAYJn_FXZl>~ ?P'I'q14ЇY&XygP%}1h!X8L>H0j; `~|;6']A1;ܮô~!>:i38zXEIADMА 7,fYꆝKh9:7i.N`\hZ޷0]lVv*m.{{`3fgF0Rsmz([ʺH`b_nD.(sD vL|xy Amtjx9.,;d䦀/2n}ҩT9ߎ\LLGFmWǽF &;Npbw:afW!Z1Qs_ۂR`xʓIӬDq-rQ&1PۇjI[4ӝ$Ǖގ%DI\~ӘE)*9Mj wU/yڵȺsX`|fD_gd]g]hscRCnƦW?RrnB/{oII(`L\:}dY%gD~zeWq,l [w12gj}"]s 7pQv{/|o{*_ ^u7(gBs=0 bp'Gw(3BOF1Ieu$v 6rK!k;V}\vXZ09B7 {#V,}RC"eՓ\[ʁ,˳N_S\ʚJ޲&=3HJ;jMHzh'nPC57IpɎo{.C"#}޺ 'N5(QzIu)⤯vUϜoAU\ "[.Θ!M~70 t.>T[k?SkX7w;D⹀MoK{Y ]2d-r~a 3Ghg:&D[21v<,}5z0~MxXx=pZ9u :>؂%Yk=mUĶ1{@ y<=i J002:SᑺŘp'-c (kbx[XR6;+qxnw"p-)aIaYz1SW>Yt/"D+*|=Y!U:=T|->|Ln@L:kPb=7R)5%CO{sC64q-gmbHe;W!6cisp VmNZCѫ=M/\5Aѯ'zCu6n?Fxv>vgqL}Ѻ4y5:jexQO0%mn T;rǧru8Cll\oξ3mGMX?,<6qsү ^3h#2c ʩ5C5F C~r{bRic-[ԹX'ھhvCwC:ɞ^,AK 3JT#SEXOw'41h=|Җ7v }'_QOS#zX>H+Ώp,^Ϟ*=55gurVC?=ˑVXB!'б=#{Fm;5 `^\P=ONfLJanxJ\nxg_Vު}hڙ\9X f_s^9m^~#BP5NfNlhxI)1/tkz7hèi c;6#v d+;fC|겪crA!Y#\[>3p3yx"΀[)ߘ#Tikζ TM6t?\DB+J2MK*U?\RO1Ό5޲_/ 'utJwؒf>Oj^'iu||*~,@#>:l/б$]wI?HΫm(%}hke`x\ZeUPԀg|'V,fC 0`3^JК7`1y8`N0ŬGB#0 3aTjD@N>SYT7ALL=dc]i8%I+Ř^qkwU*>?Ȥ #ONNK>T]BEF)Ao&k(nc. a"q{Ff{2yRiO5|QAp9Dz*{=gw-y8?e!'a 5aKh48W`\̞x5kv͡7殡y8jxh Zc @+ibV6̨VUh䂠nC~p:xCngX(TV;r {y:4dX6R. )-Wުnʪ΢F,J5,&EIcԷu!hbpY58bOzg\/Kﭯ7O{l=W2G>Sq(bvs&.XZ2eR7?7GοW e[!ƿ`C@7QԔi^\j{7 dg(_ QH'9˜t7 z%g:բ?{3ZX<1#t%ɭ-EaK6Sz>6]Nw~LIF݈Žn)`]֨[hő??v'k3pῶۆm3pP+RǎϑpwL:}K$ȓ5ff!*)-\dbQj80L~tQC͚7EX[PB=,c֫g[ާ"w:>G.de6Ӽ.tْ"[IceUOq)p H䥗1S - uyyٲ•~d8!` JqѸC!&(NTqi{j;^.XJ\6( F{{=f' ;M7-AZ݄6g' +nYssq69mf1ph<#W'݁ѾZcX[NQ}DWM#z>pR{'ẁ-~]xt#UMx-]SćKDgġz8ވ[0ъkPjZ)hgy9Xd;)@yC'5ƃ&VLv8#3IJ!x" W|JX9Tc{.F4B9Z&U=Т0Й>Kc3kdVȭerjF+'F. *Sx=\FJ7R:;UepǤo)6c@2m"Pi: AR Sr5gMyGuf0ϝy+q3E.LV%V⍞U0%G \ Q-o<!UtVTRX% l؃zJR|$t땂e?cqԕO,IQKSv~;߅P_v"л׿}pb}4[tq̀ ϼk8#0RYG nOq$`x|Q*}nąxIOd)O6)ZLK]q&u7".o)q`q2ER߼֟i."s+[Hie{!װ z=7c2';6NuK7Z+iXJ*'>,0,Zd{&2311N8;GS԰U-;>x .S*ll: 5$yE\%6u,b+ *x=_l1.u\i0hlsʶE׷Z^on4OA7CsΘ7_?~=ᎃ+1wMFw'Xqžs0. ۋ? /G3Q?AUS+2dz Le|~ٞ π zY:sjF!&JzPnaaz#"ظ VPڙ"鐙 [K}1T4` ,z`E3M(uּZ:yHͯ"x{okBL`›|oѨɤ~ұsSFt@ȱ__0>Hm\X 7')Hs I;l1yI?)w2+S&Nm/e vFS_[PPyvE2z-Aa0-%` .Dq@Dh*]eGf.-qC9JcaiN GՑ߽i>+Hܑ_Qij܋Qc~sE"ȱ`nojjК<4dñTE. °qy=͸T+Bm,AEW30яDΧ8ٞ:g9SMw@8l5uhHj63D-O=vϋ[Ym۪fL?.nlJ2Q+{&B_6>][OC.2SZ'RLԘʤZDzn/ߥ}4wQ$Z-cYMʽfCߝ_`1co'w1fٰո\&$Žn+.xTKB<Ů֭wz܄5K͗Kf(hxBFΝ|=8xeWPV*T^z_Xe\^Ԯ^M_ty#Ǧmv[X[A jmyh!X7sn@؟R.@MF`FsAq$˰]}ŷ'?-5o8XNer6{mO3Sܟ5$o/k1m[ZnkJ [ox#OD`d ZT*T8+M泌a *ZR¼0+1q :ItZu .ϼQJ5E[kBy&zo?^1c$93C%&0?%Oo*Ĺ!t!;LɎۺaKB/>2SMWz`[\DnTW~#ַ|? ["pl80b=qbZοH5%[b'eFFd}G]~9]WX’8|X>'jQ7$)ҭ|mww"|E7;'|+}[r7:Ɏ\{⎾FB4=^!kwC1sҵfݕhn Q:k2PO̓4pdpم B`| ƂKQ`ۊڝYV`Ah?N^gcY4nLEc(^`y+<]0cTQڌ$pBlݩA TQQDOg"3xMnm %znZ.(şX_Xy_V1ʌj-KB"`{yKN}L5hҥ1}*Cu^YG֨$D[w?~8w-~ǂ>Z9'9~Xpn/Gڝ3[bnwTZ?Ѝh|k?@ZRBuh[o|x&%Ï_=@v2#\ټ=c##H-u8+zION%UlA` :Ï]!m ~Gn̘8 E;(G$w=*MF#YKe5jFf([%=RA<{f%=IF*\S\_ឋXf|(>:Git. os BQ4.~W>}#S\NhЏ >%d6RqAD~.NmQsv=XZ?!~'[f. Ѱ Z1` pf:n O .}e+`B1+z1\ QŨ5, BC^TW_G+޾' ;w=2v8R 3FO[@)Wknޱ`xJ6c̔\0 Lj&.v< _BxI˨P设>wrVcwZmF Gk'zl6҂=NPK'!Ι?m?zaRmV5vFxӣ5)]wFܧ26O%4xy{ " όcà$ǦI]aJ|lbPD`܉YЀGS[ :{iH1ޮfDܺ}E}Br'Fqn{"r LlFg4eMJ[&ξ緖d1R'+AnR sp^]F\WIT1LDDS8zk/Vl Å" :ռ.tL=^omk&hA~ܪ?^K:QCSn~-*41;R=CMT a?w[zxa.=l<#S-(zkԾbX<` n܌/{LoK xڜA 鬄">l'k jgYǢ%C;uɓMsOz{=U,T~b%kjCk%. poo~b?zo6MwϣP;6%F:pڞ΀ W4RCن,GEE `wna+`Ab*Ō\}>@u {l\r x<؊'j;uGM.ڈb3d[^؍qv̀ Ϯ_=[Eqd@^m'2 _Gm22ү^<ͷԳf\6Hb[&ʭv2Eڶ)KjZˆ<+~,yok;d˅LND.u[Ja񙶶62el@wl|˫ZYeDZsJ]+ۡ:I 3aɈ{3 tAz~=/O7F{JJ"KL>lˇ<sR`b z5֔~,X5EOPo!U0%Mᔊ28^ 0.5M5ȮUD?܁zRRܛiVW.#_QF+arUF,|v;#3g=fLd 5Jv65nIl{Wy*OxHMM=:gELMLWl5JQx,N#Q˟6}huf?LT-]FEڎm)&" ~^.kÊH/H4//FnMƛo(rM3cwpCr7>7e?IOC-LЙsAQ*ޔ ׮]˗c /Xo|e2spa4bDCeJߺqc 5.s; $y˖- 2 'p:M o''PSd$qȜ2$W~-YFb+)1ZhmZ"P,`C3VWݎB"mwuoCZ -ј9d?r:r9nd_7o.?H2Y>2<ǦBz|gbL?rj2Od:+ؙO>3Xl=3`ϞkaJSlT8S~rC>nhjҪ} 5dydw GLï'qmga#:"):v@%ѝP$tQڼiOouk+M dnژVd6)7P.?x&6?=QB EGƬ}; Gz:QPـPEϻ,Ph?dFLMIÙr.$}̆P|ծZCʓ[wR I6h"c/LnvA-I[\KGHkClf1o25 Ac~^_;k$Dte9 kj\Uxiop3`;6u v#I@n=claeFe$ݑx<l67=\g@)@$xf&j’O}~#s#Q,voa[~$6.bD8e,G.7[ѹhVRzmߊ)b"Szyǎ*X Xbs0E>4,@bb9n K.s=ŋ+4zJyAtq1('?F.H~ƑH) ŸWg>mqG%v,YbOQzwFTT.\/F/Y:?Zdu[64Syp~;zcw|\< [q}EwiD1=,wމys=۟]we"Ů]c e]ZQz~6=+SNwU|`N|v\5ßi~:ܐ}zRhZ@mJ듁 veb ʬ^4]K\lQܱsํwXvBsjwbौ}8LчH9"=̀ ϺK=axm!%cB!x(Y t$:'!lLIc^`N=Z?јmoOFS]\ ַL-ٟ{3}61)p7"GkUSc[Qe]+¤K*G%` jBDؗdwt­۶QfFI7` 竵)q׏oxHE-33ӀQMtCۡdi>{) i3 5>@1)~~k&0i$(r|7YwuYěEMMMܹsrhZ0XO)wО{ Clc1q,vE &!~y60%l]!xa_ ,d}[hd™mTX<~5S O83T]#?|d"XAf9N3r>bg;/z.k0ǶKYEjlG[ʊպzߩEb+?Ɏup7lLYKѤӥ$ت$!_.K'9͙.ܺ0 ,hq$ ȦOnŪ5? ! |<-nzPs SOƛEO\W-4́۩m%AZN_xhYl[ؕV:^Cۙ0u[;D*(">P"`ٳMMFiطA-hZ,̜9̉cǚu7m=(,#9xG ϱܶu'ʗ[AG_Byw19 _@zi9ܮf]{Y,ɦ, pUKKm=Rͅ'n{5 fDqqĶߢ ѳ2V i;ȽXUu2J4ڂfl?;b{̀nvWG,D<-x fߘozOm[ѽpG`mr Q^o.E<֭5xm{)vвSl;C]~M˘cWC90><_;%N>Yoٵd{.$$5Ŀ {s{/SNEC9v1u$Fg5ԫ./k"ȕiV=#s[YؿP^Y}֊LlJ#{p㣼"*&6Xbw5gdC3Nj~Vxmdh6Fdd}},X:6 j֊•BW[Ѹ"iXh<..Dw0/[Sߌ_؎xLGA(pXgjp>".X:d=g8xr f'/"* y.D3O^]C/Aތ&׷7@A0 C8g(-OwbaE3sߵoBCbpׂ[ױq /+&ol]cJ1pёqxQm23`g/6x15c"I n$uz{/ ߝK7]l<v@5w~wg02Dx9_B.zQ>䥽<^lukF2ZZLyX畔W_5ma}< 3D@EI'茴xX:y*KNpC7B)v%f_`"pY݃Q|!)EJ%b]<%x=&ٸ߆HY^֡c{w >g('y^ìYA4+[?l* q7`ʵxش}y֓bzғ@ .T;Zp dCA!eƽLY`= &4xJ޹3Ƙ "* Aä9~gЂL`OG}=tkhæLja[^9e^v{ܟ+y_>gŅ|;PHIڵS!y{Z?r G=qY1 ͅ{l~v_1=d7!&)j/kvGGQ*PfYk]ېhUڨTqkAP>zt6LEֽ+;qy X3T1p*-C'^p+:)E\>u5x2QwyǤ< %p%PKRŸ{I"[\G5`ۃ[nŀ_RlV@IDATW\[ [S ;1!=sl¼3Hz\ }F{ZZw3|t}NjV=\"`-)s7זon4^&r[nĤ$`]n=6P&.^эEB};o^x~UE0v<W7T|thODbl=eS—eKxU̹a1 @'0(@`G$΋_v>d)g;n&Fm\,xPwx}*2޶c3n-*/ ]cj6#gc/w | yKUwKhXQnépeAjg= e׷!I-&8F1zC; '2u 끲&uv'3廿8E4۠`wӾ~7ްQP5!>X:># J}|I{glsïIVu¨TvC 0aAw'o{dp"? 9&ĕ.62ݝ(iƷ:^~F,!H=WOySmWRp˪PWC% $i9}(9Sax{o #@FN*sdž֢~0wYR5$ #/<8GIs}iEcy~F>F089#}w&汚^Yom7͛DϪdEQJkϡzO#͛єIނwPV!EGy6۟8jl~t3 ^u l)bӀaKand [M ҡtJF`y|{Qw\=m?JǰP/\21ҐȬǤh_o)ÎF~\0δ2z9K-Ru^IgD`Zj%\`ihZǺ&?g-(>tW s9Ͽ2\C]1lk{p{4E02l)tZ S ܧz BeUwnLRO7@OqhCn,=j>?iyekH;+#ːk*"cR"͌ϛM~!!1)4eAMg֣/`<r[4:NC' w텅qכuK wÝ=#txpqsb:V_o[Ii^lx%roǜm3*yIdzZ+NJx3#sm#Oc_@}%Oc+B^=ŃFf}o [ }l~T3ȣNo/o5S*q09HA #ꆎ;[Rj%n~ΐY>/& ƆUxUo z_{ %Zsڐ5QJE_ĿɊb\J4/L i)f_lc2ϥR0/Ü3NܮoFӘf&)62)lgYd?@3rU}e$Y9A8':nQΨw:y.ZBs!LFLV3a9s|MAEewYj1u;o'>밦yeFX/g|weQg|?_C:`Mf%@I/ OcQKmlI{@_cbtnO :."T+W?&Ql:i=xmm.FEv~ߙ>X | d@<88O=ԑA+{]YWw65wLU@f@Rqb5X"Ĩ֣'"E٘4pj,N#x 8rB"564c*oDQn-)JrLY3* 3uV&GqW=UF A _jQy9 =bёOWF]0*iC6$h(]xw2,{:8T{l7Mth\o"[Wg{Ӹ&ϫ,`43poيg \ԧL_piZ<>a2y?"Ml\;# (G'FbTe[} LpeHt,fog?[Kp(xPqXLI 1=֯/!oo.L4 kNQ@nIGջﱾh(/i5oS 럢y [}P?Wu݌{:H>&OXe~-8487\"۰ɲ0$fu.ۊ <,ډa.Uo}*@w'2^q?a1MMdZ; &UjZ*-5-=Gwk絴[ڈ*2k[: X'c%eL*nćXFi$-eJ_w)ߠ/~z?r3bp\00ZV?QBw֠]hiˬ>sdhʼnd e7:4 3r(my/f0 @~|vSHlh/tw|[h9q^Im4톺g<35x+靴uyI0y7 D2ݶ2|mW9T?Pl}ZToj@E1 {-'!`_CinVY'2>-2Fr8wV'eyQ8TtR6 WJ\^#?ۆm}!kF&jixo|cÌ6kDs2ޕU~;y N+itk R&#}p:>?zEcmbZa۟'S\7Cn-uy{ z|bP6.LHCubwLG{:oYtP "e Y?)+Tc*Df&#,'ū#{{TRۅsK?ě+@_q1&@b8:^&m] @.d>xYwBuJXg5".2<+b̀ "L8L%0uFiԌjj͌#/=IrrgjL+aԾ`%yN; 1\H\c -0λjiOj{FpX ՘K만&AIM .Jq\@8MEn;n\i\7=VV8-DkEh 6}\]"WMޒQ^cr"֎!'7RO Q<^q|"(} {# *U."[ |)C{mRܖt};P'YJÎW <1V wG\>H ^߁O;Dr (]hBbteHG[]#۰66?ۮx3eM@`3u-JMՌQd11"?h-l Oiv:Fom)dx:ƴvךZ >D6ϩC 8 љiTG8㊩_T3*d}i&cվvmxS%4rx@%IEC6~Pڏ /P3f?#2b)L"PA˙w_?ig\ZO,=8<6&W<~"9M (+pk?m4臏= QӖ`\Hz㭋/g^0Yޞ1hχS?ySxl=6>ּk(2EGI&ݛ:EWJ4=MKEAFB|BsG7k@΃ϹvAW]|0)!%Uh>w2ɝXC7m'iy* u=L1w{(Q:#06ψdW"]ԅ}88XLC )zQծ+3qq<^ɸTa 9 % QiR͖|/%D9Vn&`KZsQs{$$"ה (%mflo;vP+zZv- eڼѰt>}x6=xtm vlI+/ިb]0N¼=ȩg`(6. JdT=;X5qDuK?A՘T7w]L ݥ>')i:–L=^L I3-sbVWwR}1"Sٲ}>`|Ⱥgʟ-l9sf[:&c'*h 0^Fb{{PLQ`j^'{_qFӒjIgP5œ &+!w󭑴ɻ[ڰW1z%:s:4{;MM_!\ *jwTRj&]L<ζ^Бo0^VWZ&%8z>M#ؖF R5:b}:.U> ~[0' &6llkEG,V{ _Z$LJsosʀ(K";fF! 0AeVx}e%t%GPVhN\ KP 7'A{$R}nWOOS!."~Uaz{<ϋTc{mΔ)W49$2\1Ţ 0&tľh6 %7dnZ[bd8203( /(1nh_әir)IѹΡܐ$1p?x\m^Wlտ(}/$09t R4B!R:uܿiZψ֋nx}oebAr0VPsBŤt ERm\5LLMz=*pA4%yqg UiqƷ换IIq/g{r/g SMt8ŗO:i=\!4 y;yU!oۗ =:s~-`2? ĕzW4ȶ2-wRޏİ4X85ո*_Th/~x&}P?J+'ri2da2UI^kE^N<@z=NbkqCӗ3N.3ǫ~kEQI6l3p6̀ Ϧlv&SJkPzk~Լ˖ӎ7!։1;2!%4,6Idt7ڜX<&aW0DD}!É@e5KŅ9t*Eټl|f҃WfЇ{p4^ISJ >Iȣ6U* 2;p/ϻ@+xIxRhe:[ Uj)"CRAQ طIN-fFYIj},fDֽn9mI`*5Y6ZPNڰ u/ dNBԅ(b+sGc.S(׹JUyH@5y \Ek+E8'CO2y.m3pπ +%_-hYd&1TgJj<9WHF[wufH!XwdKD8 I)RcMЗ=h*IZ; ޺&S\E6 Y~,u4@.|qwI9M$XDJ-Դ =EP,! m=mqI'Ffa"C&ʔuWv5 ;yqǿiu]s4^⻋M[jGyԲG/ne*ܙ/bH8˨A̚tS`U-_iRniSnePLvAҿ^=~v ::ak.iޮ|ы{8p6ᅁz}kB?@3u*`R&TӀF] gauiBsxsO>L1^f3l~_/ uj2P TYȒJU)}}i#)'*\ъOCF/pexK+L_Eh0p*-ǻCE h{7 B WCǫ{?ݦK}crYww'=Z2fFAQvo$m&Ƚ@EE܅NU*;ˌ`M票{d+z\sT: vaݙwL`(r{ғxbku5fJA qAž >$ ko[E+</fGn],nzyx~9Xkd]WGgڶ^[ֈ'^{ tJkt|mpL?I}3 T VkL1"/UvlŤq|}Aon Ը%d2J5{/M7eSV[G(a (NȺwq}#={T ${K5mUc{A-ݐ660*|Y("TTsy#XK rÌm{SչM/'#E'sqjRbdJ_~R_\OiXJ8xB j?Jcd$_uܒ/\/L? =8]pfd_-1*B?~vLX:-#? nkBLݕ2zaٙb?eݔp%#ÔM̵eӺs* s/Vz2f7YGi\nUsͩbPaiF+!k!гu^]VqskT[`J^wNqc/mm iF3hu%)$=ct11#y e?C̪tAgucSWD.I#!s,$}㬱bOѡ{߄ea%!B6=L q,l "2mY a̼@;f(c+:2ڢ~ nGWIug}ӧ#S:q݆au1ݩ@4=>M̺!n9g\_ 81¦ $^E{Y |p!-[l3Jƙlj[ Hs8mtsc|Z4Rφ_k`fjnُWѫxrp l:[F}KVű?;x:Rwa."ܶ=G$g qBjCd%C6+CfR R$9] 6-ZGƬ\+&)3 X_[vKb*gRV*bzRZ"blSi9o@XY:s}vQp6dr!!KH4 + M[@%CzRN-oף̓2a#hdlBjZ06V˯e>3gu;ٲϞpw7eug`2>{>^_3\%[bXX ~>~>m X2`.akey6א0 =| ` V$5nN@8[}q6Vb!\%a.hm*"2*9?j]{IЛŔٶٻ%D䰦y{Hf1>p~L#_ _^ݙM$8_db\o2g{G\roM_T&}Er%hOءyŬg3*M/%͔3ff j64л!^9'Y1g@Z%֫`k  9=,jaB|luS 6 P[@Ѭ5:`8dg7*:%o]9@AeY,HVמ'DrRPs*wYk vcI+xKSrUQIu)F-s6&W u (+V(vת} ^#EkT׉omu! ՛]g}v3%eVʃVn4J:7eTP܌;[,*E$(*{GнJ5%; 4/x] qg.}!fpvϨO.96kuIIưn-bk{0te9qh~J(1Yؒ5s9͊4gv:w?08|0T5eyr)o)@s#1. +nALT\^Is[ Sg70wew+ ǨCY`ѩrdC޸EMep+ lLe#NQ PG3Ֆ63F{C$FbwHm"q'>SJ?K8$?ߡnJW05%pB͌BudT*<kˋ%f~Z һp])qL^FϼaM).hܱWh {Z_Zq+Yg.=31Rc?BfLGpV^;|SjizкaikQR^^L+$Z[7mzS ;p \ N&3>J='~fΙW̝?pN6hKxNP~ywsQQ)MǤXEIY]ّ3n,idnl>eܕE.}?E#c}r3ͨ[v: @ %`,kUv+jG QCDuenZ!f/ׂYbZ_=_,X>fFw"vVpѭS{"JyҳUuWs"y6[<}kUT%YΈ2`l*9fd2FBШa6h]^/l1[|R4eۚ<*m6n~50ij!I? IԯD6A:jWN7Ρ}1!VYJ]qwSE(k5yTG  DُQBYyCM'[za HX fr++-1'WG[F:XvM`v+P`ϊX=|ƣ.Woa&{nX Y̿ ->'.G[a҇j,y (_Vd%?hb7e33EĒsj=M].>ʲ փQe[|">›2䙸{8m:3W>o2fC6>n:ȋKuoCuË3}o O9[q{ʸs>O8H~ |,.!ZGgJ+ksI zV~OkMx8 8`+1 MkJc>g)!+U%LϺ-YR@tZI֮=ag4Q!42ѶKWw-OW똲/!:1e鑭|b/n,2A~F5qLڍd_??%&RwowixB=R#}ǝR"pl"z*Ur>[.mT m0: VY:Pm8Eα,ljIaD~gl2Mr.` ?'k=g~y גl6s'#z%kR+;Fu*r*^ӧo2qz7WUL2Jk@$FLXִh:{˪Kƫ`GQA8;no~*-m5[05, 3 KEZ q քsFTCɐ+.S 7/Ao?NFAw"Huf.ZR׎9@w+њx=\yv-aV7n2N"Ԋm_A66nzQq"WVƓ87)@KÑ퀱zA/ d+.G$wIO@;T&؞#SJ%>CfɿpqEߛL?gB^$=IU] @n~eq66]!"1 i ks =5:(T2z sKW 2ω1``hi#o^7 W@l$]La\ LhaĭJx Sn #r);Rue2Ֆ#~y@;|LU6o`1 mȢU\sGz!3@L-S.UQ-;.GqHnں+t )OBvyJ _>R'vob Vɧ r{! 6vTVEUT8ME&2.\y_fkWc+i[*NeSחE6 7e66>~gɚ̬[w3ΞyhE ^i_\%1U ߣܴ`duIY`6)=Q(hO fLNvEwPSc.mWi[e ;W|R%c2RK +y)B A/)ù)fYn΂5#hW%dBD00әtWz>7-Ϡ8UG@$S!@<`TN".\>mi)!Ƴ2nqLq~t(X~<b$vf`mclև\^%ox?Ī ۡyv4 y]bzn.o`O?/ 8έJL\v^m(&)Tl):ssW;8pc$U{3JACsڟpUM*3~ome,[5LbԶDl{W 漷7@ի EH2eXV P2Q+# 5xiQ=8"&Rlun={@ Mx--y{ِ+0vZrwN`+lGDYOkZ N̤wHsCw?6 IDAT׾hP8*T4[VV(]זMDJJ?(剾~=r]3wy wkp|{c\3K۱fUvsm_{JK/޲>Jq ݔW DR$?Y։R10eV{ULYVz}aK̻)ŋdZjzx(c3 f c^y0ydfnya~؂֓ADKNu2svYћpO.~@/ZRW˗YB+c'=@\kxo?(ւFeOf²6c)q=ZY~D&<ؒz)WxjJVgHezq'ҫ`Zאub: "FC6IMF#l$^7n\oRno|^3VMYvnwXq>7)F*}L~H^Z :ȅd|y >WΏV_7zS;SnYq'Q-$wn)@9+չ^xCS0Al} u2wUg~bVG@ںP܂K󊣲YD%HYDRԜy}uHqh/X ^ǿЎ~zYʬCQ6Wc#cA4|ҁw[a8k˨{ma }xV:|FG[;"k+3ErwWsdž/-B9%[2}xv^NuCs˼*y6_H;̬ѿ%yZmˤRITTvTǕ51fKf:nbJcP2#{Z dU??kuE'}F h~cy&n\e_uRqSG=s, h%ȁš,"GK^ r#~HWtDQO:ޟ+kkt $.`_ͱ1<2=dkno]/[b(W帻',d^>3qȚ_BW/pP%IiGmJڠfLLq<ҳETRSPHL7/_KY$YiC7/,}ҫ֯ѶkNJ[ hwsό}TͩZxO*$%~ yHHx5@owy˜뿾3^5ʌ mf@z'0ʰg;~HҚOcUa-=oVt& M>/%oY5g_6&6h鞩_{DƼ)A:n+څމ8O;w)={^Wѩ KмlX(L"pq}&Vx+ߋs} 3FJQ18{k[z{i!<6Z>".JC3֛,\V7ˣoy[R:1bPx QfbgG-WaWAv빕=߲e7TbFxʦϤ 0Wx5`ӑ""P#?B03 ޛ~Ru"Ѭ?] "yx2|B[r^Gg'O\/ALqY(O G[%bWF(VUHZ=LE86[#R@a|IՓrp%_"nq6ZCV+w=ywu  &̦֝};781V 1P\rNkn(sjU`@{0 z#|wrIu VUgKOnx2v'S_DOvGN%: XWܯQɓc8ĵ7av)?ݸft N0~SUReWc?ȎH]a3%._o`dוn\K,us r{&h_H&U#wǹ2s%5KgP]_pKc|`^vzcF~mc_r=K!.g1͍ܶR1/܍Ys* kЩ=w M]-mhx }YVߘҙZ\X+E EZG)~㸍$/ W2-GDeR7ҫ D{mJF Qkص=fAI{Ϝyt%\eN7W&UўrΚ-ka(f [; 7+ʯڋX#]U;EzgP1+-AMM mp+,]EZFx>~Yh&, 8xw[Jeg3^ ϯ _=nfD4Fv7>#\3yTR,/ ~grE]Zӂ|Hf'麗{9nu #ݍCo rMH ڒ^}r'ȁ-^SRgWg41*rRPC)ϮUƸ%Cm"RY6}m s^?nvQ[9ѕzcE|F2۷lH]+@MD^7 %]݌Q9JP.[N;aҁ ketOYVqykY 3z/?^*քҶZWp\iVڐZV-`F~׵̷|ePkpP<o{ۿœUl[k繛MEQygo%2b|̕<4B݂5"77}F@ ^mlغ3~+5ٍYuSRgR z>wdUET~C@H$@ЗVjX T0DS8}/ EOn*?wψW)+麖4dtӲJ0T|7}50 V\VxܻC"\AoҖ(E?J&`yFT:Qmx25oН2ho_+%[}w~ʩz#fyz7n:{;+zqy}zql]˜ XϜ-cLZSa:WawmBM䂶#wJHH^#[YbP~=bF=KQR34sE_LQlg- \/KdfG?c>dGX}v)b%sh| ўd\3b8ָ3֋h ,^F,9$<4S81D6aٷubhH\gw("JƕuBАaܼـ:lxn.-CLr x_:RRjfR f>jI‚XŒ!g-*AÉՊzUX<:[1rGf7xIKQ2J yژ X@lr.Ϙe9q.7l$"-%EЈuaz5 O]^>}VاM ՐBG,;BEq$8'}%`py\Uh7aYx +mg{}j6$jg^cҊFK $?:C)Qs|nlr5Vx/]E2hf<ƧULep:`;:޷.BL͐ |ytqSRR^?B9Υ52,0 v ($܇i[QcK>mOьG&㎽o2u_Vxd&NYUv [s7sփ3au So W@t3Svs!8QM (+ hz_d jC%l~B!/\ʋ"@H֫T3Ø6@(o`zK$yrMBv`RVHkTgxC.4cr{nۃh_ dsoCfӨ7xK,8UgV ώBK޻*s71)Y߀qٟOd1 lno YYcb/JHAHx zXR-_Q/aIPsù%Xn]OcHMF)X %dx]#P`WnhsA:QTWjK"ܹRAr͍\SE}k1-$zCjȫ3纐 q$U ViûaJ?Yo9V0y\m7VpjHle^h9{75sũąbumMa)PI% ?p qsiWĊFQ#Ee;J"Ps"<zftI< ;;UF{8 u:@ՖI bOzCaWPg-KBY:K s [Qwu݆A ezfלڙxFG-kҤ[3hfM69'D>p74y jCˋ2CϮꝥ}͎ HvA݇f˽ዘylJۋMFq҆6z_\=@\ko:{j/}sN} HZŬ#D!y'a_IX``[JLn"{h+ŬKG@@͊@*׬OҴr_ Ì1.\f|;Zjh `E!.ui&dDž8b<'Ȑ Ȱ',8wnmuʶ̼ x* HX*.!QЗծ-;`voIF]=Asfy0gr hpa(Ҷ ܄-]_F50ssdOw5S)>Gd/R~ _ CF-#ɬ%KI }uC>uE!pϡʗ7(tdr U 39WWGtc.E6]o?w@M@ޅf\:X6ûP]w_zkg!Yrq* HX~6!){Okgm[ӳm|,S, ۋ>%]@Y]Cw Xvuu"ȊMfѶ ҙ74y?z˸%Q ycE,яT6lfbdGi4nHfq#`:k_y37:yQv'3b-?57XV?;B/LуZ,KJ'o{?q?c&f6.TCN߻0ƼT$ϧڭN 8*_:j@h>R\#nK#z fiߙa>t( Q{ȡ@uua{L%ӟu[wh-sϳ -c,Ti_xe"JG.]hoD űs%6 tέWxf&N'c[=x}o7_s3Y6R]'|YO kJA q&[9V^h'iM-Ϗ^І%c$it,H<`݁᠍ĭ.pYw:ਁNK~# ZZ%`eڥ9s< T?;Pnk[&Jz6fôдO<GFxٮ]dMJ[j=uzlB& 11{_`j,C^mt|3^k9ߋxpS:# (՛,xHVuJ״$[יrWe_g]φeX˦G QpYYJ횑\ea?8iz#[9p}3CÒ^̰<>|EIَ8vff2ҟB >γQISjff>jj=? mC'-P+ϛF &yQ¥$z4I[2\HV bf{ K[-z{X֋̈́O)M/s& LÐh;G]}҇n"MB .n ӌSϛcoAj:=&V#z\O`n"gª{6ۤ%;/{~= q#jyr/ː^U= :Fw ΆgKMԼ[CZq5L'%K6SR$tMڽ5ėidGw|0Vց,RV%rN^ $YcRҒ 4no}hxGF[j u[}ȼ+&5휻|d%mnz~Bvv)y_1ٴ=fłLDn:qNXJ6]/n^unPV70-xo*:g&}/E EE x{fzI@% ܱ0vYGm4!Xgy l,DdX!q@X ircX㏅svSB *f΋?Hb}l 1/ B. XաW&!6su-svsÒ~PmO8G0opƶ]3hˌڹqEMb. sgnt/SM_[JmqJ5o]Ù'v?A:ux0v(|1d\;@S4gk(b*s al@RZ/S$)ps^jgۯFbQp?8m5ddi(4暫ǿy{mXĨSF|A3*wGZCy@B?g6%2+Eg/%S^'PGd͑ m'цa1 #+$q̲%^.D']|2l ״$kGպEK2bW ]ة][+m:)nɉ"![ڋf$#cJ:/0A-W M5 - o`9 &x`ڀ4 tfI0gm8'2K9ETk3fb?%-iTeuoX=, |fRpCv}fE e=o%d}qXK̭x x?)7'ݕx{;* YKP+f,AHcjK@c78 ?|b6uoV4l3~y}hCdkHH%\Bw0G.A%O bT4G;hny2]dѻ0A7@ e̪( .z-&cE  z%Hwidۀs3XnXh*`dR$J|Ұ/10W2;-p)dL!"~eMb}jy D^v+V%8-6:IxPQ Xl\g3e\͞1w>Q9]KuLMNx=wDYN+7eo`~$cl:fv}][snlڊd"9#g69w,.pޒtw"?k˨⍥. dJ{ؓFk%X=fi aC.5x3I#ŒǷ2J_SFc vtd1s“VK:SָuݘQ[m!ӸOyqٍ"HrzK"Q=03^1;Nݍs\κ7_aՆ"#k} &l0wyttFyXIs>0CK1Z|$򇀮 LTP{3)KASRz 2?WcO4V 64Xuh."Ϟ_܊AL2]] OF  uX=037Fd`9^rzl0ŋe Ȯ?qf7 UJc2n&_G[ w`\h0Q%^[їÏVmxKA6N+Zс&~@y[GZLֿG'Dl= yr/ke[V+ P)S}>ҭ+E{ȭU3O@%1QȥG&O/ALW%^gkI.gAIO\װC)sgMKѕBDjBvncٟ>60^T *9Bo~-C#,K'uz< Is>O8~#?[!WƜܒZS-K Ӳrseʏl..Uʔ }ßlq{AO8Fͦ.KT'E!3鳫U{ɼіY E x<ԲO숲[X0Ο?Weh{oõyk !B4iTƱJȾm( ٞnszaȍU TcڛW^=}LH8"?QC%kHFq\aY2Wu}LpS&3mY~miyZܴS`h4.[gρfBt^VR\`{RYGN{قr6`u-P,<]"g+Y,zl7Ra][Npiل3~IcJ&8m5㺒ґ""p|G ؎kwYYvM:\mSJ`ӧgC+2rE$\dT2ye|{\> u6z٪HzG`x2@ƽiYkJ]1w(_!\sЦ.f/"6Vhg7 XulFV>J7RRd;.oqSfz'ۣ}IurbF}A%26#Q9M5ey+DfNygbq* gFJȐmX?;3??e-_db~0Fsʡz -՗íuHB{OGI\^}GR?r\]6/P! s(Ƿna;mOXIAO_`;jG.DPe)2)+-̌_HfzhWT][чab"o~x)srB3gFvT C>k\Qs( ^WVD)W+'vo넟^̨Xo܏͕0=^[ yyr.A6~y1]WF(e@@ @7eL#?5v0z]aVx f$[(H悥UV}QgPb؈.qY]&3KnfVIo'C.sdL x^>n j/`^Ftвav7 kd\i K=:+ݺ;xcyxm d%>=}Cܵ[^С \6t7p~⚾''}""P"=Ċ6[b֜Dm{-0cFC*_췔x~?3cUL֜l1-O3bW;s `wÙ> E9ADZ6YnR1^˵vG SvIKk_{eW1,~ۙ͋ FԆH:2+[= ͌Ev" x|ބe0%rͣ{CBd&̎M\ B`ns?Dum^7z]1&1^JJKd|.VŞ2 A ϳwQF˽q(s2E`_ OO__ xw$l-kgچfLW^n|nq`YuDMa]wM NoWy~-u2~u #+[LCׅܲ reg pG\ h8<xҺ𚅔ƛ51~ȞQD'̠cos9fD85x{^oTEU4'YWnfL9#bXJįG৙F)ONHqH^{oʃJdc£W(uGN)*K[ǯQ:3=[Aws ^|(?_*=[65~qxOL]L[xƬ,Ϝ=fr#$[wk Jq?xt6}ǒg?\46)f*װ>X6A5))5; k{O2u.i_^Yj |MȆejEF)ث2mKMV{(l>ǰCٷ-mcvԮ̛o>w 2QTL˝JUxd;ݓw6k,Nt_ >nH{XUvX99.|e: Oxf>0Ndz)xtԬ$Y=Z֞s ,S^֕K-sMZ׶tvc ܖ-ῼ`YU2s\@5 *U0ӯnK UK2'vlLv/oFBTGŲU gu,wgW#\6(ݷ]))/ 3I+*Kk -V#Wkz F>`+w#~i^l#k_E_N /NejI.ᕹ Hܙx^̭8!14cayM$Zb}Ar=Zզh^""F OgK8b=,aY[2W>:ːv *n?xlwrsTsFY'[0+թM,K~%=MaQ.L}uKz3O_9û6 gj `[?͎ ٫6[T*eG{c>GAڑmM`Ve8$iGyz  zpz(o(M[vbz!W.}wN7E " NY57m@7"څ^Qv~72@ۆCD`̘GֹEjxXMЯ*{=w뛙?bڪ(jc.A~HH}))/ 3I+:L,Iüd,"{+"S/Εaf:Dvc@ۏ@ꁿإw <0wƢ4?4 rMs+?Bv諏wKG@@͎@f?w9VɬuOhK#;#./9]>E E8@Ax",(13d"hR?&꛿{+LWNH8#JSMtL#וC; ;ڔ ;ݐOm涏MԸ$[{di1Ziq/*/Ί1@k:{4ΧLP,M]:RRR" ")))))UDbӧTEWET9SRRRR8 8)))))UUt*@*p:}@@@@@UD xUD53E E E E E#N"""""PH^QMLHHHH$ӧTEWET9SRRRR8?nĻ IENDB`htmlwidgets/vignettes/develop_sizing.Rmd0000644000176200001440000002125013130653463020306 0ustar liggesusers--- title: "HTML Widget Sizing" date: "`r Sys.Date()`" output: html_document: highlight: kate toc: true toc_depth: 4 mathjax: null vignette: > %\VignetteIndexEntry{Sizing} %\VignetteEngine{knitr::rmarkdown} \usepackage[utf8]{inputenc} --- ## Overview In the spirit of HTML widgets working just like plots in R, it's important that HTML widgets intelligently size themselves to their container, whether it be the RStudio Viewer, a figure in knitr, or a UI panel within a Shiny application. The **htmlwidgets** framework provides a rich mechanism for specifying the sizing behavior of widgets. This sizing mechanism is designed to address the following constraints that affect the natural size of a widget: - **The kind of widget it is.** Some widgets may only be designed to look good at small, fixed sizes (like [sparklines](https://github.com/htmlwidgets/sparkline)) while other widgets may want every pixel that can be spared (like [network graphs](http://christophergandrud.github.io/networkD3/)). - **The context into which the widget is rendered.** While a given widget might look great at 960px by 480px in an R Markdown document, the same widget would look silly at that size in the RStudio Viewer pane, which is typically much smaller. Widget sizing is handled in two steps: 1. First, a sizing policy is specified for the widget. This is done via the `sizingPolicy` argument to the `createWidget` function. Most widgets can accept the default sizing policy (or override only one or two aspects of it) and get satisfactory sizing behavior (see details below). 2. The sizing policy is used by the framework to compute the correct width and height for a widget given where it is being rendered. This size information is then passed to the `initialize` and `resize` methods of the widgets JavaScript binding. It's up to the widget to then forward this size information to the underlying JavaScript library. ## Specifying a sizing policy The default HTML widget sizing policy treats the widget with the same sizing semantics as an R plot. When printed at the R console the widget is displayed within the RStudio Viewer and sized to fill the Viewer pane (modulo any padding). When rendered inside an R Markdown document the widget is sized based on the default size of figures in the document. Note that for most widgets the default sizing behavior is fine and you won't need to create a custom sizing policy. If you need a slightly different behavior than the default you can also selectively override the default behavior by calling the `sizingPolicy` function and passing the result to `createWidget`. For example: ```r htmlwidgets::createWidget( "sigma", x, width = width, height = height, sizingPolicy = htmlwidgets::sizingPolicy( viewer.padding = 0, viewer.paneHeight = 500, browser.fill = TRUE ) ) ``` ### Examples The [networkD3](http://christophergandrud.github.io/networkD3/) package uses custom sizing policies for all of its widgets. The `simpleNetwork` widget eliminates padding (as d3 is already providing padding) and specifies that it wants to fill up as much space as possible when displayed in a standalone web browser: ```r sizingPolicy(padding = 0, browser.fill = TRUE) ``` The `sankeyNetwork` widget requires much more space than is afforded by the RStudio Viewer or a typical knitr figure so it disables those automatic sizing behaviors. It also provides a more reasonable default width and height for knitr documents: ```r sizingPolicy(viewer.suppress = TRUE, knitr.figure = FALSE, browser.fill = TRUE, browser.padding = 75, knitr.defaultWidth = 800, knitr.defaultHeight = 500) ``` ### Available options Here are the various options that can be specified within a sizing policy: | Option | Description | |---|---| | **defaultWidth** | The default width used to display the widget. This parameter specifies the default width for viewing in all contexts (browser, viewer, and knitr) unless it is specifically overridden with e.g. browser.defaultWidth. | | **defaultHeight** | The default height used to display the widget. This parameter specifies the default height for viewing in all contexts (browser, viewer, and knitr) unless it is specifically overridden with e.g. browser.defaultHeight. | | **padding** | Padding around the widget (in pixels). This parameter specifies the padding for viewing in all contexts (browser and viewer) unless it is specifically overridden by e.g. browser.padding. | | **viewer.defaultWidth** | The default width used to display the widget within the RStudio Viewer. | | **viewer.defaultHeight** | The default height used to display the widget within the RStudio Viewer. | | **viewer.padding** | Padding around the widget when displayed in the RStudio Viewer (defaults to 15 pixels). | | **viewer.fill** | When displayed in the RStudio Viewer, automatically size the widget to the viewer dimensions (note that viewer.padding is still applied). Default to TRUE. | | **viewer.suppress** | Never display the widget within the RStudio Viewer (useful for widgets that require a large amount of space for rendering). Defaults to FALSE. | | **viewer.paneHeight** | Request that the RStudio Viewer be forced to a specific height when displaying this widget. | | **browser.defaultWidth** | The default width used to display the widget within a standalone web browser. | | **browser.defaultHeight** | The default height used to display the widget within a standalone web browser. | | **browser.padding** | Padding around the widget when displayed in a standalone browser (defaults to 40 pixels). | | **browser.fill** | When displayed in a standalone web browser, automatically size the widget to the browser dimensions (note that browser.padding is still applied). Defaults to FALSE. | | **knitr.defaultWidth** | The default width used to display the widget within documents generated by knitr (e.g. R Markdown). | | **knitr.defaultHeight** | The default height used to display the widget within documents generated by knitr (e.g. R Markdown). | | **knitr.figure** | Apply the default knitr fig.width and fig.height to the widget when it's rendered within R Markdown documents. Defaults to TRUE. | ## JavaScript resize method Specifying a sizing policy allows htmlwidgets to calculate the width and height of your widget based on where it's being displayed. However, you still need to forward this sizing information on to the underlying JavaScript library you are creating a widget for. Every JavaScript library handles dynamic sizing a bit differently. Some do it automatically, some have a resize() call to force a layout, and some require that size be set only along with data and other options. Whatever the case, the **htmlwidgets** framework will pass the computed sizes to both your `factory` function and `resize` function. Here's an empty JavaScript binding that illustrates: ```javascript HTMLWidgets.widget({ name: "demo", type: "output", factory: function(el, width, height) { return { renderValue: function(x) { }, resize: function(width, height) { } }; } }); ``` What you do with the passed width and height is up to you and depends on the re-sizing semantics of the underlying JavaScript library you are creating a widget for. A couple of illustrative examples are included in the next section. ### Examples #### dygraphs In the [dygraphs](http://rstudio.github.io/dygraphs) widget the implementation of re-sizing is relatively simple since the **dygraphs** library includes a resize() method to automatically size the graph to it's enclosing HTML element: ```javascript resize: function(width, height) { if (dygraph) dygraph.resize(); } ``` #### forceNetwork In the [forceNetwork](http://christophergandrud.github.io/networkD3/#force) widget, the passed width and height are applied to the `` element that hosts the d3 network visualization, as well as forwarded on to the underlying d3 force simulation object: ```javascript factory: function(el, width, height) { // instance data var el = el; var force = d3.layout.force(); d3.select(el).append("svg") .attr("width", width) .attr("height", height); return { renderValue: function(x) { // implementation excluded }, resize: function(width, height) { d3.select(el).select("svg") .attr("width", width) .attr("height", height); force.size([width, height]).resume(); } }; } ``` As you can see, re-sizing is handled in a wide variety of fashions in different JavaScript libraries. The `resize` method is intended to provide a flexible way to map the automatic sizing logic of **htmlwidgets** directly into the underlying library. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������htmlwidgets/vignettes/develop_advanced.Rmd����������������������������������������������������������0000644�0001762�0000144�00000021743�13130653463�020557� 0����������������������������������������������������������������������������������������������������ustar �ligges��������������������������users������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������--- title: "HTML Widgets: Advanced Topics" date: "`r Sys.Date()`" output: html_document: highlight: kate toc: true toc_depth: 4 mathjax: null vignette: > %\VignetteIndexEntry{Advanced} %\VignetteEngine{knitr::rmarkdown} \usepackage[utf8]{inputenc} --- ## Overview This article covers several aspects of creating widgets that are not required by all widgets, but are an essential part of getting bindings to certain types of JavaScript libraries to work properly. Topics covered include: * Transforming JSON representations of R objects into representations required by JavaScript libraries (e.g. an R data frame to a d3 dataset). * Tracking instance-specific widget data within JavaScript bindings. * Passing JavaScript functions from R to JavaScript (e.g. a user provided formatting or drawing function) * Generating custom HTML to enclose a widget (the default is a `
` but some libraries require a different element e.g. a ``). ## Data transformation R objects passed as part of the `x` parameter to the `createWidget()` function are transformed to JSON using the internal function `htmlwidgets:::toJSON()`^[N.B. It is not exported from **htmlwidgets**, so you are not supposed to call this function directly.], which is basically a wrapper function of `jsonlite::toJSON()` by default. However, sometimes this representation is not what is required by the JavaScript library you are interfacing with. There are two JavaScript functions that you can use to transform the JSON data. ### HTMLWidgets.dataframeToD3() R data frames are represented in "long" form (an array of named vectors) whereas d3 typically requires "wide" form (an array of objects each of which includes all names and values). Since the R representation is smaller in size and much faster to transmit over the network, we create the long-form representation of R data, and then transform the data in JavaScript using the `dataframeToD3()` helper function. Here is an example of the long-form representation of an R data frame: ```{r echo=FALSE, comment=''} htmlwidgets:::toJSON2(head(iris, 3), pretty = TRUE) ``` After we apply `HTMLWidgets.dataframeToD3()`, it will become: ```{r echo=FALSE, comment=''} htmlwidgets:::toJSON2(head(iris, 3), dataframe = 'row', pretty = TRUE) ``` As a real example, the [simpleNetwork](https://christophergandrud.github.io/networkD3/#simple) widget accepts a data frame containing network links on the R side, then transforms it to a d3 representation within the JavaScript `renderValue` function: ```javascript renderValue: function(x) { // convert links data frame to d3 friendly format var links = HTMLWidgets.dataframeToD3(x.links); // ... use the links, etc ... } ``` ### HTMLWidgets.transposeArray2D() Sometimes a 2-dimensional array requires a similar transposition. For this the `transposeArray2D()` function is provided. Here is an example array: ```{r echo=FALSE, comment=''} htmlwidgets:::toJSON2(unname(head(iris, 8)), dataframe = 'column', pretty = TRUE) ``` `HTMLWidgets.transposeArray2D()` can transpose it to: ```{r echo=FALSE, comment=''} htmlwidgets:::toJSON2(head(iris, 8), dataframe = 'values', pretty = TRUE) ``` As a real example, the [dygraphs](https://rstudio.github.io/dygraphs) widget uses this function to transpose the "file" (data) argument it gets from the R side before passing it on to the dygraphs library: ```javascript renderValue: function(x) { // ... code excluded ... // transpose array x.attrs.file = HTMLWidgets.transposeArray2D(x.attrs.file); // ... more code excluded ... } ``` ### Custom JSON serializer You may find it necessary to customize the JSON serialization of widget data when the default serializer in **htmlwidgets** does not work in the way you have expected. For widget package authors, there are two levels of customization for the JSON serialization: you can either customize the default values of arguments for `jsonlite::toJSON()`, or just customize the whole function. 1. `jsonlite::toJSON()` has a lot of arguments, and we have already changed some of its default values. Below is the JSON serializer we use in **htmlwidgets** at the moment: ```{r eval=FALSE, code=head(capture.output(htmlwidgets:::toJSON2),-1), tidy=FALSE} ``` For example, we convert data frames to JSON by columns instead of rows (the latter is `jsonlite::toJSON`'s default). If you want to change the default values of any arguments, you can attach an attribute `TOJSON_ARGS` to the widget data to be passed to `createWidget()`, e.g. ```{r eval=FALSE} fooWidget <- function(data, name, ...) { # ... process the data ... params <- list(foo = data, bar = TRUE) # customize toJSON() argument values attr(params, 'TOJSON_ARGS') <- list(digits = 7, na = 'string') htmlwidgets::createWidget(name, x = params, ...) } ``` We changed the default value of `digits` from 16 to 7, and `na` from `null` to `string` in the above example. It is up to you, the package author, whether you want to expose such customization to users. For example, you can leave an extra argument in your widget function so that users can customize the behavior of the JSON serializer: ```{r eval=FALSE} fooWidget <- function(data, name, ..., JSONArgs = list(digits = 7)) { # ... process the data ... params <- list(foo = data, bar = TRUE) # customize toJSON() argument values attr(params, 'TOJSON_ARGS') <- JSONArgs htmlwidgets::createWidget(name, x = params, ...) } ``` You can also use a global option `htmlwidgets.TOJSON_ARGS` to customize the JSON serializer arguments for all widgets in the current R session, e.g. ```{r eval=FALSE} options(htmlwidgets.TOJSON_ARGS = list(digits = 7, pretty = TRUE)) ``` 1. If you do not want to use **jsonlite**, you can completely override the serializer function by attaching an attribute `TOJSON_FUNC` to the widget data, e.g. ```{r eval=FALSE} fooWidget <- function(data, name, ...) { # ... process the data ... params <- list(foo = data, bar = TRUE) # customize the JSON serializer attr(params, 'TOJSON_FUNC') <- MY_OWN_JSON_FUNCTION htmlwidgets::createWidget(name, x = params, ...) } ``` Here `MY_OWN_JSON_FUNCTION` can be an arbitrary R function that converts R objects to JSON. If you have also specified the `TOJSON_ARGS` attribute, it will be passed to your custom JSON function as well. Note these features about custom JSON serializers require the **shiny** version to be greater than 0.11.1 if you render the widgets in Shiny apps. ## Passing JavaScript functions As you would expect, character vectors passed from R to JavaScript are converted to JavaScript strings. However, what if you want to allow users to provide custom JavaScript functions for formatting, drawing, or event handling? For this case, the **htmlwidgets** package includes a `JS()` function that allows you to request that a character value is evaluated as JavaScript when it is received on the client. For example, the [dygraphs](https://rstudio.github.io/dygraphs) widget includes a `dyCallbacks` function that allows the user to provide callback functions for a variety of contexts. These callbacks are "marked" as containing JavaScript so that they can be converted to actual JavaScript functions on the client: ```r callbacks <- list( clickCallback = JS(clickCallback) drawCallback = JS(drawCallback) highlightCallback = JS(highlightCallback) pointClickCallback = JS(pointClickCallback) underlayCallback = JS(underlayCallback) ) ``` Another example is in the [DT](https://rstudio.github.io/DT) (DataTables) widget, where users can specify an `initCallback` with JavaScript to execute after the table is loaded and initialized: ```r datatable(head(iris, 20), options = list( initComplete = JS( "function(settings, json) {", "$(this.api().table().header()).css({'background-color': '#000', 'color': '#fff'});", "}") )) ``` If multiple arguments are passed to `JS()` (as in the above example), they will be concatenated into a single string separated by `\n`. ## Custom widget HTML Typically the HTML "housing" for a widget is just a `
` element, and this is correspondingly the default behavior for new widgets that don't specify otherwise. However, sometimes you need a different element type. For example, the [sparkline](https://github.com/htmlwidgets/sparkline) widget requires a `` element so implements the following custom HTML generation function: ```r sparkline_html <- function(id, style, class, ...){ tags$span(id = id, class = class) } ``` Note that this function is looked up within the package implementing the widget by the convention `widgetname_html` so it need not be formally exported from your package or otherwise registered with **htmlwidgets**. Most widgets won't need a custom HTML function but if you need to generate custom HTML for your widget (e.g. you need an `` or a `` rather than a `
`) then you should use the **htmltools** package (as demonstrated by the code above). htmlwidgets/vignettes/develop_intro.Rmd0000644000176200001440000004102013130653463020133 0ustar liggesusers--- title: "Introduction to HTML Widgets" date: "`r Sys.Date()`" output: html_document: highlight: kate toc: true toc_depth: 4 mathjax: null vignette: > %\VignetteIndexEntry{Introduction} %\VignetteEngine{knitr::rmarkdown} \usepackage[utf8]{inputenc} --- ## Overview The **[htmlwidgets](https://cran.r-project.org/package=htmlwidgets)** package provides a framework for creating R bindings to JavaScript libraries. HTML Widgets can be: * Used at the R console for data analysis just like conventional R plots. * Embedded within [R Markdown](http://rmarkdown.rstudio.com) documents * Incorporated into [Shiny](http://shiny.rstudio.com) web applications. * Saved as standalone web pages for ad-hoc sharing via email, Dropbox, etc. By following a small set of easy-to-follow conventions, it is possible to create HTML widgets with very little code. All widgets include the following components: 1. **Dependencies**. These are the JavaScript and CSS assets used by the widget (e.g. the library you are creating a wrapper for). 3. **R binding**. This is the function that end users will call to provide input data to the widget as well as specify various options for how the widget should render. This also includes some short boilerplate functions required to use the widget within Shiny applications. 3. **JavaScript binding**. This is the JavaScript code that glues everything together, passing the data and options gathered in the R binding to the underlying JavaScript library. HTML widgets are always hosted within an R package and should include all of the source code for their dependencies. This is to ensure that code which depends on widgets is fully reproducible (i.e. doesn't require an internet connection or the ongoing availability of an internet service to run). ## Example (sigma.js) To start with we'll walk through the creation of a simple widget that wraps the [sigma.js](http://sigmajs.org) graph visualization library. When we're done we'll be able to use it to display interactive visualizations of [GEXF](http://gexf.net) (Graph Exchange XML Format) data files. For example: ```{r, eval=FALSE} library(sigma) data <- system.file("examples/ediaspora.gexf.xml", package = "sigma") sigma(data) ``` sigma Note that the above is just an image of the visualization so it's not interactive. You can play with the interactive version by following the steps in the demo section below. There is remarkably little code required to create this binding. Below we'll go through all of the components step-by-step. Then we'll describe how you can create your own widgets (including automatically generating basic scaffolding for all of the core components). ### File layout Let's assume that our widget is named **sigma** and is located within an R package of the same name. Our JavaScript binding source code file is named sigma.js. Since our widget will read GEXF data files we'll also need to include both the base sigma.min.js library as well as its GEXF plugin. Here are the files that we'll add to the package: ```text R/ | sigma.R inst/ |-- htmlwidgets/ | |-- sigma.js | |-- sigma.yaml | |-- lib/ | | |-- sigma-1.0.3/ | | | |-- sigma.min.js | | | |-- plugins/ | | | | |-- sigma.parsers.gexf.min.js ``` Note the convention that the JavaScript, YAML, and other dependencies are all contained within the `inst/htmlwidgets` directory (which will subsequently be installed into a package sub-directory named `htmlwidgets`). ### Dependencies Dependencies are the JavaScript and CSS assets used by a widget. Dependencies are included within the `inst/htmlwidgets/lib` directory. Dependencies are specified using a YAML configuration file which uses the name of the widget as its base file name. Here's what our **sigma.yaml** file looks like: ```yaml dependencies: - name: sigma version: 1.0.3 src: htmlwidgets/lib/sigma-1.0.3 script: - sigma.min.js - plugins/sigma.parsers.gexf.min.js ``` The dependency `src` specification refers to the directory that contains the library and `script` refers to specific JavaScript files. If your library contains multiple JavaScript files specify each one on a line beginning with `-` as shown here. You can also add `stylesheet` entries and even `meta` or `head` entries. Multiple dependencies may be specified in one YAML file. See the documentation on the `htmlDependency` function in the [**htmltools**](https://cran.r-project.org/package=htmltools) package for additional details. ### R binding We need to provide users with an R function that invokes our widget. Typically this function will accept input data as well as various options that control the widget's display. Here's the R function for `sigma`: ```r #' @import htmlwidgets #' @export sigma <- function(gexf, drawEdges = TRUE, drawNodes = TRUE, width = NULL, height = NULL) { # read the gexf file data <- paste(readLines(gexf), collapse="\n") # create a list that contains the settings settings <- list( drawEdges = drawEdges, drawNodes = drawNodes ) # pass the data and settings using 'x' x <- list( data = data, settings = settings ) # create the widget htmlwidgets::createWidget("sigma", x, width = width, height = height) } ``` The function takes two classes of input: the GEXF data file to render and some additional settings which control how it is rendered. This input is collected into a list named `x` which is then passed on to the `htmlwidgets::createWidget` function. This `x` variable will subsequently be made available to the JavaScript binding for sigma (this is described below). Any width or height parameter specified is also forwarded to the widget (widgets size themselves automatically by default so typically don't require an explicit width or height). We want our sigma widget to also work in Shiny applications, so we add the following boilerplate Shiny output and render functions (these are always the same for all widgets): ```r #' @export sigmaOutput <- function(outputId, width = "100%", height = "400px") { htmlwidgets::shinyWidgetOutput(outputId, "sigma", width, height, package = "sigma") } #' @export renderSigma <- function(expr, env = parent.frame(), quoted = FALSE) { if (!quoted) { expr <- substitute(expr) } # force quoted htmlwidgets::shinyRenderWidget(expr, sigmaOutput, env, quoted = TRUE) } ``` ### JavaScript binding _**Note:** An older, less intuitive JavaScript binding API was used in htmlwidgets 0.5.2 and earlier, and continues to be supported in newer versions of htmlwidgets. See this [archived version](https://cdn.rawgit.com/ramnathv/htmlwidgets/f735840bf938d35d3c4143c0d16515da6ff252bd/develop_intro.html#javascript-binding) for details on the legacy binding API. New widgets are encouraged to use the newer API described below._ The third piece in the puzzle is the JavaScript required to activate the widget. By convention we'll define our JavaScript binding in the file `inst/htmlwidgets/sigma.js`. Here is the full source code of the binding: ```javascript HTMLWidgets.widget({ name: "sigma", type: "output", factory: function(el, width, height) { // create our sigma object and bind it to the element var sig = new sigma(el.id); return { renderValue: function(x) { // parse gexf data var parser = new DOMParser(); var data = parser.parseFromString(x.data, "application/xml"); // apply settings for (var name in x.settings) sig.settings(name, x.settings[name]); // update the sigma object sigma.parsers.gexf( data, // parsed gexf data sig, // sigma object function() { // need to call refresh to reflect new settings and data sig.refresh(); } ); }, resize: function(width, height) { // forward resize on to sigma renderers for (var name in sig.renderers) sig.renderers[name].resize(width, height); }, // Make the sigma object available as a property on the widget // instance we're returning from factory(). This is generally a // good idea for extensibility--it helps users of this widget // interact directly with sigma, if needed. s: sig }; } }); ``` We provide a name and type for the widget, plus a `factory` function that takes `el` (the HTML element that will host this widget), `width`, and `height` (width and height of the HTML element, in pixels--you can always use `offsetWidth` and `offsetHeight` for this). The `factory` function should prepare the HTML element to start receiving values. In this case we create a new sigma element and pass it the `id` of the DOM element that hosts the widget on the page. We're going to need access to the sigma object later (to update its data and settings) so we save it as a variable `sig`. Note that variables declared directly inside of the factory function are tied to a particular widget instance/`el`. The return value of the `factory` function is called a _widget instance object_. It is a bridge between the htmlwidgets runtime, and the JavaScript visualization that you're wrapping. As the name implies, each widget instance object is responsible for managing a single widget instance on a page. The widget instance object you create must have one required method, and may have one optional method: 1. The required `renderValue` method actually pours our dynamic data and settings into the widget's DOM element. The `x` parameter contains the widget data and settings. We parse and update the GEXF data, apply the settings to our previously-created `sig` sigma object, and finally call `refresh` to reflect the new values on-screen. This method may be called repeatedly with different data (i.e. in Shiny), so be sure to account for that possibility. If it makes sense for your widget, consider making your visualization transition smoothly from one value of `x` to another. 2. The optional `resize` method is called whenever the element containing the widget is resized. The only reason not to implement this method is if your widget naturally scales (without additional JavaScript code needing to be invoked) when its element size changes. In the case of sigma.js, we forward the sizing information on to each of the underlying sigma renderers. All JavaScript libraries handle initialization, binding to DOM elements, dynamically updating data, and resizing slightly differently. Most of the work on the JavaScript side of creating widgets is mapping these three functions---`factory`, `renderValue`, and `resize`---correctly onto the behavior of the underlying library. The sigma.js example uses a simple object literal to create its widget instance object, but you can also use [class based objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript#Custom_objects) or any other style of object, as long as `obj.renderValue(x)` and `obj.resize(width, height)` can be invoked on it. You can add additional methods and properties on the widget instance object. Although they won't be called by htmlwidgets itself, they might be useful to users of your widget that know some JavaScript and want to further customize your widget by adding custom JS code (e.g. using the `htmlwidgets::onRender` R function). In this case we add an `s` property to make the sigma object itself available. ### Demo Our widget is now complete! If you want to test drive it without reproducing all of the code locally you can install it from GitHub as follows: ```r devtools::install_github('jjallaire/sigma') ``` Here's the code to try it out with some sample data included with the package: ```r library(sigma) sigma(system.file("examples/ediaspora.gexf.xml", package = "sigma")) ``` If you execute this code in the R console you'll see the widget displayed in the RStudio Viewer (or in an external browser if you aren't running RStudio). If you include it within an R Markdown document the widget will be embedded into the document. We can also use the widget in a Shiny application: ```r library(shiny) library(sigma) gexf <- system.file("examples/ediaspora.gexf.xml", package = "sigma") ui = shinyUI(fluidPage( checkboxInput("drawEdges", "Draw Edges", value = TRUE), checkboxInput("drawNodes", "Draw Nodes", value = TRUE), sigmaOutput('sigma') )) server = function(input, output) { output$sigma <- renderSigma( sigma(gexf, drawEdges = input$drawEdges, drawNodes = input$drawNodes) ) } shinyApp(ui = ui, server = server) ``` ## Creating your own widgets ### Requirements To implement a widget you need to create a new R package that in turn depends on the **htmlwidgets** package. You can install the package from CRAN as follows: ```r install.packages("htmlwidgets") ``` While it's not strictly required, the step-by-step instructions below for getting started also make use of the **devtools** package which you can also install from CRAN: ```r install.packages("devtools") ``` ### Scaffolding To create a new widget you can call the `scaffoldWidget` function to generate the basic structure for your widget. This function will: * Create the .R, .js, and .yaml files required for your widget; * If provided, take a [Bower](https://bower.io/) package name and automatically download the JavaScript library (and its dependencies) and add the required entries to the .yaml file. This method is highly recommended as it ensures that you get started with the right file structure. Here's an example that assumes you want to create a widget named 'mywidget' in a new package of the same name: ```r devtools::create("mywidget") # create package using devtools setwd("mywidget") # navigate to package dir htmlwidgets::scaffoldWidget("mywidget") # create widget scaffolding devtools::install() # install the package so we can try it ``` This creates a simple widget that takes a single `text` argument and displays that text within the widgets HTML element. You can try it like this: ```r library(mywidget) mywidget("hello, world") ``` This is the most minimal widget possible and doesn't yet include a JavaScript library to interface to (note that `scaffoldWidget` can optionally include JavaScript library dependencies via the `bowerPkg` argument). Before getting started with development you should review the introductory example above to make sure you understand the various components and also review the additional articles and examples linked to in the next section. ### Learning more #### Additional articles There are additional articles that cover more advanced ground: * [HTML Widget Sizing](develop_sizing.html) explains custom sizing policies and when you might need to use them and describes implementing a `resize` method within JavaScript bindings. * [HTML Widgets: Advanced Topics](develop_advanced.html) describes framework features that support per-widget instance data, data transformations (e.g. converting a data frame into a d3 dataset), and providing widget options that are live JavaScript objects (e.g. function definitions). The Sizing article is particularly important as most JavaScript libraries require some additional interaction to keep their size synchronized with their containing element. #### Examples Studying the code of other packages is a great way to learn more about creating widgets: 1. The [networkD3](https://github.com/christophergandrud/networkD3) package illustrates creating a widget on top of [D3](http://d3js.org), using a custom sizing policy for a larger widget, and providing multiple widgets from a single package. 2. The [dygraphs](https://github.com/rstudio/dygraphs/) package illustrates using widget instance data, handling dynamic re-sizing, and using [magrittr](https://github.com/smbache/magrittr) to decompose a large and flat JavaScript API into a more modular and pipeable R API. 3. The [sparkline](https://github.com/htmlwidgets/sparkline) package illustrates providing a custom HTML generation function (since sparklines must be housed in `` rather than `
` elements). #### Questions and issues If you have questions about developing widgets or run into problems during development please don't hesitate to [post an issue](https://github.com/ramnathv/htmlwidgets/issues) on the project's GitHub repository. htmlwidgets/MD50000644000176200001440000000436313230661610013121 0ustar liggesusersddf342640b149e2c963aecf3c9bfa5f3 *DESCRIPTION d5e014920944fd879bfd6c3b9d943512 *LICENSE 27ad3bb8893369b9e179fb99f2034ae4 *NAMESPACE 5d40ec402a74a27043d6284c00adb2d8 *R/htmlwidgets.R f844096ad2e59400797dd567d94499d8 *R/imports.R e73b61f39eb5e67d5dd03e7ba998a162 *R/knitr-methods.R 1d75abeb92641c43496301f22f2b3881 *R/pandoc.R afa146f1033b3a662aad17c09aa85ede *R/savewidget.R 7855788fc6a789530f884750e46e36bf *R/scaffold.R d41d8cd98f00b204e9800998ecf8427e *R/seed.R eb659aebd3386aef1e6a7a848d48cb26 *R/sizing.R 3cad2286ddaf4962a6e6f6f4db733b76 *R/utils.R 1336ac18f6021f90fe2fe1fe7af5093c *R/widgetid.R 5dd10488939f7e652564f47b966fc9cb *build/vignette.rds ba6b4cfc32f70520ed2af3fd73c168df *inst/NEWS b0d05e654c9f3175c75491e87233ab47 *inst/doc/develop_advanced.R f77a13a750cce7eaec6acc820a49ab4b *inst/doc/develop_advanced.Rmd 76f49c4e216c2d273d7f85cca0965db1 *inst/doc/develop_advanced.html ea858a6de2f9809b7e584d806c18d245 *inst/doc/develop_intro.R fd0b57315c679845dc5321d0519bfb50 *inst/doc/develop_intro.Rmd fc4fe900f0aa96bf98cce3b747ce6b3e *inst/doc/develop_intro.html 32446f6d3b91105f157004b2a7330507 *inst/doc/develop_sizing.Rmd afaa3a8db269cc3e2969d0dd49e92db4 *inst/doc/develop_sizing.html 6349641ac577c3e0f6459d53600dafff *inst/templates/widget_js.txt 69d850c68c74097c3a832af2de2e3193 *inst/templates/widget_r.txt e4c32f2be79353a70419462e28373dda *inst/www/htmlwidgets.js 2d363a23839d53aaa5a4222bb6e905d2 *man/JS.Rd f5678740e8f799a70192f561b43fddf8 *man/createWidget.Rd ff144f4809f3b3cc263afb136c66ef71 *man/getDependency.Rd e106fffad1042fb001c4e011eba913ee *man/htmlwidgets-package.Rd cf381206a20ed862e863a9aaa040ed82 *man/htmlwidgets-shiny.Rd ece3554c27c73c5ab5231d2ea83448ea *man/onRender.Rd 80b1ebdb0fdaf53753900fc629f481c0 *man/onStaticRenderComplete.Rd c373ec366965592bde33a3c8db87953e *man/prependContent.Rd 1d0bd991464b6cc1cdcc382e08382297 *man/saveWidget.Rd e2b2558c71ada5e4ad91d19b5fb9db00 *man/scaffoldWidget.Rd b23db4a276214b92dede91d150c5a9d6 *man/setWidgetIdSeed.Rd 3e52745f72b14ef8a11fbb5d641d1f89 *man/sizingPolicy.Rd f77a13a750cce7eaec6acc820a49ab4b *vignettes/develop_advanced.Rmd fd0b57315c679845dc5321d0519bfb50 *vignettes/develop_intro.Rmd 32446f6d3b91105f157004b2a7330507 *vignettes/develop_sizing.Rmd 41cec90e8b03ec9a2dfcf0d4313201b5 *vignettes/images/sigma.png htmlwidgets/build/0000755000176200001440000000000013230461367013711 5ustar liggesusershtmlwidgets/build/vignette.rds0000644000176200001440000000040513230461367016247 0ustar liggesusersۊ0ǶvUu_ x#Ih66*z/i,6.l ϟm<<~KsT@)G#S{Ɖ-L,(EϚ4e;o$GqQICbV uSddD5'W%2t.JjD]-k+`H`.Sa?Hٜ-e&B¾RO:Ǹ٨Sl ,$eud= ?rOe|Ghtmlwidgets/DESCRIPTION0000644000176200001440000000225113230661610014311 0ustar liggesusersPackage: htmlwidgets Type: Package Title: HTML Widgets for R Version: 1.0 Authors@R: c( person("Ramnath", "Vaidyanathan", role = c("aut", "cph")), person("Yihui", "Xie", role = c("aut")), person("JJ", "Allaire", role = c("aut", "cre"), email = "jj@rstudio.com"), person("Joe", "Cheng", role = c("aut")), person("Kenton", "Russell", role = c("aut", "cph")), person(family = "RStudio", role = "cph") ) Description: A framework for creating HTML widgets that render in various contexts including the R console, 'R Markdown' documents, and 'Shiny' web applications. License: MIT + file LICENSE VignetteBuilder: knitr Imports: htmltools (>= 0.3), jsonlite (>= 0.9.16), yaml Suggests: knitr (>= 1.8) Enhances: shiny (>= 0.12) URL: https://github.com/ramnathv/htmlwidgets BugReports: https://github.com/ramnathv/htmlwidgets/issues RoxygenNote: 6.0.1 NeedsCompilation: no Packaged: 2018-01-19 21:28:23 UTC; jjallaire Author: Ramnath Vaidyanathan [aut, cph], Yihui Xie [aut], JJ Allaire [aut, cre], Joe Cheng [aut], Kenton Russell [aut, cph], RStudio [cph] Maintainer: JJ Allaire Repository: CRAN Date/Publication: 2018-01-20 15:43:04 UTC htmlwidgets/man/0000755000176200001440000000000013230460521013354 5ustar liggesusershtmlwidgets/man/getDependency.Rd0000644000176200001440000000062513130653500016424 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R \name{getDependency} \alias{getDependency} \title{Get js and css dependencies for a htmlwidget} \usage{ getDependency(name, package = name) } \arguments{ \item{name}{name of the widget.} \item{package}{name of the package, defaults to the widget name.} } \description{ Get js and css dependencies for a htmlwidget } htmlwidgets/man/saveWidget.Rd0000644000176200001440000000172113230460521015746 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/savewidget.R \name{saveWidget} \alias{saveWidget} \title{Save a widget to an HTML file} \usage{ saveWidget(widget, file, selfcontained = TRUE, libdir = NULL, background = "white", title = class(widget)[[1]], knitrOptions = list()) } \arguments{ \item{widget}{Widget to save} \item{file}{File to save HTML into} \item{selfcontained}{Whether to save the HTML as a single self-contained file (with external resources base64 encoded) or a file with external resources placed in an adjacent directory.} \item{libdir}{Directory to copy HTML dependencies into (defaults to filename_files).} \item{background}{Text string giving the html background color of the widget. Defaults to white.} \item{title}{Text to use as the title of the generated page.} \item{knitrOptions}{A list of \pkg{knitr} chunk options.} } \description{ Save a rendered widget to an HTML file (e.g. for sharing with others). } htmlwidgets/man/setWidgetIdSeed.Rd0000644000176200001440000000173613130653500016667 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/widgetid.R \name{setWidgetIdSeed} \alias{setWidgetIdSeed} \title{Set the random seed for widget element ids} \usage{ setWidgetIdSeed(seed, kind = NULL, normal.kind = NULL) } \arguments{ \item{seed}{a single value, interpreted as an integer, or \code{NULL} (see \sQuote{Details}).} \item{kind}{character or \code{NULL}. If \code{kind} is a character string, set \R's RNG to the kind desired. Use \code{"default"} to return to the \R default. See \sQuote{Details} for the interpretation of \code{NULL}.} \item{normal.kind}{character string or \code{NULL}. If it is a character string, set the method of Normal generation. Use \code{"default"} to return to the \R default. \code{NULL} makes no change.} } \description{ Set a random seed for generating widget element ids. Calling this function rather than relying on the default behavior ensures stable widget ids across sessions. } htmlwidgets/man/htmlwidgets-shiny.Rd0000644000176200001440000000404413130653500017330 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/htmlwidgets.R \name{htmlwidgets-shiny} \alias{htmlwidgets-shiny} \alias{shinyWidgetOutput} \alias{shinyRenderWidget} \title{Shiny bindings for HTML widgets} \usage{ shinyWidgetOutput(outputId, name, width, height, package = name, inline = FALSE) shinyRenderWidget(expr, outputFunction, env, quoted) } \arguments{ \item{outputId}{output variable to read from} \item{name}{Name of widget to create output binding for} \item{width, height}{Must be a valid CSS unit (like \code{"100\%"}, \code{"400px"}, \code{"auto"}) or a number, which will be coerced to a string and have \code{"px"} appended.} \item{package}{Package containing widget (defaults to \code{name})} \item{inline}{use an inline (\code{span()}) or block container (\code{div()}) for the output} \item{expr}{An expression that generates an HTML widget} \item{outputFunction}{Shiny output function corresponding to this render function.} \item{env}{The environment in which to evaluate \code{expr}.} \item{quoted}{Is \code{expr} a quoted expression (with \code{quote()})? This is useful if you want to save an expression in a variable.} } \value{ An output or render function that enables the use of the widget within Shiny applications. } \description{ Helpers to create output and render functions for using HTML widgets within Shiny applications and interactive Rmd documents. } \details{ These functions are delegated to from within your widgets own shiny output and render functions. The delegation is boilerplate and always works the same for all widgets (see example below). } \examples{ # shiny output binding for a widget named 'foo' fooOutput <- function(outputId, width = "100\%", height = "400px") { htmlwidgets::shinyWidgetOutput(outputId, "foo", width, height) } # shiny render function for a widget named 'foo' renderFoo <- function(expr, env = parent.frame(), quoted = FALSE) { if (!quoted) { expr <- substitute(expr) } # force quoted htmlwidgets::shinyRenderWidget(expr, fooOutput, env, quoted = TRUE) } } htmlwidgets/man/prependContent.Rd0000644000176200001440000000160013130653500016630 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/htmlwidgets.R \name{prependContent} \alias{prependContent} \alias{appendContent} \title{Prepend/append extra HTML content to a widget} \usage{ prependContent(x, ...) appendContent(x, ...) } \arguments{ \item{x}{An HTML Widget object} \item{...}{Valid \link[htmltools]{tags}, text, and/or \code{\link[htmltools]{HTML}}, or lists thereof.} } \value{ A modified HTML Widget object. } \description{ Use these functions to attach extra HTML content (primarily JavaScript and/or CSS styles) to a widget, for rendering in standalone mode (i.e. printing at the R console) or in a knitr document. These functions are NOT supported when running in a Shiny widget rendering function, and will result in a warning if used in that context. Multiple calls are allowed, and later calls do not undo the effects of previous calls. } htmlwidgets/man/createWidget.Rd0000644000176200001440000000471713130653500016263 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/htmlwidgets.R \name{createWidget} \alias{createWidget} \title{Create an HTML Widget} \usage{ createWidget(name, x, width = NULL, height = NULL, sizingPolicy = htmlwidgets::sizingPolicy(), package = name, dependencies = NULL, elementId = NULL, preRenderHook = NULL) } \arguments{ \item{name}{Widget name (should match the base name of the YAML and JavaScript files used to implement the widget)} \item{x}{Widget instance data (underlying data to render and options that govern how it's rendered). This value will be converted to JSON using \code{\link[jsonlite]{toJSON}} and made available to the widget's JavaScript \code{renderValue} function.} \item{width}{Fixed width for widget (in css units). The default is \code{NULL}, which results in intelligent automatic sizing based on the widget's container.} \item{height}{Fixed height for widget (in css units). The default is \code{NULL}, which results in intelligent automatic sizing based on the widget's container.} \item{sizingPolicy}{Options that govern how the widget is sized in various containers (e.g. a standalone browser, the RStudio Viewer, a knitr figure, or a Shiny output binding). These options can be specified by calling the \code{\link{sizingPolicy}} function.} \item{package}{Package where the widget is defined (defaults to the widget name).} \item{dependencies}{Additional widget HTML dependencies (over and above those defined in the widget YAML). This is useful for dynamic dependencies that only exist when selected widget options are enabled (e.g. sets of map tiles or projections).} \item{elementId}{Use an explicit element ID for the widget (rather than an automatically generated one). Useful if you have other JavaScript that needs to explicitly discover and interact with a specific widget instance.} \item{preRenderHook}{A function to be run on the widget, just prior to rendering. It accepts the entire widget object as input, and should return a modified widget object.} } \value{ An object of class \code{htmlwidget} that will intelligently print itself into HTML in a variety of contexts including the R console, within R Markdown documents, and within Shiny output bindings. } \description{ Create an HTML widget based on widget YAML and JavaScript contained within the specified package. } \details{ For additional details on developing widgets, see package vignettes: \code{vignette("develop_intro", package = "htmlwidgets")}. } htmlwidgets/man/onStaticRenderComplete.Rd0000644000176200001440000000332113130653500020257 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R \name{onStaticRenderComplete} \alias{onStaticRenderComplete} \title{Execute JavaScript code after static render} \usage{ onStaticRenderComplete(jsCode) } \arguments{ \item{jsCode}{A character vector containing JavaScript code. No R error will be raised if the code is invalid, not even on JavaScript syntax errors. However, the web browser will throw errors at runtime.} } \value{ An htmltools \code{\link[htmltools]{tags}$script} object. } \description{ Convenience function for wrapping a JavaScript code string with a \code{