From b197fb26ae410d2cb4b134879d2261945140c0f6 Mon Sep 17 00:00:00 2001 From: ognevushka Date: Sat, 12 Jul 2025 12:03:50 -0700 Subject: [PATCH] Issue #484 fix --- inst/www/shared/shiny.js | 2 +- inst/www/shared/shiny.js.map | 4 ++-- inst/www/shared/shiny.min.js | 2 +- inst/www/shared/shiny.min.js.map | 4 ++-- srcts/src/utils/index.ts | 2 +- yarn.lock | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/inst/www/shared/shiny.js b/inst/www/shared/shiny.js index 79d038f65..be97d922d 100644 --- a/inst/www/shared/shiny.js +++ b/inst/www/shared/shiny.js @@ -847,7 +847,7 @@ } function formatDateUTC(date) { if (date instanceof Date) { - return date.getUTCFullYear() + "-" + padZeros(date.getUTCMonth() + 1, 2) + "-" + padZeros(date.getUTCDate(), 2); + return date.getUTCFullYear().toString().padStart(4, "0") + "-" + padZeros(date.getUTCMonth() + 1, 2) + "-" + padZeros(date.getUTCDate(), 2); } else { return null; } diff --git a/inst/www/shared/shiny.js.map b/inst/www/shared/shiny.js.map index fa621ad9a..cba484a2b 100644 --- a/inst/www/shared/shiny.js.map +++ b/inst/www/shared/shiny.js.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["globals:jquery", "../../../srcts/src/initialize/browser.ts", "../../../srcts/src/utils/browser.ts", "../../../srcts/src/utils/userAgent.ts", "../../../srcts/src/initialize/disableForm.ts", "../../../srcts/src/initialize/history.ts", "../../../srcts/src/shiny/index.ts", "../../../srcts/src/utils/index.ts", "../../../srcts/src/shiny/render.ts", "../../../srcts/src/shiny/initedMethods.ts", "../../../srcts/src/time/debounce.ts", "../../../srcts/src/time/invoke.ts", "../../../srcts/src/time/throttle.ts", "../../../srcts/src/shiny/sendImageSize.ts", "../../../srcts/src/shiny/singletons.ts", "../../../srcts/src/window/pixelRatio.ts", "../../../srcts/src/utils/object.ts", "../../../srcts/src/bindings/registry.ts", "../../../srcts/src/bindings/input/inputBinding.ts", "../../../srcts/src/bindings/input/actionbutton.ts", "../../../srcts/src/bindings/input/checkbox.ts", "../../../srcts/src/bindings/input/checkboxgroup.ts", "../../../srcts/src/bindings/input/date.ts", "../../../srcts/src/bindings/input/daterange.ts", "../../../srcts/src/bindings/input/fileinput.ts", "../../../srcts/src/file/fileProcessor.ts", "../../../srcts/src/events/inputChanged.ts", "../../../srcts/src/bindings/input/number.ts", "../../../srcts/src/bindings/input/text.ts", "../../../srcts/src/bindings/input/password.ts", "../../../srcts/src/bindings/input/radio.ts", "../../../srcts/src/bindings/input/selectInput.ts", "../../../srcts/src/utils/eval.ts", "../../../srcts/src/bindings/input/slider.ts", "../../../srcts/src/bindings/input/tabinput.ts", "../../../srcts/src/bindings/input/textarea.ts", "../../../srcts/src/bindings/input/index.ts", "../../../srcts/src/bindings/output/datatable.ts", "../../../srcts/src/bindings/output/outputBinding.ts", "../../../srcts/src/bindings/output/downloadlink.ts", "../../../srcts/src/bindings/output/html.ts", "../../../srcts/src/bindings/output/image.ts", "../../../srcts/src/imageutils/createBrush.ts", "../../../srcts/src/imageutils/initCoordmap.ts", "../../../srcts/src/imageutils/initPanelScales.ts", "../../../srcts/src/imageutils/findbox.ts", "../../../srcts/src/imageutils/shiftToRange.ts", "../../../srcts/src/imageutils/createClickInfo.ts", "../../../srcts/src/imageutils/createHandlers.ts", "../../../srcts/src/imageutils/disableDrag.ts", "../../../srcts/src/bindings/output/text.ts", "../../../srcts/src/bindings/output/index.ts", "../../../node_modules/@lit/reactive-element/src/css-tag.ts", "../../../node_modules/@lit/reactive-element/src/reactive-element.ts", "../../../node_modules/lit-html/src/lit-html.ts", "../../../node_modules/lit-element/src/lit-element.ts", "../../../srcts/src/shiny/error.ts", "../../../srcts/src/components/errorConsole.ts", "../../../srcts/src/imageutils/resetBrush.ts", "../../../srcts/src/inputPolicies/inputBatchSender.ts", "../../../srcts/src/inputPolicies/inputDeferDecorator.ts", "../../../srcts/src/inputPolicies/inputEventDecorator.ts", "../../../srcts/src/inputPolicies/splitInputNameType.ts", "../../../srcts/src/inputPolicies/inputNoResendDecorator.ts", "../../../srcts/src/inputPolicies/inputRateDecorator.ts", "../../../srcts/src/inputPolicies/inputValidateDecorator.ts", "../../../srcts/src/utils/promise.ts", "../../../srcts/src/shiny/bind.ts", "../../../srcts/src/bindings/outputAdapter.ts", "../../../srcts/src/shiny/modal.ts", "../../../srcts/src/shiny/notifications.ts", "../../../srcts/src/shiny/reconnectDialog.ts", "../../../srcts/src/shiny/shinyapp.ts", "../../../srcts/src/utils/asyncQueue.ts", "../../../srcts/src/shiny/outputProgress.ts", "../../../srcts/src/window/userAgent.ts", "../../../srcts/src/shiny/reactlog.ts", "../../../srcts/src/initialize/index.ts", "../../../srcts/src/index.ts"], - "sourcesContent": ["module.exports = window.jQuery", "import $ from \"jquery\";\n\nimport { isIE, setIEVersion, setIsIE, setIsQt } from \"../utils/browser\";\nimport { userAgent } from \"../utils/userAgent\";\n\nfunction getIEVersion() {\n const msie = userAgent.indexOf(\"MSIE \");\n\n if (isIE() && msie > 0) {\n // IE 10 or older => return version number\n return parseInt(\n userAgent.substring(msie + 5, userAgent.indexOf(\".\", msie)),\n 10\n );\n }\n const trident = userAgent.indexOf(\"Trident/\");\n\n if (trident > 0) {\n // IE 11 => return version number\n const rv = userAgent.indexOf(\"rv:\");\n\n return parseInt(\n userAgent.substring(rv + 3, userAgent.indexOf(\".\", rv)),\n 10\n );\n }\n return -1;\n}\n\nfunction determineBrowserInfo(): void {\n // For easy handling of Qt quirks using CSS\n\n if (/\\bQt\\//.test(userAgent)) {\n $(document.documentElement).addClass(\"qt\");\n setIsQt(true);\n } else {\n setIsQt(false);\n }\n\n // For Qt on Mac. Note that the target string as of RStudio 1.4.173\n // is \"QtWebEngine\" and does not have a trailing slash.\n if (/\\bQt/.test(userAgent) && /\\bMacintosh/.test(userAgent)) {\n $(document.documentElement).addClass(\"qtmac\");\n }\n\n // Enable special treatment for Qt 5 quirks on Linux\n if (/\\bQt\\/5/.test(userAgent) && /Linux/.test(userAgent)) {\n $(document.documentElement).addClass(\"qt5\");\n }\n\n // Detect IE and older (pre-Chromium) Edge\n setIsIE(/MSIE|Trident|Edge/.test(userAgent));\n\n setIEVersion(getIEVersion());\n}\n\nexport { determineBrowserInfo };\n", "let isQtVal = false;\nlet isIEVal = false;\nlet versionIE = -1;\n\nfunction setIsQt(isQt: boolean): void {\n isQtVal = isQt;\n}\nfunction setIsIE(isIE: boolean): void {\n isIEVal = isIE;\n}\nfunction setIEVersion(versionIE_: number): void {\n versionIE = versionIE_;\n}\n\nfunction isQt(): boolean {\n return isQtVal;\n}\nfunction isIE(): boolean {\n return isIEVal;\n}\n\n// (Name existed before TS conversion)\n// eslint-disable-next-line @typescript-eslint/naming-convention\nfunction IEVersion(): number {\n return versionIE;\n}\n\nexport { isQt, isIE, IEVersion, setIsQt, setIsIE, setIEVersion };\n", "type UserAgent = typeof window.navigator.userAgent;\n\nlet userAgent: UserAgent;\n\nfunction setUserAgent(userAgent_: UserAgent): void {\n userAgent = userAgent_;\n}\n\nexport type { UserAgent };\nexport { userAgent, setUserAgent };\n", "import $ from \"jquery\";\n\nfunction disableFormSubmission(): void {\n // disable form submissions\n $(document).on(\"submit\", \"form:not([action])\", function (e) {\n e.preventDefault();\n });\n}\n\nexport { disableFormSubmission };\n", "import $ from \"jquery\";\n\nfunction trackHistory(): void {\n const origPushState = window.history.pushState;\n\n window.history.pushState = function (...args) {\n const result = origPushState.apply(this, args);\n\n $(document).trigger(\"pushstate\");\n return result;\n };\n}\n\nexport { trackHistory };\n", "import $ from \"jquery\";\n\nimport { InputBinding, OutputBinding } from \"../bindings\";\nimport { initInputBindings } from \"../bindings/input\";\nimport { initOutputBindings } from \"../bindings/output\";\nimport type { BindingRegistry } from \"../bindings/registry\";\nimport { showErrorInClientConsole } from \"../components/errorConsole\";\nimport { resetBrush } from \"../imageutils/resetBrush\";\nimport type { InputPolicy } from \"../inputPolicies\";\nimport {\n InputBatchSender,\n InputDeferDecorator,\n InputEventDecorator,\n InputNoResendDecorator,\n InputRateDecorator,\n InputValidateDecorator,\n} from \"../inputPolicies\";\nimport type { InputPolicyOpts } from \"../inputPolicies/inputPolicy\";\nimport { addDefaultInputOpts } from \"../inputPolicies/inputValidateDecorator\";\nimport { debounce, Debouncer } from \"../time\";\nimport {\n $escape,\n compareVersion,\n getBoundingClientSizeBeforeZoom,\n getComputedLinkColor,\n getStyle,\n hasDefinedProperty,\n mapValues,\n pixelRatio,\n} from \"../utils\";\nimport { createInitStatus, type InitStatusPromise } from \"../utils/promise\";\nimport type { BindInputsCtx, BindScope } from \"./bind\";\nimport { bindAll, unbindAll, _bindAll } from \"./bind\";\nimport type {\n shinyBindAll,\n shinyForgetLastInputValue,\n shinyInitializeInputs,\n shinySetInputValue,\n shinyUnbindAll,\n} from \"./initedMethods\";\nimport { setFileInputBinding, setShinyObj } from \"./initedMethods\";\nimport { removeModal, showModal } from \"./modal\";\nimport { removeNotification, showNotification } from \"./notifications\";\nimport { hideReconnectDialog, showReconnectDialog } from \"./reconnectDialog\";\nimport {\n registerDependency,\n renderContent,\n renderContentAsync,\n renderDependencies,\n renderDependenciesAsync,\n renderHtml,\n renderHtmlAsync,\n} from \"./render\";\nimport { sendImageSizeFns } from \"./sendImageSize\";\nimport { addCustomMessageHandler, ShinyApp, type Handler } from \"./shinyapp\";\nimport { registerNames as singletonsRegisterNames } from \"./singletons\";\n\nclass ShinyClass {\n version: string;\n $escape: typeof $escape;\n compareVersion: typeof compareVersion;\n inputBindings: BindingRegistry;\n // eslint-disable-next-line @typescript-eslint/naming-convention\n InputBinding: typeof InputBinding;\n outputBindings: BindingRegistry;\n // eslint-disable-next-line @typescript-eslint/naming-convention\n OutputBinding: typeof OutputBinding;\n resetBrush: typeof resetBrush;\n notifications: {\n show: typeof showNotification;\n remove: typeof removeNotification;\n };\n modal: { show: typeof showModal; remove: typeof removeModal };\n showReconnectDialog: typeof showReconnectDialog;\n hideReconnectDialog: typeof hideReconnectDialog;\n renderDependenciesAsync: typeof renderDependenciesAsync;\n renderDependencies: typeof renderDependencies;\n renderContentAsync: typeof renderContentAsync;\n renderContent: typeof renderContent;\n renderHtmlAsync: typeof renderHtmlAsync;\n renderHtml: typeof renderHtml;\n addCustomMessageHandler: typeof addCustomMessageHandler;\n\n // The following are added in the initialization, by initShiny()\n createSocket?: () => WebSocket;\n user?: string;\n progressHandlers?: ShinyApp[\"progressHandlers\"];\n shinyapp?: ShinyApp;\n setInputValue?: typeof shinySetInputValue;\n onInputChange?: typeof shinySetInputValue;\n forgetLastInputValue?: typeof shinyForgetLastInputValue;\n bindAll?: typeof shinyBindAll;\n unbindAll?: typeof shinyUnbindAll;\n initializeInputs?: typeof shinyInitializeInputs;\n\n // Promise-like object that is resolved after initialization.\n initializedPromise: InitStatusPromise;\n\n // Eventually deprecate\n // For old-style custom messages - should deprecate and migrate to new\n oncustommessage?: Handler;\n\n constructor() {\n // `process.env.SHINY_VERSION` is overwritten to the Shiny version at build time.\n // During testing, the `Shiny.version` will be `\"development\"`\n this.version = process.env.SHINY_VERSION || \"development\";\n\n const { inputBindings, fileInputBinding } = initInputBindings();\n const { outputBindings } = initOutputBindings();\n\n setFileInputBinding(fileInputBinding);\n\n this.$escape = $escape;\n this.compareVersion = compareVersion;\n this.inputBindings = inputBindings;\n this.InputBinding = InputBinding;\n this.outputBindings = outputBindings;\n this.OutputBinding = OutputBinding;\n this.resetBrush = resetBrush;\n this.notifications = {\n show: showNotification,\n remove: removeNotification,\n };\n this.modal = { show: showModal, remove: removeModal };\n\n this.addCustomMessageHandler = addCustomMessageHandler;\n this.showReconnectDialog = showReconnectDialog;\n this.hideReconnectDialog = hideReconnectDialog;\n this.renderDependenciesAsync = renderDependenciesAsync;\n this.renderDependencies = renderDependencies;\n this.renderContentAsync = renderContentAsync;\n this.renderContent = renderContent;\n this.renderHtmlAsync = renderHtmlAsync;\n this.renderHtml = renderHtml;\n\n this.initializedPromise = createInitStatus();\n\n $(() => {\n // Init Shiny a little later than document ready, so user code can\n // run first (i.e. to register bindings)\n setTimeout(async () => {\n try {\n await this.initialize();\n } catch (e) {\n showErrorInClientConsole(e);\n throw e;\n }\n }, 1);\n });\n }\n\n /**\n * Method to check if Shiny is running in development mode. By packaging as a\n * method, we can we can avoid needing to look for the `__SHINY_DEV_MODE__`\n * variable in the global scope.\n * @returns `true` if Shiny is running in development mode, `false` otherwise.\n */\n inDevMode(): boolean {\n if (\"__SHINY_DEV_MODE__\" in window)\n return Boolean(window.__SHINY_DEV_MODE__);\n\n return false;\n }\n\n async initialize(): Promise {\n setShinyObj(this);\n this.shinyapp = new ShinyApp();\n const shinyapp = this.shinyapp;\n\n this.progressHandlers = shinyapp.progressHandlers;\n\n const inputBatchSender = new InputBatchSender(shinyapp);\n const inputsNoResend = new InputNoResendDecorator(inputBatchSender);\n const inputsEvent = new InputEventDecorator(inputsNoResend);\n const inputsRate = new InputRateDecorator(inputsEvent);\n const inputsDefer = new InputDeferDecorator(inputsEvent);\n\n let target: InputPolicy;\n\n if (document.querySelector(\".shiny-submit-button\")) {\n // If there is a submit button on the page, use defer decorator\n target = inputsDefer;\n\n document.querySelectorAll(\".shiny-submit-button\").forEach(function (x) {\n x.addEventListener(\"click\", function (event) {\n event.preventDefault();\n inputsDefer.submit();\n });\n });\n } else {\n // By default, use rate decorator\n target = inputsRate;\n }\n\n const inputs = new InputValidateDecorator(target);\n\n this.setInputValue = this.onInputChange = function (\n name: string,\n value: unknown,\n opts: Partial = {}\n ): void {\n const newOpts = addDefaultInputOpts(opts);\n\n inputs.setInput(name, value, newOpts);\n };\n\n // By default, Shiny deduplicates input value changes; that is, if\n // `setInputValue` is called with the same value as the input already\n // has, the call is ignored (unless opts.priority = \"event\"). Calling\n // `forgetLastInputValue` tells Shiny that the very next call to\n // `setInputValue` for this input id shouldn't be ignored, even if it\n // is a dupe of the existing value.\n this.forgetLastInputValue = function (name) {\n inputsNoResend.forget(name);\n };\n\n // MUST be called after `setShiny()`\n const inputBindings = this.inputBindings;\n const outputBindings = this.outputBindings;\n\n function shinyBindCtx(): BindInputsCtx {\n return {\n inputs,\n inputsRate,\n sendOutputHiddenState,\n maybeAddThemeObserver,\n inputBindings,\n outputBindings,\n initDeferredIframes,\n };\n }\n\n this.bindAll = async function (scope: BindScope) {\n await bindAll(shinyBindCtx(), scope);\n };\n this.unbindAll = function (scope: BindScope, includeSelf = false) {\n unbindAll(shinyBindCtx(), scope, includeSelf);\n };\n\n // Calls .initialize() for all of the input objects in all input bindings,\n // in the given scope.\n function initializeInputs(scope: BindScope = document.documentElement) {\n const bindings = inputBindings.getBindings();\n\n // Iterate over all bindings\n for (let i = 0; i < bindings.length; i++) {\n const binding = bindings[i].binding;\n const inputObjects = binding.find(scope);\n\n if (inputObjects) {\n // Iterate over all input objects for this binding\n for (let j = 0; j < inputObjects.length; j++) {\n const $inputObjectJ = $(inputObjects[j]);\n\n if (!$inputObjectJ.data(\"_shiny_initialized\")) {\n $inputObjectJ.data(\"_shiny_initialized\", true);\n binding.initialize(inputObjects[j]);\n }\n }\n }\n }\n }\n this.initializeInputs = initializeInputs;\n\n function getIdFromEl(el: HTMLElement) {\n const $el = $(el);\n const bindingAdapter = $el.data(\"shiny-output-binding\");\n\n if (!bindingAdapter) return null;\n else return bindingAdapter.getId();\n }\n\n // Initialize all input objects in the document, before binding\n initializeInputs(document.documentElement);\n\n // The input values returned by _bindAll() each have a structure like this:\n // { value: 123, opts: { ... } }\n // We want to only keep the value. This is because when the initialValues is\n // passed to ShinyApp.connect(), the ShinyApp object stores the\n // initialValues object for the duration of the session, and the opts may\n // have a reference to the DOM element, which would prevent it from being\n // GC'd.\n const initialValues = mapValues(\n await _bindAll(shinyBindCtx(), document.documentElement),\n (x) => x.value\n );\n\n // The server needs to know the size of each image and plot output element,\n // in case it is auto-sizing\n $(\".shiny-image-output, .shiny-plot-output, .shiny-report-size\").each(\n function () {\n const id = getIdFromEl(this),\n rect = getBoundingClientSizeBeforeZoom(this);\n\n if (rect.width !== 0 || rect.height !== 0) {\n initialValues[\".clientdata_output_\" + id + \"_width\"] = rect.width;\n initialValues[\".clientdata_output_\" + id + \"_height\"] = rect.height;\n }\n }\n );\n\n function getComputedBgColor(\n el: HTMLElement | null\n ): string | null | undefined {\n if (!el) {\n // Top of document, can't recurse further\n return null;\n }\n\n const bgColor = getStyle(el, \"background-color\");\n\n if (!bgColor) return bgColor;\n const m = bgColor.match(\n /^rgba\\(\\s*([\\d.]+)\\s*,\\s*([\\d.]+)\\s*,\\s*([\\d.]+)\\s*,\\s*([\\d.]+)\\s*\\)$/\n );\n\n if (bgColor === \"transparent\" || (m && parseFloat(m[4]) === 0)) {\n // No background color on this element. See if it has a background image.\n const bgImage = getStyle(el, \"background-image\");\n\n if (bgImage && bgImage !== \"none\") {\n // Failed to detect background color, since it has a background image\n return null;\n } else {\n // Recurse\n return getComputedBgColor(el.parentElement);\n }\n }\n return bgColor;\n }\n\n function getComputedFont(el: HTMLElement) {\n const fontFamily = getStyle(el, \"font-family\");\n const fontSize = getStyle(el, \"font-size\");\n\n return {\n families: fontFamily?.replace(/\"/g, \"\").split(\", \"),\n size: fontSize,\n };\n }\n\n $(\".shiny-image-output, .shiny-plot-output, .shiny-report-theme\").each(\n function () {\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n const el = this;\n const id = getIdFromEl(el);\n\n initialValues[\".clientdata_output_\" + id + \"_bg\"] =\n getComputedBgColor(el);\n initialValues[\".clientdata_output_\" + id + \"_fg\"] = getStyle(\n el,\n \"color\"\n );\n initialValues[\".clientdata_output_\" + id + \"_accent\"] =\n getComputedLinkColor(el);\n initialValues[\".clientdata_output_\" + id + \"_font\"] =\n getComputedFont(el);\n maybeAddThemeObserver(el);\n }\n );\n\n // Resend computed styles if *an output element's* class or style attribute changes.\n // This gives us some level of confidence that getCurrentOutputInfo() will be\n // properly invalidated if output container is mutated; but unfortunately,\n // we don't have a reasonable way to detect change in *inherited* styles\n // (other than session$setCurrentTheme())\n // https://github.com/rstudio/shiny/issues/3196\n // https://github.com/rstudio/shiny/issues/2998\n function maybeAddThemeObserver(el: HTMLElement): void {\n if (!window.MutationObserver) {\n return; // IE10 and lower\n }\n\n const cl = el.classList;\n const reportTheme =\n cl.contains(\"shiny-image-output\") ||\n cl.contains(\"shiny-plot-output\") ||\n cl.contains(\"shiny-report-theme\");\n\n if (!reportTheme) {\n return;\n }\n\n const $el = $(el);\n\n if ($el.data(\"shiny-theme-observer\")) {\n return; // i.e., observer is already observing\n }\n\n const observerCallback = new Debouncer(null, () => doSendTheme(el), 100);\n const observer = new MutationObserver(() =>\n observerCallback.normalCall()\n );\n const config = { attributes: true, attributeFilter: [\"style\", \"class\"] };\n\n observer.observe(el, config);\n $el.data(\"shiny-theme-observer\", observer);\n }\n\n function doSendTheme(el: HTMLElement): void {\n // Sending theme info on error isn't necessary (it'd add an unnecessary additional round-trip)\n if (el.classList.contains(\"shiny-output-error\")) {\n return;\n }\n const id = getIdFromEl(el);\n\n inputs.setInput(\n \".clientdata_output_\" + id + \"_bg\",\n getComputedBgColor(el)\n );\n inputs.setInput(\n \".clientdata_output_\" + id + \"_fg\",\n getStyle(el, \"color\")\n );\n inputs.setInput(\n \".clientdata_output_\" + id + \"_accent\",\n getComputedLinkColor(el)\n );\n inputs.setInput(\n \".clientdata_output_\" + id + \"_font\",\n getComputedFont(el)\n );\n }\n\n function doSendImageSize() {\n $(\".shiny-image-output, .shiny-plot-output, .shiny-report-size\").each(\n function () {\n const id = getIdFromEl(this),\n rect = getBoundingClientSizeBeforeZoom(this);\n\n if (rect.width !== 0 || rect.height !== 0) {\n inputs.setInput(\".clientdata_output_\" + id + \"_width\", rect.width);\n inputs.setInput(\n \".clientdata_output_\" + id + \"_height\",\n rect.height\n );\n }\n }\n );\n\n $(\".shiny-image-output, .shiny-plot-output, .shiny-report-theme\").each(\n function () {\n doSendTheme(this);\n }\n );\n\n $(\".shiny-bound-output\").each(function () {\n const $this = $(this),\n binding = $this.data(\"shiny-output-binding\");\n\n $this.trigger({\n type: \"shiny:visualchange\",\n // @ts-expect-error; Can not remove info on a established, malformed Event object\n visible: !isHidden(this),\n binding: binding,\n });\n binding.onResize();\n });\n }\n\n sendImageSizeFns.setImageSend(inputBatchSender, doSendImageSize);\n\n // Return true if the object or one of its ancestors in the DOM tree has\n // style='display:none'; otherwise return false.\n function isHidden(obj: HTMLElement | null): boolean {\n // null means we've hit the top of the tree. If width or height is\n // non-zero, then we know that no ancestor has display:none.\n if (obj === null || obj.offsetWidth !== 0 || obj.offsetHeight !== 0) {\n return false;\n } else if (getStyle(obj, \"display\") === \"none\") {\n return true;\n } else {\n return isHidden(obj.parentNode as HTMLElement | null);\n }\n }\n let lastKnownVisibleOutputs: { [key: string]: boolean } = {};\n // Set initial state of outputs to hidden, if needed\n\n $(\".shiny-bound-output\").each(function () {\n const id = getIdFromEl(this);\n\n if (isHidden(this)) {\n initialValues[\".clientdata_output_\" + id + \"_hidden\"] = true;\n } else {\n lastKnownVisibleOutputs[id] = true;\n initialValues[\".clientdata_output_\" + id + \"_hidden\"] = false;\n }\n });\n // Send update when hidden state changes\n function doSendOutputHiddenState() {\n const visibleOutputs: { [key: string]: boolean } = {};\n\n $(\".shiny-bound-output\").each(function () {\n const id = getIdFromEl(this);\n\n delete lastKnownVisibleOutputs[id];\n // Assume that the object is hidden when width and height are 0\n const hidden = isHidden(this),\n evt = {\n type: \"shiny:visualchange\",\n visible: !hidden,\n };\n\n if (hidden) {\n inputs.setInput(\".clientdata_output_\" + id + \"_hidden\", true);\n } else {\n visibleOutputs[id] = true;\n inputs.setInput(\".clientdata_output_\" + id + \"_hidden\", false);\n }\n const $this = $(this);\n\n // @ts-expect-error; Can not remove info on a established, malformed Event object\n evt.binding = $this.data(\"shiny-output-binding\");\n // @ts-expect-error; Can not remove info on a established, malformed Event object\n $this.trigger(evt);\n });\n // Anything left in lastKnownVisibleOutputs is orphaned\n for (const name in lastKnownVisibleOutputs) {\n if (hasDefinedProperty(lastKnownVisibleOutputs, name))\n inputs.setInput(\".clientdata_output_\" + name + \"_hidden\", true);\n }\n // Update the visible outputs for next time\n lastKnownVisibleOutputs = visibleOutputs;\n }\n // sendOutputHiddenState gets called each time DOM elements are shown or\n // hidden. This can be in the hundreds or thousands of times at startup.\n // We'll debounce it, so that we do the actual work once per tick.\n const sendOutputHiddenStateDebouncer = new Debouncer(\n null,\n doSendOutputHiddenState,\n 0\n );\n\n function sendOutputHiddenState() {\n sendOutputHiddenStateDebouncer.normalCall();\n }\n // We need to make sure doSendOutputHiddenState actually gets called before\n // the inputBatchSender sends data to the server. The lastChanceCallback\n // here does that - if the debouncer has a pending call, flush it.\n inputBatchSender.lastChanceCallback.push(function () {\n if (sendOutputHiddenStateDebouncer.isPending())\n sendOutputHiddenStateDebouncer.immediateCall();\n });\n\n // Given a namespace and a handler function, return a function that invokes\n // the handler only when e's namespace matches. For example, if the\n // namespace is \"bs\", it would match when e.namespace is \"bs\" or \"bs.tab\".\n // If the namespace is \"bs.tab\", it would match for \"bs.tab\", but not \"bs\".\n function filterEventsByNamespace(\n namespace: string,\n handler: (...handlerArgs: any[]) => void,\n ...args: any[]\n ) {\n const namespaceArr = namespace.split(\".\");\n\n return function (this: HTMLElement, e: JQuery.TriggeredEvent) {\n const eventNamespace = e.namespace?.split(\".\") ?? [];\n\n // If any of the namespace strings aren't present in this event, quit.\n for (let i = 0; i < namespaceArr.length; i++) {\n if (eventNamespace.indexOf(namespaceArr[i]) === -1) return;\n }\n\n handler.apply(this, [namespaceArr, handler, ...args]);\n };\n }\n\n // The size of each image may change either because the browser window was\n // resized, or because a tab was shown/hidden (hidden elements report size\n // of 0x0). It's OK to over-report sizes because the input pipeline will\n // filter out values that haven't changed.\n $(window).resize(debounce(500, sendImageSizeFns.regular));\n // Need to register callbacks for each Bootstrap 3 class.\n const bs3classes = [\n \"modal\",\n \"dropdown\",\n \"tab\",\n \"tooltip\",\n \"popover\",\n \"collapse\",\n ];\n\n $.each(bs3classes, function (idx, classname) {\n $(document.body).on(\n \"shown.bs.\" + classname + \".sendImageSize\",\n \"*\",\n filterEventsByNamespace(\"bs\", sendImageSizeFns.regular)\n );\n $(document.body).on(\n \"shown.bs.\" +\n classname +\n \".sendOutputHiddenState \" +\n \"hidden.bs.\" +\n classname +\n \".sendOutputHiddenState\",\n \"*\",\n filterEventsByNamespace(\"bs\", sendOutputHiddenState)\n );\n });\n\n // This is needed for Bootstrap 2 compatibility and for non-Bootstrap\n // related shown/hidden events (like conditionalPanel)\n $(document.body).on(\"shown.sendImageSize\", \"*\", sendImageSizeFns.regular);\n $(document.body).on(\n \"shown.sendOutputHiddenState hidden.sendOutputHiddenState\",\n \"*\",\n sendOutputHiddenState\n );\n\n // Send initial pixel ratio, and update it if it changes\n initialValues[\".clientdata_pixelratio\"] = pixelRatio();\n $(window).resize(function () {\n inputs.setInput(\".clientdata_pixelratio\", pixelRatio());\n });\n\n // Send initial URL\n initialValues[\".clientdata_url_protocol\"] = window.location.protocol;\n initialValues[\".clientdata_url_hostname\"] = window.location.hostname;\n initialValues[\".clientdata_url_port\"] = window.location.port;\n initialValues[\".clientdata_url_pathname\"] = window.location.pathname;\n\n // Send initial URL search (query string) and update it if it changes\n initialValues[\".clientdata_url_search\"] = window.location.search;\n\n $(window).on(\"pushstate\", function (e) {\n inputs.setInput(\".clientdata_url_search\", window.location.search);\n return;\n e;\n });\n\n $(window).on(\"popstate\", function (e) {\n inputs.setInput(\".clientdata_url_search\", window.location.search);\n return;\n e;\n });\n\n // This is only the initial value of the hash. The hash can change, but\n // a reactive version of this isn't sent because watching for changes can\n // require polling on some browsers. The JQuery hashchange plugin can be\n // used if this capability is important.\n initialValues[\".clientdata_url_hash_initial\"] = window.location.hash;\n initialValues[\".clientdata_url_hash\"] = window.location.hash;\n\n $(window).on(\"hashchange\", function (e) {\n inputs.setInput(\".clientdata_url_hash\", window.location.hash);\n return;\n e;\n });\n\n // The server needs to know what singletons were rendered as part of\n // the page loading\n const singletonText = (initialValues[\".clientdata_singletons\"] = $(\n 'script[type=\"application/shiny-singletons\"]'\n ).text());\n\n singletonsRegisterNames(singletonText.split(/,/));\n\n const dependencyText = $(\n 'script[type=\"application/html-dependencies\"]'\n ).text();\n\n $.each(dependencyText.split(/;/), function (i, depStr) {\n const match = /\\s*^(.+)\\[(.+)\\]\\s*$/.exec(depStr);\n\n if (match) {\n registerDependency(match[1], match[2]);\n }\n });\n\n // We've collected all the initial values--start the server process!\n inputsNoResend.reset(initialValues);\n shinyapp.connect(initialValues);\n $(document).one(\"shiny:connected\", () => {\n initDeferredIframes();\n });\n\n $(document).one(\"shiny:sessioninitialized\", () => {\n this.initializedPromise.resolve();\n });\n }\n}\n\n// Give any deferred iframes a chance to load.\nfunction initDeferredIframes(): void {\n // TODO-barret; This method uses `window.Shiny`. Could be replaced with `fullShinyObj_.shinyapp?.isConnected()`,\n // but that would not use `window.Shiny`. Is it a problem???\n if (\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore; Do not want to define `window.Shiny` as a type to discourage usage of `window.Shiny`;\n // Can not expect error when combining with window available Shiny definition\n !window.Shiny ||\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore; Do not want to define `window.Shiny` as a type to discourage usage of `window.Shiny`;\n // Can not expect error when combining with window available Shiny definition\n !window.Shiny.shinyapp ||\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore; Do not want to define `window.Shiny` as a type to discourage usage of `window.Shiny`;\n // Can not expect error when combining with window available Shiny definition\n !window.Shiny.shinyapp.isConnected()\n ) {\n // If somehow we accidentally call this before the server connection is\n // established, just ignore the call. At the time of this writing it\n // doesn't happen, but it's easy to imagine a later refactoring putting\n // us in this situation and it'd be hard to notice with either manual\n // testing or automated tests, because the only effect is on HTTP request\n // timing. (Update: Actually Aron saw this being called without even\n // window.Shiny being defined, but it was hard to repro.)\n return;\n }\n\n $(\".shiny-frame-deferred\").each(function (i, el) {\n const $el = $(el);\n\n $el.removeClass(\"shiny-frame-deferred\");\n // @ts-expect-error; If it is undefined, set using the undefined value\n $el.attr(\"src\", $el.attr(\"data-deferred-src\"));\n $el.attr(\"data-deferred-src\", null);\n });\n}\n\nexport { ShinyClass };\n", "import $ from \"jquery\";\nimport type { HtmlDep } from \"../shiny/render\";\nimport { renderContent } from \"../shiny/render\";\nimport { windowDevicePixelRatio } from \"../window/pixelRatio\";\nimport type { MapValuesUnion, MapWithResult } from \"./extraTypes\";\nimport { hasDefinedProperty, hasOwnProperty } from \"./object\";\n\nfunction escapeHTML(str: string): string {\n /* eslint-disable @typescript-eslint/naming-convention */\n const escaped: { [key: string]: string } = {\n \"&\": \"&\",\n \"<\": \"<\",\n \">\": \">\",\n // eslint-disable-next-line prettier/prettier\n '\"': \""\",\n \"'\": \"'\",\n \"/\": \"/\",\n };\n /* eslint-enable @typescript-eslint/naming-convention */\n\n return str.replace(/[&<>'\"/]/g, function (m) {\n return escaped[m] as string;\n });\n}\n\nfunction randomId(): string {\n return Math.floor(0x100000000 + Math.random() * 0xf00000000).toString(16);\n}\n\nfunction strToBool(str: string): boolean | undefined {\n if (!str || !str.toLowerCase) return undefined;\n\n switch (str.toLowerCase()) {\n case \"true\":\n return true;\n case \"false\":\n return false;\n default:\n return undefined;\n }\n}\n\n// A wrapper for getComputedStyle that is compatible with older browsers.\n// This is significantly faster than jQuery's .css() function.\nfunction getStyle(el: Element, styleProp: string): string | undefined {\n let x = undefined;\n\n if (\"currentStyle\" in el) {\n // @ts-expect-error; Old, IE 5+ attribute only - https://developer.mozilla.org/en-US/docs/Web/API/Element/currentStyle\n x = el.currentStyle[styleProp];\n } else {\n // getComputedStyle can return null when we're inside a hidden iframe on\n // Firefox; don't attempt to retrieve style props in this case.\n // https://bugzilla.mozilla.org/show_bug.cgi?id=548397\n const style = document?.defaultView?.getComputedStyle(el, null);\n\n if (style) x = style.getPropertyValue(styleProp);\n }\n return x;\n}\n\n// Convert a number to a string with leading zeros\nfunction padZeros(n: number, digits: number): string {\n let str = n.toString();\n\n while (str.length < digits) str = \"0\" + str;\n return str;\n}\n\n// Round to a specified number of significant digits.\nfunction roundSignif(x: number, digits = 1): number {\n if (digits < 1) throw \"Significant digits must be at least 1.\";\n\n // This converts to a string and back to a number, which is inelegant, but\n // is less prone to FP rounding error than an alternate method which used\n // Math.round().\n return parseFloat(x.toPrecision(digits));\n}\n\n// Take a string with format \"YYYY-MM-DD\" and return a Date object.\n// IE8 and QTWebKit don't support YYYY-MM-DD, but they support YYYY/MM/DD\nfunction parseDate(dateString: string): Date {\n let date = new Date(dateString);\n\n if (date.toString() === \"Invalid Date\") {\n date = new Date(dateString.replace(/-/g, \"/\"));\n }\n return date;\n}\n\n// Given a Date object, return a string in yyyy-mm-dd format, using the\n// UTC date. This may be a day off from the date in the local time zone.\nfunction formatDateUTC(x: Date): string;\nfunction formatDateUTC(date: Date | null): string | null {\n if (date instanceof Date) {\n return (\n date.getUTCFullYear() +\n \"-\" +\n padZeros(date.getUTCMonth() + 1, 2) +\n \"-\" +\n padZeros(date.getUTCDate(), 2)\n );\n } else {\n return null;\n }\n}\n\n// Given an element and a function(width, height), returns a function(). When\n// the output function is called, it calls the input function with the offset\n// width and height of the input element--but only if the size of the element\n// is non-zero and the size is different than the last time the output\n// function was called.\n//\n// Basically we are trying to filter out extraneous calls to func, so that\n// when the window size changes or whatever, we don't run resize logic for\n// elements that haven't actually changed size or aren't visible anyway.\ntype LastSizeInterface = {\n w?: number;\n h?: number;\n};\nfunction makeResizeFilter(\n el: HTMLElement,\n func: (\n width: HTMLElement[\"offsetWidth\"],\n height: HTMLElement[\"offsetHeight\"]\n ) => void\n): () => void {\n let lastSize: LastSizeInterface = {};\n\n return function () {\n const rect = el.getBoundingClientRect();\n const size = { w: rect.width, h: rect.height };\n\n if (size.w === 0 && size.h === 0) return;\n if (size.w === lastSize.w && size.h === lastSize.h) return;\n lastSize = size;\n func(size.w, size.h);\n };\n}\n\nfunction pixelRatio(): number {\n if (windowDevicePixelRatio()) {\n return Math.round(windowDevicePixelRatio() * 100) / 100;\n } else {\n return 1;\n }\n}\n\nfunction getBoundingClientSizeBeforeZoom(el: HTMLElement): {\n width: number;\n height: number;\n} {\n const rect = el.getBoundingClientRect();\n // Cast to any because currentCSSZoom isn't in the type def of HTMLElement\n // TODO: typescript >= 5.5.2 added this property to the type definition\n const zoom = (el as any).currentCSSZoom || 1;\n return {\n width: rect.width / zoom,\n height: rect.height / zoom,\n };\n}\n\n// Takes a string expression and returns a function that takes an argument.\n//\n// When the function is executed, it will evaluate that expression using\n// \"with\" on the argument value, and return the result.\nfunction scopeExprToFunc(expr: string): (scope: unknown) => unknown {\n /*jshint evil: true */\n const exprEscaped = expr\n .replace(/[\\\\\"']/g, \"\\\\$&\")\n // eslint-disable-next-line no-control-regex\n .replace(/\\u0000/g, \"\\\\0\")\n .replace(/\\n/g, \"\\\\n\")\n .replace(/\\r/g, \"\\\\r\")\n // \\b has a special meaning; need [\\b] to match backspace char.\n .replace(/[\\b]/g, \"\\\\b\");\n\n let func: () => unknown;\n\n try {\n // @ts-expect-error; Do not know how to type this _dangerous_ situation\n func = new Function(\n `with (this) {\n try {\n return (${expr});\n } catch (e) {\n console.error('Error evaluating expression: ${exprEscaped}');\n throw e;\n }\n }`\n );\n } catch (e) {\n console.error(\"Error parsing expression: \" + expr);\n throw e;\n }\n\n return function (scope: unknown): unknown {\n return func.call(scope);\n };\n}\n\nfunction asArray(value: T | T[] | null | undefined): T[] {\n if (value === null || value === undefined) return [];\n if (Array.isArray(value)) return value;\n return [value];\n}\n\n// We need a stable sorting algorithm for ordering\n// bindings by priority and insertion order.\nfunction mergeSort(\n list: Item[],\n sortfunc: (a: Item, b: Item) => boolean | number\n): Item[] {\n function merge(a: Item[], b: Item[]) {\n let ia = 0;\n let ib = 0;\n const sorted = [];\n\n while (ia < a.length && ib < b.length) {\n if (sortfunc(a[ia], b[ib]) <= 0) {\n sorted.push(a[ia++]);\n } else {\n sorted.push(b[ib++]);\n }\n }\n while (ia < a.length) sorted.push(a[ia++]);\n while (ib < b.length) sorted.push(b[ib++]);\n return sorted;\n }\n\n // Don't mutate list argument\n list = list.slice(0);\n\n for (let chunkSize = 1; chunkSize < list.length; chunkSize *= 2) {\n for (let i = 0; i < list.length; i += chunkSize * 2) {\n const listA = list.slice(i, i + chunkSize);\n const listB = list.slice(i + chunkSize, i + chunkSize * 2);\n const merged = merge(listA, listB);\n const args = [i, merged.length] as [number, number];\n\n Array.prototype.push.apply(args, merged);\n Array.prototype.splice.apply(list, args);\n }\n }\n\n return list;\n}\n\n// Escape jQuery selector metacharacters: !\"#$%&'()*+,./:;<=>?@[\\]^`{|}~\nfunction $escape(val: undefined): undefined;\nfunction $escape(val: string): string;\nfunction $escape(val: string | undefined): string | undefined {\n if (typeof val === \"undefined\") return val;\n return val.replace(/([!\"#$%&'()*+,./:;<=>?@[\\\\\\]^`{|}~])/g, \"\\\\$1\");\n}\n\n// Maps a function over an object, preserving keys. Like the mapValues\n// function from lodash.\nfunction mapValues(\n obj: T,\n f: (value: MapValuesUnion, key: string, object: typeof obj) => R\n): MapWithResult {\n const newObj = {} as MapWithResult;\n\n Object.keys(obj).forEach((key: keyof typeof obj) => {\n newObj[key] = f(obj[key], key as string, obj);\n });\n return newObj;\n}\n\n// This is does the same as Number.isNaN, but that function unfortunately does\n// not exist in any version of IE.\nfunction isnan(x: unknown): boolean {\n return typeof x === \"number\" && isNaN(x);\n}\n\n// Binary equality function used by the equal function.\n// (Name existed before TS conversion)\n// eslint-disable-next-line @typescript-eslint/naming-convention\nfunction _equal(x: unknown, y: unknown): boolean {\n if ($.type(x) === \"object\" && $.type(y) === \"object\") {\n const xo = x as { [key: string]: unknown };\n const yo = y as { [key: string]: unknown };\n\n if (Object.keys(xo).length !== Object.keys(yo).length) return false;\n for (const prop in xo) {\n if (!hasOwnProperty(yo, prop) || !_equal(xo[prop], yo[prop]))\n return false;\n }\n return true;\n } else if ($.type(x) === \"array\" && $.type(y) === \"array\") {\n const xa = x as unknown[];\n const ya = y as unknown[];\n\n if (xa.length !== ya.length) return false;\n for (let i = 0; i < xa.length; i++) if (!_equal(xa[i], ya[i])) return false;\n return true;\n } else {\n return x === y;\n }\n}\n\n// Structural or \"deep\" equality predicate. Tests two or more arguments for\n// equality, traversing arrays and objects (as determined by $.type) as\n// necessary.\n//\n// Objects other than objects and arrays are tested for equality using ===.\nfunction equal(...args: unknown[]): boolean {\n if (args.length < 2)\n throw new Error(\"equal requires at least two arguments.\");\n for (let i = 0; i < args.length - 1; i++) {\n if (!_equal(args[i], args[i + 1])) return false;\n }\n return true;\n}\n\n// Compare version strings like \"1.0.1\", \"1.4-2\". `op` must be a string like\n// \"==\" or \"<\".\nconst compareVersion = function (\n a: string,\n op: \"<\" | \"<=\" | \"==\" | \">\" | \">=\",\n b: string\n): boolean {\n function versionParts(ver: string) {\n return (ver + \"\")\n .replace(/-/, \".\")\n .replace(/(\\.0)+[^.]*$/, \"\")\n .split(\".\");\n }\n\n function cmpVersion(a: string, b: string) {\n const aParts = versionParts(a);\n const bParts = versionParts(b);\n const len = Math.min(aParts.length, bParts.length);\n let cmp;\n\n for (let i = 0; i < len; i++) {\n cmp = parseInt(aParts[i], 10) - parseInt(bParts[i], 10);\n if (cmp !== 0) {\n return cmp;\n }\n }\n return aParts.length - bParts.length;\n }\n\n const diff = cmpVersion(a, b);\n\n if (op === \"==\") return diff === 0;\n else if (op === \">=\") return diff >= 0;\n else if (op === \">\") return diff > 0;\n else if (op === \"<=\") return diff <= 0;\n else if (op === \"<\") return diff < 0;\n else throw `Unknown operator: ${op}`;\n};\n\nasync function updateLabel(\n labelContent: string | { html: string; deps: HtmlDep[] } | undefined,\n labelNode: JQuery\n): Promise {\n // Only update if label was specified in the update method\n if (typeof labelContent === \"undefined\") return;\n if (labelNode.length !== 1) {\n throw new Error(\"labelNode must be of length 1\");\n }\n\n if (typeof labelContent === \"string\") {\n labelContent = {\n html: labelContent,\n deps: [],\n };\n }\n\n if (labelContent.html === \"\") {\n labelNode.addClass(\"shiny-label-null\");\n } else {\n await renderContent(labelNode, labelContent);\n labelNode.removeClass(\"shiny-label-null\");\n }\n}\n\n// Compute the color property of an a tag, scoped within the element\nfunction getComputedLinkColor(el: HTMLElement): string {\n const a = document.createElement(\"a\");\n\n a.href = \"/\";\n const div = document.createElement(\"div\");\n\n div.style.setProperty(\"position\", \"absolute\", \"important\");\n div.style.setProperty(\"top\", \"-1000px\", \"important\");\n div.style.setProperty(\"left\", \"0\", \"important\");\n div.style.setProperty(\"width\", \"30px\", \"important\");\n div.style.setProperty(\"height\", \"10px\", \"important\");\n div.appendChild(a);\n el.appendChild(div);\n const linkColor = window.getComputedStyle(a).getPropertyValue(\"color\");\n\n el.removeChild(div);\n return linkColor;\n}\n\nfunction isBS3(): boolean {\n // @ts-expect-error; Check if `window.bootstrap` exists\n return !window.bootstrap;\n}\n\nfunction toLowerCase(str: T): Lowercase {\n return str.toLowerCase() as Lowercase;\n}\n\nexport {\n escapeHTML,\n randomId,\n strToBool,\n getStyle,\n padZeros,\n roundSignif,\n parseDate,\n formatDateUTC,\n makeResizeFilter,\n pixelRatio,\n getBoundingClientSizeBeforeZoom,\n scopeExprToFunc,\n asArray,\n mergeSort,\n $escape,\n mapValues,\n isnan,\n _equal,\n equal,\n compareVersion,\n updateLabel,\n getComputedLinkColor,\n hasOwnProperty,\n hasDefinedProperty,\n isBS3,\n toLowerCase,\n};\n", "import $ from \"jquery\";\nimport { asArray, hasDefinedProperty } from \"../utils\";\nimport { isIE } from \"../utils/browser\";\nimport type { BindScope } from \"./bind\";\nimport {\n shinyBindAll,\n shinyInitializeInputs,\n shinyUnbindAll,\n} from \"./initedMethods\";\nimport { sendImageSizeFns } from \"./sendImageSize\";\n\nimport type { WherePosition } from \"./singletons\";\nimport { renderHtml as singletonsRenderHtml } from \"./singletons\";\n\n// There are synchronous and asynchronous versions of the exported functions\n// renderContent(), renderHtml(), and renderDependencies(). This is because they\n// the original versions of these functions were synchronous, but we added\n// support for asynchronous rendering, to avoid the deprecated XMLHttpRequest\n// function (https://github.com/rstudio/shiny/pull/3666).\n//\n// At the bottom, there is the appendScriptTags(), which calls $.append(), which\n// in turn calls (synchronous) XMLHttpRequest(); and its counterpart\n// appendScriptTagsAsync(), which uses a different (asynchronous) method. The\n// sync and async versions of this function necessitate the sync and async\n// versions of the other functions.\n//\n// The async versions of these functions are used internally and should be used\n// for new external code when possible, but for backward compatibility for\n// external code that calls these functions, we'll keep the synchronous versions\n// around as well.\n\n// =============================================================================\n// renderContent\n// =============================================================================\n// Render HTML in a DOM element, add dependencies, and bind Shiny\n// inputs/outputs. `content` can be null, a string, or an object with\n// properties 'html' and 'deps'.\nasync function renderContentAsync(\n el: BindScope,\n content: string | { html: string; deps?: HtmlDep[] } | null,\n where: WherePosition = \"replace\"\n): Promise {\n if (where === \"replace\") {\n shinyUnbindAll(el);\n }\n\n let html = \"\";\n let dependencies: HtmlDep[] = [];\n\n if (content === null) {\n html = \"\";\n } else if (typeof content === \"string\") {\n html = content;\n } else if (typeof content === \"object\") {\n html = content.html;\n dependencies = content.deps || [];\n }\n\n await renderHtmlAsync(html, el, dependencies, where);\n\n let scope: BindScope = el;\n\n if (where === \"replace\") {\n shinyInitializeInputs(el);\n await shinyBindAll(el);\n } else {\n const $parent = $(el).parent();\n\n if ($parent.length > 0) {\n scope = $parent;\n if (where === \"beforeBegin\" || where === \"afterEnd\") {\n const $grandparent = $parent.parent();\n\n if ($grandparent.length > 0) scope = $grandparent;\n }\n }\n shinyInitializeInputs(scope);\n await shinyBindAll(scope);\n }\n}\n\nfunction renderContent(\n el: BindScope,\n content: string | { html: string; deps?: HtmlDep[] } | null,\n where: WherePosition = \"replace\"\n): Promise {\n if (where === \"replace\") {\n shinyUnbindAll(el);\n }\n\n let html = \"\";\n let dependencies: HtmlDep[] = [];\n\n if (content === null) {\n html = \"\";\n } else if (typeof content === \"string\") {\n html = content;\n } else if (typeof content === \"object\") {\n html = content.html;\n dependencies = content.deps || [];\n }\n\n renderHtml(html, el, dependencies, where);\n\n let scope: BindScope = el;\n\n if (where === \"replace\") {\n shinyInitializeInputs(el);\n return shinyBindAll(el);\n } else {\n const $parent = $(el).parent();\n\n if ($parent.length > 0) {\n scope = $parent;\n if (where === \"beforeBegin\" || where === \"afterEnd\") {\n const $grandparent = $parent.parent();\n\n if ($grandparent.length > 0) scope = $grandparent;\n }\n }\n shinyInitializeInputs(scope);\n return shinyBindAll(scope);\n }\n}\n\n// =============================================================================\n// renderHtml\n// =============================================================================\n// Render HTML in a DOM element, inserting singletons into head as needed\nasync function renderHtmlAsync(\n html: string,\n el: BindScope,\n dependencies: HtmlDep[],\n where: WherePosition = \"replace\"\n): Promise> {\n await renderDependenciesAsync(dependencies);\n return singletonsRenderHtml(html, el, where);\n}\n\n// Render HTML in a DOM element, inserting singletons into head as needed\nfunction renderHtml(\n html: string,\n el: BindScope,\n dependencies: HtmlDep[],\n where: WherePosition = \"replace\"\n): ReturnType {\n renderDependencies(dependencies);\n return singletonsRenderHtml(html, el, where);\n}\n\n// =============================================================================\n// renderDependencies\n// =============================================================================\nasync function renderDependenciesAsync(\n dependencies: HtmlDep[] | null\n): Promise {\n if (dependencies) {\n for (const dep of dependencies) {\n await renderDependencyAsync(dep);\n }\n }\n}\n\nfunction renderDependencies(dependencies: HtmlDep[] | null): void {\n if (dependencies) {\n for (const dep of dependencies) {\n renderDependency(dep);\n }\n }\n}\n\n// =============================================================================\n// HTML dependency types\n// =============================================================================\ntype HtmlDepVersion = string;\n\ntype MetaItem = {\n name: string;\n content: string;\n [x: string]: string;\n};\n\ntype StylesheetItem = {\n href: string;\n rel?: string;\n type?: string;\n};\n\ntype ScriptItem = {\n src: string;\n [x: string]: string;\n};\n\ntype AttachmentItem = {\n key: string;\n href: string;\n [x: string]: string;\n};\n\n// This supports the older R htmltools HtmlDependency structure, and it also\n// encompasses the newer, consistent HTMLDependency structure.\ntype HtmlDep = {\n name: string;\n version: HtmlDepVersion;\n restyle?: boolean;\n src?: { href: string };\n meta?: MetaItem[] | { [x: string]: string };\n stylesheet?: string[] | StylesheetItem | StylesheetItem[] | string;\n script?: ScriptItem | ScriptItem[] | string[] | string;\n attachment?: AttachmentItem[] | string[] | string | { [key: string]: string };\n head?: string;\n};\n\n// This is the newer, consistent HTMLDependency structure.\ntype HtmlDepNormalized = {\n name: string;\n version: HtmlDepVersion;\n restyle?: boolean;\n meta: MetaItem[];\n stylesheet: StylesheetItem[];\n script: ScriptItem[];\n attachment: AttachmentItem[];\n head?: string;\n};\n\n// =============================================================================\n// renderDependency helper functions\n// =============================================================================\nconst htmlDependencies: { [key: string]: HtmlDepVersion } = {};\n\nfunction registerDependency(name: string, version: HtmlDepVersion): void {\n htmlDependencies[name] = version;\n}\n\n// Re-render stylesheet(s) if the dependency has specificially requested it\n// and it matches an existing dependency (name and version)\nfunction needsRestyle(dep: HtmlDepNormalized) {\n if (!dep.restyle) {\n return false;\n }\n const names = Object.keys(htmlDependencies);\n const idx = names.indexOf(dep.name);\n\n if (idx === -1) {\n return false;\n }\n return htmlDependencies[names[idx]] === dep.version;\n}\n\nfunction addStylesheetsAndRestyle(links: HTMLLinkElement[]): void {\n const $head = $(\"head\").first();\n\n // This inline