diff --git a/NEWS.md b/NEWS.md
index b693d0f8f..892664be1 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -6,6 +6,8 @@
* `textAreaInput()` gains a `autoresize` option, which automatically resizes the text area to fit its content. (#4210)
+* The `callback` argument of Shiny.js' `InputBinding.subscribe()` method gains support for a value of `"event"`. This makes it possible for an input binding to use event priority when updating the value (i.e., send immediately and always resend, even if the value hasn't changed). (#4211)
+
## Changes
* Shiny no longer suspends input changes when _any_ ` ` or `` is on the page. Instead, it now only suspends when a `submitButton()` is present. If you have reason for creating a submit button from custom HTML, add a CSS class of `shiny-submit-button` to the button. (#4209)
diff --git a/inst/www/shared/shiny.js b/inst/www/shared/shiny.js
index 8d0417ebb..a746f4e22 100644
--- a/inst/www/shared/shiny.js
+++ b/inst/www/shared/shiny.js
@@ -5608,19 +5608,14 @@
function isJQuery(value) {
return Boolean(value && value.jquery);
}
- function valueChangeCallback(inputs, binding, el, allowDeferred) {
+ function valueChangeCallback(inputs, binding, el, priority) {
let id = binding.getId(el);
if (id) {
const value = binding.getValue(el);
const type = binding.getType(el);
if (type)
id = id + ":" + type;
- const opts = {
- priority: allowDeferred ? "deferred" : "immediate",
- binding,
- el
- };
- inputs.setInput(id, value, opts);
+ inputs.setInput(id, value, { priority, binding, el });
}
}
var bindingsRegistry = (() => {
@@ -5735,8 +5730,9 @@ ${duplicateIdMsg}`;
const thisCallback = function() {
const thisBinding = binding;
const thisEl = el;
- return function(allowDeferred) {
- valueChangeCallback(inputs, thisBinding, thisEl, allowDeferred);
+ return function(priority) {
+ const normalizedPriority = typeof priority !== "boolean" ? priority : priority ? "deferred" : "immediate";
+ valueChangeCallback(inputs, thisBinding, thisEl, normalizedPriority);
};
}();
binding.subscribe(el, thisCallback);
diff --git a/inst/www/shared/shiny.js.map b/inst/www/shared/shiny.js.map
index ff51baa77..46045b49c 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/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/shiny/initedMethods.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/time/debounce.ts", "../../../srcts/src/time/invoke.ts", "../../../srcts/src/time/throttle.ts", "../../../srcts/src/bindings/output/outputBinding.ts", "../../../srcts/src/bindings/output/downloadlink.ts", "../../../srcts/src/bindings/output/html.ts", "../../../srcts/src/shiny/render.ts", "../../../srcts/src/shiny/sendImageSize.ts", "../../../srcts/src/shiny/singletons.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 { 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\nfunction updateLabel(\n labelTxt: string | undefined,\n labelNode: JQuery\n): void {\n // Only update if label was specified in the update method\n if (typeof labelTxt === \"undefined\") return;\n if (labelNode.length !== 1) {\n throw new Error(\"labelNode must be of length 1\");\n }\n\n // Should the label be empty?\n const emptyLabel = Array.isArray(labelTxt) && labelTxt.length === 0;\n\n if (emptyLabel) {\n labelNode.addClass(\"shiny-label-null\");\n } else {\n labelNode.text(labelTxt);\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", "function windowDevicePixelRatio(): number {\n return window.devicePixelRatio;\n}\n\nexport { windowDevicePixelRatio };\n", "import type { NotUndefined } from \"./extraTypes\";\n\n// Inspriation from https://fettblog.eu/typescript-hasownproperty/\n// But mixing with \"NonNullable key of Obj\" instead of \"key to unknown values\"\nfunction hasOwnProperty(\n obj: X,\n prop: Prop\n): obj is X & { [key in NonNullable]: X[key] } {\n return Object.prototype.hasOwnProperty.call(obj, prop);\n}\n\n// Return true if the key exists on the object and the value is not undefined.\n//\n// This method is mainly used in input bindings' `receiveMessage` method.\n// Since we know that the values are sent by Shiny via `{jsonlite}`,\n// then we know that there are no `undefined` values. `null` is possible, but not `undefined`.\nfunction hasDefinedProperty<\n Prop extends keyof X,\n X extends { [key: string]: any }\n>(\n obj: X,\n prop: Prop\n): obj is X & { [key in NonNullable]: NotUndefined } {\n return (\n Object.prototype.hasOwnProperty.call(obj, prop) && obj[prop] !== undefined\n );\n}\n\n// Return type for non-null value\nfunction ifUndefined, Y>(\n value: X,\n alternate: Y\n): NotUndefined;\n// Return type for null value\nfunction ifUndefined(value: X, alternate: Y): Y;\n// Logic\nfunction ifUndefined(value: X, alternate: Y): X | Y {\n if (value === undefined) return alternate;\n return value;\n}\n\nexport { hasOwnProperty, hasDefinedProperty, ifUndefined };\n", "import { mergeSort } from \"../utils\";\n\ninterface BindingBase {\n name: string;\n}\n\ninterface BindingObj {\n binding: Binding;\n priority: number;\n name?: string;\n}\n\nclass BindingRegistry {\n name!: string;\n bindings: Array> = [];\n bindingNames: { [key: string]: BindingObj } = {};\n\n register(binding: Binding, bindingName: string, priority = 0): void {\n const bindingObj = { binding, priority };\n\n this.bindings.unshift(bindingObj);\n if (bindingName) {\n this.bindingNames[bindingName] = bindingObj;\n binding.name = bindingName;\n }\n }\n\n setPriority(bindingName: string, priority: number): void {\n const bindingObj = this.bindingNames[bindingName];\n\n if (!bindingObj)\n throw \"Tried to set priority on unknown binding \" + bindingName;\n bindingObj.priority = priority || 0;\n }\n\n getPriority(bindingName: string): number | false {\n const bindingObj = this.bindingNames[bindingName];\n\n if (!bindingObj) return false;\n return bindingObj.priority;\n }\n\n getBindings(): Array> {\n // Sort the bindings. The ones with higher priority are consulted\n // first; ties are broken by most-recently-registered.\n return mergeSort(this.bindings, function (a, b) {\n return b.priority - a.priority;\n });\n }\n}\n\nexport { BindingRegistry };\n", "import type { RatePolicyModes } from \"../../inputPolicies/inputRateDecorator\";\nimport type { BindScope } from \"../../shiny/bind\";\n\nclass InputBinding {\n name!: string;\n\n // Returns a jQuery object or element array that contains the\n // descendants of scope that match this binding\n find(scope: BindScope): JQuery {\n throw \"Not implemented\";\n scope; // unused var\n }\n\n getId(el: HTMLElement): string {\n return el.getAttribute(\"data-input-id\") || el.id;\n }\n\n // Gives the input a type in case the server needs to know it\n // to deserialize the JSON correctly\n getType(el: HTMLElement): string | null {\n return null;\n el; // unused var\n }\n getValue(el: HTMLElement): any {\n throw \"Not implemented\";\n el; // unused var\n }\n\n // The callback method takes one argument, whose value is boolean. If true,\n // allow deferred (debounce or throttle) sending depending on the value of\n // getRatePolicy. If false, send value immediately. Default behavior is `false`\n subscribe(el: HTMLElement, callback: (value: boolean) => void): void {\n // empty\n el; // unused var\n callback; // unused var\n }\n unsubscribe(el: HTMLElement): void {\n // empty\n el; // unused var\n }\n\n // This is used for receiving messages that tell the input object to do\n // things, such as setting values (including min, max, and others).\n // 'data' should be an object with elements corresponding to value, min,\n // max, etc., as appropriate for the type of input object. It also should\n // trigger a change event.\n receiveMessage(el: HTMLElement, data: unknown): Promise | void {\n throw \"Not implemented\";\n el; // unused var\n data; // unused var\n }\n getState(el: HTMLElement): unknown {\n throw \"Not implemented\";\n el; // unused var\n }\n\n getRatePolicy(\n el: HTMLElement\n ): { policy: RatePolicyModes; delay: number } | null {\n return null;\n el; // unused var\n }\n\n // Some input objects need initialization before being bound. This is\n // called when the document is ready (for statically-added input objects),\n // and when new input objects are added to the document with\n // htmlOutputBinding.renderValue() (for dynamically-added input objects).\n // This is called before the input is bound.\n initialize(el: HTMLElement): void {\n //empty\n el;\n }\n\n // This is called after unbinding the output.\n dispose(el: HTMLElement): void {\n //empty\n el;\n }\n}\n\n//// NOTES FOR FUTURE DEV\n// Turn register systemin into something that is intialized for every instance.\n// \"Have a new instance for every item, not an instance that does work on every item\"\n//\n// * Keep register as is for historical purposes\n// make a new register function that would take a class\n// these class could be constructed at build time\n// store the constructed obj on the ele and retrieve\n\n// Then the classes could store their information within their local class, rather than on the element\n// VERY CLEAN!!!\n\n// to invoke methods, it would be something like `el.shinyClass.METHOD(x,y,z)`\n// * See https://github.com/rstudio/shinyvalidate/blob/c8becd99c01fac1bac03b50e2140f49fca39e7f4/srcjs/shinyvalidate.js#L157-L167\n// these methods would be added using a new method like `inputBindings.registerClass(ClassObj, name)`\n\n// things to watch out for:\n// * unbind, then rebind. Maybe we stash the local content.\n\n// Updates:\n// * Feel free to alter method names on classes. (And make them private)\n//// END NOTES FOR FUTURE DEV\n\nexport { InputBinding };\n", "import $ from \"jquery\";\nimport { hasDefinedProperty } from \"../../utils\";\nimport { InputBinding } from \"./inputBinding\";\n\ntype ActionButtonReceiveMessageData = {\n label?: string;\n icon?: string | [];\n disabled?: boolean;\n};\n\nclass ActionButtonInputBinding extends InputBinding {\n find(scope: HTMLElement): JQuery {\n return $(scope).find(\".action-button\");\n }\n getValue(el: HTMLElement): number {\n return $(el).data(\"val\") || 0;\n }\n setValue(el: HTMLElement, value: number): void {\n $(el).data(\"val\", value);\n }\n getType(el: HTMLElement): string {\n return \"shiny.action\";\n el;\n }\n subscribe(el: HTMLElement, callback: (x: boolean) => void): void {\n $(el).on(\n \"click.actionButtonInputBinding\",\n // e: Event\n function () {\n const $el = $(this);\n const val = $el.data(\"val\") || 0;\n\n $el.data(\"val\", val + 1);\n\n callback(false);\n }\n );\n }\n getState(el: HTMLElement): { value: number } {\n return { value: this.getValue(el) };\n }\n receiveMessage(el: HTMLElement, data: ActionButtonReceiveMessageData): void {\n const $el = $(el);\n\n if (hasDefinedProperty(data, \"label\") || hasDefinedProperty(data, \"icon\")) {\n // retrieve current label and icon\n let label: string = $el.text();\n let icon = \"\";\n\n // to check (and store) the previous icon, we look for a $el child\n // object that has an i tag, and some (any) class (this prevents\n // italicized text - which has an i tag but, usually, no class -\n // from being mistakenly selected)\n if ($el.find(\"i[class]\").length > 0) {\n const iconHtml = $el.find(\"i[class]\")[0];\n\n if (iconHtml === $el.children()[0]) {\n // another check for robustness\n icon = $(iconHtml).prop(\"outerHTML\");\n }\n }\n\n // update the requested properties\n if (hasDefinedProperty(data, \"label\")) {\n label = data.label;\n }\n if (hasDefinedProperty(data, \"icon\")) {\n // `data.icon` can be an [] if user gave `character(0)`.\n icon = Array.isArray(data.icon) ? \"\" : data.icon ?? \"\";\n }\n\n // produce new html\n $el.html(icon + \" \" + label);\n }\n\n if (hasDefinedProperty(data, \"disabled\")) {\n if (data.disabled) {\n $el.attr(\"disabled\", \"\");\n } else {\n $el.attr(\"disabled\", null);\n }\n }\n }\n\n unsubscribe(el: HTMLElement): void {\n $(el).off(\".actionButtonInputBinding\");\n }\n}\n\n// TODO-barret should this be put in the init methods?\n$(document).on(\"click\", \"a.action-button\", function (e) {\n e.preventDefault();\n});\n\nexport { ActionButtonInputBinding };\nexport type { ActionButtonReceiveMessageData };\n", "import $ from \"jquery\";\nimport { hasDefinedProperty } from \"../../utils\";\nimport { InputBinding } from \"./inputBinding\";\n\ntype CheckedHTMLElement = HTMLInputElement;\n\ntype CheckboxChecked = CheckedHTMLElement[\"checked\"];\ntype CheckboxReceiveMessageData = { value?: CheckboxChecked; label?: string };\n\nclass CheckboxInputBinding extends InputBinding {\n find(scope: HTMLElement): JQuery {\n // Inputs also have .shiny-input-checkbox class\n return $(scope).find('input[type=\"checkbox\"]');\n }\n getValue(el: CheckedHTMLElement): CheckboxChecked {\n return el.checked;\n }\n setValue(el: CheckedHTMLElement, value: CheckboxChecked): void {\n el.checked = value;\n }\n subscribe(el: HTMLElement, callback: (x: boolean) => void): void {\n $(el).on(\"change.checkboxInputBinding\", function () {\n callback(true);\n });\n }\n unsubscribe(el: HTMLElement): void {\n $(el).off(\".checkboxInputBinding\");\n }\n getState(el: CheckedHTMLElement): { label: string; value: CheckboxChecked } {\n return {\n label: $(el).parent().find(\"span\").text(),\n value: el.checked,\n };\n }\n receiveMessage(\n el: CheckedHTMLElement,\n data: CheckboxReceiveMessageData\n ): void {\n if (hasDefinedProperty(data, \"value\")) {\n el.checked = data.value;\n }\n\n // checkboxInput()'s label works different from other\n // input labels...the label container should always exist\n if (hasDefinedProperty(data, \"label\")) {\n $(el).parent().find(\"span\").text(data.label);\n }\n\n $(el).trigger(\"change\");\n }\n}\n\nexport { CheckboxInputBinding };\nexport type { CheckedHTMLElement, CheckboxReceiveMessageData };\n", "import $ from \"jquery\";\n\nimport { $escape, hasDefinedProperty, updateLabel } from \"../../utils\";\nimport type { CheckedHTMLElement } from \"./checkbox\";\nimport { InputBinding } from \"./inputBinding\";\n\ntype CheckboxGroupHTMLElement = CheckedHTMLElement;\ntype ValueLabelObject = {\n value: HTMLInputElement[\"value\"];\n label: string;\n};\ntype CheckboxGroupReceiveMessageData = {\n options?: string;\n value?: Parameters[1];\n label: string;\n};\n\ntype CheckboxGroupValue = CheckboxGroupHTMLElement[\"value\"];\n\n// Get the DOM element that contains the top-level label\nfunction getLabelNode(el: CheckboxGroupHTMLElement): JQuery {\n return $(el).find('label[for=\"' + $escape(el.id) + '\"]');\n}\n// Given an input DOM object, get the associated label. Handles labels\n// that wrap the input as well as labels associated with 'for' attribute.\nfunction getLabel(obj: HTMLElement): string | null {\n const parentNode = obj.parentNode as HTMLElement;\n\n // If label text \n if (parentNode.tagName === \"LABEL\") {\n return $(parentNode).find(\"span\").text().trim();\n }\n\n return null;\n}\n// Given an input DOM object, set the associated label. Handles labels\n// that wrap the input as well as labels associated with 'for' attribute.\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nfunction setLabel(obj: HTMLElement, value: string): null {\n const parentNode = obj.parentNode as HTMLElement;\n\n // If label text \n if (parentNode.tagName === \"LABEL\") {\n $(parentNode).find(\"span\").text(value);\n }\n\n return null;\n}\n\nclass CheckboxGroupInputBinding extends InputBinding {\n find(scope: HTMLElement): JQuery {\n return $(scope).find(\".shiny-input-checkboxgroup\");\n }\n\n getValue(el: CheckboxGroupHTMLElement): CheckboxGroupValue[] {\n // Select the checkbox objects that have name equal to the grouping div's id\n const $objs = $('input:checkbox[name=\"' + $escape(el.id) + '\"]:checked');\n const values = new Array($objs.length);\n\n for (let i = 0; i < $objs.length; i++) {\n values[i] = ($objs[i] as CheckboxGroupHTMLElement).value;\n }\n return values;\n }\n setValue(el: HTMLElement, value: string[] | string | null): void {\n // Null value should be treated as empty array\n value = value ?? [];\n\n // Clear all checkboxes\n $('input:checkbox[name=\"' + $escape(el.id) + '\"]').prop(\"checked\", false);\n\n // Accept array\n if (value instanceof Array) {\n for (let i = 0; i < value.length; i++) {\n $(\n 'input:checkbox[name=\"' +\n $escape(el.id) +\n '\"][value=\"' +\n $escape(value[i]) +\n '\"]'\n ).prop(\"checked\", true);\n }\n // Else assume it's a single value\n } else {\n $(\n 'input:checkbox[name=\"' +\n $escape(el.id) +\n '\"][value=\"' +\n $escape(value) +\n '\"]'\n ).prop(\"checked\", true);\n }\n }\n getState(el: CheckboxGroupHTMLElement): {\n label: string;\n value: ReturnType;\n options: ValueLabelObject[];\n } {\n const $objs = $(\n 'input:checkbox[name=\"' + $escape(el.id) + '\"]'\n ) as JQuery;\n\n // Store options in an array of objects, each with with value and label\n const options = new Array($objs.length);\n\n for (let i = 0; i < options.length; i++) {\n options[i] = { value: $objs[i].value, label: getLabel($objs[i]) };\n }\n\n return {\n label: getLabelNode(el).text(),\n value: this.getValue(el),\n options: options,\n };\n }\n receiveMessage(\n el: CheckboxGroupHTMLElement,\n data: CheckboxGroupReceiveMessageData\n ): void {\n const $el = $(el);\n\n // This will replace all the options\n if (hasDefinedProperty(data, \"options\")) {\n // Clear existing options and add each new one\n $el.find(\"div.shiny-options-group\").remove();\n // Backward compatibility: for HTML generated by shinybootstrap2 package\n $el.find(\"label.checkbox\").remove();\n $el.append(data.options);\n }\n\n if (hasDefinedProperty(data, \"value\")) {\n this.setValue(el, data.value);\n }\n\n updateLabel(data.label, getLabelNode(el));\n\n $(el).trigger(\"change\");\n }\n subscribe(\n el: CheckboxGroupHTMLElement,\n callback: (x: boolean) => void\n ): void {\n $(el).on(\"change.checkboxGroupInputBinding\", function () {\n callback(false);\n });\n }\n unsubscribe(el: CheckboxGroupHTMLElement): void {\n $(el).off(\".checkboxGroupInputBinding\");\n }\n}\n\nexport { CheckboxGroupInputBinding };\nexport type { CheckboxGroupReceiveMessageData };\n", "import $ from \"jquery\";\nimport {\n $escape,\n formatDateUTC,\n hasDefinedProperty,\n parseDate,\n updateLabel,\n} from \"../../utils\";\nimport type { NotUndefined } from \"../../utils/extraTypes\";\nimport { InputBinding } from \"./inputBinding\";\n\ndeclare global {\n interface JQuery {\n // Adjustment of https://github.com/DefinitelyTyped/DefinitelyTyped/blob/1626e0bac175121ec2e9f766a770e03a91843c31/types/bootstrap-datepicker/index.d.ts#L113-L114\n bsDatepicker(methodName: \"getUTCDate\"): Date;\n // Infinity is not allowed as a literal return type. Using `1e9999` as a placeholder that resolves to Infinity\n // https://github.com/microsoft/TypeScript/issues/32277\n // eslint-disable-next-line @typescript-eslint/no-loss-of-precision\n bsDatepicker(methodName: \"getStartDate\"): Date | -1e9999;\n // eslint-disable-next-line @typescript-eslint/no-loss-of-precision\n bsDatepicker(methodName: \"getEndDate\"): Date | 1e9999;\n bsDatepicker(methodName: string): void;\n bsDatepicker(methodName: string, params: Date | null): void;\n }\n}\n\ntype DateReceiveMessageData = {\n label: string;\n min?: Date | null;\n max?: Date | null;\n value?: Date | null;\n};\n\nclass DateInputBindingBase extends InputBinding {\n find(scope: HTMLElement): JQuery {\n return $(scope).find(\".shiny-date-input\");\n }\n getType(el: HTMLElement): string {\n return \"shiny.date\";\n el;\n }\n subscribe(el: HTMLElement, callback: (x: boolean) => void): void {\n // Don't update when in the middle of typing; listening on keyup or input\n // tends to send spurious values to the server, based on unpredictable\n // browser-dependant interpretation of partially-typed date strings.\n $(el).on(\n \"changeDate.dateInputBinding change.dateInputBinding\",\n // event: Event\n function () {\n // Send immediately when clicked\n // Or if typing, when enter pressed or focus lost\n callback(false);\n }\n );\n }\n unsubscribe(el: HTMLElement): void {\n $(el).off(\".dateInputBinding\");\n }\n\n getRatePolicy(): { policy: \"debounce\"; delay: 250 } {\n return {\n policy: \"debounce\",\n delay: 250,\n };\n }\n\n setValue(el: HTMLElement, data: unknown): void {\n throw \"not implemented\";\n el;\n data;\n }\n initialize(el: HTMLElement): void {\n const $input = $(el).find(\"input\");\n\n // The challenge with dates is that we want them to be at 00:00 in UTC so\n // that we can do comparisons with them. However, the Date object itself\n // does not carry timezone information, so we should call _floorDateTime()\n // on Dates as soon as possible so that we know we're always working with\n // consistent objects.\n\n let date = $input.data(\"initial-date\");\n // If initial_date is null, set to current date\n\n if (date === undefined || date === null) {\n // Get local date, but normalized to beginning of day in UTC.\n date = this._floorDateTime(this._dateAsUTC(new Date()));\n }\n\n this.setValue(el, date);\n\n // Set the start and end dates, from min-date and max-date. These always\n // use yyyy-mm-dd format, instead of bootstrap-datepicker's built-in\n // support for date-startdate and data-enddate, which use the current\n // date format.\n if ($input.data(\"min-date\") !== undefined) {\n this._setMin($input[0], $input.data(\"min-date\"));\n }\n if ($input.data(\"max-date\") !== undefined) {\n this._setMax($input[0], $input.data(\"max-date\"));\n }\n }\n protected _getLabelNode(el: HTMLElement): JQuery {\n return $(el).find('label[for=\"' + $escape(el.id) + '\"]');\n }\n // Given a format object from a date picker, return a string\n protected _formatToString(format: {\n parts: string[];\n separators: string[];\n }): string {\n // Format object has structure like:\n // { parts: ['mm', 'dd', 'yy'], separators: ['', '/', '/' ,''] }\n let str = \"\";\n\n let i;\n\n for (i = 0; i < format.parts.length; i++) {\n str += format.separators[i] + format.parts[i];\n }\n str += format.separators[i];\n return str;\n }\n // Given an unambiguous date string or a Date object, set the min (start) date.\n // null will unset. undefined will result in no change,\n protected _setMin(el: HTMLElement, date: Date | null): void {\n if (date === null) {\n $(el).bsDatepicker(\"setStartDate\", null);\n return;\n }\n\n const parsedDate = this._newDate(date);\n\n // If date parsing fails, do nothing\n if (parsedDate === null) return;\n\n // (Assign back to date as a Date object)\n date = parsedDate as Date;\n\n if (isNaN(date.valueOf())) return;\n // Workarounds for\n // https://github.com/rstudio/shiny/issues/2335\n const curValue = $(el).bsDatepicker(\"getUTCDate\");\n\n // Note that there's no `setUTCStartDate`, so we need to convert this Date.\n // It starts at 00:00 UTC, and we convert it to 00:00 in local time, which\n // is what's needed for `setStartDate`.\n $(el).bsDatepicker(\"setStartDate\", this._utcDateAsLocal(date));\n\n // If the new min is greater than the current date, unset the current date.\n if (date && curValue && date.getTime() > curValue.getTime()) {\n $(el).bsDatepicker(\"clearDates\");\n } else {\n // Setting the date needs to be done AFTER `setStartDate`, because the\n // datepicker has a bug where calling `setStartDate` will clear the date\n // internally (even though it will still be visible in the UI) when a\n // 2-digit year format is used.\n // https://github.com/eternicode/bootstrap-datepicker/issues/2010\n $(el).bsDatepicker(\"setUTCDate\", curValue);\n }\n }\n // Given an unambiguous date string or a Date object, set the max (end) date\n // null will unset.\n protected _setMax(el: HTMLElement, date: Date | null): void {\n if (date === null) {\n $(el).bsDatepicker(\"setEndDate\", null);\n return;\n }\n\n const parsedDate = this._newDate(date);\n\n // If date parsing fails, do nothing\n if (parsedDate === null) return;\n\n date = parsedDate as Date;\n\n if (isNaN(date.valueOf())) return;\n\n // Workaround for same issue as in _setMin.\n const curValue = $(el).bsDatepicker(\"getUTCDate\");\n\n $(el).bsDatepicker(\"setEndDate\", this._utcDateAsLocal(date));\n\n // If the new min is greater than the current date, unset the current date.\n if (date && curValue && date.getTime() < curValue.getTime()) {\n $(el).bsDatepicker(\"clearDates\");\n } else {\n $(el).bsDatepicker(\"setUTCDate\", curValue);\n }\n }\n // Given a date string of format yyyy-mm-dd, return a Date object with\n // that date at 12AM UTC.\n // If date is a Date object, return it unchanged.\n protected _newDate(date: Date | never | string): Date | null {\n if (date instanceof Date) return date;\n if (!date) return null;\n\n // Get Date object - this will be at 12AM in UTC, but may print\n // differently at the Javascript console.\n const d = parseDate(date);\n\n // If invalid date, return null\n if (isNaN(d.valueOf())) return null;\n\n return d;\n }\n // A Date can have any time during a day; this will return a new Date object\n // set to 00:00 in UTC.\n protected _floorDateTime(date: Date): Date {\n date = new Date(date.getTime());\n date.setUTCHours(0, 0, 0, 0);\n return date;\n }\n // Given a Date object, return a Date object which has the same \"clock time\"\n // in UTC. For example, if input date is 2013-02-01 23:00:00 GMT-0600 (CST),\n // output will be 2013-02-01 23:00:00 UTC. Note that the JS console may\n // print this in local time, as \"Sat Feb 02 2013 05:00:00 GMT-0600 (CST)\".\n protected _dateAsUTC(date: Date): Date {\n return new Date(date.getTime() - date.getTimezoneOffset() * 60000);\n }\n // The inverse of _dateAsUTC. This is needed to adjust time zones because\n // some bootstrap-datepicker methods only take local dates as input, and not\n // UTC.\n protected _utcDateAsLocal(date: Date): Date {\n return new Date(date.getTime() + date.getTimezoneOffset() * 60000);\n }\n}\n\nclass DateInputBinding extends DateInputBindingBase {\n // Return the date in an unambiguous format, yyyy-mm-dd (as opposed to a\n // format like mm/dd/yyyy)\n getValue(el: HTMLElement): string {\n const date = $(el).find(\"input\").bsDatepicker(\"getUTCDate\");\n\n return formatDateUTC(date);\n }\n // value must be an unambiguous string like '2001-01-01', or a Date object.\n setValue(el: HTMLElement, value: Date | null): void {\n // R's NA, which is null here will remove current value\n if (value === null) {\n $(el).find(\"input\").val(\"\").bsDatepicker(\"update\");\n return;\n }\n\n const date = this._newDate(value);\n\n if (date === null) {\n return;\n }\n\n // If date is invalid, do nothing\n if (isNaN((date as Date).valueOf())) return;\n\n $(el).find(\"input\").bsDatepicker(\"setUTCDate\", date);\n }\n getState(el: HTMLElement): {\n label: string;\n value: string | null;\n valueString: string[] | number | string;\n min: string | null;\n max: string | null;\n language: string | null;\n weekstart: number;\n format: string;\n startview: DatepickerViewModes;\n } {\n const $el = $(el);\n const $input = $el.find(\"input\");\n\n let min = $input.data(\"datepicker\").startDate;\n let max = $input.data(\"datepicker\").endDate;\n\n // Stringify min and max. If min and max aren't set, they will be\n // -Infinity and Infinity; replace these with null.\n min = min === -Infinity ? null : formatDateUTC(min);\n max = max === Infinity ? null : formatDateUTC(max);\n\n // startViewMode is stored as a number; convert to string\n let startview = $input.data(\"datepicker\").startViewMode;\n\n if (startview === 2) startview = \"decade\";\n else if (startview === 1) startview = \"year\";\n else if (startview === 0) startview = \"month\";\n\n return {\n label: this._getLabelNode(el).text(),\n value: this.getValue(el),\n valueString: $input.val() as NotUndefined>,\n min: min,\n max: max,\n language: $input.data(\"datepicker\").language,\n weekstart: $input.data(\"datepicker\").weekStart,\n format: this._formatToString($input.data(\"datepicker\").format),\n startview: startview,\n };\n }\n receiveMessage(el: HTMLElement, data: DateReceiveMessageData): void {\n const $input = $(el).find(\"input\");\n\n updateLabel(data.label, this._getLabelNode(el));\n\n if (hasDefinedProperty(data, \"min\")) this._setMin($input[0], data.min);\n\n if (hasDefinedProperty(data, \"max\")) this._setMax($input[0], data.max);\n\n // Must set value only after min and max have been set. If new value is\n // outside the bounds of the previous min/max, then the result will be a\n // blank input.\n if (hasDefinedProperty(data, \"value\")) this.setValue(el, data.value);\n\n $(el).trigger(\"change\");\n }\n}\n\nexport { DateInputBinding, DateInputBindingBase };\nexport type { DateReceiveMessageData };\n", "import $ from \"jquery\";\n\nimport {\n $escape,\n formatDateUTC,\n hasDefinedProperty,\n updateLabel,\n} from \"../../utils\";\nimport { DateInputBindingBase } from \"./date\";\n\ntype DateRangeReceiveMessageData = {\n label: string;\n min?: Date;\n max?: Date;\n value?: { start?: Date; end?: Date };\n};\n\nfunction getLabelNode(el: HTMLElement): JQuery {\n return $(el).find('label[for=\"' + $escape(el.id) + '\"]');\n}\nclass DateRangeInputBinding extends DateInputBindingBase {\n find(scope: HTMLElement): JQuery {\n return $(scope).find(\".shiny-date-range-input\");\n }\n // Return the date in an unambiguous format, yyyy-mm-dd (as opposed to a\n // format like mm/dd/yyyy)\n getValue(el: HTMLElement): [string, string] {\n const $inputs = $(el).find(\"input\");\n const start = $inputs.eq(0).bsDatepicker(\"getUTCDate\");\n const end = $inputs.eq(1).bsDatepicker(\"getUTCDate\");\n\n return [formatDateUTC(start), formatDateUTC(end)];\n }\n // value must be an object, with optional fields `start` and `end`. These\n // should be unambiguous strings like '2001-01-01', or Date objects.\n setValue(el: HTMLElement, value: { start?: Date; end?: Date }): void {\n if (!(value instanceof Object)) {\n return;\n }\n\n // Get the start and end input objects\n const $inputs = $(el).find(\"input\");\n\n // If value is undefined, don't try to set\n // null will remove the current value\n if (value.start !== undefined) {\n if (value.start === null) {\n $inputs.eq(0).val(\"\").bsDatepicker(\"update\");\n } else {\n const start = this._newDate(value.start);\n\n $inputs.eq(0).bsDatepicker(\"setUTCDate\", start);\n }\n }\n if (value.end !== undefined) {\n if (value.end === null) {\n $inputs.eq(1).val(\"\").bsDatepicker(\"update\");\n } else {\n const end = this._newDate(value.end);\n\n $inputs.eq(1).bsDatepicker(\"setUTCDate\", end);\n }\n }\n }\n getState(el: HTMLElement): {\n label: string;\n value: [string, string];\n valueString: [string, string];\n min: ReturnType | null;\n max: ReturnType | null;\n weekstart: string;\n format: string;\n language: string;\n startview: string;\n } {\n const $el = $(el);\n const $inputs = $el.find(\"input\");\n const $startinput = $inputs.eq(0);\n const $endinput = $inputs.eq(1);\n\n // For many of the properties, assume start and end have the same values\n const min = $startinput.bsDatepicker(\"getStartDate\");\n const max = $startinput.bsDatepicker(\"getEndDate\");\n\n // Stringify min and max. If min and max aren't set, they will be\n // -Infinity and Infinity; replace these with null.\n const minStr = min === -Infinity ? null : formatDateUTC(min as Date);\n const maxStr = max === Infinity ? null : formatDateUTC(max as Date);\n\n // startViewMode is stored as a number; convert to string\n let startview = $startinput.data(\"datepicker\").startView;\n\n if (startview === 2) startview = \"decade\";\n else if (startview === 1) startview = \"year\";\n else if (startview === 0) startview = \"month\";\n\n return {\n label: getLabelNode(el).text(),\n value: this.getValue(el),\n valueString: [$startinput.val() as string, $endinput.val() as string],\n min: minStr,\n max: maxStr,\n weekstart: $startinput.data(\"datepicker\").weekStart,\n format: this._formatToString($startinput.data(\"datepicker\").format),\n language: $startinput.data(\"datepicker\").language,\n startview: startview,\n };\n }\n receiveMessage(el: HTMLElement, data: DateRangeReceiveMessageData): void {\n const $el = $(el);\n const $inputs = $el.find(\"input\");\n const $startinput = $inputs.eq(0);\n const $endinput = $inputs.eq(1);\n\n updateLabel(data.label, getLabelNode(el));\n\n if (hasDefinedProperty(data, \"min\")) {\n this._setMin($startinput[0], data.min);\n this._setMin($endinput[0], data.min);\n }\n\n if (hasDefinedProperty(data, \"max\")) {\n this._setMax($startinput[0], data.max);\n this._setMax($endinput[0], data.max);\n }\n\n // Must set value only after min and max have been set. If new value is\n // outside the bounds of the previous min/max, then the result will be a\n // blank input.\n if (hasDefinedProperty(data, \"value\")) {\n this.setValue(el, data.value);\n }\n\n $el.trigger(\"change\");\n }\n\n initialize(el: HTMLElement): void {\n const $el = $(el);\n const $inputs = $el.find(\"input\");\n const $startinput = $inputs.eq(0);\n const $endinput = $inputs.eq(1);\n\n let start = $startinput.data(\"initial-date\");\n let end = $endinput.data(\"initial-date\");\n\n // If empty/null, use local date, but as UTC\n if (start === undefined || start === null)\n start = this._dateAsUTC(new Date());\n\n if (end === undefined || end === null) end = this._dateAsUTC(new Date());\n\n this.setValue(el, { start: start, end: end });\n\n // // Set the start and end dates, from min-date and max-date. These always\n // // use yyyy-mm-dd format, instead of bootstrap-datepicker's built-in\n // // support for date-startdate and data-enddate, which use the current\n // // date format.\n this._setMin($startinput[0], $startinput.data(\"min-date\"));\n this._setMin($endinput[0], $startinput.data(\"min-date\"));\n this._setMax($startinput[0], $endinput.data(\"max-date\"));\n this._setMax($endinput[0], $endinput.data(\"max-date\"));\n }\n subscribe(el: HTMLElement, callback: (x: boolean) => void): void {\n // Don't update when in the middle of typing; listening on keyup or input\n // tends to send spurious values to the server, based on unpredictable\n // browser-dependant interpretation of partially-typed date strings.\n $(el).on(\n \"changeDate.dateRangeInputBinding change.dateRangeInputBinding\",\n // event: Event\n function () {\n // Send immediately when clicked\n // Or if typing, when enter pressed or focus lost\n callback(false);\n }\n );\n }\n unsubscribe(el: HTMLElement): void {\n $(el).off(\".dateRangeInputBinding\");\n }\n}\n\nexport { DateRangeInputBinding };\nexport type { DateRangeReceiveMessageData };\n", "import $ from \"jquery\";\nimport { FileUploader } from \"../../file/fileProcessor\";\nimport { shinyShinyApp } from \"../../shiny/initedMethods\";\nimport { InputBinding } from \"./inputBinding\";\n\nconst zoneActive = \"shiny-file-input-active\";\nconst zoneOver = \"shiny-file-input-over\";\n\nfunction zoneOf(el: HTMLElement | JQuery): JQuery {\n return $(el).closest(\"div.input-group\");\n}\n\n// This function makes it possible to attach listeners to the dragenter,\n// dragleave, and drop events of a single element with children. It's not\n// intuitive to do directly because outer elements fire \"dragleave\" events\n// both when the drag leaves the element and when the drag enters a child. To\n// make it easier, we maintain a count of the elements being dragged across\n// and trigger 3 new types of event:\n//\n// 1. draghover:enter - When a drag enters el and any of its children.\n// 2. draghover:leave - When the drag leaves el and all of its children.\n// 3. draghover:drop - When an item is dropped on el or any of its children.\nfunction enableDraghover(el: JQuery): JQuery {\n const $el = $(el);\n let childCounter = 0;\n\n /* eslint-disable @typescript-eslint/naming-convention */\n $el.on({\n \"dragenter.draghover\": (e) => {\n if (childCounter++ === 0) {\n $el.trigger(\"draghover:enter\", e);\n }\n },\n \"dragleave.draghover\": (e) => {\n if (--childCounter === 0) {\n $el.trigger(\"draghover:leave\", e);\n }\n if (childCounter < 0) {\n console.error(\"draghover childCounter is negative somehow\");\n }\n },\n \"dragover.draghover\": (e) => {\n e.preventDefault();\n },\n \"drop.draghover\": (e) => {\n childCounter = 0;\n $el.trigger(\"draghover:drop\", e);\n e.preventDefault();\n },\n });\n return $el;\n}\nfunction disableDraghover(el: JQuery): JQuery {\n return $(el).off(\".draghover\");\n}\nfunction enableDocumentEvents(): void {\n const $doc = $(\"html\");\n\n enableDraghover($doc).on({\n \"draghover:enter.draghover\":\n // e: Event\n () => {\n zoneOf($fileInputs).addClass(zoneActive);\n },\n \"draghover:leave.draghover\":\n // e: Event\n () => {\n zoneOf($fileInputs).removeClass(zoneActive);\n },\n \"draghover:drop.draghover\":\n // e: Event\n () => {\n zoneOf($fileInputs).removeClass(zoneOver).removeClass(zoneActive);\n },\n });\n}\nfunction disableDocumentEvents(): void {\n const $doc = $(\"html\");\n\n $doc.off(\".draghover\");\n disableDraghover($doc);\n}\nfunction canSetFiles(fileList: FileList): boolean {\n const testEl = document.createElement(\"input\");\n\n testEl.type = \"file\";\n try {\n testEl.files = fileList;\n } catch (e) {\n return false;\n }\n return true;\n}\nfunction handleDrop(e: JQuery.DragEventBase, el: HTMLInputElement): void {\n const files = e.originalEvent?.dataTransfer?.files,\n $el = $(el);\n\n if (files === undefined || files === null) {\n // 1. The FileList object isn't supported by this browser, and\n // there's nothing else we can try. (< IE 10)\n console.log(\n \"Dropping files is not supported on this browser. (no FileList)\"\n );\n } else if (!canSetFiles(files)) {\n // 2. The browser doesn't support assigning a type=file input's .files\n // property, but we do have a FileList to work with. (IE10+/Edge)\n $el.val(\"\");\n uploadDroppedFilesIE10Plus(el, files);\n } else {\n // 3. The browser supports FileList and input.files assignment.\n // (Chrome, Safari)\n $el.val(\"\");\n el.files = files;\n // Recent versions of Firefox (57+, or \"Quantum\" and beyond) don't seem to\n // automatically trigger a change event, so we trigger one manually here.\n // On browsers that do trigger change, this operation appears to be\n // idempotent, as el.files doesn't change between events.\n $el.trigger(\"change\");\n }\n}\n\n// NOTE On Safari, at least version 10.1.2, *if the developer console is open*,\n// setting the input's value will behave strangely because of a Safari bug. The\n// uploaded file's name will appear over the placeholder value, instead of\n// replacing it. The workaround is to restart Safari. When I (Alan Dipert) ran\n// into this bug Winston Chang helped me diagnose the exact problem, and Winston\n// then submitted a bug report to Apple.\nfunction setFileText($el: JQuery, files: FileList) {\n const $fileText = $el.closest(\"div.input-group\").find(\"input[type=text]\");\n\n if (files.length === 1) {\n $fileText.val(files[0].name);\n } else {\n $fileText.val(files.length + \" files\");\n }\n}\n\n// If previously selected files are uploading, abort that.\nfunction abortCurrentUpload($el: JQuery) {\n const uploader = $el.data(\"currentUploader\");\n\n if (uploader) uploader.abort();\n // Clear data-restore attribute if present.\n $el.removeAttr(\"data-restore\");\n}\n\nfunction uploadDroppedFilesIE10Plus(\n el: HTMLInputElement,\n files: FileList\n): void {\n const $el = $(el);\n\n abortCurrentUpload($el);\n\n // Set the label in the text box\n setFileText($el, files);\n\n // Start the new upload and put the uploader in 'currentUploader'.\n $el.data(\n \"currentUploader\",\n new FileUploader(shinyShinyApp(), fileInputBindingGetId(el), files, el)\n );\n}\n\nfunction uploadFiles(evt: JQuery.DragEvent): void {\n const $el = $(evt.target);\n\n abortCurrentUpload($el);\n\n const files = evt.target.files;\n const id = fileInputBindingGetId(evt.target);\n\n if (files.length === 0) return;\n\n // Set the label in the text box\n setFileText($el, files);\n\n // Start the new upload and put the uploader in 'currentUploader'.\n $el.data(\n \"currentUploader\",\n new FileUploader(shinyShinyApp(), id, files, evt.target)\n );\n}\n\n// Here we maintain a list of all the current file inputs. This is necessary\n// because we need to trigger events on them in order to respond to file drag\n// events. For example, they should all light up when a file is dragged on to\n// the page.\n// TODO-barret ; Should this be an internal class property?\nlet $fileInputs = $();\n\nfunction fileInputBindingGetId(this: any, el: HTMLInputElement): string {\n return InputBinding.prototype.getId.call(this, el) || el.name;\n}\n\nclass FileInputBinding extends InputBinding {\n find(scope: HTMLElement): JQuery {\n // Inputs also have .shiny-input-file class\n return $(scope).find('input[type=\"file\"]');\n }\n getId(el: HTMLInputElement): string {\n return fileInputBindingGetId(el);\n }\n getValue(el: HTMLElement): { name?: string } | null {\n // This returns a non-undefined value only when there's a 'data-restore'\n // attribute, which is set only when restoring Shiny state. If a file is\n // uploaded through the browser, 'data-restore' gets cleared.\n const data = $(el).attr(\"data-restore\");\n\n if (data) {\n const dataParsed = JSON.parse(data);\n\n // Set the label in the text box\n const $fileText = $(el)\n .closest(\"div.input-group\")\n .find(\"input[type=text]\");\n\n if (dataParsed.name.length === 1) {\n $fileText.val(dataParsed.name[0]);\n } else {\n $fileText.val(dataParsed.name.length + \" files\");\n }\n\n // Manually set up progress bar. A bit inelegant because it duplicates\n // code from FileUploader, but duplication is less bad than alternatives.\n const $progress = $(el).closest(\"div.form-group\").find(\".progress\");\n const $bar = $progress.find(\".progress-bar\");\n\n $progress.removeClass(\"active\");\n $bar.width(\"100%\");\n $bar.css(\"visibility\", \"visible\");\n\n return dataParsed;\n } else {\n return null;\n }\n }\n setValue(el: HTMLElement, value: void): void {\n // Not implemented\n el;\n value;\n }\n getType(el: HTMLElement): string {\n // This will be used only when restoring a file from a saved state.\n return \"shiny.file\";\n el;\n }\n\n subscribe(el: HTMLInputElement, callback: (x: boolean) => void): void {\n callback;\n\n $(el).on(\"change.fileInputBinding\", uploadFiles);\n // Here we try to set up the necessary events for Drag and Drop (\"DnD\").\n if ($fileInputs.length === 0) enableDocumentEvents();\n $fileInputs = $fileInputs.add(el);\n const $zone = zoneOf(el);\n\n enableDraghover($zone).on({\n \"draghover:enter.draghover\": (e) => {\n e;\n $zone.addClass(zoneOver);\n },\n \"draghover:leave.draghover\": (e) => {\n $zone.removeClass(zoneOver);\n // Prevent this event from bubbling to the document handler,\n // which would deactivate all zones.\n e.stopPropagation();\n },\n \"draghover:drop.draghover\": (e, dropEvent) => {\n e;\n handleDrop(dropEvent, el);\n },\n });\n }\n\n unsubscribe(el: HTMLElement): void {\n const $el = $(el),\n $zone = zoneOf(el);\n\n $zone.removeClass(zoneOver).removeClass(zoneActive);\n\n disableDraghover($zone);\n $el.off(\".fileInputBinding\");\n $zone.off(\".draghover\");\n\n // Remove el from list of inputs and (maybe) clean up global event handlers.\n $fileInputs = $fileInputs.not(el);\n if ($fileInputs.length === 0) disableDocumentEvents();\n }\n}\n\nexport { FileInputBinding };\n", "import $ from \"jquery\";\nimport { triggerFileInputChanged } from \"../events/inputChanged\";\nimport { getFileInputBinding } from \"../shiny/initedMethods\";\nimport type { ShinyApp } from \"../shiny/shinyapp\";\nimport { $escape } from \"../utils\";\n\ntype JobId = string;\ntype UploadUrl = string;\ntype UploadInitValue = { jobId: JobId; uploadUrl: UploadUrl };\ntype UploadEndValue = never;\n\n// Generic driver class for doing chunk-wise asynchronous processing of a\n// FileList object. Subclass/clone it and override the `on*` functions to\n// make it do something useful.\nclass FileProcessor {\n files: File[];\n fileIndex = -1;\n // Currently need to use small chunk size because R-Websockets can't\n // handle continuation frames\n aborted = false;\n completed = false;\n\n constructor(files: FileList, exec$run = true) {\n this.files = Array.from(files);\n\n // TODO: Register error/abort callbacks\n if (exec$run) {\n this.$run();\n }\n }\n\n // Begin callbacks. Subclassers/cloners may override any or all of these.\n onBegin(files: File[], cont: () => void): void {\n files;\n setTimeout(cont, 0);\n }\n onFile(file: File, cont: () => void): void {\n file;\n setTimeout(cont, 0);\n }\n onComplete(): void {\n return;\n }\n onAbort(): void {\n return;\n }\n // End callbacks\n\n // Aborts processing, unless it's already completed\n abort(): void {\n if (this.completed || this.aborted) return;\n\n this.aborted = true;\n this.onAbort();\n }\n\n // Returns a bound function that will call this.$run one time.\n $getRun(): () => void {\n let called = false;\n\n return () => {\n if (called) return;\n called = true;\n this.$run();\n };\n }\n\n // This function will be called multiple times to advance the process.\n // It relies on the state of the object's fields to know what to do next.\n $run(): void {\n if (this.aborted || this.completed) return;\n\n if (this.fileIndex < 0) {\n // Haven't started yet--begin\n this.fileIndex = 0;\n this.onBegin(this.files, this.$getRun());\n return;\n }\n\n if (this.fileIndex === this.files.length) {\n // Just ended\n this.completed = true;\n this.onComplete();\n return;\n }\n\n // If we got here, then we have a file to process, or we are\n // in the middle of processing a file, or have just finished\n // processing a file.\n\n const file = this.files[this.fileIndex++];\n\n this.onFile(file, this.$getRun());\n }\n}\n\nclass FileUploader extends FileProcessor {\n shinyapp: ShinyApp;\n id: string;\n el: HTMLElement;\n\n jobId!: JobId;\n uploadUrl!: UploadUrl;\n progressBytes!: number;\n totalBytes!: number;\n\n constructor(\n shinyapp: ShinyApp,\n id: string,\n files: FileList,\n el: HTMLElement\n ) {\n // Init super with files, do not execute `this.$run()` before setting variables\n super(files, false);\n this.shinyapp = shinyapp;\n this.id = id;\n this.el = el;\n this.$run();\n }\n\n makeRequest(\n method: \"uploadInit\",\n args: Array>,\n onSuccess: (value: UploadInitValue) => void,\n onFailure: Parameters[3],\n blobs: Parameters[4]\n ): void;\n makeRequest(\n method: \"uploadEnd\",\n args: [string, string],\n // UploadEndValue can not be used as the type will not conform\n onSuccess: (value: unknown) => void,\n onFailure: Parameters[3],\n blobs: Parameters[4]\n ): void;\n makeRequest(\n method: string,\n args: unknown[],\n onSuccess: Parameters[2],\n onFailure: Parameters[3],\n blobs: Parameters[4]\n ): void {\n this.shinyapp.makeRequest(method, args, onSuccess, onFailure, blobs);\n }\n onBegin(files: File[], cont: () => void): void {\n // Reset progress bar\n this.$setError(null);\n this.$setActive(true);\n this.$setVisible(true);\n this.onProgress(null, 0);\n\n this.totalBytes = 0;\n this.progressBytes = 0;\n $.each(files, (i, file) => {\n this.totalBytes += file.size;\n });\n\n const fileInfo = $.map(files, function (file: File) {\n return {\n name: file.name,\n size: file.size,\n type: file.type,\n };\n });\n\n this.makeRequest(\n \"uploadInit\",\n [fileInfo],\n (response) => {\n this.jobId = response.jobId;\n this.uploadUrl = response.uploadUrl;\n cont();\n },\n (error) => {\n this.onError(error);\n },\n undefined\n );\n }\n onFile(file: File, cont: () => void): void {\n this.onProgress(file, 0);\n\n /* eslint-disable-next-line @typescript-eslint/no-floating-promises */\n $.ajax(this.uploadUrl, {\n type: \"POST\",\n cache: false,\n xhr: () => {\n if (typeof $.ajaxSettings.xhr !== \"function\")\n throw \"jQuery's XHR is not a function\";\n\n const xhrVal = $.ajaxSettings.xhr();\n\n if (xhrVal.upload) {\n xhrVal.upload.onprogress = (e) => {\n if (e.lengthComputable) {\n this.onProgress(\n file,\n (this.progressBytes + e.loaded) / this.totalBytes\n );\n }\n };\n }\n return xhrVal;\n },\n data: file,\n contentType: \"application/octet-stream\",\n processData: false,\n success: () => {\n this.progressBytes += file.size;\n cont();\n },\n error: (jqXHR, textStatus, errorThrown) => {\n errorThrown;\n this.onError(jqXHR.responseText || textStatus);\n },\n });\n }\n onComplete(): void {\n const fileInfo = $.map(this.files, function (file: File, i) {\n return {\n name: file.name,\n size: file.size,\n type: file.type,\n };\n i;\n });\n\n // Trigger shiny:inputchanged. Unlike a normal shiny:inputchanged event,\n // it's not possible to modify the information before the values get\n // sent to the server.\n const evt = triggerFileInputChanged(\n this.id,\n fileInfo,\n getFileInputBinding(),\n this.el,\n \"shiny.fileupload\",\n document\n );\n\n this.makeRequest(\n \"uploadEnd\",\n [this.jobId, this.id],\n () => {\n this.$setActive(false);\n this.onProgress(null, 1);\n this.$bar().text(\"Upload complete\");\n // Reset the file input's value to \"\". This allows the same file to be\n // uploaded again. https://stackoverflow.com/a/22521275\n $(evt.el as HTMLElement).val(\"\");\n },\n (error) => {\n this.onError(error);\n },\n undefined\n );\n this.$bar().text(\"Finishing upload\");\n }\n onError(message: string): void {\n this.$setError(message || \"\");\n this.$setActive(false);\n }\n onAbort(): void {\n this.$setVisible(false);\n }\n onProgress(file: File | null, completed: number): void {\n this.$bar().width(Math.round(completed * 100) + \"%\");\n this.$bar().text(file ? file.name : \"\");\n }\n $container(): JQuery {\n return $(\"#\" + $escape(this.id) + \"_progress.shiny-file-input-progress\");\n }\n $bar(): JQuery {\n return $(\n \"#\" +\n $escape(this.id) +\n \"_progress.shiny-file-input-progress .progress-bar\"\n );\n }\n $setVisible(visible: boolean): void {\n this.$container().css(\"visibility\", visible ? \"visible\" : \"hidden\");\n }\n $setError(error: string | null): void {\n this.$bar().toggleClass(\"progress-bar-danger\", error !== null);\n if (error !== null) {\n this.onProgress(null, 1);\n this.$bar().text(error);\n }\n }\n $setActive(active: boolean): void {\n this.$container().toggleClass(\"active\", !!active);\n }\n}\n\nexport { FileUploader };\nexport type { UploadInitValue, UploadEndValue };\n", "import $ from \"jquery\";\nimport type { FileInputBinding } from \"../bindings/input/fileinput\";\nimport type { ShinyEventInputChanged } from \"./shinyEvents\";\n\nfunction triggerFileInputChanged(\n name: string,\n value: unknown,\n binding: FileInputBinding,\n el: HTMLElement,\n inputType: string,\n onEl: typeof document\n): ShinyEventInputChanged {\n const evt = $.Event(\"shiny:inputchanged\") as ShinyEventInputChanged;\n\n evt.name = name;\n evt.value = value;\n evt.binding = binding;\n evt.el = el;\n evt.inputType = inputType;\n\n $(onEl).trigger(evt);\n\n return evt;\n}\n\nexport { triggerFileInputChanged };\n", "import type { ShinyClass } from \".\";\nimport type { FileInputBinding } from \"../bindings/input/fileinput\";\nimport type { OutputBindingAdapter } from \"../bindings/outputAdapter\";\nimport type { EventPriority } from \"../inputPolicies\";\nimport type { BindScope } from \"./bind\";\nimport type { Handler, ShinyApp } from \"./shinyapp\";\n\nlet fullShinyObj: FullShinyDef;\n\n// TODO-future; It would be nice to have a way to export this type value instead of / in addition to `Shiny`\ntype FullShinyDef = Required<\n Pick<\n ShinyClass,\n | \"bindAll\"\n | \"forgetLastInputValue\"\n | \"initializeInputs\"\n | \"oncustommessage\"\n | \"setInputValue\"\n | \"shinyapp\"\n | \"unbindAll\"\n | \"user\"\n >\n> &\n ShinyClass;\n\nfunction setShinyObj(shiny: ShinyClass): void {\n fullShinyObj = shiny as FullShinyDef;\n}\n\nfunction validateShinyHasBeenSet(): FullShinyDef {\n if (typeof fullShinyObj === \"undefined\") {\n throw \"Shiny has not finish initialization yet. Please wait for the 'shiny-initialized' event.\";\n }\n return fullShinyObj;\n}\n\n//// 2021/03: TypeScript Conversion note\n// These methods are here due to the delayed initialization of `Shiny.shinyapp`. I\n// In theory, there could be multiple instances of `shinyapp`. In practice (and implementation), this is not possible and is a 1:1 coupling with `window.Shiny`.\n// To avoid calls to a large Shiny object, helper methods are created to wrap around calling the fully instantiated window.Shiny value.\n// TODO-barret; Why is `initShiny()` delayed? Is this to allow users to shim in some code? Why can't it be defined in the init method (maybe w/ an extra trigger call?)\nfunction shinySetInputValue(\n name: string,\n value: unknown,\n opts?: { priority?: EventPriority }\n): void {\n validateShinyHasBeenSet().setInputValue(name, value, opts);\n}\nfunction shinyShinyApp(): ShinyApp {\n return validateShinyHasBeenSet().shinyapp;\n}\nfunction setShinyUser(user: string): void {\n validateShinyHasBeenSet().user = user;\n}\nfunction shinyForgetLastInputValue(name: string): void {\n validateShinyHasBeenSet().forgetLastInputValue(name);\n}\nasync function shinyBindAll(scope: BindScope): Promise {\n await validateShinyHasBeenSet().bindAll(scope);\n}\nfunction shinyUnbindAll(scope: BindScope, includeSelf = false): void {\n validateShinyHasBeenSet().unbindAll(scope, includeSelf);\n}\nfunction shinyInitializeInputs(scope: BindScope): void {\n validateShinyHasBeenSet().initializeInputs(scope);\n}\n\nasync function shinyAppBindOutput(\n id: string,\n binding: OutputBindingAdapter\n): Promise {\n await shinyShinyApp().bindOutput(id, binding);\n}\n\nfunction shinyAppUnbindOutput(\n id: string,\n binding: OutputBindingAdapter\n): boolean {\n return shinyShinyApp().unbindOutput(id, binding);\n}\n\nfunction getShinyOnCustomMessage(): Handler | null {\n return validateShinyHasBeenSet().oncustommessage;\n}\n\nlet fileInputBinding: FileInputBinding;\n\nfunction getFileInputBinding(): FileInputBinding {\n return fileInputBinding;\n}\nfunction setFileInputBinding(fileInputBinding_: FileInputBinding): void {\n fileInputBinding = fileInputBinding_;\n}\n\nfunction getShinyCreateWebsocket(): (() => WebSocket) | void {\n return validateShinyHasBeenSet().createSocket;\n}\n\nexport {\n setShinyObj,\n shinySetInputValue,\n shinyShinyApp,\n setShinyUser,\n shinyForgetLastInputValue,\n shinyBindAll,\n shinyUnbindAll,\n shinyInitializeInputs,\n shinyAppBindOutput,\n shinyAppUnbindOutput,\n getShinyOnCustomMessage,\n getFileInputBinding,\n setFileInputBinding,\n getShinyCreateWebsocket,\n};\n", "import $ from \"jquery\";\nimport { $escape, hasDefinedProperty, updateLabel } from \"../../utils\";\nimport { TextInputBindingBase } from \"./text\";\n\ntype NumberHTMLElement = HTMLInputElement;\n\ntype NumberReceiveMessageData = {\n label: string;\n value?: string | null;\n min?: string | null;\n max?: string | null;\n step?: string | null;\n};\n\nfunction getLabelNode(el: NumberHTMLElement): JQuery {\n return $(el)\n .parent()\n .find('label[for=\"' + $escape(el.id) + '\"]');\n}\n\nclass NumberInputBinding extends TextInputBindingBase {\n find(scope: HTMLElement): JQuery {\n // Inputs also have .shiny-input-number class\n return $(scope).find('input[type=\"number\"]');\n }\n\n getValue(\n el: NumberHTMLElement\n ): string[] | number | string | null | undefined {\n const numberVal = $(el).val();\n\n if (typeof numberVal == \"string\") {\n if (/^\\s*$/.test(numberVal))\n // Return null if all whitespace\n return null;\n }\n\n // If valid Javascript number string, coerce to number\n const numberValue = Number(numberVal);\n\n if (!isNaN(numberValue)) {\n return numberValue;\n }\n\n return numberVal; // If other string like \"1e6\", send it unchanged\n }\n setValue(el: NumberHTMLElement, value: number): void {\n el.value = \"\" + value;\n }\n getType(el: NumberHTMLElement): string {\n return \"shiny.number\";\n el;\n }\n receiveMessage(el: NumberHTMLElement, data: NumberReceiveMessageData): void {\n // Setting values to `\"\"` will remove the attribute value from the DOM element.\n // The attr key will still remain, but there is not value... ex: ` `\n if (hasDefinedProperty(data, \"value\")) el.value = data.value ?? \"\";\n if (hasDefinedProperty(data, \"min\")) el.min = data.min ?? \"\";\n if (hasDefinedProperty(data, \"max\")) el.max = data.max ?? \"\";\n if (hasDefinedProperty(data, \"step\")) el.step = data.step ?? \"\";\n\n updateLabel(data.label, getLabelNode(el));\n\n $(el).trigger(\"change\");\n }\n\n getState(el: NumberHTMLElement): {\n label: string;\n value: ReturnType;\n min: number;\n max: number;\n step: number;\n } {\n return {\n label: getLabelNode(el).text(),\n value: this.getValue(el),\n min: Number(el.min),\n max: Number(el.max),\n step: Number(el.step),\n };\n }\n}\n\nexport { NumberInputBinding };\nexport type { NumberReceiveMessageData };\n", "import $ from \"jquery\";\nimport { $escape, hasDefinedProperty, updateLabel } from \"../../utils\";\n\nimport { InputBinding } from \"./inputBinding\";\n\n// interface TextHTMLElement extends NameValueHTMLElement {\n// placeholder: any;\n// }\n\ntype TextHTMLElement = HTMLInputElement;\ntype TextReceiveMessageData = {\n label: string;\n value?: TextHTMLElement[\"value\"];\n placeholder?: TextHTMLElement[\"placeholder\"];\n};\n\nfunction getLabelNode(el: HTMLElement): JQuery {\n return $(el)\n .parent()\n .find('label[for=\"' + $escape(el.id) + '\"]');\n}\n\nclass TextInputBindingBase extends InputBinding {\n find(scope: HTMLElement): JQuery {\n const $inputs = $(scope).find(\n 'input[type=\"text\"], input[type=\"search\"], input[type=\"url\"], input[type=\"email\"]'\n );\n // selectize.js 0.12.4 inserts a hidden text input with an\n // id that ends in '-selectized'. The .not() selector below\n // is to prevent textInputBinding from accidentally picking up\n // this hidden element as a shiny input (#2396)\n //\n // Inputs also now have .shiny-input-text class\n return $inputs.not('input[type=\"text\"][id$=\"-selectized\"]');\n }\n\n getId(el: TextHTMLElement): string {\n return super.getId(el) || el.name;\n // return InputBinding.prototype.getId.call(this, el) || el.name;\n }\n\n getValue(el: TextHTMLElement): unknown {\n throw \"not implemented\";\n el;\n }\n setValue(el: TextHTMLElement, value: unknown): void {\n throw \"not implemented\";\n el;\n value;\n }\n\n subscribe(el: TextHTMLElement, callback: (x: boolean) => void): void {\n const $el = $(el);\n const updateOn = $el.data(\"update-on\") || \"change\";\n\n if (updateOn === \"change\") {\n $el.on(\n \"keyup.textInputBinding input.textInputBinding\",\n // event: Event\n function () {\n callback(true);\n }\n );\n } else if (updateOn === \"blur\") {\n $el.on(\"blur.textInputBinding\", function () {\n callback(false);\n });\n $el.on(\"keydown.textInputBinding\", function (event: JQuery.Event) {\n if (event.key !== \"Enter\") return;\n if ($el.is(\"textarea\")) {\n if (!(event.ctrlKey || event.metaKey)) return;\n }\n callback(false);\n });\n }\n\n $el.on(\"change.textInputBinding\", function () {\n if (updateOn === \"blur\" && $el.is(\":focus\")) {\n return;\n }\n callback(false);\n });\n }\n\n unsubscribe(el: TextHTMLElement): void {\n $(el).off(\".textInputBinding\");\n }\n\n receiveMessage(el: TextHTMLElement, data: unknown): void {\n throw \"not implemented\";\n el;\n data;\n }\n\n getState(el: TextHTMLElement): unknown {\n throw \"not implemented\";\n el;\n }\n\n getRatePolicy(el: HTMLElement): { policy: \"debounce\"; delay: 250 } {\n return {\n policy: \"debounce\",\n delay: 250,\n };\n el;\n }\n}\n\nclass TextInputBinding extends TextInputBindingBase {\n setValue(el: TextHTMLElement, value: string): void {\n el.value = value;\n }\n\n getValue(el: TextHTMLElement): TextHTMLElement[\"value\"] {\n return el.value;\n }\n\n getState(el: TextHTMLElement): {\n label: string;\n value: string;\n placeholder: string;\n } {\n return {\n label: getLabelNode(el).text(),\n value: el.value,\n placeholder: el.placeholder,\n };\n }\n receiveMessage(el: TextHTMLElement, data: TextReceiveMessageData): void {\n if (hasDefinedProperty(data, \"value\")) this.setValue(el, data.value);\n\n updateLabel(data.label, getLabelNode(el));\n\n if (hasDefinedProperty(data, \"placeholder\"))\n el.placeholder = data.placeholder;\n\n $(el).trigger(\"change\");\n }\n}\n\nexport { TextInputBinding, TextInputBindingBase };\nexport type { TextHTMLElement, TextReceiveMessageData };\n", "import $ from \"jquery\";\n\nimport { TextInputBinding } from \"./text\";\n\nclass PasswordInputBinding extends TextInputBinding {\n find(scope: HTMLElement): JQuery {\n // Inputs also have .shiny-input-password class\n return $(scope).find('input[type=\"password\"]');\n }\n\n getType(el: HTMLElement): string {\n return \"shiny.password\";\n el;\n }\n}\n\nexport { PasswordInputBinding };\n", "import $ from \"jquery\";\nimport { $escape, hasDefinedProperty, updateLabel } from \"../../utils\";\nimport { InputBinding } from \"./inputBinding\";\n\ntype RadioHTMLElement = HTMLInputElement;\n\ntype ValueLabelObject = {\n value: HTMLInputElement[\"value\"];\n label: string;\n};\n\ntype RadioReceiveMessageData = {\n value?: string | [];\n options?: ValueLabelObject[];\n label: string;\n};\n\n// Get the DOM element that contains the top-level label\nfunction getLabelNode(el: RadioHTMLElement): JQuery {\n return $(el)\n .parent()\n .find('label[for=\"' + $escape(el.id) + '\"]');\n}\n// Given an input DOM object, get the associated label. Handles labels\n// that wrap the input as well as labels associated with 'for' attribute.\nfunction getLabel(obj: HTMLElement): string | null {\n const parentNode = obj.parentNode as HTMLElement;\n\n // If label text \n if (parentNode.tagName === \"LABEL\") {\n return $(parentNode).find(\"span\").text().trim();\n }\n\n return null;\n}\n// Given an input DOM object, set the associated label. Handles labels\n// that wrap the input as well as labels associated with 'for' attribute.\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nfunction setLabel(obj: HTMLElement, value: string): null {\n const parentNode = obj.parentNode as HTMLElement;\n\n // If label text \n if (parentNode.tagName === \"LABEL\") {\n $(parentNode).find(\"span\").text(value);\n }\n\n return null;\n}\n\nclass RadioInputBinding extends InputBinding {\n find(scope: HTMLElement): JQuery {\n return $(scope).find(\".shiny-input-radiogroup\");\n }\n getValue(\n el: RadioHTMLElement\n ): string[] | number | string | null | undefined {\n // Select the radio objects that have name equal to the grouping div's id\n const checkedItems = $(\n 'input:radio[name=\"' + $escape(el.id) + '\"]:checked'\n );\n\n if (checkedItems.length === 0) {\n // If none are checked, the input will return null (it's the default on load,\n // but it wasn't emptied when calling updateRadioButtons with character(0)\n return null;\n }\n\n return checkedItems.val();\n }\n setValue(el: RadioHTMLElement, value: string | []): void {\n if (Array.isArray(value) && value.length === 0) {\n // Removing all checked item if the sent data is empty\n $('input:radio[name=\"' + $escape(el.id) + '\"]').prop(\"checked\", false);\n } else {\n $(\n 'input:radio[name=\"' +\n $escape(el.id) +\n '\"][value=\"' +\n $escape(value) +\n '\"]'\n ).prop(\"checked\", true);\n }\n }\n getState(el: RadioHTMLElement): {\n label: string;\n value: ReturnType;\n options: ValueLabelObject[];\n } {\n const $objs = $(\n 'input:radio[name=\"' + $escape(el.id) + '\"]'\n ) as JQuery;\n\n // Store options in an array of objects, each with with value and label\n const options = new Array($objs.length);\n\n for (let i = 0; i < options.length; i++) {\n options[i] = { value: $objs[i].value, label: getLabel($objs[i]) };\n }\n\n return {\n label: getLabelNode(el).text(),\n value: this.getValue(el),\n options: options,\n };\n }\n receiveMessage(el: RadioHTMLElement, data: RadioReceiveMessageData): void {\n const $el = $(el);\n // This will replace all the options\n\n if (hasDefinedProperty(data, \"options\")) {\n // Clear existing options and add each new one\n $el.find(\"div.shiny-options-group\").remove();\n // Backward compatibility: for HTML generated by shinybootstrap2 package\n $el.find(\"label.radio\").remove();\n // @ts-expect-error; TODO-barret; IDK what this line is doing\n // TODO-barret; Should this line be setting attributes instead?\n // `data.options` is an array of `{value, label}` objects\n $el.append(data.options);\n }\n\n if (hasDefinedProperty(data, \"value\")) {\n this.setValue(el, data.value);\n }\n\n updateLabel(data.label, getLabelNode(el));\n\n $(el).trigger(\"change\");\n }\n subscribe(el: RadioHTMLElement, callback: (x: boolean) => void): void {\n $(el).on(\"change.radioInputBinding\", function () {\n callback(false);\n });\n }\n unsubscribe(el: RadioHTMLElement): void {\n $(el).off(\".radioInputBinding\");\n }\n}\n\nexport { RadioInputBinding };\nexport type { RadioReceiveMessageData };\n", "import $ from \"jquery\";\nimport { $escape, hasDefinedProperty, updateLabel } from \"../../utils\";\nimport { indirectEval } from \"../../utils/eval\";\nimport { InputBinding } from \"./inputBinding\";\n\ntype SelectHTMLElement = HTMLSelectElement & { nonempty: boolean };\n\ntype SelectInputReceiveMessageData = {\n label: string;\n options?: string;\n config?: string;\n url?: string;\n value?: string;\n};\n\ntype SelectizeOptions = Selectize.IOptions;\ntype SelectizeInfo = Selectize.IApi & {\n settings: SelectizeOptions;\n};\n\nfunction getLabelNode(el: SelectHTMLElement): JQuery {\n let escapedId = $escape(el.id);\n\n if (isSelectize(el)) {\n escapedId += \"-selectized\";\n }\n return $(el)\n .parent()\n .parent()\n .find('label[for=\"' + escapedId + '\"]');\n}\n// Return true if it's a selectize input, false if it's a regular select input.\n// eslint-disable-next-line camelcase\nfunction isSelectize(el: HTMLElement): boolean {\n const config = $(el)\n .parent()\n .find('script[data-for=\"' + $escape(el.id) + '\"]');\n\n return config.length > 0;\n}\n\nclass SelectInputBinding extends InputBinding {\n find(scope: HTMLElement): JQuery {\n // Inputs also have .shiny-input-select class\n return $(scope).find(\"select\");\n }\n getType(el: HTMLElement): string | null {\n const $el = $(el);\n\n if (!$el.hasClass(\"symbol\")) {\n // default character type\n return null;\n }\n if ($el.attr(\"multiple\") === \"multiple\") {\n return \"shiny.symbolList\";\n } else {\n return \"shiny.symbol\";\n }\n }\n getId(el: SelectHTMLElement): string {\n return InputBinding.prototype.getId.call(this, el) || el.name;\n }\n getValue(el: SelectHTMLElement): any {\n if (!isSelectize(el)) {\n return $(el).val();\n } else {\n const selectize = this._selectize(el);\n\n return selectize?.getValue();\n }\n }\n setValue(el: SelectHTMLElement, value: string): void {\n if (!isSelectize(el)) {\n $(el).val(value);\n } else {\n const selectize = this._selectize(el);\n\n selectize?.setValue(value);\n }\n }\n getState(el: SelectHTMLElement): {\n label: JQuery;\n value: ReturnType;\n options: Array<{ value: string; label: string }>;\n } {\n // Store options in an array of objects, each with with value and label\n const options: Array<{ value: string; label: string }> = new Array(\n el.length\n );\n\n for (let i = 0; i < el.length; i++) {\n options[i] = {\n // TODO-barret; Is this a safe assumption?; Are there no Option Groups?\n value: (el[i] as HTMLOptionElement).value,\n label: el[i].label,\n };\n }\n\n return {\n label: getLabelNode(el),\n value: this.getValue(el),\n options: options,\n };\n }\n receiveMessage(\n el: SelectHTMLElement,\n data: SelectInputReceiveMessageData\n ): void {\n const $el = $(el);\n\n // This will replace all the options\n if (hasDefinedProperty(data, \"options\")) {\n const selectize = this._selectize(el);\n\n // Must destroy selectize before appending new options, otherwise\n // selectize will restore the original select\n selectize?.destroy();\n // Clear existing options and add each new one\n $el.empty().append(data.options);\n this._selectize(el);\n }\n\n // re-initialize selectize\n if (hasDefinedProperty(data, \"config\")) {\n $el\n .parent()\n .find('script[data-for=\"' + $escape(el.id) + '\"]')\n .replaceWith(data.config);\n this._selectize(el, true);\n }\n\n // use server-side processing for selectize\n if (hasDefinedProperty(data, \"url\")) {\n type CallbackFn = Parameters<\n NonNullable\n >[1];\n const selectize = this._selectize(el) as ReturnType<\n SelectInputBinding[\"_selectize\"]\n > & {\n settings: {\n load: (query: string, callback: CallbackFn) => any;\n };\n };\n\n // Calling selectize.clear() first works around https://github.com/selectize/selectize.js/issues/2146\n // As of selectize.js >= v0.13.1, .clearOptions() clears the selection,\n // but does NOT remove the previously-selected options. So unless we call\n // .clear() first, the current selection(s) will remain as (deselected)\n // options. See #3966 #4142\n selectize.clear();\n selectize.clearOptions();\n let loaded = false;\n\n selectize.settings.load = function (query: string, callback: CallbackFn) {\n const settings = selectize.settings;\n\n /* eslint-disable-next-line @typescript-eslint/no-floating-promises */\n $.ajax({\n url: data.url,\n data: {\n query: query,\n field: JSON.stringify([settings.searchField]),\n value: settings.valueField,\n conju: settings.searchConjunction,\n maxop: settings.maxOptions,\n },\n type: \"GET\",\n error: function () {\n callback();\n },\n success: function (res) {\n // res = [{label: '1', value: '1', group: '1'}, ...]\n // success is called after options are added, but\n // groups need to be added manually below\n $.each(res, function (index, elem) {\n // Call selectize.addOptionGroup once for each optgroup; the\n // first argument is the group ID, the second is an object with\n // the group's label and value. We use the current settings of\n // the selectize object to decide the fieldnames of that obj.\n const optgroupId = elem[settings.optgroupField || \"optgroup\"];\n const optgroup: { [key: string]: string } = {};\n\n optgroup[settings.optgroupLabelField || \"label\"] = optgroupId;\n optgroup[settings.optgroupValueField || \"value\"] = optgroupId;\n selectize.addOptionGroup(optgroupId, optgroup);\n });\n callback(res);\n if (!loaded) {\n if (hasDefinedProperty(data, \"value\")) {\n selectize.setValue(data.value as any);\n } else if (settings.maxItems === 1) {\n // first item selected by default only for single-select\n selectize.setValue(res[0].value);\n }\n }\n loaded = true;\n },\n });\n };\n // perform an empty search after changing the `load` function\n selectize.load(function (callback) {\n selectize.settings.load.apply(selectize, [\"\", callback]);\n });\n } else if (hasDefinedProperty(data, \"value\")) {\n this.setValue(el, data.value);\n }\n\n updateLabel(data.label, getLabelNode(el));\n\n $(el).trigger(\"change\");\n }\n subscribe(el: SelectHTMLElement, callback: (x: boolean) => void): void {\n $(el).on(\n \"change.selectInputBinding\",\n // event: Event\n () => {\n // https://github.com/rstudio/shiny/issues/2162\n // Prevent spurious events that are gonna be squelched in\n // a second anyway by the onItemRemove down below\n if (el.nonempty && this.getValue(el) === \"\") {\n return;\n }\n callback(false);\n }\n );\n }\n unsubscribe(el: HTMLElement): void {\n $(el).off(\".selectInputBinding\");\n }\n initialize(el: SelectHTMLElement): void {\n this._selectize(el);\n }\n protected _selectize(\n el: SelectHTMLElement,\n update = false\n ): SelectizeInfo | undefined {\n // Apps like 008-html do not have the selectize js library\n // Safe-guard against missing the selectize js library\n if (!$.fn.selectize) return undefined;\n const $el = $(el);\n const config = $el\n .parent()\n .find('script[data-for=\"' + $escape(el.id) + '\"]');\n\n if (config.length === 0) return undefined;\n\n let options: SelectizeOptions & {\n labelField: \"label\";\n valueField: \"value\";\n searchField: [\"label\"];\n onItemRemove?: (value: string) => void;\n onDropdownClose?: () => void;\n } = $.extend(\n {\n labelField: \"label\",\n valueField: \"value\",\n searchField: [\"label\"],\n },\n JSON.parse(config.html())\n );\n\n // selectize created from selectInput()\n if (typeof config.data(\"nonempty\") !== \"undefined\") {\n el.nonempty = true;\n options = $.extend(options, {\n onItemRemove: function (this: SelectizeInfo, value: string) {\n if (this.getValue() === \"\")\n $(\"select#\" + $escape(el.id))\n .empty()\n .append(\n $(\" \", {\n value: value,\n selected: true,\n })\n )\n .trigger(\"change\");\n },\n onDropdownClose:\n // $dropdown: any\n function (this: SelectizeInfo) {\n if (this.getValue() === \"\") {\n this.setValue($(\"select#\" + $escape(el.id)).val() as string);\n }\n },\n });\n } else {\n el.nonempty = false;\n }\n // options that should be eval()ed\n if (config.data(\"eval\") instanceof Array)\n $.each(config.data(\"eval\"), function (i, x: string) {\n /*jshint evil: true*/\n // @ts-expect-error; Need to type `options` keys to know exactly which values are accessed.\n options[x] = indirectEval(\"(\" + options[x] + \")\");\n });\n let control = $el.selectize(options)[0].selectize as SelectizeInfo;\n // .selectize() does not really update settings; must destroy and rebuild\n\n if (update) {\n const settings = $.extend(control.settings, options);\n\n control.destroy();\n control = $el.selectize(settings)[0].selectize as SelectizeInfo;\n }\n\n return control;\n }\n}\n\nexport { SelectInputBinding };\nexport type { SelectInputReceiveMessageData };\n", "//esbuild.github.io/content-types/#direct-eval\n//tl/dr;\n// * Direct usage of `eval(\"x\")` is bad with bundled code.\n// * Instead, use indirect calls to `eval` such as `indirectEval(\"x\")`\n// * Even just renaming the function works well enough.\n// > This is known as \"indirect eval\" because eval is not being called directly, and so does not trigger the grammatical special case for direct eval in the JavaScript VM. You can call indirect eval using any syntax at all except for an expression of the exact form eval('x'). For example, var eval2 = eval; eval2('x') and [eval][0]('x') and window.eval('x') are all indirect eval calls.\n// > When you use indirect eval, the code is evaluated in the global scope instead of in the inline scope of the caller.\n\nconst indirectEval = eval;\n\nexport { indirectEval };\n", "import type {\n IonRangeSliderEvent,\n IonRangeSliderOptions,\n} from \"ion-rangeslider\";\nimport $ from \"jquery\";\n// import { NameValueHTMLElement } from \".\";\nimport {\n $escape,\n formatDateUTC,\n hasDefinedProperty,\n updateLabel,\n} from \"../../utils\";\n\nimport type { TextHTMLElement } from \"./text\";\nimport { TextInputBindingBase } from \"./text\";\n\n// interface SliderHTMLElement extends NameValueHTMLElement {\n// checked?: any;\n// }\n\ntype TimeFormatter = (fmt: string, dt: Date) => string;\n// Backward compatible code for old-style jsliders (Shiny <= 0.10.2.2),\ntype LegacySlider = {\n canStepNext: () => boolean;\n stepNext: () => void;\n resetToStart: () => void;\n};\n\ntype SliderReceiveMessageData = {\n label: string;\n value?: Array | number | string;\n min?: number;\n max?: number;\n step?: number;\n // eslint-disable-next-line @typescript-eslint/naming-convention\n \"data-type\"?: string;\n // eslint-disable-next-line @typescript-eslint/naming-convention\n \"time-format\"?: string;\n timezone?: string;\n};\n\n// MUST use window.strftime as the javascript dependency is dynamic\n// and could be needed after shiny has initialized.\ndeclare global {\n interface Window {\n strftime: TimeFormatter & {\n utc: () => TimeFormatter;\n timezone: (timezone: string) => TimeFormatter;\n };\n }\n}\n\n// Necessary to get hidden sliders to send their updated values\nfunction forceIonSliderUpdate(slider: any) {\n if (slider.$cache && slider.$cache.input)\n slider.$cache.input.trigger(\"change\");\n else console.log(\"Couldn't force ion slider to update\");\n}\n\ntype Prettify = (num: number) => string;\nfunction getTypePrettifyer(\n dataType: string,\n timeFormat: string,\n timezone: string\n) {\n let timeFormatter: TimeFormatter;\n let prettify: Prettify;\n\n if (dataType === \"date\") {\n timeFormatter = window.strftime.utc();\n prettify = function (num) {\n return timeFormatter(timeFormat, new Date(num));\n };\n } else if (dataType === \"datetime\") {\n if (timezone) timeFormatter = window.strftime.timezone(timezone);\n else timeFormatter = window.strftime;\n\n prettify = function (num) {\n return timeFormatter(timeFormat, new Date(num));\n };\n } else {\n // The default prettify function for ion.rangeSlider adds thousands\n // separators after the decimal mark, so we have our own version here.\n // (#1958)\n prettify = function (this: IonRangeSliderOptions, num: number) {\n // When executed, `this` will refer to the `IonRangeSlider.options`\n // object.\n return formatNumber(num, this.prettify_separator);\n };\n }\n return prettify;\n}\n\nfunction getLabelNode(el: HTMLElement): JQuery {\n return $(el)\n .parent()\n .find('label[for=\"' + $escape(el.id) + '\"]');\n}\n// Number of values; 1 for single slider, 2 for range slider\nfunction numValues(el: HTMLElement): 1 | 2 {\n if ($(el).data(\"ionRangeSlider\").options.type === \"double\") return 2;\n else return 1;\n}\n\nclass SliderInputBinding extends TextInputBindingBase {\n find(scope: HTMLElement): JQuery {\n // Check if ionRangeSlider plugin is loaded\n if (!$.fn.ionRangeSlider) {\n // Return empty set of _found_ items\n return $();\n }\n\n return $(scope).find(\"input.js-range-slider\");\n }\n\n getType(el: HTMLElement): string | null {\n const dataType = $(el).data(\"data-type\");\n\n if (dataType === \"date\") return \"shiny.date\";\n else if (dataType === \"datetime\") return \"shiny.datetime\";\n else return null;\n }\n getValue(\n el: TextHTMLElement\n ): number | string | [number | string, number | string] {\n const $el = $(el);\n const result = $(el).data(\"ionRangeSlider\").result as IonRangeSliderEvent;\n\n // Function for converting numeric value from slider to appropriate type.\n let convert: (val: unknown) => number | string;\n const dataType = $el.data(\"data-type\");\n\n if (dataType === \"date\") {\n convert = function (val: unknown) {\n return formatDateUTC(new Date(Number(val)));\n };\n } else if (dataType === \"datetime\") {\n convert = function (val: unknown) {\n // Convert ms to s\n return Number(val) / 1000;\n };\n } else {\n convert = function (val: unknown) {\n return Number(val);\n };\n }\n\n if (numValues(el) === 2) {\n return [convert(result.from), convert(result.to)];\n } else {\n return convert(result.from);\n }\n }\n setValue(\n el: HTMLElement,\n value: number | string | [number | string, number | string]\n ): void {\n const $el = $(el);\n const slider = $el.data(\"ionRangeSlider\");\n\n $el.data(\"immediate\", true);\n try {\n if (numValues(el) === 2 && value instanceof Array) {\n slider.update({ from: value[0], to: value[1] });\n } else {\n slider.update({ from: value });\n }\n\n forceIonSliderUpdate(slider);\n } finally {\n $el.data(\"immediate\", false);\n }\n }\n subscribe(el: HTMLElement, callback: (x: boolean) => void): void {\n $(el).on(\"change.sliderInputBinding\", function () {\n callback(!$(el).data(\"immediate\") && !$(el).data(\"animating\"));\n });\n }\n unsubscribe(el: HTMLElement): void {\n $(el).off(\".sliderInputBinding\");\n }\n receiveMessage(el: HTMLElement, data: SliderReceiveMessageData): void {\n const $el = $(el);\n const slider = $el.data(\"ionRangeSlider\");\n const msg: {\n from?: number | string;\n to?: number | string;\n min?: number;\n max?: number;\n step?: number;\n prettify?: Prettify;\n } = {};\n\n if (hasDefinedProperty(data, \"value\")) {\n if (numValues(el) === 2 && data.value instanceof Array) {\n msg.from = data.value[0];\n msg.to = data.value[1];\n } else {\n if (Array.isArray(data.value)) {\n const errorReason = [\n \"an empty array.\",\n \"a single-value array.\",\n \"an array with more than two values.\",\n ];\n throw (\n \"Slider requires two values to update with an array, \" +\n \"but message value was \" +\n errorReason[Math.min(data.value.length, 2)]\n );\n }\n msg.from = data.value;\n }\n }\n\n const sliderFeatures: Array<\"max\" | \"min\" | \"step\"> = [\n \"min\",\n \"max\",\n \"step\",\n ];\n\n for (let i = 0; i < sliderFeatures.length; i++) {\n const feats = sliderFeatures[i];\n\n if (hasDefinedProperty(data, feats)) {\n msg[feats] = data[feats];\n }\n }\n\n updateLabel(data.label, getLabelNode(el));\n\n // (maybe) update data elements\n const domElements: Array<\"data-type\" | \"time-format\" | \"timezone\"> = [\n \"data-type\",\n \"time-format\",\n \"timezone\",\n ];\n\n for (let i = 0; i < domElements.length; i++) {\n const elem = domElements[i];\n\n if (hasDefinedProperty(data, elem)) {\n $el.data(elem, data[elem]);\n }\n }\n\n // retrieve latest data values\n const dataType = $el.data(\"data-type\");\n const timeFormat = $el.data(\"time-format\");\n const timezone = $el.data(\"timezone\");\n\n msg.prettify = getTypePrettifyer(dataType, timeFormat, timezone);\n\n $el.data(\"immediate\", true);\n try {\n slider.update(msg);\n forceIonSliderUpdate(slider);\n } finally {\n $el.data(\"immediate\", false);\n }\n }\n getRatePolicy(el: HTMLElement): { policy: \"debounce\"; delay: 250 } {\n return {\n policy: \"debounce\",\n delay: 250,\n };\n el;\n }\n // TODO-barret Why not implemented?\n getState(el: HTMLInputElement): void {\n // empty\n el;\n }\n\n initialize(el: HTMLElement): void {\n const $el = $(el);\n const dataType = $el.data(\"data-type\");\n const timeFormat = $el.data(\"time-format\");\n const timezone = $el.data(\"timezone\");\n\n const opts = {\n prettify: getTypePrettifyer(dataType, timeFormat, timezone),\n };\n\n $el.ionRangeSlider(opts);\n }\n}\n\n// Format numbers for nicer output.\n// formatNumber(1234567.12345) === \"1,234,567.12345\"\n// formatNumber(1234567.12345, \".\", \",\") === \"1.234.567,12345\"\n// formatNumber(1000, \" \") === \"1 000\"\n// formatNumber(20) === \"20\"\n// formatNumber(1.2345e24) === \"1.2345e+24\"\nfunction formatNumber(\n num: number,\n thousandSep = \",\",\n decimalSep = \".\"\n): string {\n const parts = num.toString().split(\".\");\n\n // Add separators to portion before decimal mark.\n parts[0] = parts[0].replace(\n /(\\d{1,3}(?=(?:\\d\\d\\d)+(?!\\d)))/g,\n \"$1\" + thousandSep\n );\n\n if (parts.length === 1) return parts[0];\n else if (parts.length === 2) return parts[0] + decimalSep + parts[1];\n else return \"\";\n}\n\n// TODO-barret ; this should be put in the \"init\" areas, correct?\n$(document).on(\"click\", \".slider-animate-button\", function (evt: Event) {\n evt.preventDefault();\n const self = $(this);\n const target = $(\"#\" + $escape(self.attr(\"data-target-id\") as string));\n const startLabel = \"Play\";\n const stopLabel = \"Pause\";\n const loop =\n self.attr(\"data-loop\") !== undefined &&\n !/^\\s*false\\s*$/i.test(self.attr(\"data-loop\") as string);\n let animInterval = self.attr(\"data-interval\") as number | string;\n\n if (isNaN(animInterval as number)) animInterval = 1500;\n else animInterval = Number(animInterval);\n\n if (!target.data(\"animTimer\")) {\n let timer;\n\n // Separate code paths:\n // Backward compatible code for old-style jsliders (Shiny <= 0.10.2.2),\n // and new-style ionsliders.\n if (target.hasClass(\"jslider\")) {\n const slider = target.slider() as unknown as LegacySlider;\n\n // If we're currently at the end, restart\n if (!slider.canStepNext()) slider.resetToStart();\n\n timer = setInterval(function () {\n if (loop && !slider.canStepNext()) {\n slider.resetToStart();\n } else {\n slider.stepNext();\n if (!loop && !slider.canStepNext()) {\n // TODO-barret replace with self.trigger(\"click\")\n self.click(); // stop the animation\n }\n }\n }, animInterval);\n } else {\n const slider = target.data(\"ionRangeSlider\");\n // Single sliders have slider.options.type == \"single\", and only the\n // `from` value is used. Double sliders have type == \"double\", and also\n // use the `to` value for the right handle.\n const sliderCanStep = function () {\n if (slider.options.type === \"double\")\n return slider.result.to < slider.result.max;\n else return slider.result.from < slider.result.max;\n };\n const sliderReset = function () {\n const val: { from: number; to?: number } = { from: slider.result.min };\n // Preserve the current spacing for double sliders\n\n if (slider.options.type === \"double\")\n val.to = val.from + (slider.result.to - slider.result.from);\n\n slider.update(val);\n forceIonSliderUpdate(slider);\n };\n const sliderStep = function () {\n // Don't overshoot the end\n const val: { from: number; to?: number } = {\n from: Math.min(\n slider.result.max,\n slider.result.from + slider.options.step\n ),\n };\n\n if (slider.options.type === \"double\")\n val.to = Math.min(\n slider.result.max,\n slider.result.to + slider.options.step\n );\n\n slider.update(val);\n forceIonSliderUpdate(slider);\n };\n\n // If we're currently at the end, restart\n if (!sliderCanStep()) sliderReset();\n\n timer = setInterval(function () {\n if (loop && !sliderCanStep()) {\n sliderReset();\n } else {\n sliderStep();\n if (!loop && !sliderCanStep()) {\n self.click(); // stop the animation\n }\n }\n }, animInterval);\n }\n\n target.data(\"animTimer\", timer);\n self.attr(\"title\", stopLabel);\n self.addClass(\"playing\");\n target.data(\"animating\", true);\n } else {\n clearTimeout(target.data(\"animTimer\"));\n target.removeData(\"animTimer\");\n self.attr(\"title\", startLabel);\n self.removeClass(\"playing\");\n target.removeData(\"animating\");\n }\n});\n\nexport { SliderInputBinding };\nexport type { SliderReceiveMessageData };\n", "import $ from \"jquery\";\nimport { hasDefinedProperty, isBS3 } from \"../../utils\";\nimport { InputBinding } from \"./inputBinding\";\n\ntype TabInputReceiveMessageData = { value?: string };\n\nfunction getTabName(anchor: JQuery): string {\n return anchor.attr(\"data-value\") || anchor.text();\n}\n\nclass BootstrapTabInputBinding extends InputBinding {\n find(scope: HTMLElement): JQuery {\n return $(scope).find(\"ul.nav.shiny-tab-input\");\n }\n getValue(el: HTMLElement): string | null {\n // prettier-ignore\n // The BS4+ selectors may not work as is for dropdowns within dropdowns, but BS3+ dropped support for those anyway\n const anchor = isBS3()\n ? $(el).find(\"li:not(.dropdown).active > a\")\n : $(el).find(\n \".nav-link:not(.dropdown-toggle).active, .dropdown-menu .dropdown-item.active\"\n );\n\n if (anchor.length === 1) return getTabName(anchor);\n\n return null;\n }\n setValue(el: HTMLElement, value: string | undefined): void {\n let success = false;\n\n if (value) {\n // prettier-ignore\n // The BS4+ selectors may not work as is for dropdowns within dropdowns, but BS3+ dropped support for those anyway\n const anchors = isBS3()\n ? $(el).find(\"li:not(.dropdown) > a\")\n : $(el).find(\n \".nav-link:not(.dropdown-toggle), .dropdown-menu .dropdown-item\"\n );\n\n anchors.each(function () {\n if (getTabName($(this)) === value) {\n $(this).tab(\"show\");\n success = true;\n return false; // Break out of each()\n }\n return;\n });\n }\n if (!success) {\n // This is to handle the case where nothing is selected, e.g. the last tab\n // was removed using removeTab.\n $(el).trigger(\"change\");\n }\n }\n getState(el: HTMLElement): { value: string | null } {\n return { value: this.getValue(el) };\n }\n receiveMessage(el: HTMLElement, data: TabInputReceiveMessageData): void {\n if (hasDefinedProperty(data, \"value\")) this.setValue(el, data.value);\n $(el).trigger(\"change\");\n }\n subscribe(el: HTMLElement, callback: (x: boolean) => void): void {\n $(el).on(\n \"change shown.bootstrapTabInputBinding shown.bs.tab.bootstrapTabInputBinding\",\n // event: Event\n function () {\n callback(false);\n }\n );\n }\n unsubscribe(el: HTMLElement): void {\n $(el).off(\".bootstrapTabInputBinding\");\n }\n}\n\nexport { BootstrapTabInputBinding };\nexport type { TabInputReceiveMessageData };\n", "import $ from \"jquery\";\n\nimport { TextInputBinding } from \"./text\";\n\n// When a textarea becomes visible, update the height\nconst intersectObserver = new IntersectionObserver((entries) => {\n entries.forEach((entry) => {\n if (entry.isIntersecting) {\n updateHeight(entry.target as HTMLInputElement);\n }\n });\n});\n\nclass TextareaInputBinding extends TextInputBinding {\n #inputHandler: EventListener | null = null;\n\n find(scope: HTMLElement): JQuery {\n // Inputs now also have the .shiny-input-textarea class\n return $(scope).find(\"textarea\");\n }\n\n initialize(el: HTMLInputElement): void {\n super.initialize(el);\n updateHeight(el);\n }\n\n subscribe(el: HTMLInputElement, callback: (x: boolean) => void): void {\n super.subscribe(el, callback);\n\n this.#inputHandler = (e) => updateHeight(e.target as HTMLInputElement);\n el.addEventListener(\"input\", this.#inputHandler);\n intersectObserver.observe(el);\n }\n\n unsubscribe(el: HTMLInputElement): void {\n super.unsubscribe(el);\n\n if (this.#inputHandler) el.removeEventListener(\"input\", this.#inputHandler);\n intersectObserver.unobserve(el);\n }\n}\n\nfunction updateHeight(el: HTMLInputElement) {\n if (!el.classList.contains(\"textarea-autoresize\")) {\n return;\n }\n if (el.scrollHeight == 0) {\n return;\n }\n el.style.height = \"auto\";\n el.style.height = el.scrollHeight + \"px\";\n}\n\nexport { TextareaInputBinding };\n", "import { BindingRegistry } from \"../registry\";\n\nimport { InputBinding } from \"./inputBinding\";\n\nimport { ActionButtonInputBinding } from \"./actionbutton\";\nimport { CheckboxInputBinding } from \"./checkbox\";\nimport { CheckboxGroupInputBinding } from \"./checkboxgroup\";\nimport { DateInputBinding } from \"./date\";\nimport { DateRangeInputBinding } from \"./daterange\";\nimport { FileInputBinding } from \"./fileinput\";\nimport { NumberInputBinding } from \"./number\";\nimport { PasswordInputBinding } from \"./password\";\nimport { RadioInputBinding } from \"./radio\";\nimport { SelectInputBinding } from \"./selectInput\";\nimport { SliderInputBinding } from \"./slider\";\nimport { BootstrapTabInputBinding } from \"./tabinput\";\nimport { TextInputBinding } from \"./text\";\nimport { TextareaInputBinding } from \"./textarea\";\n\n// TODO-barret make this an init method\nfunction initInputBindings(): {\n inputBindings: BindingRegistry;\n fileInputBinding: FileInputBinding;\n} {\n const inputBindings = new BindingRegistry();\n\n inputBindings.register(new TextInputBinding(), \"shiny.textInput\");\n inputBindings.register(new TextareaInputBinding(), \"shiny.textareaInput\");\n inputBindings.register(new PasswordInputBinding(), \"shiny.passwordInput\");\n inputBindings.register(new NumberInputBinding(), \"shiny.numberInput\");\n inputBindings.register(new CheckboxInputBinding(), \"shiny.checkboxInput\");\n inputBindings.register(\n new CheckboxGroupInputBinding(),\n \"shiny.checkboxGroupInput\"\n );\n inputBindings.register(new RadioInputBinding(), \"shiny.radioInput\");\n inputBindings.register(new SliderInputBinding(), \"shiny.sliderInput\");\n inputBindings.register(new DateInputBinding(), \"shiny.dateInput\");\n inputBindings.register(new DateRangeInputBinding(), \"shiny.dateRangeInput\");\n inputBindings.register(new SelectInputBinding(), \"shiny.selectInput\");\n inputBindings.register(\n new ActionButtonInputBinding(),\n \"shiny.actionButtonInput\"\n );\n inputBindings.register(\n new BootstrapTabInputBinding(),\n \"shiny.bootstrapTabInput\"\n );\n const fileInputBinding = new FileInputBinding();\n\n inputBindings.register(fileInputBinding, \"shiny.fileInputBinding\");\n\n return { inputBindings, fileInputBinding };\n}\n\nexport { initInputBindings, InputBinding };\n", "import $ from \"jquery\";\n\nimport { shinyUnbindAll } from \"../../shiny/initedMethods\";\nimport type { ErrorsMessageValue } from \"../../shiny/shinyapp\";\nimport { debounce } from \"../../time\";\nimport { escapeHTML } from \"../../utils\";\nimport { indirectEval } from \"../../utils/eval\";\nimport { OutputBinding } from \"./outputBinding\";\n\nclass DatatableOutputBinding extends OutputBinding {\n find(scope: HTMLElement): JQuery {\n return $(scope).find(\".shiny-datatable-output\");\n }\n onValueError(el: HTMLElement, err: ErrorsMessageValue): void {\n shinyUnbindAll(el);\n this.renderError(el, err);\n }\n renderValue(\n el: HTMLElement,\n data: {\n colnames?: string[];\n options?: {\n searching?: boolean;\n search?: { caseInsensitive?: boolean };\n // To be sent to data table;\n // Will copy in R value to this location\n escape?: string;\n } | null;\n escape?: string; // Incoming from R\n action?: string;\n evalOptions?: string[];\n callback?: string;\n searchDelay?: number;\n } | null\n ): void {\n const $el = $(el).empty();\n\n if (!data || !data.colnames) return;\n\n const colnames = $.makeArray(data.colnames);\n let header = $.map(colnames, function (x) {\n return \"\" + x + \" \";\n }).join(\"\");\n\n header = \"\" + header + \" \";\n let footer = \"\";\n\n if (data.options?.searching ?? true) {\n footer = $.map(colnames, function (x) {\n // placeholder needs to be escaped (and HTML tags are stripped off)\n return (\n ' ]+)>)/gi, \"\")) +\n '\" /> '\n );\n }).join(\"\");\n footer = \"\" + footer + \" \";\n }\n const content =\n '' +\n header +\n footer +\n \"
\";\n\n $el.append(content);\n\n // options that should be eval()ed\n if (data.evalOptions) {\n $.each(data.evalOptions, function (i, x) {\n /*jshint evil: true */\n // @ts-expect-error; If `evalOptions` is defined, `data.options` should be defined\n data.options[x] = indirectEval(\"(\" + data.options[x] + \")\");\n });\n }\n\n // caseInsensitive searching? default true\n const searchCI = data.options?.search?.caseInsensitive !== false;\n const oTable = $(el)\n .children(\"table\")\n .DataTable(\n $.extend(\n {\n processing: true,\n serverSide: true,\n order: [],\n orderClasses: false,\n pageLength: 25,\n ajax: {\n url: data.action,\n type: \"POST\",\n data: function (d: NonNullable) {\n d.search || (d.search = {});\n d.search.caseInsensitive = searchCI;\n // Copy from the R value (`data.escape`) to the escape option\n // (`d.escape`) similar to `data.options.escape`;\n // Note: this logic may be wrong, but the method is strongly\n // deprecated in favor of DT package. So users should not\n // naturally run this line of code\n d.escape = data.escape;\n },\n },\n },\n data.options\n )\n );\n // the table object may need post-processing\n\n if (typeof data.callback === \"string\") {\n /*jshint evil: true */\n const callback = indirectEval(\"(\" + data.callback + \")\");\n\n if (typeof callback === \"function\") callback(oTable);\n }\n\n // use debouncing for searching boxes\n $el\n .find(\"label input\")\n .first()\n .unbind(\"keyup\")\n .keyup(\n debounce(data.searchDelay, function (this: HTMLInputElement) {\n oTable.search(this.value).draw();\n })\n );\n const searchInputs = $el.find(\"tfoot input\");\n\n if (searchInputs.length > 0) {\n // this is a little weird: aoColumns/bSearchable are still in DT 1.10\n // https://github.com/DataTables/DataTables/issues/388\n $.each(oTable.settings()[0].aoColumns, function (i, x) {\n // hide the text box if not searchable\n if (!x.bSearchable) searchInputs.eq(i as number).hide();\n });\n searchInputs.keyup(\n debounce(data.searchDelay, function (this: HTMLInputElement) {\n oTable.column(searchInputs.index(this)).search(this.value).draw();\n })\n );\n }\n // FIXME: ugly scrollbars in tab panels b/c Bootstrap uses 'visible: auto'\n $el.parents(\".tab-content\").css(\"overflow\", \"visible\");\n }\n}\n\nexport { DatatableOutputBinding };\n", "import type { InputPolicy } from \"../inputPolicies\";\nimport type { InputRatePolicy } from \"../inputPolicies/inputRatePolicy\";\nimport type { AnyVoidFunction } from \"../utils/extraTypes\";\n\nclass Debouncer implements InputRatePolicy {\n target: InputPolicy | null;\n func: X;\n delayMs: number | undefined;\n timerId: ReturnType | null;\n args: Parameters | null;\n\n constructor(\n target: InputPolicy | null,\n func: X,\n delayMs: number | undefined\n ) {\n this.target = target;\n this.func = func;\n this.delayMs = delayMs;\n\n this.timerId = null;\n this.args = null;\n }\n\n normalCall(...args: Parameters): void {\n this.$clearTimer();\n this.args = args;\n\n this.timerId = setTimeout(() => {\n // IE8 doesn't reliably clear timeout, so this additional\n // check is needed\n if (this.timerId === null) return;\n this.$clearTimer();\n this.$invoke();\n }, this.delayMs);\n }\n immediateCall(...args: Parameters): void {\n this.$clearTimer();\n this.args = args;\n this.$invoke();\n }\n isPending(): boolean {\n return this.timerId !== null;\n }\n $clearTimer(): void {\n if (this.timerId !== null) {\n clearTimeout(this.timerId);\n this.timerId = null;\n }\n }\n $invoke(): void {\n if (this.args && this.args.length > 0) {\n this.func.apply(this.target, this.args);\n } else {\n this.func.apply(this.target);\n }\n this.args = null;\n }\n}\n\n// Returns a debounced version of the given function.\n// Debouncing means that when the function is invoked,\n// there is a delay of `threshold` milliseconds before\n// it is actually executed, and if the function is\n// invoked again before that threshold has elapsed then\n// the clock starts over.\n//\n// For example, if a function is debounced with a\n// threshold of 1000ms, then calling it 17 times at\n// 900ms intervals will result in a single execution\n// of the underlying function, 1000ms after the 17th\n// call.\nfunction debounce void>(\n threshold: number | undefined,\n func: T\n): (...args: Parameters) => void {\n let timerId: ReturnType | null = null;\n\n // Do not alter `function()` into an arrow function.\n // The `this` context needs to be dynamically bound\n return function thisFunc(...args: Parameters) {\n if (timerId !== null) {\n clearTimeout(timerId);\n timerId = null;\n }\n timerId = setTimeout(() => {\n // IE8 doesn't reliably clear timeout, so this additional\n // check is needed\n if (timerId === null) return;\n timerId = null;\n // Applying on `thisFunc` passes through the `this` context\n func.apply(thisFunc, args);\n }, threshold);\n };\n}\n\nexport { Debouncer, debounce };\n", "import type { InputPolicy } from \"../inputPolicies\";\nimport type { InputRatePolicy } from \"../inputPolicies/inputRatePolicy\";\nimport type { AnyVoidFunction } from \"../utils/extraTypes\";\n\nclass Invoker implements InputRatePolicy {\n target: InputPolicy | null;\n func: X;\n\n constructor(target: InputPolicy | null, func: X) {\n this.target = target;\n this.func = func;\n }\n\n // TODO-barret - Don't know how to define the method twice and still have access to \"this\"\n normalCall(...args: Parameters): void {\n this.func.apply(this.target, args);\n }\n immediateCall(...args: Parameters): void {\n this.func.apply(this.target, args);\n }\n}\n\nexport { Invoker };\n", "/* eslint-disable indent */\nimport type { InputPolicy } from \"../inputPolicies\";\nimport type { InputRatePolicy } from \"../inputPolicies/inputRatePolicy\";\nimport type { AnyVoidFunction } from \"../utils/extraTypes\";\n\nclass Throttler implements InputRatePolicy {\n target: InputPolicy | null;\n func: X;\n delayMs: number | undefined;\n timerId: ReturnType | null;\n args: Parameters | null;\n\n constructor(\n target: InputPolicy | null,\n func: X,\n delayMs: number | undefined\n ) {\n this.target = target;\n this.func = func;\n this.delayMs = delayMs;\n\n this.timerId = null;\n this.args = null;\n }\n\n // If no timer is currently running, immediately call the function and set the\n // timer; if a timer is running out, just queue up the args for the call when\n // the timer runs out. Later calls during the same timeout will overwrite\n // earlier ones.\n normalCall(...args: Parameters): void {\n // This will be an empty array (not null) if called without arguments, and\n // `[null]` if called with `null`.\n this.args = args;\n\n // Only invoke immediately if there isn't a timer running.\n if (this.timerId === null) {\n this.$invoke();\n }\n }\n\n // Reset the timer if active and call immediately\n immediateCall(...args: Parameters): void {\n this.$clearTimer();\n this.args = args;\n this.$invoke();\n }\n\n // Is there a call waiting to send?\n isPending(): boolean {\n return this.args !== null;\n }\n\n $clearTimer(): void {\n if (this.timerId !== null) {\n clearTimeout(this.timerId);\n this.timerId = null;\n }\n }\n\n // Invoke the throttled function with the currently-stored args and start the\n // timer.\n $invoke(): void {\n if (this.args === null) {\n // Shouldn't get here, because $invoke should only be called right after\n // setting this.args. But just in case.\n return;\n }\n\n this.func.apply(this.target, this.args);\n\n // Clear the stored args. This is used to track if a call is pending.\n this.args = null;\n\n // Set this.timerId to a newly-created timer, which will invoke a call with\n // the most recently called args (if any) when it expires.\n this.timerId = setTimeout(() => {\n // IE8 doesn't reliably clear timeout, so this additional check is needed\n if (this.timerId === null) return;\n\n this.$clearTimer();\n // Do we have a call queued up?\n if (this.isPending()) {\n // If so, invoke the call with queued args and reset timer.\n this.$invoke();\n }\n }, this.delayMs);\n }\n}\n\n// // Returns a throttled version of the given function.\n// // Throttling means that the underlying function will\n// // be executed no more than once every `threshold`\n// // milliseconds.\n// //\n// // For example, if a function is throttled with a\n// // threshold of 1000ms, then calling it 17 times at\n// // 900ms intervals will result in something like 15\n// // or 16 executions of the underlying function.\n// // eslint-disable-next-line no-unused-vars\n// function throttle(\n// threshold: number,\n// func: (...args: T[]) => void\n// ): (...args: T[]) => void {\n// let executionPending = false;\n// let timerId: number | null = null;\n// let self: unknown, args: T[] | null;\n\n// function throttled(...argumentVals: T[]) {\n// self = null;\n// args = null;\n// if (timerId === null) {\n// // Haven't seen a call recently. Execute now and\n// // start a timer to buffer any subsequent calls.\n// timerId = setTimeout(function () {\n// // When time expires, clear the timer; and if\n// // there has been a call in the meantime, repeat.\n// timerId = null;\n// if (executionPending) {\n// executionPending = false;\n// throttled.apply(self, args || []);\n// }\n// }, threshold);\n// func.apply(this, argumentVals);\n// } else {\n// // Something executed recently. Don't do anything\n// // except set up target/arguments to be called later\n// executionPending = true;\n// self = this as unknown;\n// args = argumentVals;\n// }\n// }\n// return throttled;\n// }\n\nexport {\n Throttler,\n // throttle\n};\n", "import $ from \"jquery\";\nimport type { ErrorsMessageValue } from \"../../shiny/shinyapp\";\nimport { asArray } from \"../../utils\";\n\nclass OutputBinding {\n name!: string;\n\n // Returns a jQuery object or element array that contains the\n // descendants of scope that match this binding\n find(scope: HTMLElement | JQuery): JQuery {\n throw \"Not implemented\";\n scope;\n }\n renderValue(el: HTMLElement, data: unknown): Promise | void {\n throw \"Not implemented\";\n el;\n data;\n }\n\n getId(el: HTMLElement): string {\n return el.getAttribute(\"data-input-id\") || el.id;\n }\n\n async onValueChange(el: HTMLElement, data: unknown): Promise {\n this.clearError(el);\n await this.renderValue(el, data);\n }\n onValueError(el: HTMLElement, err: ErrorsMessageValue): void {\n this.renderError(el, err);\n }\n renderError(el: HTMLElement, err: ErrorsMessageValue): void {\n this.clearError(el);\n if (err.message === \"\") {\n // not really error, but we just need to wait (e.g. action buttons)\n $(el).empty();\n return;\n }\n let errClass = \"shiny-output-error\";\n\n if (err.type !== null) {\n // use the classes of the error condition as CSS class names\n errClass =\n errClass +\n \" \" +\n $.map(asArray(err.type), function (type) {\n return errClass + \"-\" + type;\n }).join(\" \");\n }\n $(el).addClass(errClass).text(err.message);\n }\n clearError(el: HTMLElement): void {\n $(el).attr(\"class\", function (i, c) {\n return c.replace(/(^|\\s)shiny-output-error\\S*/g, \"\");\n });\n }\n showProgress(el: HTMLElement, show: boolean): void {\n const recalcClass = \"recalculating\";\n\n if (show) $(el).addClass(recalcClass);\n else $(el).removeClass(recalcClass);\n }\n}\n\nexport { OutputBinding };\n", "import $ from \"jquery\";\n\nimport { OutputBinding } from \"./outputBinding\";\n\nclass DownloadLinkOutputBinding extends OutputBinding {\n find(scope: HTMLElement): JQuery {\n return $(scope).find(\"a.shiny-download-link\");\n }\n renderValue(el: HTMLElement, data: string): void {\n el.setAttribute(\"href\", data);\n el.classList.remove(\"disabled\");\n el.removeAttribute(\"aria-disabled\");\n el.removeAttribute(\"tabindex\");\n }\n // Progress shouldn't be shown on the download button\n // (progress will be shown as a page level pulse instead)\n showProgress(el: HTMLElement, show: boolean): void {\n return;\n el;\n show;\n }\n}\n\ninterface FileDownloadEvent extends JQuery.Event {\n name: string;\n href: string;\n}\n\n// TODO-barret should this be in an init method?\n// Trigger shiny:filedownload event whenever a downloadButton/Link is clicked\n$(document).on(\n \"click.shinyDownloadLink\",\n \"a.shiny-download-link\",\n function (e: Event) {\n e;\n\n const evt: FileDownloadEvent = $.Event(\"shiny:filedownload\");\n\n evt.name = this.id;\n evt.href = this.href;\n $(document).trigger(evt);\n }\n);\n\nexport { DownloadLinkOutputBinding };\n", "import $ from \"jquery\";\n\nimport { shinyUnbindAll } from \"../../shiny/initedMethods\";\nimport { renderContentAsync } from \"../../shiny/render\";\nimport type { ErrorsMessageValue } from \"../../shiny/shinyapp\";\nimport { OutputBinding } from \"./outputBinding\";\n\nclass HtmlOutputBinding extends OutputBinding {\n find(scope: HTMLElement): JQuery {\n return $(scope).find(\".shiny-html-output\");\n }\n onValueError(el: HTMLElement, err: ErrorsMessageValue): void {\n shinyUnbindAll(el);\n this.renderError(el, err);\n }\n override async renderValue(\n el: HTMLElement,\n data: Parameters[1]\n ): Promise {\n await renderContentAsync(el, data);\n }\n}\n\nexport { HtmlOutputBinding };\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