1(function() { 2 // If window.HTMLWidgets is already defined, then use it; otherwise create a 3 // new object. This allows preceding code to set options that affect the 4 // initialization process (though none currently exist). 5 window.HTMLWidgets = window.HTMLWidgets || {}; 6 7 // See if we're running in a viewer pane. If not, we're in a web browser. 8 var viewerMode = window.HTMLWidgets.viewerMode = 9 /\bviewer_pane=1\b/.test(window.location); 10 11 // See if we're running in Shiny mode. If not, it's a static document. 12 // Note that static widgets can appear in both Shiny and static modes, but 13 // obviously, Shiny widgets can only appear in Shiny apps/documents. 14 var shinyMode = window.HTMLWidgets.shinyMode = 15 typeof(window.Shiny) !== "undefined" && !!window.Shiny.outputBindings; 16 17 // We can't count on jQuery being available, so we implement our own 18 // version if necessary. 19 function querySelectorAll(scope, selector) { 20 if (typeof(jQuery) !== "undefined" && scope instanceof jQuery) { 21 return scope.find(selector); 22 } 23 if (scope.querySelectorAll) { 24 return scope.querySelectorAll(selector); 25 } 26 } 27 28 function asArray(value) { 29 if (value === null) 30 return []; 31 if ($.isArray(value)) 32 return value; 33 return [value]; 34 } 35 36 // Implement jQuery's extend 37 function extend(target /*, ... */) { 38 if (arguments.length == 1) { 39 return target; 40 } 41 for (var i = 1; i < arguments.length; i++) { 42 var source = arguments[i]; 43 for (var prop in source) { 44 if (source.hasOwnProperty(prop)) { 45 target[prop] = source[prop]; 46 } 47 } 48 } 49 return target; 50 } 51 52 // IE8 doesn't support Array.forEach. 53 function forEach(values, callback, thisArg) { 54 if (values.forEach) { 55 values.forEach(callback, thisArg); 56 } else { 57 for (var i = 0; i < values.length; i++) { 58 callback.call(thisArg, values[i], i, values); 59 } 60 } 61 } 62 63 // Replaces the specified method with the return value of funcSource. 64 // 65 // Note that funcSource should not BE the new method, it should be a function 66 // that RETURNS the new method. funcSource receives a single argument that is 67 // the overridden method, it can be called from the new method. The overridden 68 // method can be called like a regular function, it has the target permanently 69 // bound to it so "this" will work correctly. 70 function overrideMethod(target, methodName, funcSource) { 71 var superFunc = target[methodName] || function() {}; 72 var superFuncBound = function() { 73 return superFunc.apply(target, arguments); 74 }; 75 target[methodName] = funcSource(superFuncBound); 76 } 77 78 // Add a method to delegator that, when invoked, calls 79 // delegatee.methodName. If there is no such method on 80 // the delegatee, but there was one on delegator before 81 // delegateMethod was called, then the original version 82 // is invoked instead. 83 // For example: 84 // 85 // var a = { 86 // method1: function() { console.log('a1'); } 87 // method2: function() { console.log('a2'); } 88 // }; 89 // var b = { 90 // method1: function() { console.log('b1'); } 91 // }; 92 // delegateMethod(a, b, "method1"); 93 // delegateMethod(a, b, "method2"); 94 // a.method1(); 95 // a.method2(); 96 // 97 // The output would be "b1", "a2". 98 function delegateMethod(delegator, delegatee, methodName) { 99 var inherited = delegator[methodName]; 100 delegator[methodName] = function() { 101 var target = delegatee; 102 var method = delegatee[methodName]; 103 104 // The method doesn't exist on the delegatee. Instead, 105 // call the method on the delegator, if it exists. 106 if (!method) { 107 target = delegator; 108 method = inherited; 109 } 110 111 if (method) { 112 return method.apply(target, arguments); 113 } 114 }; 115 } 116 117 // Implement a vague facsimilie of jQuery's data method 118 function elementData(el, name, value) { 119 if (arguments.length == 2) { 120 return el["htmlwidget_data_" + name]; 121 } else if (arguments.length == 3) { 122 el["htmlwidget_data_" + name] = value; 123 return el; 124 } else { 125 throw new Error("Wrong number of arguments for elementData: " + 126 arguments.length); 127 } 128 } 129 130 // http://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex 131 function escapeRegExp(str) { 132 return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); 133 } 134 135 function hasClass(el, className) { 136 var re = new RegExp("\\b" + escapeRegExp(className) + "\\b"); 137 return re.test(el.className); 138 } 139 140 // elements - array (or array-like object) of HTML elements
141 // className - class name to test for 142 // include - if true, only return elements with given className; 143 // if false, only return elements *without* given className 144 function filterByClass(elements, className, include) { 145 var results = []; 146 for (var i = 0; i < elements.length; i++) { 147 if (hasClass(elements[i], className) == include) 148 results.push(elements[i]); 149 } 150 return results; 151 } 152 153 function on(obj, eventName, func) { 154 if (obj.addEventListener) { 155 obj.addEventListener(eventName, func, false); 156 } else if (obj.attachEvent) { 157 obj.attachEvent(eventName, func); 158 } 159 } 160 161 function off(obj, eventName, func) { 162 if (obj.removeEventListener) 163 obj.removeEventListener(eventName, func, false); 164 else if (obj.detachEvent) { 165 obj.detachEvent(eventName, func); 166 } 167 } 168 169 // Translate array of values to top/right/bottom/left, as usual with 170 // the "padding" CSS property 171 // https://developer.mozilla.org/en-US/docs/Web/CSS/padding 172 function unpackPadding(value) { 173 if (typeof(value) === "number") 174 value = [value]; 175 if (value.length === 1) { 176 return {top: value[0], right: value[0], bottom: value[0], left: value[0]}; 177 } 178 if (value.length === 2) { 179 return {top: value[0], right: value[1], bottom: value[0], left: value[1]}; 180 } 181 if (value.length === 3) { 182 return {top: value[0], right: value[1], bottom: value[2], left: value[1]}; 183 } 184 if (value.length === 4) { 185 return {top: value[0], right: value[1], bottom: value[2], left: value[3]}; 186 } 187 } 188 189 // Convert an unpacked padding object to a CSS value 190 function paddingToCss(paddingObj) { 191 return paddingObj.top + "px " + paddingObj.right + "px " + paddingObj.bottom + "px " + paddingObj.left + "px"; 192 } 193 194 // Makes a number suitable for CSS 195 function px(x) { 196 if (typeof(x) === "number") 197 return x + "px"; 198 else 199 return x; 200 } 201 202 // Retrieves runtime widget sizing information for an element. 203 // The return value is either null, or an object with fill, padding, 204 // defaultWidth, defaultHeight fields. 205 function sizingPolicy(el) { 206 var sizingEl = document.querySelector("script[data-for='" + el.id + "'][type='application/htmlwidget-sizing']"); 207 if (!sizingEl) 208 return null; 209 var sp = JSON.parse(sizingEl.textContent || sizingEl.text || "{}"); 210 if (viewerMode) { 211 return sp.viewer; 212 } else { 213 return sp.browser; 214 } 215 } 216 217 // @param tasks Array of strings (or falsy value, in which case no-op). 218 // Each element must be a valid JavaScript expression that yields a 219 // function. Or, can be an array of objects with "code" and "data" 220 // properties; in this case, the "code" property should be a string 221 // of JS that's an expr that yields a function, and "data" should be 222 // an object that will be added as an additional argument when that 223 // function is called. 224 // @param target The object that will be "this" for each function 225 // execution. 226 // @param args Array of arguments to be passed to the functions. (The 227 // same arguments will be passed to all functions.) 228 function evalAndRun(tasks, target, args) { 229 if (tasks) { 230 forEach(tasks, function(task) { 231 var theseArgs = args; 232 if (typeof(task) === "object") { 233 theseArgs = theseArgs.concat([task.data]); 234 task = task.code; 235 } 236 var taskFunc = tryEval(task); 237 if (typeof(taskFunc) !== "function") { 238 throw new Error("Task must be a function! Source:\n" + task); 239 } 240 taskFunc.apply(target, theseArgs); 241 }); 242 } 243 } 244 245 // Attempt eval() both with and without enclosing in parentheses. 246 // Note that enclosing coerces a function declaration into 247 // an expression that eval() can parse 248 // (otherwise, a SyntaxError is thrown) 249 function tryEval(code) { 250 var result = null; 251 try { 252 result = eval(code); 253 } catch(error) { 254 if (!error instanceof SyntaxError) { 255 throw error; 256 } 257 try { 258 result = eval("(" + code + ")"); 259 } catch(e) { 260 if (e instanceof SyntaxError) { 261 throw error; 262 } else { 263 throw e; 264 } 265 } 266 } 267 return result; 268 } 269 270 function initSizing(el) { 271 var sizing = sizingPolicy(el); 272 if (!sizing) 273 return; 274 275 var cel = document.getElementById("htmlwidget_container"); 276 if (!cel) 277 return; 278 279 if (typeof(sizing.padding) !== "undefined") { 280 document.body.style.margin = "0"; 281 document.body.style.padding = paddingToCss(unpackPadding(sizing.padding)); 282 } 283 284 if (sizing.fill) { 285 document.body.style.overflow = "hidden"; 286 document.body.style.width = "100%"; 287 document.body.style.height = "100%"; 288 document.documentElement.style.width = "100%"; 289 document.documentElement.style.height = "100%"; 290 if (cel) { 291 cel.style.position = "absolute"; 292 var pad = unpackPadding(sizing.padding); 293 cel.style.top = pad.top + "px"; 294 cel.style.right = pad.right + "px"; 295 cel.style.bottom = pad.bottom + "px"; 296 cel.style.left = pad.left + "px"; 297 el.style.width = "100%"; 298 el.style.height = "100%"; 299 } 300 301 return { 302 getWidth: function() { return cel.offsetWidth; }, 303 getHeight: function() { return cel.offsetHeight; } 304 }; 305 306 } else { 307 el.style.width = px(sizing.width); 308 el.style.height = px(sizing.height); 309 310 return { 311 getWidth: function() { return el.offsetWidth; }, 312 getHeight: function() { return el.offsetHeight; } 313 }; 314 } 315 } 316 317 // Default implementations for methods 318 var defaults = { 319 find: function(scope) { 320 return querySelectorAll(scope, "." + this.name); 321 }, 322 renderError: function(el, err) { 323 var $el = $(el); 324 325 this.clearError(el); 326 327 // Add all these error classes, as Shiny does 328 var errClass = "shiny-output-error"; 329 if (err.type !== null) { 330 // use the classes of the error condition as CSS class names 331 errClass = errClass + " " + $.map(asArray(err.type), function(type) { 332 return errClass + "-" + type; 333 }).join(" "); 334 } 335 errClass = errClass + " htmlwidgets-error";
336 337 // Is el inline or block? If inline or inline-block, just display:none it 338 // and add an inline error. 339 var display = $el.css("display"); 340 $el.data("restore-display-mode", display); 341 342 if (display === "inline" || display === "inline-block") { 343 $el.hide(); 344 if (err.message !== "") { 345 var errorSpan = $("<span>").addClass(errClass); 346 errorSpan.text(err.message); 347 $el.after(errorSpan); 348 } 349 } else if (display === "block") { 350 // If block, add an error just after the el, set visibility:none on the 351 // el, and position the error to be on top of the el. 352 // Mark it with a unique ID and CSS class so we can remove it later. 353 $el.css("visibility", "hidden"); 354 if (err.message !== "") { 355 var errorDiv = $("<div>").addClass(errClass).css("position", "absolute") 356 .css("top", el.offsetTop) 357 .css("left", el.offsetLeft) 358 // setting width can push out the page size, forcing otherwise 359 // unnecessary scrollbars to appear and making it impossible for 360 // the element to shrink; so use max-width instead 361 .css("maxWidth", el.offsetWidth) 362 .css("height", el.offsetHeight); 363 errorDiv.text(err.message); 364 $el.after(errorDiv); 365 366 // Really dumb way to keep the size/position of the error in sync with 367 // the parent element as the window is resized or whatever. 368 var intId = setInterval(function() { 369 if (!errorDiv[0].parentElement) { 370 clearInterval(intId); 371 return; 372 } 373 errorDiv 374 .css("top", el.offsetTop) 375 .css("left", el.offsetLeft) 376 .css("maxWidth", el.offsetWidth) 377 .css("height", el.offsetHeight); 378 }, 500); 379 } 380 } 381 }, 382 clearError: function(el) { 383 var $el = $(el); 384 var display = $el.data("restore-display-mode"); 385 $el.data("restore-display-mode", null); 386 387 if (display === "inline" || display === "inline-block") { 388 if (display) 389 $el.css("display", display); 390 $(el.nextSibling).filter(".htmlwidgets-error").remove(); 391 } else if (display === "block"){ 392 $el.css("visibility", "inherit"); 393 $(el.nextSibling).filter(".htmlwidgets-error").remove(); 394 } 395 }, 396 sizing: {} 397 }; 398 399 // Called by widget bindings to register a new type of widget. The definition 400 // object can contain the following properties: 401 // - name (required) - A string indicating the binding name, which will be 402 // used by default as the CSS classname to look for. 403 // - initialize (optional) - A function(el) that will be called once per 404 // widget element; if a value is returned, it will be passed as the third 405 // value to renderValue. 406 // - renderValue (required) - A function(el, data, initValue) that will be 407 // called with data. Static contexts will cause this to be called once per 408 // element; Shiny apps will cause this to be called multiple times per 409 // element, as the data changes. 410 window.HTMLWidgets.widget = function(definition) { 411 if (!definition.name) { 412 throw new Error("Widget must have a name"); 413 } 414 if (!definition.type) { 415 throw new Error("Widget must have a type"); 416 } 417 // Currently we only support output widgets 418 if (definition.type !== "output") { 419 throw new Error("Unrecognized widget type '" + definition.type + "'"); 420 } 421 // TODO: Verify that .name is a valid CSS classname 422 423 // Support new-style instance-bound definitions. Old-style class-bound 424 // definitions have one widget "object" per widget per type/class of 425 // widget; the renderValue and resize methods on such widget objects 426 // take el and instance arguments, because the widget object can't 427 // store them. New-style instance-bound definitions have one widget 428 // object per widget instance; the definition that's passed in doesn't 429 // provide renderValue or resize methods at all, just the single method 430 // factory(el, width, height) 431 // which returns an object that has renderValue(x) and resize(w, h). 432 // This enables a far more natural programming style for the widget 433 // author, who can store per-instance state using either OO-style 434 // instance fields or functional-style closure variables (I guess this 435 // is in contrast to what can only be called C-style pseudo-OO which is 436 // what we required before). 437 if (definition.factory) { 438 definition = createLegacyDefinitionAdapter(definition); 439 } 440 441 if (!definition.renderValue) { 442 throw new Error("Widget must have a renderValue function"); 443 } 444 445 // For static rendering (non-Shiny), use a simple widget registration 446 // scheme. We also use this scheme for Shiny apps/documents that also 447 // contain static widgets. 448 window.HTMLWidgets.widgets = window.HTMLWidgets.widgets || []; 449 // Merge defaults into the definition; don't mutate the original definition. 450 var staticBinding = extend({}, defaults, definition); 451 overrideMethod(staticBinding, "find", function(superfunc) { 452 return function(scope) { 453 var results = superfunc(scope); 454 // Filter out Shiny outputs, we only want the static kind 455 return filterByClass(results, "html-widget-output", false); 456 }; 457 }); 458 window.HTMLWidgets.widgets.push(staticBinding); 459 460 if (shinyMode) { 461 // Shiny is running. Register the definition with an output binding. 462 // The definition itself will not be the output binding, instead 463 // we will make an output binding object that delegates to the 464 // definition. This is because we foolishly used the same method 465 // name (renderValue) for htmlwidgets definition and Shiny bindings 466 // but they actually have quite different semantics (the Shiny 467 // bindings receive data that includes lots of metadata that it 468 // strips off before calling htmlwidgets renderValue). We can't 469 // just ignore the difference because in some widgets it's helpful 470 // to call this.renderValue() from inside of resize(), and if 471 // we're not delegating, then that call will go to the Shiny 472 // version instead of the htmlwidgets version. 473 474 // Merge defaults with definition, without mutating either. 475 var bindingDef = extend({}, defaults, definition); 476 477 // This object will be our actual Shiny binding. 478 var shinyBinding = new Shiny.OutputBinding(); 479
480 // With a few exceptions, we'll want to simply use the bindingDef's 481 // version of methods if they are available, otherwise fall back to 482 // Shiny's defaults. NOTE: If Shiny's output bindings gain additional 483 // methods in the future, and we want them to be overrideable by 484 // HTMLWidget binding definitions, then we'll need to add them to this 485 // list. 486 delegateMethod(shinyBinding, bindingDef, "getId"); 487 delegateMethod(shinyBinding, bindingDef, "onValueChange"); 488 delegateMethod(shinyBinding, bindingDef, "onValueError"); 489 delegateMethod(shinyBinding, bindingDef, "renderError"); 490 delegateMethod(shinyBinding, bindingDef, "clearError"); 491 delegateMethod(shinyBinding, bindingDef, "showProgress"); 492 493 // The find, renderValue, and resize are handled differently, because we 494 // want to actually decorate the behavior of the bindingDef methods. 495 496 shinyBinding.find = function(scope) { 497 var results = bindingDef.find(scope); 498 499 // Only return elements that are Shiny outputs, not static ones 500 var dynamicResults = results.filter(".html-widget-output"); 501 502 // It's possible that whatever caused Shiny to think there might be 503 // new dynamic outputs, also caused there to be new static outputs. 504 // Since there might be lots of different htmlwidgets bindings, we 505 // schedule execution for later--no need to staticRender multiple 506 // times. 507 if (results.length !== dynamicResults.length) 508 scheduleStaticRender(); 509 510 return dynamicResults; 511 }; 512 513 // Wrap renderValue to handle initialization, which unfortunately isn't 514 // supported natively by Shiny at the time of this writing. 515 516 shinyBinding.renderValue = function(el, data) { 517 Shiny.renderDependencies(data.deps); 518 // Resolve strings marked as javascript literals to objects 519 if (!(data.evals instanceof Array)) data.evals = [data.evals]; 520 for (var i = 0; data.evals && i < data.evals.length; i++) { 521 window.HTMLWidgets.evaluateStringMember(data.x, data.evals[i]); 522 } 523 if (!bindingDef.renderOnNullValue) { 524 if (data.x === null) { 525 el.style.visibility = "hidden"; 526 return; 527 } else { 528 el.style.visibility = "inherit"; 529 } 530 } 531 if (!elementData(el, "initialized")) { 532 initSizing(el); 533 534 elementData(el, "initialized", true); 535 if (bindingDef.initialize) { 536 var result = bindingDef.initialize(el, el.offsetWidth, 537 el.offsetHeight); 538 elementData(el, "init_result", result); 539 } 540 } 541 bindingDef.renderValue(el, data.x, elementData(el, "init_result")); 542 evalAndRun(data.jsHooks.render, elementData(el, "init_result"), [el, data.x]); 543 }; 544 545 // Only override resize if bindingDef implements it 546 if (bindingDef.resize) { 547 shinyBinding.resize = function(el, width, height) { 548 // Shiny can call resize before initialize/renderValue have been 549 // called, which doesn't make sense for widgets. 550 if (elementData(el, "initialized")) { 551 bindingDef.resize(el, width, height, elementData(el, "init_result")); 552 } 553 }; 554 } 555 556 Shiny.outputBindings.register(shinyBinding, bindingDef.name); 557 } 558 }; 559 560 var scheduleStaticRenderTimerId = null; 561 function scheduleStaticRender() { 562 if (!scheduleStaticRenderTimerId) { 563 scheduleStaticRenderTimerId = setTimeout(function() { 564 scheduleStaticRenderTimerId = null; 565 window.HTMLWidgets.staticRender(); 566 }, 1); 567 } 568 } 569 570 // Render static widgets after the document finishes loading 571 // Statically render all elements that are of this widget's class 572 window.HTMLWidgets.staticRender = function() { 573 var bindings = window.HTMLWidgets.widgets || []; 574 forEach(bindings, function(binding) { 575 var matches = binding.find(document.documentElement); 576 forEach(matches, function(el) { 577 var sizeObj = initSizing(el, binding); 578 579 if (hasClass(el, "html-widget-static-bound")) 580 return; 581 el.className = el.className + " html-widget-static-bound"; 582 583 var initResult; 584 if (binding.initialize) { 585 initResult = binding.initialize(el, 586 sizeObj ? sizeObj.getWidth() : el.offsetWidth, 587 sizeObj ? sizeObj.getHeight() : el.offsetHeight 588 ); 589 elementData(el, "init_result", initResult); 590 } 591 592 if (binding.resize) { 593 var lastSize = { 594 w: sizeObj ? sizeObj.getWidth() : el.offsetWidth, 595 h: sizeObj ? sizeObj.getHeight() : el.offsetHeight 596 }; 597 var resizeHandler = function(e) { 598 var size = { 599 w: sizeObj ? sizeObj.getWidth() : el.offsetWidth, 600 h: sizeObj ? sizeObj.getHeight() : el.offsetHeight 601 }; 602 if (size.w === 0 && size.h === 0) 603 return; 604 if (size.w === lastSize.w && size.h === lastSize.h) 605 return; 606 lastSize = size; 607 binding.resize(el, size.w, size.h, initResult); 608 }; 609 610 on(window, "resize", resizeHandler); 611 612 // This is needed for cases where we're running in a Shiny 613 // app, but the widget itself is not a Shiny output, but 614 // rather a simple static widget. One example of this is 615 // an rmarkdown document that has runtime:shiny and widget 616 // that isn't in a render function. Shiny only knows to 617 // call resize handlers for Shiny outputs, not for static 618 // widgets, so we do it ourselves. 619 if (window.jQuery) { 620 window.jQuery(document).on( 621 "shown.htmlwidgets shown.bs.tab.htmlwidgets shown.bs.collapse.htmlwidgets", 622 resizeHandler 623 ); 624 window.jQuery(document).on( 625 "hidden.htmlwidgets hidden.bs.tab.htmlwidgets hidden.bs.collapse.htmlwidgets", 626 resizeHandler 627 ); 628 } 629 630 // This is needed for the specific case of ioslides, which 631 // flips slides between display:none and display:block. 632 // Ideally we would not have to have ioslide-specific code 633 // here, but rather have ioslides raise a generic event, 634 // but the rmarkdown package just went to CRAN so the 635 // window to getting that fixed may be long. 636 if (window.addEventListener) { 637 // It's OK to limit this to window.addEventListener 638 // browsers because ioslides itself only supports 639 // such browsers. 640 on(document, "slideenter", resizeHandler); 641 on(document, "slideleave", resizeHandler); 642 } 643 } 644 645 var scriptData = document.querySelector("script[data-for='" + el.id + "'][type='application/json']"); 646 if (scriptData) { 647 var data = JSON.parse(scriptData.textContent || scriptData.text);
648 // Resolve strings marked as javascript literals to objects 649 if (!(data.evals instanceof Array)) data.evals = [data.evals]; 650 for (var k = 0; data.evals && k < data.evals.length; k++) { 651 window.HTMLWidgets.evaluateStringMember(data.x, data.evals[k]); 652 } 653 binding.renderValue(el, data.x, initResult); 654 evalAndRun(data.jsHooks.render, initResult, [el, data.x]); 655 } 656 }); 657 }); 658 659 invokePostRenderHandlers(); 660 } 661 662 663 function has_jQuery3() { 664 if (!window.jQuery) { 665 return false; 666 } 667 var $version = window.jQuery.fn.jquery; 668 var $major_version = parseInt($version.split(".")[0]); 669 return $major_version >= 3; 670 } 671 672 /* 673 / Shiny 1.4 bumped jQuery from 1.x to 3.x which means jQuery's 674 / on-ready handler (i.e., $(fn)) is now asyncronous (i.e., it now 675 / really means $(setTimeout(fn)). 676 / https://jquery.com/upgrade-guide/3.0/#breaking-change-document-ready-handlers-are-now-asynchronous 677 / 678 / Since Shiny uses $() to schedule initShiny, shiny>=1.4 calls initShiny 679 / one tick later than it did before, which means staticRender() is 680 / called renderValue() earlier than (advanced) widget authors might be expecting. 681 / https://github.com/rstudio/shiny/issues/2630 682 / 683 / For a concrete example, leaflet has some methods (e.g., updateBounds) 684 / which reference Shiny methods registered in initShiny (e.g., setInputValue). 685 / Since leaflet is privy to this life-cycle, it knows to use setTimeout() to 686 / delay execution of those methods (until Shiny methods are ready) 687 / https://github.com/rstudio/leaflet/blob/18ec981/javascript/src/index.js#L266-L268 688 / 689 / Ideally widget authors wouldn't need to use this setTimeout() hack that 690 / leaflet uses to call Shiny methods on a staticRender(). In the long run, 691 / the logic initShiny should be broken up so that method registration happens 692 / right away, but binding happens later. 693 */ 694 function maybeStaticRenderLater() { 695 if (shinyMode && has_jQuery3()) { 696 window.jQuery(window.HTMLWidgets.staticRender); 697 } else { 698 window.HTMLWidgets.staticRender(); 699 } 700 } 701 702 if (document.addEventListener) { 703 document.addEventListener("DOMContentLoaded", function() { 704 document.removeEventListener("DOMContentLoaded", arguments.callee, false); 705 maybeStaticRenderLater(); 706 }, false); 707 } else if (document.attachEvent) { 708 document.attachEvent("onreadystatechange", function() { 709 if (document.readyState === "complete") { 710 document.detachEvent("onreadystatechange", arguments.callee); 711 maybeStaticRenderLater(); 712 } 713 }); 714 } 715 716 717 window.HTMLWidgets.getAttachmentUrl = function(depname, key) { 718 // If no key, default to the first item 719 if (typeof(key) === "undefined") 720 key = 1; 721 722 var link = document.getElementById(depname + "-" + key + "-attachment"); 723 if (!link) { 724 throw new Error("Attachment " + depname + "/" + key + " not found in document"); 725 } 726 return link.getAttribute("href"); 727 }; 728 729 window.HTMLWidgets.dataframeToD3 = function(df) { 730 var names = []; 731 var length; 732 for (var name in df) { 733 if (df.hasOwnProperty(name)) 734 names.push(name); 735 if (typeof(df[name]) !== "object" || typeof(df[name].length) === "undefined") { 736 throw new Error("All fields must be arrays"); 737 } else if (typeof(length) !== "undefined" && length !== df[name].length) { 738 throw new Error("All fields must be arrays of the same length"); 739 } 740 length = df[name].length; 741 } 742 var results = []; 743 var item; 744 for (var row = 0; row < length; row++) { 745 item = {}; 746 for (var col = 0; col < names.length; col++) { 747 item[names[col]] = df[names[col]][row]; 748 } 749 results.push(item); 750 } 751 return results; 752 }; 753 754 window.HTMLWidgets.transposeArray2D = function(array) { 755 if (array.length === 0) return array; 756 var newArray = array[0].map(function(col, i) { 757 return array.map(function(row) { 758 return row[i] 759 }) 760 }); 761 return newArray; 762 }; 763 // Split value at splitChar, but allow splitChar to be escaped 764 // using escapeChar. Any other characters escaped by escapeChar 765 // will be included as usual (including escapeChar itself). 766 function splitWithEscape(value, splitChar, escapeChar) { 767 var results = []; 768 var escapeMode = false;
769 var currentResult = ""; 770 for (var pos = 0; pos < value.length; pos++) { 771 if (!escapeMode) { 772 if (value[pos] === splitChar) { 773 results.push(currentResult); 774 currentResult = ""; 775 } else if (value[pos] === escapeChar) { 776 escapeMode = true; 777 } else { 778 currentResult += value[pos]; 779 } 780 } else { 781 currentResult += value[pos]; 782 escapeMode = false; 783 } 784 } 785 if (currentResult !== "") { 786 results.push(currentResult); 787 } 788 return results; 789 } 790 // Function authored by Yihui/JJ Allaire 791 window.HTMLWidgets.evaluateStringMember = function(o, member) { 792 var parts = splitWithEscape(member, '.', '\\'); 793 for (var i = 0, l = parts.length; i < l; i++) { 794 var part = parts[i]; 795 // part may be a character or 'numeric' member name 796 if (o !== null && typeof o === "object" && part in o) { 797 if (i == (l - 1)) { // if we are at the end of the line then evalulate 798 if (typeof o[part] === "string") 799 o[part] = tryEval(o[part]); 800 } else { // otherwise continue to next embedded object 801 o = o[part]; 802 } 803 } 804 } 805 }; 806 807 // Retrieve the HTMLWidget instance (i.e. the return value of an 808 // HTMLWidget binding's initialize() or factory() function) 809 // associated with an element, or null if none. 810 window.HTMLWidgets.getInstance = function(el) { 811 return elementData(el, "init_result"); 812 }; 813 814 // Finds the first element in the scope that matches the selector, 815 // and returns the HTMLWidget instance (i.e. the return value of 816 // an HTMLWidget binding's initialize() or factory() function) 817 // associated with that element, if any. If no element matches the 818 // selector, or the first matching element has no HTMLWidget 819 // instance associated with it, then null is returned. 820 // 821 // The scope argument is optional, and defaults to window.document. 822 window.HTMLWidgets.find = function(scope, selector) { 823 if (arguments.length == 1) { 824 selector = scope; 825 scope = document; 826 } 827 828 var el = scope.querySelector(selector); 829 if (el === null) { 830 return null; 831 } else { 832 return window.HTMLWidgets.getInstance(el); 833 } 834 }; 835 836 // Finds all elements in the scope that match the selector, and 837 // returns the HTMLWidget instances (i.e. the return values of 838 // an HTMLWidget binding's initialize() or factory() function) 839 // associated with the elements, in an array. If elements that 840 // match the selector don't have an associated HTMLWidget 841 // instance, the returned array will contain nulls. 842 // 843 // The scope argument is optional, and defaults to window.document. 844 window.HTMLWidgets.findAll = function(scope, selector) { 845 if (arguments.length == 1) { 846 selector = scope; 847 scope = document; 848 } 849 850 var nodes = scope.querySelectorAll(selector); 851 var results = []; 852 for (var i = 0; i < nodes.length; i++) { 853 results.push(window.HTMLWidgets.getInstance(nodes[i])); 854 } 855 return results; 856 }; 857 858 var postRenderHandlers = []; 859 function invokePostRenderHandlers() { 860 while (postRenderHandlers.length) { 861 var handler = postRenderHandlers.shift(); 862 if (handler) { 863 handler(); 864 } 865 } 866 } 867 868 // Register the given callback function to be invoked after the 869 // next time static widgets are rendered. 870 window.HTMLWidgets.addPostRenderHandler = function(callback) { 871 postRenderHandlers.push(callback); 872 }; 873 874 // Takes a new-style instance-bound definition, and returns an 875 // old-style class-bound definition. This saves us from having 876 // to rewrite all the logic in this file to accomodate both 877 // types of definitions. 878 function createLegacyDefinitionAdapter(defn) { 879 var result = { 880 name: defn.name, 881 type: defn.type, 882 initialize: function(el, width, height) { 883 return defn.factory(el, width, height); 884 }, 885 renderValue: function(el, x, instance) { 886 return instance.renderValue(x); 887 }, 888 resize: function(el, width, height, instance) { 889 return instance.resize(width, height); 890 } 891 }; 892 893 if (defn.find) 894 result.find = defn.find; 895 if (defn.renderError) 896 result.renderError = defn.renderError; 897 if (defn.clearError) 898 result.clearError = defn.clearError; 899 900 return result; 901 } 902})(); 903
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.