\n// This content is searchable\n//
\n//\n// This content is not searchable (but still visible)\n//
\n// \n// This content is searchable\n// \n//{ "version": 3, "sources": ["../../../javascript/bc/global.js", "../../../javascript/bc/helpers/url_helpers.js", "../../../javascript/bc/helpers/performance_timeline.js", "../../../javascript/bc/helpers/object_helpers.js", "../../../javascript/bc/helpers/scroll_event_helpers.js", "../../../javascript/bc/helpers/sound_helpers.js", "../../../javascript/bc/helpers/array_helpers.js", "../../../javascript/bc/helpers/page_title_helpers.js", "../../../javascript/vendor/selector_observer.js", "../../../javascript/all/initializers/selector_observer.js", "../../../javascript/bc/helpers/remote_form_validation.js", "../../../javascript/bc/helpers/function_helpers.js", "../../../javascript/bc/helpers/turbolinks_helpers.js", "../../../javascript/bc/helpers/string_helpers.js", "../../../javascript/bc/helpers/templates.js", "../../../javascript/bc/helpers/request_helpers.js", "../../../javascript/bc/helpers/regexp_helpers.js", "../../../javascript/bc/helpers/platform_helpers.js", "../../../javascript/bc/helpers/document_helpers.js", "../../../javascript/bc/helpers/utf16_string.js", "../../../javascript/bc/helpers/animation_helpers.js", "../../../javascript/bc/helpers/element_helpers.js", "../../../javascript/bc/helpers/emoji_helper.js", "../../../javascript/all/behaviors/element/flash_notice.jst.ejs", "../../../javascript/bc/helpers/flash_message_helper.js", "../../../javascript/bc/helpers/event_helpers.js", "../../../javascript/bc/helpers/uuid.js", "../../../javascript/bc/helpers/focus_mode_helpers.js", "../../../javascript/bc/helpers/input_type_helpers.js", "../../../javascript/bc/helpers/dom_helpers.js", "../../../javascript/bc/helpers/debug_helpers.js", "../../../javascript/bc/helpers/ready.js", "../../../javascript/bc/helpers/keycodes.js", "../../../javascript/bc/helpers/color_helpers.js", "../../../javascript/lib/cookie.js", "../../../javascript/helpers/meta_helpers.js", "../../../javascript/bc/initializers/authenticity_token.js", "../../../javascript/bc/initializers/current.js", "../../../javascript/bc/initializers/env.js", "../../../javascript/bc/file_uploads/file_upload_preview_completion.js", "../../../javascript/bc/file_uploads/navigation_warning.js", "../../../javascript/bc/file_uploads/picker.js", "../../../javascript/bc/file_uploads/file_upload_manager.js", "../../../javascript/bc/file_uploads/file_upload.js", "../../../javascript/lib/direct_upload_error_reporting.js", "../../../javascript/bc/file_uploads/direct_file_upload.js", "../../../javascript/bc/file_uploads/index.js", "../../../javascript/bc/background_operation_monitor.js", "../../../javascript/bc/elements/sticky.js", "../../../javascript/bc/elements/pie_chart.js", "../../../javascript/bc/elements/content_filter/config.js", "../../../javascript/bc/elements/content_filter/element_index.js", "../../../javascript/bc/elements/content_filter/query_results.js", "../../../javascript/bc/elements/content_filter/controller.js", "../../../javascript/bc/elements/chat_room.js", "../../../javascript/bc/elements/require.js", "../../../javascript/bc/elements/infinite_page.js", "../../../javascript/helpers/timing_helpers.js", "../../../javascript/bc/elements/modal.js", "../../../javascript/bc/elements/content_filter.js", "../../../javascript/bc/elements/grouped_dates.js", "../../../javascript/bc/inputs/suggestion/select_element.js", "../../../javascript/bc/inputs/suggestion/option_element.js", "../../../javascript/bc/inputs/suggestion/controller.js", "../../../javascript/bc/inputs/suggestion/results_controller.js", "../../../javascript/bc/inputs/suggestion/context.js", "../../../javascript/bc/inputs/autocomplete/controller.js", "../../../javascript/bc/inputs/autocomplete/element.js", "../../../javascript/bc/inputs/autocomplete/templates/autocompletable.jst.ejs", "../../../javascript/bc/inputs/autocomplete/templates/suggestions.jst.ejs", "../../../javascript/bc/inputs/autocomplete/templates/status.jst.ejs", "../../../javascript/bc/inputs/autocomplete/helpers.js", "../../../javascript/bc/inputs/autocomplete/collection.js", "../../../javascript/bc/inputs/datetime/element.js", "../../../javascript/bc/inputs/segmented/controller.js", "../../../javascript/bc/inputs/segmented/element.js", "../../../javascript/bc/inputs/date/element.js", "../../../javascript/bc/inputs/time/element.js", "../../../javascript/bc/inputs/time/controller.js", "../../../javascript/bc/inputs/relative_date/element.js", "../../../javascript/bc/inputs/relative_date/picker_element.js", "../../../javascript/bc/inputs/relative_date/picker.jst.ejs", "../../../javascript/bc/inputs/relative_date/controller.js", "../../../javascript/bc/inputs/relative_date/picker_controller.js", "../../../javascript/bc/sortable/controller.js", "../../../javascript/bc/sortable/mouse_controller.js", "../../../javascript/bc/sortable/scroll_manager.js", "../../../javascript/bc/sortable/touch_controller.js", "../../../javascript/bc/sortable/index.js", "../../../javascript/sessions/populate_time_zone.js", "../../../javascript/all/behaviors/page/apply_email_validation_pattern.js"], "sourcesContent": ["window.BC = { Timeline: {} }\n", "BC.getQueryParams = function(location = window.location) {\n const params = {}\n const url = new URL(location.toString(), window.location.origin)\n for (const [key, value] of url.searchParams) {\n params[key] = value\n }\n return params\n}\n\nBC.formatURL = (url, params = {}) => expandPath(BC.formatPath(url, params))\n\nBC.formatPath = function(path, params = {}) {\n let queryString = $.param(params)\n if (queryString.length) {\n if (/\\?/.test(path)) {\n queryString = `&${queryString}`\n } else {\n queryString = `?${queryString}`\n }\n }\n\n return `${path}${queryString}`\n}\n\nBC.accountPath = (path, params = {}) => formatPathWithPrefix(path, BC.current.account.slugPath, params)\n\nBC.bucketPath = (path, params = {}) => formatPathWithPrefix(path, BC.current.bucket.path, params)\n\nBC.isUrlWithinCurrentAccount = (url) => {\n try {\n const path = new URL(url, document.baseURI).pathname\n const accountPath = BC.accountPath()\n return path === accountPath || path.startsWith(`${accountPath}/`)\n } catch {\n return false\n }\n}\n\nconst formatPathWithPrefix = function(path = \"\", prefix, params = {}) {\n if (prefix == null) { return }\n\n if (path.slice(0, prefix.length) !== prefix) {\n path = prefix + path\n }\n\n return BC.formatPath(path, params)\n}\n\nconst expandPath = function(path) {\n const element = document.createElement(\"a\")\n element.setAttribute(\"href\", path)\n return element.href\n}\n", "if (BC.debug && window.performance) {\n\n window.addEventListener(\"load\", () => BC.defer(function() {\n console.group(\"[Timeline]\")\n console.table(formatEntriesForTable(getPerformanceEntries()))\n return console.groupEnd()\n }))\n\n const windowMarkers = \"navigationStart requestStart responseEnd \\\n domLoading domInteractive domContentLoadedEventStart domContentLoadedEventEnd \\\n loadEventStart loadEventEnd\".split(\" \")\n\n const resourceMarkers = \"requestStart responseEnd\".split(\" \")\n const timing = window.performance.timing\n\n const getPerformanceEntries = function() {\n const entries = []\n\n const start = timing.navigationStart\n\n Array.from(windowMarkers).forEach((marker) => {\n const time = timing[marker] - start\n entries.push({time, marker})\n })\n\n Array.from(getTimingEntriesForResourceType(\"script\", \"link\", \"xmlhttprequest\", \"\")).forEach((timing) => {\n const resource = formatFilename(timing.name)\n\n Array.from(resourceMarkers).forEach((marker) => {\n const time = Math.round(timing[marker])\n entries.push({time, marker, resource})\n })\n })\n\n const loadTimes = window.chrome?.loadTimes?.()\n if (loadTimes) {\n const marker = \"firstPaintTime\"\n const time = Math.round((loadTimes[marker] * 1000) - start)\n entries.push({time, marker})\n }\n\n return entries.sort((a, b) => a.time - b.time)\n }\n\n const getTimingEntriesForResourceType = function(...types) {\n const entries = window.performance?.getEntries?.()\n if (entries) {\n return Array.from(entries).filter((entry) => Array.from(types).includes(entry.initiatorType))\n } else {\n return []\n }\n }\n\n const formatFilename = function(name) {\n const parts = name.split(\"/\")\n let filename = parts[parts.length - 1]\n if (filename.length > 20) {\n filename = `${filename.slice(0, 10)}\u2026${filename.slice(-10)}`\n }\n return filename\n }\n\n const formatEntriesForTable = function(entries) {\n const data = {}\n\n Array.from(entries).forEach(({time, resource, marker}) => {\n if (resource == null) { resource = \"window\" }\n data[`${time}ms`] = {marker, resource}\n })\n\n return data\n }\n}\n", "BC.extendObject = function(object, properties) {\n for (const key in properties) {\n const value = properties[key]\n object[key] = value\n }\n return object\n}\n\nBC.copyObject = object => BC.extendObject({}, object)\n\nBC.sliceObject = function(object, ...keys) {\n const result = {}\n\n Array.from(keys).forEach((key) => {\n result[key] = object[key] != null ? object[key] : null\n })\n\n return result\n}\n\nBC.objectsAreEqual = function(a = {}, b = {}) {\n if (Object.keys(a).length !== Object.keys(b).length) { return false }\n for (const key in a) {\n const value = a[key]\n if (value !== b[key]) { return false }\n }\n return true\n}\n", "BC.onScroll = function(callback) {\n const throttledCallback = _.throttle(callback, 200)\n const handler = () => requestAnimationFrame(throttledCallback)\n\n const install = function() {\n window.addEventListener(\"scroll\", handler)\n return document.addEventListener(\"bc-infinite-page:scroll\", handler)\n }\n\n const uninstall = function() {\n window.removeEventListener(\"scroll\", handler)\n return document.removeEventListener(\"bc-infinite-page:scroll\", handler)\n }\n\n install()\n return uninstall\n}\n", "const sounds = {}\n\nBC.playSound = (url) => {\n if (!sounds[url]) sounds[url] = new Audio(url)\n sounds[url].play()\n}\n\n", "BC.arrayWithout = function(array, object) {\n if (Array.isArray(object)) {\n return array.filter(value => !object.includes(value))\n } else {\n return array.filter(value => value !== object)\n }\n}\n\nBC.uniqueValues = function(array) {\n const set = new Set()\n Array.from(array).forEach(value => set.add(value))\n return Array.from(set)\n}\n", "BC.fetchTitleForURL = function(url) {\n const request = new BC.PageTitleRequest(url)\n return request.perform()\n}\n\n\nconst TIMEOUT = 5000\nBC.PageTitleRequest = class PageTitleRequest {\n\n static titleExtractors = {\n json(data) {\n return `${data.title} - ${data.bucket.name}`\n },\n\n document(document) {\n const page = BC.readObjectFromMetaTags(\"current-page\", document)\n let {\n title\n } = page\n if (title) {\n if (page.subtitle) { title += ` - ${page.subtitle}` }\n return title\n } else {\n return document.querySelector(\"title\")?.textContent\n }\n }\n }\n\n constructor(url) {\n this.xhrDidLoad = this.xhrDidLoad.bind(this)\n this.url = url\n this.xhr = this.createXHR()\n this.xhr.addEventListener(\"load\", this.xhrDidLoad)\n this.promise = new Promise(resolve => {\n this.resolve = resolve\n\n })\n }\n\n perform() {\n this.xhr?.send()\n return this.promise\n }\n\n abort() {\n this.xhr?.abort()\n this.xhr = null\n }\n\n // Private\n\n createXHR() {\n const {url, responseType} = this.getRequestDetails()\n const xhr = new XMLHttpRequest\n xhr.open(\"get\", url, true)\n xhr.timeout = TIMEOUT\n xhr.responseType = responseType\n return xhr\n }\n\n xhrDidLoad(event) {\n const title = this.getTitle()\n return this.resolve(title)\n }\n\n getTitle() {\n const {responseType, response} = this.getResponseDetails()\n return PageTitleRequest.titleExtractors[responseType](response)\n }\n\n getResponseDetails() {\n let {responseType, response} = this.xhr\n // Prop up lack of `xhr.responseType = \"json\"` support\n if (!responseType && (this.getRequestDetails().responseType === \"json\")) {\n responseType = \"json\"\n response = JSON.parse(response)\n }\n return {responseType, response}\n }\n\n getRequestDetails() {\n const dataURL = this.getURLForRecordingData()\n if (dataURL) {\n return {\n url: dataURL,\n responseType: \"json\"\n }\n } else {\n return {\n url: this.url,\n responseType: \"document\"\n }\n }\n }\n\n getURLForRecordingData() {\n const recordingId = this.getRecordingIdFromURL()\n if (recordingId) {\n const bucketId = this.getBucketIdFromURL()\n if (bucketId) {\n return BC.accountPath(`/buckets/${bucketId}/recordings/${recordingId}.json`)\n }\n }\n }\n\n getRecordingIdFromURL() {\n const hash = getHashFromURL(this.url)\n if (hash) {\n return hash.match(/recording_(\\d+)/)?.[1]\n }\n }\n\n getBucketIdFromURL() {\n return this.url.match(/buckets\\/(\\d+)\\//)?.[1]\n }\n}\n\nconst getHashFromURL = function(url) {\n const a = document.createElement(\"a\")\n a.href = url\n return a.hash\n}\n", "window.SelectorObserver = class SelectorObserver {\n constructor() {\n this.mutationObserver = new MutationObserver(document.documentElement, this);\n this.definitionsBySelector = new SelectorSet;\n this.definitionsByElement = new WeakMap;\n }\n\n observe(selector, definition) {\n if (typeof definition === \"function\") {\n definition = {selectorMatched: definition};\n }\n\n this.definitionsBySelector.add(selector, definition);\n\n afterDOMContentLoaded(() => {\n for (const element of document.querySelectorAll(selector)) {\n const definitions = this.getDefinitionsForElement(element);\n if (!definitions.has(definition)) {\n definitions.add(definition);\n definition.selectorMatched?.(element);\n }\n }\n });\n }\n\n matchElement(element) {\n return this.definitionsBySelector.matches(element);\n }\n\n findMatchingElementsInTree(element) {\n return this.definitionsBySelector.findMatchingElementsInTree(element);\n }\n\n elementAttached(element) {\n const definitions = this.getDefinitionsForElement(element);\n this.definitionsBySelector.matchEachSelector(element, function(selector, definition, matched) {\n if (matched && !definitions.has(definition)) {\n definitions.add(definition);\n definition.selectorMatched?.(element);\n }\n });\n }\n\n elementAttributeChanged(element, attributeName) {\n const definitions = this.getDefinitionsForElement(element);\n this.definitionsBySelector.matchEachSelector(element, function(selector, definition, matched) {\n const present = definitions.has(definition);\n if (matched && !present) {\n definitions.add(definition);\n definition.selectorMatched?.(element);\n } else if (present && !matched) {\n definitions.delete(definition);\n definition.selectorUnmatched?.(element);\n }\n });\n }\n\n elementDetached(element) {\n const definitions = this.getDefinitionsForElement(element);\n for (const definition of definitions) {\n definition.selectorUnmatched?.(element);\n }\n definitions.clear();\n }\n\n getDefinitionsForElement(element) {\n let definitions = this.definitionsByElement.get(element);\n if (!definitions) {\n this.definitionsByElement.set(element, (definitions = new Set));\n }\n return definitions;\n }\n};\n\n\nclass MutationObserver {\n constructor(element, definition) {\n this.processMutations = this.processMutations.bind(this);\n this.element = element;\n this.definition = definition;\n this.elements = new Set;\n this.observer = new window.MutationObserver(this.processMutations);\n this.observer.observe(this.element, {attributes: true, childList: true, subtree: true});\n }\n\n processMutations(mutations) {\n for (const mutation of mutations) {\n switch (mutation.type) {\n case \"attributes\":\n this.processAttributeChange(mutation.target, mutation.attributeName);\n break;\n case \"childList\":\n if (mutation.removedNodes.length) this.processRemovedNodes(mutation.removedNodes);\n if (mutation.addedNodes.length) this.processAddedNodes(mutation.addedNodes);\n break;\n }\n }\n }\n\n processAttributeChange(element, attributeName) {\n if (this.elements.has(element)) {\n if (this.nodeIsProcessable(element)) {\n this.definition.elementAttributeChanged?.(element, attributeName);\n } else {\n this.removeElement(element);\n }\n } else if (this.nodeIsProcessable(element)) {\n this.addElement(element);\n }\n }\n\n processRemovedNodes(nodes) {\n for (const node of nodes) {\n this.forEachProcessableElementInTree(node, this.removeElement);\n }\n }\n\n processAddedNodes(nodes) {\n for (const node of nodes) {\n this.forEachProcessableElementInTree(node, this.addElement);\n }\n }\n\n nodeIsProcessable(node) {\n return (node.nodeType === Node.ELEMENT_NODE) && this.definition.matchElement?.(node);\n }\n\n forEachProcessableElementInTree(parentElement, callback) {\n for (const element of this.definition.findMatchingElementsInTree?.(parentElement) ?? []) {\n callback.call(this, element);\n }\n }\n\n addElement(element) {\n // There is an edge cases where a detached element is passed as argument, to protect from this make sure\n // that the element to add exists in the DOM before adding it.\n //\n // See https://3.basecamp.com/2914079/buckets/29375700/todos/5454764252\n if (!this.elements.has(element) && document.contains(element)) {\n this.elements.add(element);\n this.definition.elementAttached?.(element);\n }\n }\n\n removeElement(element) {\n if (this.elements.has(element)) {\n this.elements.delete(element);\n this.definition.elementDetached?.(element);\n }\n }\n}\n\n\nclass SelectorSet {\n constructor() {\n this.values = {};\n this.compositeSelector = null;\n }\n\n add(selector, value) {\n const values = this.values[selector] ?? [];\n if (value != null && !values.includes(value)) {\n this.values[selector] = [...values, value];\n this.compositeSelector = null;\n }\n return value;\n }\n\n remove(selector, value) {\n if (value != null) {\n this.values[selector] = (this.values[selector] ?? []).filter(v => v !== value);\n this.compositeSelector = null;\n }\n return value;\n }\n\n get(selector) {\n return this.values[selector] ?? [];\n }\n\n matchEachSelector(element, callback) {\n for (const selector in this.values) {\n const matched = elementMatchesSelector(element, selector);\n for (const value of this.values[selector]) {\n callback(selector, value, matched);\n }\n }\n }\n\n matches(element) {\n return elementMatchesSelector(element, this.getCompositeSelector());\n }\n\n findMatchingElementsInTree(element) {\n const composite = this.getCompositeSelector();\n const result = this.matches(element) ? [element] : [];\n for (const child of element?.querySelectorAll?.(composite) ?? []) {\n result.push(child);\n }\n return result;\n }\n\n getCompositeSelector() {\n return this.compositeSelector ??= Object.keys(this.values).join(\", \");\n }\n}\n\n\nfunction elementMatchesSelector(element, selector) {\n return element?.nodeType === Node.ELEMENT_NODE && element.matches(selector);\n}\n\nvar afterDOMContentLoaded = (function() {\n const readyCallbacks = [];\n\n document.addEventListener(\"DOMContentLoaded\", () => {\n let callback;\n while ((callback = readyCallbacks.shift())) {\n callback();\n }\n }, false);\n\n return function(callback) {\n if (document.readyState === \"loading\") {\n readyCallbacks.push(callback);\n } else {\n requestAnimationFrame(callback);\n }\n };\n})();\n", "import \"../../vendor/selector_observer\"\n\nBC.selectorObserver = new window.SelectorObserver()\n\nBC.observe = (selector, definition) => BC.selectorObserver.observe(selector, definition)\n", "import \"../../all/initializers/selector_observer\"\n\nBC.observe(\"form[data-behavior~=remote_form_validation]\", form => new RemoteFormValidator(form))\n\nclass RemoteFormValidator {\n constructor(form) {\n this.onInput = this.onInput.bind(this)\n this.onSubmit = this.onSubmit.bind(this)\n this.$form = $(form)\n this.inputs = {}\n\n this.setInputs()\n this.installEventHandlers()\n }\n\n setInputs() {\n return Array.from(this.$form.find(\"[data-validation-name]\")).map((input) =>\n (this.inputs[input.dataset.validationName] = input))\n }\n\n installEventHandlers() {\n this.$form.on(\"input\", this.onInput)\n return this.$form.on(\"submit\", this.onSubmit)\n }\n\n onInput(event) {\n return this.resetElement(event.target)\n }\n\n onSubmit(event) {\n event.preventDefault()\n\n const $submit = this.$form.find(\":submit\")\n $submit.prop(\"disabled\", true)\n\n return this.validate(valid => {\n if (valid) {\n return this.$form[0].submit()\n } else {\n return $submit.prop(\"disabled\", false)\n }\n })\n }\n\n invalidateElement(element, message) {\n $(element).addClass(\"error\")\n return $(element).closest(\"div\").find(\"[data-role~=error_message]\").text(message)\n }\n\n resetElement(element) {\n $(element).removeClass(\"error\")\n return $(element).closest(\"div\").find(\"[data-role~=error_message]\").empty()\n }\n\n validate(callback) {\n if (!this.validateRequiredInputs()) { return callback(false) }\n return this.performValidationRequest(errors => {\n for (const name in this.inputs) {\n const element = this.inputs[name]\n const message = errors[name]?.[0]\n if (message) {\n this.invalidateElement(element, message)\n }\n }\n return callback(this.isValid())\n })\n }\n\n validateRequiredInputs() {\n for (const required of Array.from(this.$form.find(\"[required]\"))) {\n if (!required.value.trim()) { return false }\n }\n return true\n }\n\n performValidationRequest(callback) {\n return $.post(this.$form.data(\"validation-url\"), this.$form.serialize(), data => callback(data.errors))\n }\n\n isValid() {\n return this.$form.find(\".error\").length === 0\n }\n}\n", "BC.defer = callback => setTimeout(callback, 1)\n\nBC.synchronize = function(fn) {\n const monitorCallbacks = new WeakMap\n return function(callback) {\n let callbacks = monitorCallbacks.get(this)\n if (!callbacks) {\n monitorCallbacks.set(this, (callbacks = []))\n }\n callbacks.push(callback)\n\n if (callbacks.length === 1) {\n return fn.call(this, () => {\n Array.from(callbacks).forEach((callback) => { callback?.() })\n return monitorCallbacks.delete(this)\n })\n }\n }\n}\n\nBC.measure =\n (window.performance?.now != null) ?\n function(block) {\n const started = performance.now()\n block()\n return performance.now() - started\n }\n :\n function(block) {\n const started = new Date().getTime()\n block()\n return new Date().getTime() - started\n }\n\nBC.throttle = function(fn) {\n let request = null\n return function(...args) {\n if (request) return request\n request = requestAnimationFrame(() => {\n request = null\n return fn.apply(this, args)\n })\n }\n}\n\nBC.throttleWithDelay = function(fn, delay = 0) {\n let throttled = false\n return function(...args) {\n if (!throttled) {\n throttled = true\n fn.apply(this, args)\n\n setTimeout(() => throttled = false, delay)\n }\n }\n}\n\nBC.debounce = function(fn, delay = 10) {\n let timeoutId = null\n const debounced = function(...args) {\n const callback = () => fn.apply(this, args)\n clearTimeout(timeoutId)\n timeoutId = setTimeout(callback, delay)\n }\n debounced.cancel = () => clearTimeout(timeoutId)\n return debounced\n}\n", "BC.displayingTurboPreview = () => document.documentElement.dataset.turboPreview != null\n", "/* eslint-disable\n no-useless-escape,\n*/\nexport function camelize(dashString) {\n const element = document.createElement(\"span\")\n element.setAttribute(`data-${dashString}`, \"\")\n return Object.keys(element.dataset)[0]\n}\n\nexport function dasherize(camelString) {\n const element = document.createElement(\"span\")\n element.dataset[camelString] = \"\"\n return element.attributes[0]?.name.slice(5)\n}\n\nexport function normalize(string) {\n return string.normalize(\"NFKD\").replace(/\\p{Diacritic}/gu, \"\")\n}\n\nexport const regexpEscape = string => string.toString().replace(/[\\-\\[\\]\\/\\{\\}\\(\\)\\*\\+\\?\\.\\\\\\^\\$\\|]/g, \"\\\\$&\")\n\nexport function stringIsEntirelyEmoji(string) {\n if (!string?.trim()) { return false }\n const content = string.replace(BC.EMOJI_PATTERN, \"\")\n return !content.match(/\\S/)\n}\n\nexport function truncate(string, length, omission = \"\u2026\") {\n if (string.length <= length) {\n return string\n } else {\n return string.slice(0, length - omission.length) + omission\n }\n}\n\nexport function toSentence(strings) {\n if (strings.length === 0) {\n return \"\"\n } else if (strings.length === 1) {\n return strings[0]\n } else {\n return strings.slice(0, -1).join(\", \") + \" and \" + strings.at(-1)\n }\n}\n\nBC.camelize = camelize\nBC.dasherize = dasherize\nBC.normalize = normalize\nBC.regexpEscape = regexpEscape\nBC.stringIsEntirelyEmoji = stringIsEntirelyEmoji\nBC.truncate = truncate\nBC.toSentence = toSentence\n", "// Keep in sync with Template::RelativeDates::BASE_DATE\nBC.TEMPLATE_BASE_DATE = CalendarDate.parse(\"1978-01-01\")\n", "BC.fetch = function(url, options = {}) {\n let fetch, headers\n if (!url?.length) { throw new TypeError(\"Missing required URL argument\") }\n\n const contentType = options.contentType != null ? options.contentType : getContentTypeForBody(options.body)\n const responseType = options.as != null ? options.as : \"text\"\n\n const fetchOptions = {\n credentials: options.credentials != null ? options.credentials : \"same-origin\",\n method: options.method != null ? options.method : \"get\",\n headers: (headers = BC.copyObject(options.headers != null ? options.headers : {})),\n body: ((options.body != null) ? options.body : undefined),\n keepalive: options.keepalive\n }\n\n if (options.signal) {\n fetchOptions.signal = options.signal\n }\n\n headers[\"X-Requested-With\"] = \"XMLHttpRequest\"\n headers[\"X-CSRF-Token\"] = BC.getAuthenticityToken()\n if (contentType != null) { headers[\"Content-Type\"] = contentType }\n\n if ((responseType === \"json\") || (contentType === \"application/json\")) {\n if (headers.Accept == null) { headers.Accept = \"application/json\" }\n }\n\n if (nativeFetchIsBuggyForMethod(fetchOptions.method)) {\n headers[\"X-Fetch-Type\"] = \"polyfill-forced\"\n fetch = window.fetchPolyfill\n } else {\n headers[\"X-Fetch-Type\"] = fetchIsNative() ? \"native\" : \"polyfill\";\n ({\n fetch\n } = window)\n }\n\n return fetch(url, fetchOptions)\n .then(checkStatus)\n .then(parseBody(responseType))\n}\n\nconst nativeFetchIsBuggyForMethod = function(method = \"\") {\n return fetchIsNative() &&\n /Windows.*Edge/.test(navigator.userAgent) &&\n [\"put\", \"patch\"].includes(method.toLowerCase())\n}\n\nconst fetchIsNative = () => window.fetch !== window.fetchPolyfill\n\nconst getContentTypeForBody = function(body) {\n if ((body == null) || body instanceof FormData) {\n return null\n } else if (body instanceof File) {\n return body.type\n } else {\n return \"application/octet-stream\"\n }\n}\n\nconst checkStatus = function(response) {\n let authUrl\n if (200 <= response.status && response.status < 300) {\n return response\n } else if ((response.status === 401) && (authUrl = response.getResponseHeader(\"WWW-Authenticate\"))) {\n return window.location.replace(authUrl)\n } else {\n const error = new Error(response.statusText)\n error.response = response\n throw error\n }\n}\n\nconst parseBody = responseType => (function(response) {\n switch (responseType) {\n case \"blob\": return response.blob()\n case \"json\": return response.json()\n case \"text\": return response.text()\n }\n})\n\nBC.xhr = function(url, options = {}) {\n if (!url?.length) { throw new TypeError(\"Missing required URL argument\") }\n const xhr = new XMLHttpRequest\n const method = options.method != null ? options.method : \"get\"\n xhr.open(method, url, true)\n xhr.setRequestHeader(\"X-Requested-With\", \"XMLHttpRequest\")\n xhr.setRequestHeader(\"X-CSRF-Token\", BC.getAuthenticityToken())\n if (options.contentType != null) { xhr.setRequestHeader(\"Content-Type\", options.contentType) }\n if (options.credentials) { xhr.withCredentials = true }\n return xhr\n}\n", "/* eslint-disable\n no-useless-escape,\n no-misleading-character-class,\n*/\n// Unicode letter, number, and punctuation regular expressions generated by XRegExp\n\nBC.LETTER_PATTERN = /[\\u0041-\\u005A\\u0061-\\u007A\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u0527\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0620-\\u064A\\u066E\\u066F\\u0671-\\u06D3\\u06D5\\u06E5\\u06E6\\u06EE\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4\\u07F5\\u07FA\\u0800-\\u0815\\u081A\\u0824\\u0828\\u0840-\\u0858\\u08A0\\u08A2-\\u08AC\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971-\\u0977\\u0979-\\u097F\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC\\u09DD\\u09DF-\\u09E1\\u09F0\\u09F1\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0\\u0AE1\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C33\\u0C35-\\u0C39\\u0C3D\\u0C58\\u0C59\\u0C60\\u0C61\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDE\\u0CE0\\u0CE1\\u0CF1\\u0CF2\\u0D05-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D\\u0D4E\\u0D60\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32\\u0E33\\u0E40-\\u0E46\\u0E81\\u0E82\\u0E84\\u0E87\\u0E88\\u0E8A\\u0E8D\\u0E94-\\u0E97\\u0E99-\\u0E9F\\u0EA1-\\u0EA3\\u0EA5\\u0EA7\\u0EAA\\u0EAB\\u0EAD-\\u0EB0\\u0EB2\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC-\\u0EDF\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8C\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F4\\u1401-\\u166C\\u166F-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u1700-\\u170C\\u170E-\\u1711\\u1720-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1877\\u1880-\\u18A8\\u18AA\\u18B0-\\u18F5\\u1900-\\u191C\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19C1-\\u19C7\\u1A00-\\u1A16\\u1A20-\\u1A54\\u1AA7\\u1B05-\\u1B33\\u1B45-\\u1B4B\\u1B83-\\u1BA0\\u1BAE\\u1BAF\\u1BBA-\\u1BE5\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1CE9-\\u1CEC\\u1CEE-\\u1CF1\\u1CF5\\u1CF6\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u209C\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2183\\u2184\\u2C00-\\u2C2E\\u2C30-\\u2C5E\\u2C60-\\u2CE4\\u2CEB-\\u2CEE\\u2CF2\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005\\u3006\\u3031-\\u3035\\u303B\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312D\\u3131-\\u318E\\u31A0-\\u31BA\\u31F0-\\u31FF\\u3400-\\u4DB5\\u4E00-\\u9FCC\\uA000-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A\\uA62B\\uA640-\\uA66E\\uA67F-\\uA697\\uA6A0-\\uA6E5\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA78E\\uA790-\\uA793\\uA7A0-\\uA7AA\\uA7F8-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA8F2-\\uA8F7\\uA8FB\\uA90A-\\uA925\\uA930-\\uA946\\uA960-\\uA97C\\uA984-\\uA9B2\\uA9CF\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAA60-\\uAA76\\uAA7A\\uAA80-\\uAAAF\\uAAB1\\uAAB5\\uAAB6\\uAAB9-\\uAABD\\uAAC0\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEA\\uAAF2-\\uAAF4\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uABC0-\\uABE2\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC]/\n\nBC.NUMBER_PATTERN = /[\\u0030-\\u0039\\u00B2\\u00B3\\u00B9\\u00BC-\\u00BE\\u0660-\\u0669\\u06F0-\\u06F9\\u07C0-\\u07C9\\u0966-\\u096F\\u09E6-\\u09EF\\u09F4-\\u09F9\\u0A66-\\u0A6F\\u0AE6-\\u0AEF\\u0B66-\\u0B6F\\u0B72-\\u0B77\\u0BE6-\\u0BF2\\u0C66-\\u0C6F\\u0C78-\\u0C7E\\u0CE6-\\u0CEF\\u0D66-\\u0D75\\u0E50-\\u0E59\\u0ED0-\\u0ED9\\u0F20-\\u0F33\\u1040-\\u1049\\u1090-\\u1099\\u1369-\\u137C\\u16EE-\\u16F0\\u17E0-\\u17E9\\u17F0-\\u17F9\\u1810-\\u1819\\u1946-\\u194F\\u19D0-\\u19DA\\u1A80-\\u1A89\\u1A90-\\u1A99\\u1B50-\\u1B59\\u1BB0-\\u1BB9\\u1C40-\\u1C49\\u1C50-\\u1C59\\u2070\\u2074-\\u2079\\u2080-\\u2089\\u2150-\\u2182\\u2185-\\u2189\\u2460-\\u249B\\u24EA-\\u24FF\\u2776-\\u2793\\u2CFD\\u3007\\u3021-\\u3029\\u3038-\\u303A\\u3192-\\u3195\\u3220-\\u3229\\u3248-\\u324F\\u3251-\\u325F\\u3280-\\u3289\\u32B1-\\u32BF\\uA620-\\uA629\\uA6E6-\\uA6EF\\uA830-\\uA835\\uA8D0-\\uA8D9\\uA900-\\uA909\\uA9D0-\\uA9D9\\uAA50-\\uAA59\\uABF0-\\uABF9\\uFF10-\\uFF19]/\n\nBC.PUNCTUATION_PATTERN = /[\\u0021-\\u0023\\u0025-\\u002A\\u002C-\\u002F\\u003A\\u003B\\u003F\\u0040\\u005B-\\u005D\\u005F\\u007B\\u007D\\u00A1\\u00A7\\u00AB\\u00B6\\u00B7\\u00BB\\u00BF\\u037E\\u0387\\u055A-\\u055F\\u0589\\u058A\\u05BE\\u05C0\\u05C3\\u05C6\\u05F3\\u05F4\\u0609\\u060A\\u060C\\u060D\\u061B\\u061E\\u061F\\u066A-\\u066D\\u06D4\\u0700-\\u070D\\u07F7-\\u07F9\\u0830-\\u083E\\u085E\\u0964\\u0965\\u0970\\u0AF0\\u0DF4\\u0E4F\\u0E5A\\u0E5B\\u0F04-\\u0F12\\u0F14\\u0F3A-\\u0F3D\\u0F85\\u0FD0-\\u0FD4\\u0FD9\\u0FDA\\u104A-\\u104F\\u10FB\\u1360-\\u1368\\u1400\\u166D\\u166E\\u169B\\u169C\\u16EB-\\u16ED\\u1735\\u1736\\u17D4-\\u17D6\\u17D8-\\u17DA\\u1800-\\u180A\\u1944\\u1945\\u1A1E\\u1A1F\\u1AA0-\\u1AA6\\u1AA8-\\u1AAD\\u1B5A-\\u1B60\\u1BFC-\\u1BFF\\u1C3B-\\u1C3F\\u1C7E\\u1C7F\\u1CC0-\\u1CC7\\u1CD3\\u2010-\\u2027\\u2030-\\u2043\\u2045-\\u2051\\u2053-\\u205E\\u207D\\u207E\\u208D\\u208E\\u2329\\u232A\\u2768-\\u2775\\u27C5\\u27C6\\u27E6-\\u27EF\\u2983-\\u2998\\u29D8-\\u29DB\\u29FC\\u29FD\\u2CF9-\\u2CFC\\u2CFE\\u2CFF\\u2D70\\u2E00-\\u2E2E\\u2E30-\\u2E3B\\u3001-\\u3003\\u3008-\\u3011\\u3014-\\u301F\\u3030\\u303D\\u30A0\\u30FB\\uA4FE\\uA4FF\\uA60D-\\uA60F\\uA673\\uA67E\\uA6F2-\\uA6F7\\uA874-\\uA877\\uA8CE\\uA8CF\\uA8F8-\\uA8FA\\uA92E\\uA92F\\uA95F\\uA9C1-\\uA9CD\\uA9DE\\uA9DF\\uAA5C-\\uAA5F\\uAADE\\uAADF\\uAAF0\\uAAF1\\uABEB\\uFD3E\\uFD3F\\uFE10-\\uFE19\\uFE30-\\uFE52\\uFE54-\\uFE61\\uFE63\\uFE68\\uFE6A\\uFE6B\\uFF01-\\uFF03\\uFF05-\\uFF0A\\uFF0C-\\uFF0F\\uFF1A\\uFF1B\\uFF1F\\uFF20\\uFF3B-\\uFF3D\\uFF3F\\uFF5B\\uFF5D\\uFF5F-\\uFF65]/\n\n// https://github.com/basecamp/emoji-regexp\nBC.EMOJI_PATTERN = /(?:(?:[\\#\\*0-9](?:\\uFE0F\\u20E3?))|(?:[\\u00A9\\u00AE\\u203C\\u2049\\u2122\\u2139\\u2194-\\u2199\\u21A9\\u21AA\\u2328\\u23CF\\u23ED-\\u23EF\\u23F1\\u23F2\\u23F8-\\u23FA\\u24C2\\u25AA\\u25AB\\u25B6\\u25C0\\u25FB\\u25FC\\u2600-\\u2604\\u260E\\u2611\\u2618\\u2620\\u2622\\u2623\\u2626\\u262A\\u262E\\u262F\\u2638-\\u263A\\u2640\\u2642\\u265F\\u2660\\u2663\\u2665\\u2666\\u2668\\u267B\\u267E\\u2692\\u2694-\\u2697\\u2699\\u269B\\u269C\\u26A0\\u26A7\\u26B0\\u26B1\\u26C8\\u26CF\\u26D1\\u26D3\\u26E9\\u26F0\\u26F1\\u26F4\\u26F7\\u26F8\\u2702\\u2708\\u2709\\u270F\\u2712\\u2714\\u2716\\u271D\\u2721\\u2733\\u2734\\u2744\\u2747\\u2763\\u27A1\\u2934\\u2935\\u2B05-\\u2B07\\u3030\\u303D\\u3297\\u3299]\\uFE0F)|(?:[\\u261D\\u270C\\u270D](?:(?:\\uD83C[\\uDFFB-\\uDFFF])|\\uFE0F))|(?:[\\u270A\\u270B](?:\\uD83C[\\uDFFB-\\uDFFF])?)|(?:\\u26F9(?:(?:\\uD83C(?:[\\uDFFB-\\uDFFF](?:\\u200D(?:[\\u2640\\u2642]\\uFE0F))?))|(?:\\uFE0F(?:\\u200D(?:[\\u2640\\u2642]\\uFE0F))?)))|(?:\\u2764(?:\\uFE0F(?:\\u200D(?:(?:\\uD83D\\uDD25)|(?:\\uD83E\\uDE79)))?))|(?:\\uD83C(?:(?:[\\uDD70\\uDD71\\uDD7E\\uDD7F\\uDE02\\uDE37\\uDF21\\uDF24-\\uDF2C\\uDF36\\uDF7D\\uDF96\\uDF97\\uDF99-\\uDF9B\\uDF9E\\uDF9F\\uDFCD\\uDFCE\\uDFD4-\\uDFDF\\uDFF5\\uDFF7]\\uFE0F)|(?:[\\uDF85\\uDFC2\\uDFC7](?:\\uD83C[\\uDFFB-\\uDFFF])?)|(?:[\\uDFC3\\uDFC4\\uDFCA](?:(?:\\u200D(?:[\\u2640\\u2642]\\uFE0F))|(?:\\uD83C(?:[\\uDFFB-\\uDFFF](?:\\u200D(?:[\\u2640\\u2642]\\uFE0F))?)))?)|(?:[\\uDFCB\\uDFCC](?:(?:\\uD83C(?:[\\uDFFB-\\uDFFF](?:\\u200D(?:[\\u2640\\u2642]\\uFE0F))?))|(?:\\uFE0F(?:\\u200D(?:[\\u2640\\u2642]\\uFE0F))?)))|(?:\\uDDE6(?:\\uD83C[\\uDDE8-\\uDDEC\\uDDEE\\uDDF1\\uDDF2\\uDDF4\\uDDF6-\\uDDFA\\uDDFC\\uDDFD\\uDDFF])?)|(?:\\uDDE7(?:\\uD83C[\\uDDE6\\uDDE7\\uDDE9-\\uDDEF\\uDDF1-\\uDDF4\\uDDF6-\\uDDF9\\uDDFB\\uDDFC\\uDDFE\\uDDFF])?)|(?:\\uDDE8(?:\\uD83C[\\uDDE6\\uDDE8\\uDDE9\\uDDEB-\\uDDEE\\uDDF0-\\uDDF5\\uDDF7\\uDDFA-\\uDDFF])?)|(?:\\uDDE9(?:\\uD83C[\\uDDEA\\uDDEC\\uDDEF\\uDDF0\\uDDF2\\uDDF4\\uDDFF])?)|(?:\\uDDEA(?:\\uD83C[\\uDDE6\\uDDE8\\uDDEA\\uDDEC\\uDDED\\uDDF7-\\uDDFA])?)|(?:\\uDDEB(?:\\uD83C[\\uDDEE-\\uDDF0\\uDDF2\\uDDF4\\uDDF7])?)|(?:\\uDDEC(?:\\uD83C[\\uDDE6\\uDDE7\\uDDE9-\\uDDEE\\uDDF1-\\uDDF3\\uDDF5-\\uDDFA\\uDDFC\\uDDFE])?)|(?:\\uDDED(?:\\uD83C[\\uDDF0\\uDDF2\\uDDF3\\uDDF7\\uDDF9\\uDDFA])?)|(?:\\uDDEE(?:\\uD83C[\\uDDE8-\\uDDEA\\uDDF1-\\uDDF4\\uDDF6-\\uDDF9])?)|(?:\\uDDEF(?:\\uD83C[\\uDDEA\\uDDF2\\uDDF4\\uDDF5])?)|(?:\\uDDF0(?:\\uD83C[\\uDDEA\\uDDEC-\\uDDEE\\uDDF2\\uDDF3\\uDDF5\\uDDF7\\uDDFC\\uDDFE\\uDDFF])?)|(?:\\uDDF1(?:\\uD83C[\\uDDE6-\\uDDE8\\uDDEE\\uDDF0\\uDDF7-\\uDDFB\\uDDFE])?)|(?:\\uDDF2(?:\\uD83C[\\uDDE6\\uDDE8-\\uDDED\\uDDF0-\\uDDFF])?)|(?:\\uDDF3(?:\\uD83C[\\uDDE6\\uDDE8\\uDDEA-\\uDDEC\\uDDEE\\uDDF1\\uDDF4\\uDDF5\\uDDF7\\uDDFA\\uDDFF])?)|(?:\\uDDF4(?:\\uD83C\\uDDF2)?)|(?:\\uDDF5(?:\\uD83C[\\uDDE6\\uDDEA-\\uDDED\\uDDF0-\\uDDF3\\uDDF7-\\uDDF9\\uDDFC\\uDDFE])?)|(?:\\uDDF6(?:\\uD83C\\uDDE6)?)|(?:\\uDDF7(?:\\uD83C[\\uDDEA\\uDDF4\\uDDF8\\uDDFA\\uDDFC])?)|(?:\\uDDF8(?:\\uD83C[\\uDDE6-\\uDDEA\\uDDEC-\\uDDF4\\uDDF7-\\uDDF9\\uDDFB\\uDDFD-\\uDDFF])?)|(?:\\uDDF9(?:\\uD83C[\\uDDE6\\uDDE8\\uDDE9\\uDDEB-\\uDDED\\uDDEF-\\uDDF4\\uDDF7\\uDDF9\\uDDFB\\uDDFC\\uDDFF])?)|(?:\\uDDFA(?:\\uD83C[\\uDDE6\\uDDEC\\uDDF2\\uDDF3\\uDDF8\\uDDFE\\uDDFF])?)|(?:\\uDDFB(?:\\uD83C[\\uDDE6\\uDDE8\\uDDEA\\uDDEC\\uDDEE\\uDDF3\\uDDFA])?)|(?:\\uDDFC(?:\\uD83C[\\uDDEB\\uDDF8])?)|(?:\\uDDFD(?:\\uD83C\\uDDF0)?)|(?:\\uDDFE(?:\\uD83C[\\uDDEA\\uDDF9])?)|(?:\\uDDFF(?:\\uD83C[\\uDDE6\\uDDF2\\uDDFC])?)|(?:\\uDFF3(?:\\uFE0F(?:\\u200D(?:(?:\\u26A7\\uFE0F)|(?:\\uD83C\\uDF08)))?))|(?:\\uDFF4(?:(?:\\u200D(?:\\u2620\\uFE0F))|(?:\\uDB40(?:\\uDC67(?:\\uDB40(?:\\uDC62(?:\\uDB40(?:(?:\\uDC65(?:\\uDB40(?:\\uDC6E(?:\\uDB40(?:\\uDC67(?:\\uDB40\\uDC7F))))))|(?:\\uDC73(?:\\uDB40(?:\\uDC63(?:\\uDB40(?:\\uDC74(?:\\uDB40\\uDC7F))))))|(?:\\uDC77(?:\\uDB40(?:\\uDC6C(?:\\uDB40(?:\\uDC73(?:\\uDB40\\uDC7F)))))))))))))?)|[\\uDC04\\uDCCF\\uDD8E\\uDD91-\\uDD9A\\uDE01\\uDE1A\\uDE2F\\uDE32-\\uDE36\\uDE38-\\uDE3A\\uDE50\\uDE51\\uDF00-\\uDF20\\uDF2D-\\uDF35\\uDF37-\\uDF7C\\uDF7E-\\uDF84\\uDF86-\\uDF93\\uDFA0-\\uDFC1\\uDFC5\\uDFC6\\uDFC8\\uDFC9\\uDFCF-\\uDFD3\\uDFE0-\\uDFF0\\uDFF8-\\uDFFF]))|(?:\\uD83D(?:(?:[\\uDC3F\\uDCFD\\uDD49\\uDD4A\\uDD6F\\uDD70\\uDD73\\uDD76-\\uDD79\\uDD87\\uDD8A-\\uDD8D\\uDDA5\\uDDA8\\uDDB1\\uDDB2\\uDDBC\\uDDC2-\\uDDC4\\uDDD1-\\uDDD3\\uDDDC-\\uDDDE\\uDDE1\\uDDE3\\uDDE8\\uDDEF\\uDDF3\\uDDFA\\uDECB\\uDECD-\\uDECF\\uDEE0-\\uDEE5\\uDEE9\\uDEF0\\uDEF3]\\uFE0F)|(?:[\\uDC42\\uDC43\\uDC46-\\uDC50\\uDC66\\uDC67\\uDC6B-\\uDC6D\\uDC72\\uDC74-\\uDC76\\uDC78\\uDC7C\\uDC83\\uDC85\\uDC8F\\uDC91\\uDCAA\\uDD7A\\uDD95\\uDD96\\uDE4C\\uDE4F\\uDEC0\\uDECC](?:\\uD83C[\\uDFFB-\\uDFFF])?)|(?:[\\uDC6E\\uDC70\\uDC71\\uDC73\\uDC77\\uDC81\\uDC82\\uDC86\\uDC87\\uDE45-\\uDE47\\uDE4B\\uDE4D\\uDE4E\\uDEA3\\uDEB4-\\uDEB6](?:(?:\\u200D(?:[\\u2640\\u2642]\\uFE0F))|(?:\\uD83C(?:[\\uDFFB-\\uDFFF](?:\\u200D(?:[\\u2640\\u2642]\\uFE0F))?)))?)|(?:[\\uDD74\\uDD90](?:(?:\\uD83C[\\uDFFB-\\uDFFF])|\\uFE0F))|(?:\\uDC08(?:\\u200D\\u2B1B)?)|(?:\\uDC15(?:\\u200D(?:\\uD83E\\uDDBA))?)|(?:\\uDC3B(?:\\u200D(?:\\u2744\\uFE0F))?)|(?:\\uDC41(?:\\uFE0F(?:\\u200D(?:\\uD83D(?:\\uDDE8\\uFE0F)))?))|(?:\\uDC68(?:(?:\\u200D(?:(?:[\\u2695\\u2696\\u2708]\\uFE0F)|(?:\\u2764(?:\\uFE0F(?:\\u200D(?:\\uD83D(?:(?:\\uDC8B(?:\\u200D(?:\\uD83D\\uDC68)))|\\uDC68)))))|(?:\\uD83C[\\uDF3E\\uDF73\\uDF7C\\uDF93\\uDFA4\\uDFA8\\uDFEB\\uDFED])|(?:\\uD83D(?:(?:[\\uDC68\\uDC69](?:\\u200D(?:\\uD83D(?:(?:\\uDC66(?:\\u200D(?:\\uD83D\\uDC66))?)|(?:\\uDC67(?:\\u200D(?:\\uD83D[\\uDC66\\uDC67]))?)))))|(?:\\uDC66(?:\\u200D(?:\\uD83D\\uDC66))?)|(?:\\uDC67(?:\\u200D(?:\\uD83D[\\uDC66\\uDC67]))?)|[\\uDCBB\\uDCBC\\uDD27\\uDD2C\\uDE80\\uDE92]))|(?:\\uD83E[\\uDDAF-\\uDDB3\\uDDBC\\uDDBD])))|(?:\\uD83C(?:(?:\\uDFFB(?:\\u200D(?:(?:[\\u2695\\u2696\\u2708]\\uFE0F)|(?:\\u2764(?:\\uFE0F(?:\\u200D(?:\\uD83D(?:(?:\\uDC68(?:\\uD83C[\\uDFFB-\\uDFFF]))|(?:\\uDC8B(?:\\u200D(?:\\uD83D(?:\\uDC68(?:\\uD83C[\\uDFFB-\\uDFFF]))))))))))|(?:\\uD83C[\\uDF3E\\uDF73\\uDF7C\\uDF93\\uDFA4\\uDFA8\\uDFEB\\uDFED])|(?:\\uD83D[\\uDCBB\\uDCBC\\uDD27\\uDD2C\\uDE80\\uDE92])|(?:\\uD83E(?:(?:\\uDD1D(?:\\u200D(?:\\uD83D(?:\\uDC68(?:\\uD83C[\\uDFFC-\\uDFFF])))))|[\\uDDAF-\\uDDB3\\uDDBC\\uDDBD]))))?)|(?:\\uDFFC(?:\\u200D(?:(?:[\\u2695\\u2696\\u2708]\\uFE0F)|(?:\\u2764(?:\\uFE0F(?:\\u200D(?:\\uD83D(?:(?:\\uDC68(?:\\uD83C[\\uDFFB-\\uDFFF]))|(?:\\uDC8B(?:\\u200D(?:\\uD83D(?:\\uDC68(?:\\uD83C[\\uDFFB-\\uDFFF]))))))))))|(?:\\uD83C[\\uDF3E\\uDF73\\uDF7C\\uDF93\\uDFA4\\uDFA8\\uDFEB\\uDFED])|(?:\\uD83D[\\uDCBB\\uDCBC\\uDD27\\uDD2C\\uDE80\\uDE92])|(?:\\uD83E(?:(?:\\uDD1D(?:\\u200D(?:\\uD83D(?:\\uDC68(?:\\uD83C[\\uDFFB\\uDFFD-\\uDFFF])))))|[\\uDDAF-\\uDDB3\\uDDBC\\uDDBD]))))?)|(?:\\uDFFD(?:\\u200D(?:(?:[\\u2695\\u2696\\u2708]\\uFE0F)|(?:\\u2764(?:\\uFE0F(?:\\u200D(?:\\uD83D(?:(?:\\uDC68(?:\\uD83C[\\uDFFB-\\uDFFF]))|(?:\\uDC8B(?:\\u200D(?:\\uD83D(?:\\uDC68(?:\\uD83C[\\uDFFB-\\uDFFF]))))))))))|(?:\\uD83C[\\uDF3E\\uDF73\\uDF7C\\uDF93\\uDFA4\\uDFA8\\uDFEB\\uDFED])|(?:\\uD83D[\\uDCBB\\uDCBC\\uDD27\\uDD2C\\uDE80\\uDE92])|(?:\\uD83E(?:(?:\\uDD1D(?:\\u200D(?:\\uD83D(?:\\uDC68(?:\\uD83C[\\uDFFB\\uDFFC\\uDFFE\\uDFFF])))))|[\\uDDAF-\\uDDB3\\uDDBC\\uDDBD]))))?)|(?:\\uDFFE(?:\\u200D(?:(?:[\\u2695\\u2696\\u2708]\\uFE0F)|(?:\\u2764(?:\\uFE0F(?:\\u200D(?:\\uD83D(?:(?:\\uDC68(?:\\uD83C[\\uDFFB-\\uDFFF]))|(?:\\uDC8B(?:\\u200D(?:\\uD83D(?:\\uDC68(?:\\uD83C[\\uDFFB-\\uDFFF]))))))))))|(?:\\uD83C[\\uDF3E\\uDF73\\uDF7C\\uDF93\\uDFA4\\uDFA8\\uDFEB\\uDFED])|(?:\\uD83D[\\uDCBB\\uDCBC\\uDD27\\uDD2C\\uDE80\\uDE92])|(?:\\uD83E(?:(?:\\uDD1D(?:\\u200D(?:\\uD83D(?:\\uDC68(?:\\uD83C[\\uDFFB-\\uDFFD\\uDFFF])))))|[\\uDDAF-\\uDDB3\\uDDBC\\uDDBD]))))?)|(?:\\uDFFF(?:\\u200D(?:(?:[\\u2695\\u2696\\u2708]\\uFE0F)|(?:\\u2764(?:\\uFE0F(?:\\u200D(?:\\uD83D(?:(?:\\uDC68(?:\\uD83C[\\uDFFB-\\uDFFF]))|(?:\\uDC8B(?:\\u200D(?:\\uD83D(?:\\uDC68(?:\\uD83C[\\uDFFB-\\uDFFF]))))))))))|(?:\\uD83C[\\uDF3E\\uDF73\\uDF7C\\uDF93\\uDFA4\\uDFA8\\uDFEB\\uDFED])|(?:\\uD83D[\\uDCBB\\uDCBC\\uDD27\\uDD2C\\uDE80\\uDE92])|(?:\\uD83E(?:(?:\\uDD1D(?:\\u200D(?:\\uD83D(?:\\uDC68(?:\\uD83C[\\uDFFB-\\uDFFE])))))|[\\uDDAF-\\uDDB3\\uDDBC\\uDDBD]))))?))))?)|(?:\\uDC69(?:(?:\\u200D(?:(?:[\\u2695\\u2696\\u2708]\\uFE0F)|(?:\\u2764(?:\\uFE0F(?:\\u200D(?:\\uD83D(?:(?:\\uDC8B(?:\\u200D(?:\\uD83D[\\uDC68\\uDC69])))|[\\uDC68\\uDC69])))))|(?:\\uD83C[\\uDF3E\\uDF73\\uDF7C\\uDF93\\uDFA4\\uDFA8\\uDFEB\\uDFED])|(?:\\uD83D(?:(?:\\uDC66(?:\\u200D(?:\\uD83D\\uDC66))?)|(?:\\uDC67(?:\\u200D(?:\\uD83D[\\uDC66\\uDC67]))?)|(?:\\uDC69(?:\\u200D(?:\\uD83D(?:(?:\\uDC66(?:\\u200D(?:\\uD83D\\uDC66))?)|(?:\\uDC67(?:\\u200D(?:\\uD83D[\\uDC66\\uDC67]))?)))))|[\\uDCBB\\uDCBC\\uDD27\\uDD2C\\uDE80\\uDE92]))|(?:\\uD83E[\\uDDAF-\\uDDB3\\uDDBC\\uDDBD])))|(?:\\uD83C(?:(?:\\uDFFB(?:\\u200D(?:(?:[\\u2695\\u2696\\u2708]\\uFE0F)|(?:\\u2764(?:\\uFE0F(?:\\u200D(?:\\uD83D(?:(?:[\\uDC68\\uDC69](?:\\uD83C[\\uDFFB-\\uDFFF]))|(?:\\uDC8B(?:\\u200D(?:\\uD83D(?:[\\uDC68\\uDC69](?:\\uD83C[\\uDFFB-\\uDFFF]))))))))))|(?:\\uD83C[\\uDF3E\\uDF73\\uDF7C\\uDF93\\uDFA4\\uDFA8\\uDFEB\\uDFED])|(?:\\uD83D[\\uDCBB\\uDCBC\\uDD27\\uDD2C\\uDE80\\uDE92])|(?:\\uD83E(?:(?:\\uDD1D(?:\\u200D(?:\\uD83D(?:[\\uDC68\\uDC69](?:\\uD83C[\\uDFFC-\\uDFFF])))))|[\\uDDAF-\\uDDB3\\uDDBC\\uDDBD]))))?)|(?:\\uDFFC(?:\\u200D(?:(?:[\\u2695\\u2696\\u2708]\\uFE0F)|(?:\\u2764(?:\\uFE0F(?:\\u200D(?:\\uD83D(?:(?:[\\uDC68\\uDC69](?:\\uD83C[\\uDFFB-\\uDFFF]))|(?:\\uDC8B(?:\\u200D(?:\\uD83D(?:[\\uDC68\\uDC69](?:\\uD83C[\\uDFFB-\\uDFFF]))))))))))|(?:\\uD83C[\\uDF3E\\uDF73\\uDF7C\\uDF93\\uDFA4\\uDFA8\\uDFEB\\uDFED])|(?:\\uD83D[\\uDCBB\\uDCBC\\uDD27\\uDD2C\\uDE80\\uDE92])|(?:\\uD83E(?:(?:\\uDD1D(?:\\u200D(?:\\uD83D(?:[\\uDC68\\uDC69](?:\\uD83C[\\uDFFB\\uDFFD-\\uDFFF])))))|[\\uDDAF-\\uDDB3\\uDDBC\\uDDBD]))))?)|(?:\\uDFFD(?:\\u200D(?:(?:[\\u2695\\u2696\\u2708]\\uFE0F)|(?:\\u2764(?:\\uFE0F(?:\\u200D(?:\\uD83D(?:(?:[\\uDC68\\uDC69](?:\\uD83C[\\uDFFB-\\uDFFF]))|(?:\\uDC8B(?:\\u200D(?:\\uD83D(?:[\\uDC68\\uDC69](?:\\uD83C[\\uDFFB-\\uDFFF]))))))))))|(?:\\uD83C[\\uDF3E\\uDF73\\uDF7C\\uDF93\\uDFA4\\uDFA8\\uDFEB\\uDFED])|(?:\\uD83D[\\uDCBB\\uDCBC\\uDD27\\uDD2C\\uDE80\\uDE92])|(?:\\uD83E(?:(?:\\uDD1D(?:\\u200D(?:\\uD83D(?:[\\uDC68\\uDC69](?:\\uD83C[\\uDFFB\\uDFFC\\uDFFE\\uDFFF])))))|[\\uDDAF-\\uDDB3\\uDDBC\\uDDBD]))))?)|(?:\\uDFFE(?:\\u200D(?:(?:[\\u2695\\u2696\\u2708]\\uFE0F)|(?:\\u2764(?:\\uFE0F(?:\\u200D(?:\\uD83D(?:(?:[\\uDC68\\uDC69](?:\\uD83C[\\uDFFB-\\uDFFF]))|(?:\\uDC8B(?:\\u200D(?:\\uD83D(?:[\\uDC68\\uDC69](?:\\uD83C[\\uDFFB-\\uDFFF]))))))))))|(?:\\uD83C[\\uDF3E\\uDF73\\uDF7C\\uDF93\\uDFA4\\uDFA8\\uDFEB\\uDFED])|(?:\\uD83D[\\uDCBB\\uDCBC\\uDD27\\uDD2C\\uDE80\\uDE92])|(?:\\uD83E(?:(?:\\uDD1D(?:\\u200D(?:\\uD83D(?:[\\uDC68\\uDC69](?:\\uD83C[\\uDFFB-\\uDFFD\\uDFFF])))))|[\\uDDAF-\\uDDB3\\uDDBC\\uDDBD]))))?)|(?:\\uDFFF(?:\\u200D(?:(?:[\\u2695\\u2696\\u2708]\\uFE0F)|(?:\\u2764(?:\\uFE0F(?:\\u200D(?:\\uD83D(?:(?:[\\uDC68\\uDC69](?:\\uD83C[\\uDFFB-\\uDFFF]))|(?:\\uDC8B(?:\\u200D(?:\\uD83D(?:[\\uDC68\\uDC69](?:\\uD83C[\\uDFFB-\\uDFFF]))))))))))|(?:\\uD83C[\\uDF3E\\uDF73\\uDF7C\\uDF93\\uDFA4\\uDFA8\\uDFEB\\uDFED])|(?:\\uD83D[\\uDCBB\\uDCBC\\uDD27\\uDD2C\\uDE80\\uDE92])|(?:\\uD83E(?:(?:\\uDD1D(?:\\u200D(?:\\uD83D(?:[\\uDC68\\uDC69](?:\\uD83C[\\uDFFB-\\uDFFE])))))|[\\uDDAF-\\uDDB3\\uDDBC\\uDDBD]))))?))))?)|(?:\\uDC6F(?:\\u200D(?:[\\u2640\\u2642]\\uFE0F))?)|(?:\\uDD75(?:(?:\\uD83C(?:[\\uDFFB-\\uDFFF](?:\\u200D(?:[\\u2640\\u2642]\\uFE0F))?))|(?:\\uFE0F(?:\\u200D(?:[\\u2640\\u2642]\\uFE0F))?)))|(?:\\uDE2E(?:\\u200D(?:\\uD83D\\uDCA8))?)|(?:\\uDE35(?:\\u200D(?:\\uD83D\\uDCAB))?)|(?:\\uDE36(?:\\u200D(?:\\uD83C(?:\\uDF2B\\uFE0F)))?)|[\\uDC00-\\uDC07\\uDC09-\\uDC14\\uDC16-\\uDC3A\\uDC3C-\\uDC3E\\uDC40\\uDC44\\uDC45\\uDC51-\\uDC65\\uDC6A\\uDC79-\\uDC7B\\uDC7D-\\uDC80\\uDC84\\uDC88-\\uDC8E\\uDC90\\uDC92-\\uDCA9\\uDCAB-\\uDCFC\\uDCFF-\\uDD3D\\uDD4B-\\uDD4E\\uDD50-\\uDD67\\uDDA4\\uDDFB-\\uDE2D\\uDE2F-\\uDE34\\uDE37-\\uDE44\\uDE48-\\uDE4A\\uDE80-\\uDEA2\\uDEA4-\\uDEB3\\uDEB7-\\uDEBF\\uDEC1-\\uDEC5\\uDED0-\\uDED2\\uDED5-\\uDED7\\uDEDD-\\uDEDF\\uDEEB\\uDEEC\\uDEF4-\\uDEFC\\uDFE0-\\uDFEB\\uDFF0]))|(?:\\uD83E(?:(?:[\\uDD0C\\uDD0F\\uDD18-\\uDD1F\\uDD30-\\uDD34\\uDD36\\uDD77\\uDDB5\\uDDB6\\uDDBB\\uDDD2\\uDDD3\\uDDD5\\uDEC3-\\uDEC5\\uDEF0\\uDEF2-\\uDEF6](?:\\uD83C[\\uDFFB-\\uDFFF])?)|(?:[\\uDD26\\uDD35\\uDD37-\\uDD39\\uDD3D\\uDD3E\\uDDB8\\uDDB9\\uDDCD-\\uDDCF\\uDDD4\\uDDD6-\\uDDDD](?:(?:\\u200D(?:[\\u2640\\u2642]\\uFE0F))|(?:\\uD83C(?:[\\uDFFB-\\uDFFF](?:\\u200D(?:[\\u2640\\u2642]\\uFE0F))?)))?)|(?:[\\uDD3C\\uDDDE\\uDDDF](?:\\u200D(?:[\\u2640\\u2642]\\uFE0F))?)|(?:\\uDDD1(?:(?:\\u200D(?:(?:[\\u2695\\u2696\\u2708]\\uFE0F)|(?:\\uD83C[\\uDF3E\\uDF73\\uDF7C\\uDF84\\uDF93\\uDFA4\\uDFA8\\uDFEB\\uDFED])|(?:\\uD83D[\\uDCBB\\uDCBC\\uDD27\\uDD2C\\uDE80\\uDE92])|(?:\\uD83E(?:(?:\\uDD1D(?:\\u200D(?:\\uD83E\\uDDD1)))|[\\uDDAF-\\uDDB3\\uDDBC\\uDDBD]))))|(?:\\uD83C(?:(?:\\uDFFB(?:\\u200D(?:(?:[\\u2695\\u2696\\u2708]\\uFE0F)|(?:\\u2764(?:\\uFE0F(?:\\u200D(?:(?:\\uD83D(?:\\uDC8B(?:\\u200D(?:\\uD83E(?:\\uDDD1(?:\\uD83C[\\uDFFC-\\uDFFF]))))))|(?:\\uD83E(?:\\uDDD1(?:\\uD83C[\\uDFFC-\\uDFFF])))))))|(?:\\uD83C[\\uDF3E\\uDF73\\uDF7C\\uDF84\\uDF93\\uDFA4\\uDFA8\\uDFEB\\uDFED])|(?:\\uD83D[\\uDCBB\\uDCBC\\uDD27\\uDD2C\\uDE80\\uDE92])|(?:\\uD83E(?:(?:\\uDD1D(?:\\u200D(?:\\uD83E(?:\\uDDD1(?:\\uD83C[\\uDFFB-\\uDFFF])))))|[\\uDDAF-\\uDDB3\\uDDBC\\uDDBD]))))?)|(?:\\uDFFC(?:\\u200D(?:(?:[\\u2695\\u2696\\u2708]\\uFE0F)|(?:\\u2764(?:\\uFE0F(?:\\u200D(?:(?:\\uD83D(?:\\uDC8B(?:\\u200D(?:\\uD83E(?:\\uDDD1(?:\\uD83C[\\uDFFB\\uDFFD-\\uDFFF]))))))|(?:\\uD83E(?:\\uDDD1(?:\\uD83C[\\uDFFB\\uDFFD-\\uDFFF])))))))|(?:\\uD83C[\\uDF3E\\uDF73\\uDF7C\\uDF84\\uDF93\\uDFA4\\uDFA8\\uDFEB\\uDFED])|(?:\\uD83D[\\uDCBB\\uDCBC\\uDD27\\uDD2C\\uDE80\\uDE92])|(?:\\uD83E(?:(?:\\uDD1D(?:\\u200D(?:\\uD83E(?:\\uDDD1(?:\\uD83C[\\uDFFB-\\uDFFF])))))|[\\uDDAF-\\uDDB3\\uDDBC\\uDDBD]))))?)|(?:\\uDFFD(?:\\u200D(?:(?:[\\u2695\\u2696\\u2708]\\uFE0F)|(?:\\u2764(?:\\uFE0F(?:\\u200D(?:(?:\\uD83D(?:\\uDC8B(?:\\u200D(?:\\uD83E(?:\\uDDD1(?:\\uD83C[\\uDFFB\\uDFFC\\uDFFE\\uDFFF]))))))|(?:\\uD83E(?:\\uDDD1(?:\\uD83C[\\uDFFB\\uDFFC\\uDFFE\\uDFFF])))))))|(?:\\uD83C[\\uDF3E\\uDF73\\uDF7C\\uDF84\\uDF93\\uDFA4\\uDFA8\\uDFEB\\uDFED])|(?:\\uD83D[\\uDCBB\\uDCBC\\uDD27\\uDD2C\\uDE80\\uDE92])|(?:\\uD83E(?:(?:\\uDD1D(?:\\u200D(?:\\uD83E(?:\\uDDD1(?:\\uD83C[\\uDFFB-\\uDFFF])))))|[\\uDDAF-\\uDDB3\\uDDBC\\uDDBD]))))?)|(?:\\uDFFE(?:\\u200D(?:(?:[\\u2695\\u2696\\u2708]\\uFE0F)|(?:\\u2764(?:\\uFE0F(?:\\u200D(?:(?:\\uD83D(?:\\uDC8B(?:\\u200D(?:\\uD83E(?:\\uDDD1(?:\\uD83C[\\uDFFB-\\uDFFD\\uDFFF]))))))|(?:\\uD83E(?:\\uDDD1(?:\\uD83C[\\uDFFB-\\uDFFD\\uDFFF])))))))|(?:\\uD83C[\\uDF3E\\uDF73\\uDF7C\\uDF84\\uDF93\\uDFA4\\uDFA8\\uDFEB\\uDFED])|(?:\\uD83D[\\uDCBB\\uDCBC\\uDD27\\uDD2C\\uDE80\\uDE92])|(?:\\uD83E(?:(?:\\uDD1D(?:\\u200D(?:\\uD83E(?:\\uDDD1(?:\\uD83C[\\uDFFB-\\uDFFF])))))|[\\uDDAF-\\uDDB3\\uDDBC\\uDDBD]))))?)|(?:\\uDFFF(?:\\u200D(?:(?:[\\u2695\\u2696\\u2708]\\uFE0F)|(?:\\u2764(?:\\uFE0F(?:\\u200D(?:(?:\\uD83D(?:\\uDC8B(?:\\u200D(?:\\uD83E(?:\\uDDD1(?:\\uD83C[\\uDFFB-\\uDFFE]))))))|(?:\\uD83E(?:\\uDDD1(?:\\uD83C[\\uDFFB-\\uDFFE])))))))|(?:\\uD83C[\\uDF3E\\uDF73\\uDF7C\\uDF84\\uDF93\\uDFA4\\uDFA8\\uDFEB\\uDFED])|(?:\\uD83D[\\uDCBB\\uDCBC\\uDD27\\uDD2C\\uDE80\\uDE92])|(?:\\uD83E(?:(?:\\uDD1D(?:\\u200D(?:\\uD83E(?:\\uDDD1(?:\\uD83C[\\uDFFB-\\uDFFF])))))|[\\uDDAF-\\uDDB3\\uDDBC\\uDDBD]))))?))))?)|(?:\\uDEF1(?:\\uD83C(?:(?:\\uDFFB(?:\\u200D(?:\\uD83E(?:\\uDEF2(?:\\uD83C[\\uDFFC-\\uDFFF]))))?)|(?:\\uDFFC(?:\\u200D(?:\\uD83E(?:\\uDEF2(?:\\uD83C[\\uDFFB\\uDFFD-\\uDFFF]))))?)|(?:\\uDFFD(?:\\u200D(?:\\uD83E(?:\\uDEF2(?:\\uD83C[\\uDFFB\\uDFFC\\uDFFE\\uDFFF]))))?)|(?:\\uDFFE(?:\\u200D(?:\\uD83E(?:\\uDEF2(?:\\uD83C[\\uDFFB-\\uDFFD\\uDFFF]))))?)|(?:\\uDFFF(?:\\u200D(?:\\uD83E(?:\\uDEF2(?:\\uD83C[\\uDFFB-\\uDFFE]))))?)))?)|[\\uDD0D\\uDD0E\\uDD10-\\uDD17\\uDD20-\\uDD25\\uDD27-\\uDD2F\\uDD3A\\uDD3F-\\uDD45\\uDD47-\\uDD76\\uDD78-\\uDDB4\\uDDB7\\uDDBA\\uDDBC-\\uDDCC\\uDDD0\\uDDE0-\\uDDFF\\uDE70-\\uDE74\\uDE78-\\uDE7C\\uDE80-\\uDE86\\uDE90-\\uDEAC\\uDEB0-\\uDEBA\\uDEC0-\\uDEC2\\uDED0-\\uDED9\\uDEE0-\\uDEE7]))|(?:\\uDB40[\\uDC20-\\uDC7F])|[\\u200D\\u20E3\\u231A\\u231B\\u23E9-\\u23EC\\u23F0\\u23F3\\u25FD\\u25FE\\u2614\\u2615\\u2648-\\u2653\\u267F\\u2693\\u26A1\\u26AA\\u26AB\\u26BD\\u26BE\\u26C4\\u26C5\\u26CE\\u26D4\\u26EA\\u26F2\\u26F3\\u26F5\\u26FA\\u26FD\\u2705\\u2728\\u274C\\u274E\\u2753-\\u2755\\u2757\\u2795-\\u2797\\u27B0\\u27BF\\u2B1B\\u2B1C\\u2B50\\u2B55\\uFE0F])/g\n\nBC.EMOJI_SKIN_TONE_PATTERN = /(?:\\uD83C[\\uDFFB-\\uDFFF])/g\n", "const {userAgent} = window.navigator\n\nconst ios = /iPhone|iPad|iOS/.test(userAgent)\nconst android = /Android/.test(userAgent)\nconst mobile = ios || android\n\nconst iosApp = /BC3 iOS/.test(userAgent)\nconst androidApp = /BC3 Android/.test(userAgent)\nconst mobileApp = iosApp || androidApp\nconst desktopApp = /Electron/.test(userAgent)\n\nconst mac = /Mac/.test(userAgent)\nconst windows = /Windows/.test(userAgent)\n\nconst isChrome = userAgent.match(/chrome/i)\nconst isSafari = (userAgent.match(/safari/i) && !isChrome) || iosApp\nconst isFirefox = userAgent.match(/firefox/i)\n\nBC.platform = {\n isIOS: () => ios,\n isAndroid: () => android,\n isMobile: () => mobile ,\n isIOSApp: () => iosApp,\n isAndroidApp: () => androidApp,\n isMobileApp: () => mobileApp,\n isDesktopApp: () => desktopApp,\n isMac: () => mac,\n isWindows: () => windows,\n isSafari: () => isSafari,\n isChrome: () => isChrome,\n os: (() => {\n if (mac) {\n return \"mac\"\n } else if (windows) {\n return \"windows\"\n } else {\n return \"\"\n }\n })(),\n browser: (() => {\n if (isChrome) {\n return \"chrome\"\n } else if (isSafari) {\n return \"safari\"\n } else if (isFirefox) {\n return \"firefox\"\n } else {\n return \"\"\n }\n })(),\n buildNumber() {\n const match = userAgent.match(/\\(build (\\d+);/)\n if (match) { return match[1] } else { return 0 }\n }\n}\n\nBC.platformConfigurationIncludes = configuration => BC.matches(document.documentElement, `[data-bridge-configuration~=${configuration}]`)\n", "import \"./platform_helpers\"\nBC.documentIsActive = (function() {\n const isVisible = () => document.visibilityState === \"visible\"\n const isFocused = () => document.hasFocus()\n\n if (BC.platform.isMobile() || BC.platform.isDesktopApp()) {\n return () => isVisible()\n } else {\n return () => isVisible() && isFocused()\n }\n})()\n", "// Copied from Trix. Useful independently as we lazy-load trix\n// so can't guarantee the equivalent class will be available\n// when trix hasn't loaded.\n\nlet ucs2decode, ucs2encode\nBC.UTF16String = class UTF16String {\n static box(value = \"\") {\n if (value instanceof this) {\n return value\n } else {\n return this.fromUCS2String(value?.toString())\n }\n }\n\n static fromUCS2String(ucs2String) {\n return new (this)(ucs2String, ucs2decode(ucs2String))\n }\n\n static fromCodepoints(codepoints) {\n return new (this)(ucs2encode(codepoints), codepoints)\n }\n\n constructor(ucs2String, codepoints) {\n this.ucs2String = ucs2String\n this.codepoints = codepoints\n this.length = this.codepoints.length\n this.ucs2Length = this.ucs2String.length\n }\n\n offsetToUCS2Offset(offset) {\n return ucs2encode(this.codepoints.slice(0, Math.max(0, offset))).length\n }\n\n offsetFromUCS2Offset(ucs2Offset) {\n return ucs2decode(this.ucs2String.slice(0, Math.max(0, ucs2Offset))).length\n }\n\n slice() {\n return this.constructor.fromCodepoints(this.codepoints.slice(...arguments))\n }\n\n charAt(offset) {\n return this.slice(offset, offset + 1)\n }\n\n isEqualTo(value) {\n return this.constructor.box(value).ucs2String === this.ucs2String\n }\n\n toJSON() {\n return this.ucs2String\n }\n\n getCacheKey() {\n return this.ucs2String\n }\n\n toString() {\n return this.ucs2String\n }\n}\n\nconst hasArrayFrom = Array.from?.(\"\\ud83d\\udc7c\").length === 1\nconst hasStringCodePointAt = (\" \".codePointAt?.(0) != null)\nconst hasStringFromCodePoint = String.fromCodePoint?.(32, 128124) === \" \\ud83d\\udc7c\"\n\n// UCS-2 conversion helpers ported from Mathias Bynens' Punycode.js:\n// https://github.com/bestiejs/punycode.js#punycodeucs2\n\n// Creates an array containing the numeric code points of each Unicode\n// character in the string. While JavaScript uses UCS-2 internally,\n// this function will convert a pair of surrogate halves (each of which\n// UCS-2 exposes as separate characters) into a single code point,\n// matching UTF-16.\nif (hasArrayFrom && hasStringCodePointAt) {\n ucs2decode = string => Array.from(string).map(char => char.codePointAt(0))\n} else {\n ucs2decode = function(string) {\n const output = []\n let counter = 0\n const {\n length\n } = string\n\n while (counter < length) {\n let value = string.charCodeAt(counter++)\n if ((0xD800 <= value && value <= 0xDBFF) && (counter < length)) {\n // high surrogate, and there is a next character\n const extra = string.charCodeAt(counter++)\n if ((extra & 0xFC00) === 0xDC00) {\n // low surrogate\n value = ((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000\n } else {\n // unmatched surrogate; only append this code unit, in case the\n // next code unit is the high surrogate of a surrogate pair\n counter--\n }\n }\n output.push(value)\n }\n\n return output\n }\n}\n\n// Creates a string based on an array of numeric code points.\nif (hasStringFromCodePoint) {\n ucs2encode = array => String.fromCodePoint(...Array.from(array || []))\n} else {\n ucs2encode = function(array) {\n const characters = []\n\n Array.from(array).forEach((value) => {\n let output = \"\"\n if (value > 0xFFFF) {\n value -= 0x10000\n output += String.fromCharCode(((value >>> 10) & 0x3FF) | 0xD800)\n value = 0xDC00 | (value & 0x3FF)\n }\n characters.push(output + String.fromCharCode(value))\n })\n\n return characters.join(\"\")\n }\n}\n", "BC.animateElementWithClass = (element, className, callback) => applyClassAwaitingEvent(element, className, \"animationend\", callback)\n\nBC.transitionElementWithClass = (element, className, callback) => applyClassAwaitingEvent(element, className, \"transitionend\", callback)\n\nBC.shakeElement = element => BC.animateElementWithClass(element, \"oops-shake-it\")\n\nconst applyClassAwaitingEvent = function(element, className, eventName, callback) {\n let timeout\n let uninstalled = false\n\n const uninstall = function() {\n if (!uninstalled) {\n uninstalled = true\n element.removeEventListener(eventName, uninstall)\n return requestAnimationFrame(function() {\n element.classList.remove(className)\n return callback?.()\n })\n }\n }\n\n element.addEventListener(eventName, uninstall)\n element.classList.add(className)\n\n // Failsafe: If we don't receive a {transition,animation}end event\n // for some reason, ensure that uninstall is still called.\n const duration = getDuration(element, eventName)\n if (duration) {\n timeout = duration + 200\n } else {\n timeout = 500\n }\n\n return setTimeout(uninstall, timeout)\n}\n\nconst getDuration = function(element, eventName) {\n const type = eventName === \"animationend\" ? \"animation\" : \"transition\"\n const duration = getComputedStyle(element)[`${type}Duration`]\n if (duration) {\n if (/ms/.test(duration)) {\n return parseInt(duration, 10)\n } else {\n return parseFloat(duration) * 1000\n }\n }\n}\n", "BC.makeElement = function(tagName, attributes) {\n const element = document.createElement(tagName)\n for (const name in attributes) { const value = attributes[name]; element.setAttribute(name, value) }\n return element\n}\n", "BC.EMOJI = {\n \"\uD83E\uDD29\": \"Face with starry eyes\",\n \"\uD83E\uDD73\": \"Partying face\",\n \"\uD83D\uDE0A\": \"Smiling face with flush cheeks\",\n \"\uD83D\uDE00\": \"Grinning face\",\n \"\uD83D\uDE05\": \"Grinning face with sweat drop\",\n \"\uD83D\uDE02\": \"Face with tears of joy\",\n \"\uD83D\uDE0E\": \"Smiling face with sunglasses\",\n \"\uD83D\uDE09\": \"Winking face\",\n \"\uD83D\uDE1C\": \"Winking face with stuck out tongue\",\n \"\uD83D\uDE2C\": \"Grimacing face\",\n \"\uD83D\uDE2E\": \"Surprised face with open mouth\",\n \"\uD83D\uDE33\": \"Flushed face\",\n \"\uD83E\uDD14\": \"Thinking face\",\n \"\uD83D\uDE12\": \"Unamused face\",\n \"\uD83D\uDE22\": \"Crying face\",\n \"\uD83D\uDE2D\": \"Loudly crying face\",\n \"\uD83D\uDE31\": \"Face screaming in fear\",\n \"\uD83D\uDC4D\": \"Thumbs up\",\n \"\uD83D\uDC4F\": \"Clapping\",\n \"\uD83D\uDC4B\": \"Waving hand\",\n \"\uD83D\uDE4C\": \"Hands raised in celebration\",\n \"\uD83D\uDE4F\": \"Hands pressed together\",\n \"\uD83D\uDCAA\": \"Flexed bicep\",\n \"\uD83E\uDD18\": \"Sign of the horns\",\n \"\u270A\": \"Raised fist\",\n \"\uD83D\uDC4E\": \"Thumbs down\",\n \"\u270C\uFE0F\": \"Peace\",\n \"\uD83D\uDC48\": \"Finger pointing left\",\n \"\uD83D\uDC46\": \"Finger pointing Up\",\n \"\u270B\": \"Raised hand\",\n \"\uD83C\uDF89\": \"Party popper\",\n \"\u2728\": \"Sparkles\",\n \"\u2764\uFE0F\": \"Red heart\",\n \"\uD83D\uDCAF\": \"100 points\",\n \"\uD83D\uDC40\": \"Eyes\",\n \"\uD83D\uDCA9\": \"Pile of poop\",\n \"\u2600\uFE0F\": \"Sun\",\n \"\uD83C\uDF19\": \"Moon\",\n \"\uD83D\uDCA5\": \"Collision\",\n \"\uD83D\uDD25\": \"Fire\",\n \"\uD83C\uDF82\": \"Birthday cake\",\n \"\uD83C\uDF74\": \"Fork and knife\",\n \"\uD83D\uDCB0\": \"Money bag\",\n \"\uD83E\uDD47\": \"Gold medal\",\n \"\uD83D\uDEA8\": \"Red flashing light\",\n \"\uD83D\uDCA1\": \"Light bulb\",\n \"\uD83D\uDEE0\": \"Hammer and wrench\",\n \"\uD83D\uDCC8\": \"Chart with upward trend\",\n \"\u2705\": \"Check mark\",\n \"\uD83D\uDCE2\": \"Public address loudspeaker\"\n}\n", "if (!window.JST) window.JST = {}\nwindow.JST['all/behaviors/element/flash_notice'] = function(object){\nvar __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\n__p+='
\n// This content is not searchable (but still visible)\n//
\n// \n// This content is searchable\n// \n//\n// Hide me when searching\n//
\n//\n//\n// Show me when there are no search results\n//
\n//\\n Added '+\n((__t=( object.addedOption ))==null?'':_.escape(__t))+\n'.\\n
\\n';\n } \n__p+='\\n\\n';\n if (object.removedOption) { \n__p+='\\n\\n Removed '+\n((__t=( object.removedOption ))==null?'':_.escape(__t))+\n'.\\n
\\n';\n } \n__p+='\\n\\n\\n Autocomplete \"'+\n((__t=( object.description ))==null?'':_.escape(__t))+\n'\". '+\n((__t=( object.nOptions ))==null?'':_.escape(__t))+\n' options available.\\n\\n ';\n if (object.selected.length > 0) { \n__p+='\\n Currently selected: '+\n((__t=( object.selected.join(\", \") ))==null?'':_.escape(__t))+\n'\\n ';\n } else { \n__p+='\\n No option selected. Press down arrow to view options.\\n ';\n } \n__p+='\\n
\\n\\n';\nreturn __p;\n}", "import \"./templates/autocompletable.jst.ejs\"\nimport \"./templates/suggestions.jst.ejs\"\nimport \"./templates/status.jst.ejs\"\n\nlet fetchCache = {}\n\naddEventListener(\"turbo:visit\", () => fetchCache = {})\n\nBC.fetchAutocompletables = function(url) {\n if (url) {\n return fetchCache[url] != null ? fetchCache[url] : (fetchCache[url] = BC.fetch(url, {as: \"json\"}))\n } else {\n return Promise.resolve()\n }\n}\n\nBC.renderAutocompletable = autocompletable => JST[\"bc/inputs/autocomplete/templates/autocompletable\"](autocompletable)\n\nBC.renderAutocompletables = (autocompletables = []) => autocompletables.map(BC.renderAutocompletable).join(\"\\n\")\n\nBC.renderAutocompletableStatus = (status) => JST[\"bc/inputs/autocomplete/templates/status\"](status)\n\nBC.renderAutocompletableSuggestions = (autocompletables, {selectedAutocompletable} = {}) => JST[\"bc/inputs/autocomplete/templates/suggestions\"]({autocompletables, selectedAutocompletable})\n", "BC.AutocompletableCollection = class AutocompletableCollection {\n constructor(autocompletables = [], options = {}) {\n this.index = new Map()\n this.autocompletables = new Array()\n\n Array.from(autocompletables).forEach((autocompletable) => {\n this.index.set(uniqueAutocompleteableKey(autocompletable), autocompletable)\n })\n\n this.index.forEach(autocompletable => {\n this.autocompletables.push(autocompletable)\n })\n\n if (options.sort !== false) {\n this.autocompletables.sort(compareAutocompletables)\n }\n\n this.length = this.autocompletables.length\n }\n\n get(value) {\n return this.index.get(value.toString())\n }\n\n has(value) {\n return this.index.has(value.toString())\n }\n\n add(autocompletables = [], collectionOptions) {\n return new this.constructor(this.autocompletables.concat(autocompletables), collectionOptions)\n }\n\n getValues() {\n return this.autocompletables.map(autocompletable => autocompletable.value)\n }\n\n withValues(values = [], collectionOptions) {\n const autocompletables = values.map((value) => this.get(value)).filter(Boolean)\n return new this.constructor(autocompletables, collectionOptions)\n }\n\n withoutValues(values = [], collectionOptions) {\n const autocompletables = []\n this.index.forEach(function(autocompletable, value) {\n const allGroupMembersAreAdded = autocompletable.type == \"group\" && autocompletable.value.split(\",\").every(id => values.includes(id))\n\n if (!values.includes(value) && !allGroupMembersAreAdded) {\n return autocompletables.push(autocompletable)\n }\n })\n return new this.constructor(autocompletables, collectionOptions)\n }\n\n filter(callback, collectionOptions) {\n if (!callback) return this\n\n const autocompletables = []\n this.index.forEach(function(autocompletable, value) {\n if (callback(autocompletable)) {\n return autocompletables.push(autocompletable)\n }\n })\n\n return new this.constructor(autocompletables, collectionOptions)\n }\n\n matchingQuery(query) {\n if (!query) return this\n const autocompletables = matchAutocompletablesByNameOrDescription(this.autocompletables, query)\n return new this.constructor(autocompletables, {sort: false})\n }\n\n toArray() {\n return this.autocompletables.slice(0)\n }\n\n toJSON() {\n return this.toArray()\n }\n\n isEqualTo(collection) {\n if (!collection) return false\n if (this.length !== collection.length) return false\n return JSON.stringify(this) === JSON.stringify(collection)\n }\n}\n\n// Helpers\n\nconst compareAutocompletables = (autocompletable, otherAutocompletable) => autocompletable.name.localeCompare(otherAutocompletable.name)\n\nconst matchAutocompletablesByNameOrDescription = (autocompletables, query) => BC.uniqueValues([].concat(\n matchAutocompletablesByNameAtHead(autocompletables, query),\n matchAutocompletablesByRestOfName(autocompletables, query),\n matchAutocompletablesByRestOfDescription(autocompletables, query))\n)\n\nconst matchAutocompletablesByNameAtHead = (autocompletables, query) => matchAutocompletablesByRegExp(autocompletables, regexpForQuery(query, \"^\"))\n\nconst matchAutocompletablesByRestOfName = (autocompletables, query) => matchAutocompletablesByRegExp(autocompletables, regexpForQuery(query, \"\\\\s\"))\n\nconst matchAutocompletablesByRestOfDescription = (autocompletables, query) => matchAutocompletablesByRegExp(autocompletables, regexpForQuery(query), \"description\")\n\nconst matchAutocompletablesByRegExp = (autocompletables, regexp, propertyName = \"name\") => {\n return autocompletables.filter(autocompletable => {\n const normalizedPropertyName = `normalized${BC.camelize(propertyName)}`\n const property = autocompletable[propertyName]\n\n if (property) {\n if (!autocompletable[normalizedPropertyName]) autocompletable[normalizedPropertyName] = BC.normalize(property)\n return regexp.test(autocompletable[normalizedPropertyName])\n }\n })\n}\n\nconst regexpForQuery = (query, prefix = \"\") => new RegExp(prefix + patternForQuery(query), \"i\")\n\nconst patternForQuery = (query) => BC.normalize(query.toString()).split(\"\").map(BC.regexpEscape).\n join(\"(.*\\\\s)?\").replace(/\\(\\.\\*\\\\s\\)\\? /g, \"[^ ]* \")\n\nconst uniqueAutocompleteableKey = (autocompletable) => {\n if (autocompletable.type == \"group\") {\n return autocompletable.name + \":\" + autocompletable.value.toString()\n } else {\n return autocompletable.value.toString()\n }\n}\n", "class BcDatetimeInput extends HTMLElement {\n get name() {\n return this.getAttribute(\"name\")\n }\n\n get dateTime() {\n if ((this.date) && (this.time)) {\n return new CalendarDateTime(this.date, this.time)\n } else {\n return null\n }\n }\n\n set dateTime(dateTime) {\n this.date = dateTime?.calendarDate\n this.time = dateTime?.calendarTime\n }\n\n get date() {\n return this.datePicker?.date\n }\n\n set date(date) {\n if (this.datePicker) this.datePicker.date = date\n }\n\n get time() {\n return this.timePicker?.time\n }\n\n set time(time) {\n if (this.timePicker) this.timePicker.time = time\n }\n\n connectedCallback() {\n BC.ready(() => {\n this.getChildElements(({datePicker, timePicker}) => {\n this.datePicker = datePicker\n this.timePicker = timePicker\n this.setAttribute(\"initialized\", \"\")\n })\n })\n }\n\n shake() {\n this.datePicker.shake()\n this.timePicker.shake()\n }\n\n getChildElements(callback) {\n const datePicker = this.querySelector(\"bc-date-picker[initialized], bc-relative-date-input[initialized]\")\n const timePicker = this.querySelector(\"bc-time-picker[initialized]\")\n\n if ((datePicker) && (timePicker)) {\n callback({datePicker, timePicker})\n } else {\n requestAnimationFrame(() => {\n this.getChildElements(callback)\n })\n }\n }\n}\n\nBC.DatetimeInputElement = BcDatetimeInput\nwindow.customElements.define(\"bc-datetime-picker\", BcDatetimeInput)\n\n", "BC.SegmentedInputController = class SegmentedInputController {\n constructor(element) {\n this.didPressKey = this.didPressKey.bind(this)\n this.dummyDidFocus = this.dummyDidFocus.bind(this)\n this.element = element\n this.dummyElement = this.element.dummyElement\n this.inputElement = this.element.inputElement\n this.fieldElements = this.element.fieldElements\n }\n\n initialize() {\n this.installListeners()\n this.autofocus()\n this.initiallyDisable()\n }\n\n destroy() {\n return this.uninstallListeners()\n }\n\n push(value) {\n const field = this.fieldElements[this.getIndex()]\n if (field) {\n field.value = `${value}`\n return this.setValue(`${this.getValue()}${value}`)\n }\n }\n\n pop() {\n const field = this.fieldElements[this.getIndex() - 1]\n if (field) {\n field.value = \"\"\n return this.setValue(this.getValue().slice(0, -1))\n }\n }\n\n reset() {\n while (this.getIndex()) {\n this.pop()\n }\n }\n\n getIndex() {\n return this.getValue().length\n }\n\n getValue() {\n return `${this.inputElement.value}`\n }\n\n setValue(value) {\n this.inputElement.value = value\n const field = this.fieldElements[this.getIndex()]\n if (field) {\n this.dummyElement.style.left = getLeftOffset(field) + \"px\"\n this.dummyElement.style.opacity = \"\"\n } else {\n this.dummyElement.blur()\n this.dummyElement.style.opacity = \"0\"\n }\n\n return this.triggerCompletedEvent()\n }\n\n getSize() {\n return this.fieldElements.length\n }\n\n isCompleted() {\n return this.getIndex() === this.getSize()\n }\n\n // Keyboard events\n\n didPressKey(event) {\n if (48 <= event.keyCode && event.keyCode <= 57) {\n this.didPressNumericKey(event.keyCode - 48)\n } else if (96 <= event.keyCode && event.keyCode <= 105) {\n this.didPressNumericKey(event.keyCode - 96)\n } else if (event.keyCode === 8) {\n this.didPressBackspaceKey()\n } else if (event.keyCode === 27) {\n this.didPressEscapeKey()\n } else {\n return\n }\n\n event.preventDefault()\n }\n\n didPressNumericKey(value) {\n return this.push(value)\n }\n\n didPressBackspaceKey() {\n return this.pop()\n }\n\n didPressEscapeKey() {\n return this.reset()\n }\n\n // Focus and click events\n\n dummyDidFocus(event) {\n if (this.element.disabled) {\n return this.dummyElement.blur()\n } else if (this.isCompleted()) {\n return this.reset()\n }\n }\n\n didClick() {\n return this.dummyElement.focus()\n }\n\n // Private\n\n installListeners() {\n this.dummyElement.addEventListener(\"focus\", this.dummyDidFocus)\n this.element.addEventListener(\"keydown\", this.didPressKey)\n this.element.addEventListener(\"click\", this.didClick)\n this.element.addEventListener(\"keypress\", this.suppress)\n this.element.addEventListener(\"input\", this.suppress)\n return this.element.addEventListener(\"paste\", this.suppress)\n }\n\n autofocus() {\n if (this.element.autofocus) {\n return this.dummyElement.focus()\n }\n }\n\n initiallyDisable() {\n if (this.element.disabled) {\n return this.element.disable()\n }\n }\n\n uninstallListeners() {\n this.dummyElement.removeEventListener(\"focus\", this.dummyDidFocus)\n this.element.removeEventListener(\"keydown\", this.didPressKey)\n this.element.removeEventListener(\"click\", this.didClick)\n this.element.removeEventListener(\"keypress\", this.suppress)\n this.element.removeEventListener(\"input\", this.suppress)\n return this.element.removeEventListener(\"paste\", this.suppress)\n }\n\n triggerCompletedEvent() {\n if (this.isCompleted()) { return BC.triggerEvent(this.element, \"completed\") }\n }\n}\n\nconst getLeftOffset = function(el) {\n const parentOffset = el.offsetParent.getBoundingClientRect()\n const offset = el.getBoundingClientRect()\n return offset.left - parentOffset.left\n}\n", "import \"./controller\"\n\nlet identifier = 0\n\nclass BcSegmentedInput extends HTMLElement {\n get size() {\n return this.getAttribute(\"size\")\n }\n\n get autofocus() {\n return this.hasAttribute(\"autofocus\")\n }\n\n get identifier() {\n if (this.hasAttribute(\"identifier\")) {\n return this.getAttribute(\"identifier\")\n } else {\n this.setAttribute(\"identifier\", identifier++)\n return this.identifier\n }\n }\n\n get disabled() {\n return this.hasAttribute(\"disabled\")\n }\n\n get inputElement() {\n if (this.hasAttribute(\"input\")) {\n return document.getElementById(this.getAttribute(\"input\"))\n } else {\n const element = BC.makeElement(\"input\", {type: \"hidden\", id: `segmented_input_${this.identifier}`})\n this.setAttribute(\"input\", element.id)\n return this.parentElement.insertBefore(element, this.nextElementSibling)\n }\n }\n\n get dummyElement() {\n if (this.hasAttribute(\"dummy\")) {\n return document.getElementById(this.getAttribute(\"dummy\"))\n } else {\n const element = BC.makeElement(\"input\", {\n type: \"tel\",\n id: `segmented_input_dummy_${this.identifier}`,\n pattern: \"[0-9]*\",\n size: \"1\",\n maxlength: \"0\",\n autocomplete: \"off\",\n class: \"input text-center segmented-input__dummy\"\n }\n )\n this.setAttribute(\"dummy\", element.id)\n return this.appendChild(element)\n }\n }\n\n get labelElement() {\n if (this.hasAttribute(\"label\")) {\n return document.getElementById(this.getAttribute(\"label\"))\n } else {\n const element = BC.makeElement(\"label\", {id: `segmented_input_label_${this.identifier}`, for: this.dummyElement.id, class: \"segmented-input__label\"})\n this.setAttribute(\"label\", element.id)\n return this.insertBefore(element, this.dummyElement.nextElementSibling)\n }\n }\n\n get fieldElements() {\n const fields = this.labelElement.querySelectorAll(\"[data-role=field]\")\n if (fields.length) {\n return fields\n } else {\n const elements = []\n\n for (let i = 0; i < this.size; i++) {\n const element = BC.makeElement(\"input\", {\n type: \"text\",\n disabled: \"disabled\",\n size: \"1\",\n class: \"input text-center segmented-input__field\",\n \"data-role\": \"field\"\n })\n elements.push(this.labelElement.appendChild(element))\n }\n\n return elements\n }\n }\n\n connectedCallback() {\n if (this.controller == null) { this.controller = new BC.SegmentedInputController(this) }\n this.controller.initialize()\n }\n\n disconnectedCallback() {\n this.controller?.destroy()\n }\n\n disable() {\n this.setAttribute(\"disabled\", \"\")\n this.dummyElement.blur()\n this.dummyElement.style.opacity = \"0\"\n }\n\n enable() {\n this.removeAttribute(\"disabled\")\n this.dummyElement.style.opacity = \"\"\n }\n}\n\nwindow.customElements.define(\"bc-segmented-input\", BcSegmentedInput)\n\n", "class BcDateInput extends HTMLElement {\n connectedCallback() {\n BC.ready(() => {\n this.input = findOrCreateInput(this)\n this.duetPicker = initializeDuetPicker(this)\n\n this.addEventListener(\"duetChange\", this.duetPickerChanged)\n this.addEventListener(\"duetFocus\", this.duetPickerFocused)\n this.addEventListener(\"duetOpen\", this.onOpen)\n this.addEventListener(\"duetClose\", this.onClose)\n this.addEventListener(\"keydown\", this.onKeydown)\n\n this.setAttribute(\"initialized\", \"\")\n this.configureFocusability()\n })\n }\n\n disconnectedCallback() {\n this.removeEventListener(\"duetChange\", this.duetPickerChanged)\n this.removeEventListener(\"duetFocus\", this.duetPickerFocused)\n this.removeEventListener(\"duetOpen\", this.onOpen)\n this.removeEventListener(\"duetClose\", this.onClose)\n this.removeEventListener(\"keydown\", this.onKeydown)\n }\n\n open() {\n this.duetPicker.show()\n }\n\n shake() {\n BC.shakeElement(this)\n }\n\n configureFocusability = () => {\n if (!this.isConnected) return\n\n const input = this.duetPicker.datePickerInput\n const button = this.duetPicker.datePickerButton\n if (input && button) {\n input.tabIndex = -1\n button.tabIndex = 0\n const label = this.dataset.label\n button.setAttribute(\"aria-label\", label ? `Choose ${label}` : \"Choose date\")\n } else {\n requestAnimationFrame(this.configureFocusability)\n }\n }\n\n duetPickerChanged = async (event) => {\n if (event.detail.value) {\n this.dispatchEvent(new Event(\"bc:before-date-change\", { bubbles: true }))\n this.value = event.detail.value\n this.dispatchEvent(new Event(\"bc:date-change\", { bubbles: true }))\n }\n }\n\n duetPickerFocused = async () => {\n this.duetPicker.datePickerInput.readOnly = true\n }\n\n onOpen = () => {\n this.opened = true\n this.autoAdjustDirection()\n }\n\n onClose = () => {\n this.opened = false\n // Don't reset position-adjustment state here \u2014 the dialog has a fade-out\n // transition, and clearing it immediately would snap the dialog back to\n // its default spot mid-animation. autoAdjustDirection resets on next open.\n }\n\n onKeydown = (event) => {\n if (event.key === \"Escape\" && this.opened) {\n event.stopPropagation()\n this.duetPicker.hide()\n } else if (event.key.startsWith(\"Arrow\") && this.opened) {\n event.stopPropagation()\n }\n }\n\n autoAdjustDirection = () => {\n const content = this.duetPicker.querySelector(\".duet-date__dialog-content\")\n const dialog = this.duetPicker.querySelector(\".duet-date__dialog\")\n if (!content || !dialog) return\n\n // Reset state from the previous open before re-evaluating.\n this.duetPicker.direction = \"right\"\n dialog.classList.remove(\"is-above\")\n dialog.style.insetInlineEnd = \"\"\n\n const { right, bottom, width } = content.getBoundingClientRect()\n\n if (window.innerWidth - right < width) this.duetPicker.direction = \"left\"\n if (bottom > window.innerHeight) dialog.classList.add(\"is-above\")\n\n this.alignToSidebarEdge(dialog)\n }\n\n // When the picker sits inside a narrow popup (e.g. bubble-up action-sheet) that lives in the\n // sidebar, the calendar's `right: 0` anchors it to the input's right edge \u2014 leaving the calendar\n // protruding past the popup's left edge and looking detached. Shift it so the right edge aligns\n // with the sidebar instead, keeping the whole calendar inside the sidebar's visual bounds.\n // Skipped for RTL layouts since the alignment math derives from physical edges; BC apps render\n // LTR today, and a future RTL pass would need to mirror this against the inline-end edge.\n alignToSidebarEdge = (dialog) => {\n const sidebar = this.closest(\".sidebar\")\n const popup = this.closest(\".action-sheet\")\n if (!sidebar || !popup) return\n if (getComputedStyle(this).direction === \"rtl\") return\n\n const sidebarRect = sidebar.getBoundingClientRect()\n const myRect = this.getBoundingClientRect()\n const offset = (sidebarRect.right - 8) - myRect.right\n if (offset > 0) dialog.style.insetInlineEnd = `-${offset}px`\n }\n\n get minDate() {\n const minDate = this.getAttribute(\"data-min-date\")\n return minDate ? CalendarDate.parse(minDate) : undefined\n }\n\n set minDate(value) {\n value = CalendarDate.parse(value).toString()\n this.duetPicker.min = value\n this.setAttribute(\"data-min-date\", value)\n }\n\n get maxDate() {\n const maxDate = this.getAttribute(\"data-max-date\")\n return maxDate ? CalendarDate.parse(maxDate) : undefined\n }\n\n set maxDate(value) {\n value = CalendarDate.parse(value).toString()\n this.duetPicker.max = value\n this.setAttribute(\"data-max-date\", value)\n }\n\n get name() {\n return this.getAttribute(\"name\")\n }\n\n get value() {\n return this.getAttribute(\"value\")\n }\n\n set value(value) {\n if (value != null) {\n this.duetPicker.value = value\n this.setAttribute(\"value\", value)\n this.input?.setAttribute(\"value\", value)\n } else {\n this.duetPicker.value = null\n this.removeAttribute(\"value\")\n this.input?.removeAttribute(\"value\", value)\n }\n }\n\n get date() {\n return this.value ? CalendarDate.parse(this.value) : null\n }\n\n set date(date) {\n this.value = date?.toString()\n }\n\n get disabled() {\n return this.hasAttribute(\"disabled\")\n }\n\n set disabled(value) {\n const boolValue = !!value // toggleAttribute second argument only works with booleans\n\n this.input.disabled = boolValue\n this.duetPicker.disabled = boolValue\n this.toggleAttribute(\"disabled\", boolValue)\n }\n\n get required() {\n return this.hasAttribute(\"required\")\n }\n\n set required(value) {\n this.duetPicker.required = !!value\n this.toggleAttribute(\"required\", !!value)\n }\n}\n\nconst findOrCreateInput = function(element) {\n return findInput(element) || createInput(element)\n}\n\nconst findInput = element => element.querySelector(\"input[type=hidden]\")\n\nconst createInput = function(element) {\n if (element.name != null) {\n const input = BC.makeElement(\"input\", { type: \"hidden\", name: element.name, value: element.value != null ? element.value : \"\" })\n element.appendChild(input)\n return input\n }\n}\n\nconst initializeDuetPicker = function(element) {\n findDuetPicker(element)?.remove()\n return createDuetPicker(element)\n}\n\nconst findDuetPicker = element => element.querySelector(\"duet-date-picker\")\n\nconst createDuetPicker = function(element) {\n const attributes = { \"first-day-of-week\": parseInt(BC.current.person.firstWeekDayInt, 10) || 0 }\n attributes.value = element.value || \"\"\n\n if (element.id) attributes.identifier = `${element.id}_picker`\n if (element.disabled) attributes.disabled = \"disabled\"\n if (element.required) attributes.required = \"required\"\n if (element.maxDate) attributes.max = element.maxDate\n if (element.minDate) attributes.min = element.minDate\n\n const duetPicker = BC.makeElement(\"duet-date-picker\", attributes)\n duetPicker.dateAdapter.format = buildFormatAdapter(element.dataset.formatType || \"default\")\n\n const label = element.dataset.label\n const buttonLabel = label ? `Choose ${label}` : \"Choose date\"\n duetPicker.localization.buttonLabel = buttonLabel\n duetPicker.localization.placeholder = label || \"Choose date\"\n element.appendChild(duetPicker)\n\n return duetPicker\n}\n\nconst buildFormatAdapter = (type) => {\n let formatter\n\n switch (type) {\n case \"month_day\":\n formatter = new Intl.DateTimeFormat(\"en\", { day: \"numeric\", month: \"short\" })\n break\n case \"month_day_year\":\n formatter = new Intl.DateTimeFormat(\"en\", { day: \"numeric\", month: \"short\", year: \"numeric\" })\n break\n default:\n formatter = new Intl.DateTimeFormat(\"en\", { weekday: \"short\", day: \"numeric\", month: \"short\", year: \"numeric\" })\n }\n\n return (date) => formatter.format(date)\n}\n\nBC.DateInputElement = BcDateInput\nwindow.customElements.define(\"bc-date-picker\", BcDateInput)\n", "class TimeInputElement extends HTMLElement {\n get value() {\n return this.getAttribute(\"value\")\n }\n\n set value(value) {\n this.setAttribute(\"value\", value)\n }\n\n get time() {\n if (this.value) {\n try {\n return CalendarTime.parse(this.value)\n } catch (error) {\n return null\n }\n }\n\n return null\n }\n\n set time(time) {\n this.value = time || \"\"\n this.input.value = time?.toString({12: this.format == \"twelve_hour\", short: true}) || \"\"\n this.dispatchEvent(new Event(\"bc:time-change\", { bubbles: true }))\n }\n\n connectedCallback() {\n BC.ready(() => {\n this.input = findOrCreateInput(this)\n this.format = this.dataset.format\n this.controller = new BC.TimeInputController(this)\n this.time = this.controller.parseTimeFromInput()\n this.setAttribute(\"initialized\", \"\")\n })\n }\n\n disconnectedCallback() {\n this.controller?.destroy()\n }\n\n shake() {\n BC.shakeElement(this.input)\n }\n}\n\nconst findOrCreateInput = function(element) {\n const input = element.querySelector(\"input\") || createInput(element)\n input.setAttribute(\"autocomplete\", \"off\")\n input.setAttribute(\"autocorrect\", \"off\")\n return input\n}\n\nconst createInput = function(element) {\n const input = document.createElement(\"input\")\n input.type = \"text\"\n input.classList.add(\"input\")\n element.appendChild(input)\n return input\n}\n\nBC.TimeInputElement = TimeInputElement\nwindow.customElements.define(\"bc-time-picker\", TimeInputElement)\n\n", "const OPTIONS = function(format) {\n const times = []\n\n if (format == \"twelve_hour\") {\n [\"am\", \"pm\"].forEach((p) => {\n Array.from([12].concat([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])).forEach((i) => {\n [\"00\", \"30\"].forEach((m) => times.push(`${i}:${m}${p}`))\n })\n })\n } else {\n for (let hour = 0; hour < 24; hour++) {\n [\"00\", \"30\"].forEach((minute) => times.push(`${hour}:${minute}`))\n }\n }\n\n return times\n}\n\nBC.TimeInputController = class TimeInputController {\n constructor(element) {\n this.didFocusInput = this.didFocusInput.bind(this)\n this.didClickInput = this.didClickInput.bind(this)\n this.didChangeInput = this.didChangeInput.bind(this)\n this.didPressKey = this.didPressKey.bind(this)\n this.didClick = this.didClick.bind(this)\n this.didClickOption = this.didClickOption.bind(this)\n this.didWindowFocus = this.didWindowFocus.bind(this)\n this.element = element\n this.format = this.element.format\n this.input = this.element.input\n this.listboxId = `${this.input.id}_listbox`\n this.installClickListener()\n this.installInputListeners()\n this.configureComboboxRole()\n window.addEventListener(\"focus\", this.didWindowFocus)\n }\n\n destroy() {\n this.uninstallClickListener()\n window.removeEventListener(\"focus\", this.didWindowFocus)\n }\n\n didFocusInput() {\n if (this.windowJustActivated) return\n this.revealOptions()\n this.scrollToCurrentOption()\n return this.highlightCurrentOptionElement()\n }\n\n didWindowFocus() {\n this.windowJustActivated = true\n requestAnimationFrame(() => { this.windowJustActivated = false })\n }\n\n didClickInput() {\n if (this.optionsAreHidden) {\n this.revealOptions()\n this.scrollToCurrentOption()\n this.highlightCurrentOptionElement()\n }\n }\n\n didChangeInput() {\n this.element.time = this.parseTimeFromInput()\n return this.hideOptions()\n }\n\n didPressKey(event) {\n if (event.keyCode === BC.keyCodes.tab) {\n return this.hideOptions()\n } else if (event.keyCode === BC.keyCodes.escape && !this.optionsAreHidden) {\n event.stopPropagation()\n return this.hideOptions()\n } else if (event.keyCode === BC.keyCodes.up || event.keyCode === BC.keyCodes.down) {\n requestAnimationFrame(() => this.updateSelectedOption())\n }\n }\n\n didClick(event) {\n if (this.element.contains(event.target)) {\n return event.preventDefault()\n } else {\n return this.hideOptions()\n }\n }\n\n didClickOption(event) {\n if (event.target.getAttribute(\"data-role\") === \"option\") {\n this.element.time = CalendarTime.parse(event.target.textContent)\n this.hideOptions()\n this.focusInputWithoutOpeningOptions()\n return BC.triggerEvent(this.element, \"change\")\n }\n }\n\n // Private\n\n installClickListener() {\n return window.addEventListener(\"click\", this.didClick)\n }\n\n uninstallClickListener() {\n return window.removeEventListener(\"click\", this.didClick)\n }\n\n installInputListeners() {\n this.input.addEventListener(\"focus\", this.didFocusInput)\n this.input.addEventListener(\"click\", this.didClickInput)\n this.input.addEventListener(\"change\", this.didChangeInput)\n return this.input.addEventListener(\"keydown\", this.didPressKey)\n }\n\n focusInputWithoutOpeningOptions() {\n this.input.removeEventListener(\"focus\", this.didFocusInput)\n this.input.focus()\n this.input.addEventListener(\"focus\", this.didFocusInput)\n }\n\n get optionsAreHidden() {\n return !this.optionsElement || this.optionsElement.style.display === \"none\"\n }\n\n configureComboboxRole() {\n this.input.setAttribute(\"role\", \"combobox\")\n this.input.setAttribute(\"aria-haspopup\", \"listbox\")\n this.input.setAttribute(\"aria-expanded\", \"false\")\n this.input.setAttribute(\"aria-controls\", this.listboxId)\n }\n\n updateSelectedOption() {\n const selectedOption = this.optionsList?.querySelector(\".time-picker__item--selected\")\n if (selectedOption) {\n selectedOption.scrollIntoView({ block: \"nearest\" })\n this.input.setAttribute(\"aria-activedescendant\", selectedOption.id)\n } else {\n this.input.removeAttribute(\"aria-activedescendant\")\n }\n }\n\n parseTimeFromInput() {\n let time\n try {\n if (this.input.value) time = CalendarTime.parse(this.input.value)\n } catch (error) {\n // eslint-disable-next-line no-empty\n }\n return time\n }\n\n hideOptions() {\n if (this.optionsElement != null) {\n this.optionsElement.style.display = \"none\"\n }\n this.input.setAttribute(\"aria-expanded\", \"false\")\n this.input.removeAttribute(\"aria-activedescendant\")\n }\n\n revealOptions() {\n if (this.optionsElement == null) {\n this.optionsElement = this.createAndPopulateOptionsElement()\n this.element.appendChild(this.optionsElement)\n this.optionsElement.addEventListener(\"click\", this.didClickOption)\n }\n this.optionsElement.style.display = \"block\"\n this.input.setAttribute(\"aria-expanded\", \"true\")\n }\n\n createAndPopulateOptionsElement() {\n // Need two elements (wrapper + list) in order to workaround a Safari rendering bug\n // that makes the first overflowed element clickable even though it's hidden\n const optionsElement = document.createElement(\"div\")\n optionsElement.classList.add(\"time-picker__options-wrapper\")\n optionsElement.innerHTML = \"\"\n this.optionsList = optionsElement.firstChild\n this.optionsList.setAttribute(\"role\", \"listbox\")\n this.optionsList.id = this.listboxId\n\n Array.from(OPTIONS(this.format)).forEach((option) => {\n const optionElement = document.createElement(\"li\")\n const itemElement = document.createElement(\"a\")\n const timeValue = CalendarTime.parse(option)\n itemElement.classList.add(\"time-picker__item\")\n itemElement.setAttribute(\"data-role\", \"option\")\n itemElement.setAttribute(\"role\", \"option\")\n itemElement.id = `${this.listboxId}_${timeValue}`\n itemElement.textContent = option\n itemElement.dataset.option = timeValue\n itemElement.dataset.listSelectionTarget = \"item\"\n itemElement.setAttribute(\"href\", \"#\")\n itemElement.setAttribute(\"tabindex\", \"-1\")\n\n optionElement.appendChild(itemElement)\n this.optionsList.appendChild(optionElement)\n })\n\n return optionsElement\n }\n\n scrollToCurrentOption() {\n if (this.optionsList != null) {\n this.optionsList.scrollTop = this.scrollOffsetToCurrentOption()\n }\n }\n\n scrollOffsetToCurrentOption() {\n const currentOptionElement = this.findCurrentOptionElement()\n if (currentOptionElement) {\n const currentOptionElementHeight = currentOptionElement.getBoundingClientRect().height\n return currentOptionElement.offsetTop - currentOptionElementHeight\n }\n }\n\n findCurrentOptionElement() {\n if (this.element.time != null) { return this.optionsList?.querySelector(`a[data-option='${this.element.time.toString()}']`) }\n }\n\n highlightCurrentOptionElement() {\n const currentOptionElement = this.findCurrentOptionElement()\n if (currentOptionElement) {\n $(this.element).find(\"[data-role=option]\").removeClass(\"time-picker__item--selected\")\n this.element.dataset.listSelectionIndex = $(this.element).find(\"[data-role=option]\").index(currentOptionElement)\n currentOptionElement.classList.add(\"time-picker__item--selected\")\n }\n this.updateSelectedOption()\n }\n}\n", "class RelativeDateInputElement extends HTMLElement {\n connectedCallback() {\n BC.ready(() => {\n this.input = findOrCreateInput(this)\n this.trigger = findOrCreateTrigger(this)\n this.valueDidChange()\n this.controller = new BC.RelativeDateInputController(this)\n this.setAttribute(\"initialized\", \"\")\n })\n }\n\n shake() {\n BC.shakeElement(this.trigger)\n }\n\n open() {\n this.controller?.openPicker()\n }\n\n valueDidChange() {\n // Temporarily turning readOnly on/off works around an issue in Safari where\n // both the placeholder content and the input value are simultaneously displayed\n this.trigger.readOnly = false\n\n if (this.date) {\n this.trigger.value = `Week ${this.week}, ${this.date.getAbbreviatedWeekdayName()}`\n } else {\n this.trigger.value = \"\"\n }\n\n this.trigger.readOnly = true\n\n this.dispatchEvent(new Event(\"bc:date-change\", { bubbles: true }))\n }\n\n get name() {\n return this.getAttribute(\"name\")\n }\n\n get value() {\n return this.getAttribute(\"value\")\n }\n\n set value(value) {\n const originalValue = this.value\n\n if (value) {\n this.setAttribute(\"value\", value)\n this.input?.setAttribute(\"value\", value)\n } else {\n this.removeAttribute(\"value\")\n this.input?.removeAttribute(\"value\")\n }\n\n if (this.value !== originalValue) this.valueDidChange()\n }\n\n get date() {\n return this.value ? CalendarDate.parse(this.value) : null\n }\n\n set date(date) {\n this.value = date?.toString()\n }\n\n get week() {\n return this.date ? this.date.weeksSince(BC.TEMPLATE_BASE_DATE) + 1 : null\n }\n\n get disabled() {\n return this.input.disabled\n }\n\n set disabled(value) {\n this.input.disabled = !!value\n }\n}\n\nconst findOrCreateInput = function(element) {\n const input = findInput(element)\n return input || createInput(element)\n}\n\nconst findInput = element => element.querySelector(\"input[type=hidden]\")\n\nconst createInput = function(element) {\n if (element.name) {\n const value = element.value || \"\"\n const input = BC.makeElement(\"input\", {type: \"hidden\", name: element.name, value: value})\n element.appendChild(input)\n return input\n }\n}\n\nconst findOrCreateTrigger = function(element) {\n const trigger = findTrigger(element)\n return trigger || createTrigger(element)\n}\n\nconst findTrigger = element => element.querySelector(\"input:not([type=hidden])\")\n\nconst createTrigger = function(element) {\n const trigger = BC.makeElement(\"input\", {type: \"text\", class: \"input\"})\n trigger.readOnly = true\n element.appendChild(trigger)\n return trigger\n}\n\nBC.RelativeDateInputElement = RelativeDateInputElement\nwindow.customElements.define(\"bc-relative-date-input\", RelativeDateInputElement)\n\n", "class BcRelativeDatePicker extends HTMLElement {\n set date(date) {\n this.chosenDate = date\n this.visibleDate = date\n }\n\n get date() {\n return this.chosenDate\n }\n\n get week() {\n return this.visibleDate.weeksSince(BC.TEMPLATE_BASE_DATE) + 1\n }\n\n // Private\n\n get visibleDate() {\n return this._visibleDate || BC.TEMPLATE_BASE_DATE\n }\n\n set visibleDate(date) {\n this._visibleDate = date\n this.dateDidChange()\n }\n\n connectedCallback() {\n BC.ready(() => {\n this.controller = new BC.RelativeDatePickerController(this)\n this.weekNameElement = this.querySelector(\"[data-role=week-name]\")\n this.daysContainerElement = this.querySelector(\"[data-role=days-list]\")\n this.dayElement = this.querySelector(\"[data-role=day]\")\n this.previousArrowElement = this.querySelector(\"[data-role=previous-week]\")\n this.setAttribute(\"initialized\", \"\")\n this.hide()\n })\n }\n\n showPreviousWeek() {\n if (this.week > 1) {\n this.visibleDate = this.visibleDate.previous(7)\n }\n }\n\n showNextWeek() {\n this.visibleDate = this.visibleDate.next(7)\n }\n\n show() {\n if (this.style) { this.style.display = \"block\" }\n }\n\n hide() {\n if (this.style) { this.style.display = \"none\" }\n }\n\n dateDidChange() {\n this.updateWeekName()\n this.updateDays()\n this.updatePreviousArrow()\n }\n\n updateWeekName() {\n if (this.weekNameElement) {\n this.weekNameElement.textContent = `Week ${this.week}`\n }\n }\n\n updateDays() {\n let child = this.daysContainerElement.firstChild\n\n while (child) {\n this.daysContainerElement.removeChild(child)\n child = this.daysContainerElement.firstChild\n }\n\n for (let n = 0; n <= 6; n++) {\n const day = this.visibleDate.beginningOfWeek().next(n)\n const dayOption = this.dayElement.cloneNode(true)\n dayOption.date = day\n dayOption.textContent = day.getWeekdayName()\n if (day.toString() === this.chosenDate?.toString()) {\n dayOption.classList.add(\"relative-datepicker__day--active\")\n }\n this.daysContainerElement.appendChild(dayOption)\n }\n }\n\n updatePreviousArrow() {\n if (this.week < 2) {\n this.previousArrowElement.classList.add(\"ui-datepicker-prev--disabled\")\n } else {\n this.previousArrowElement.classList.remove(\"ui-datepicker-prev--disabled\")\n }\n }\n}\n\nBC.RelativeDatePickerElement = BcRelativeDatePicker\nwindow.customElements.define(\"bc-relative-date-picker\", BcRelativeDatePicker)\n\n", "if (!window.JST) window.JST = {}\nwindow.JST['bc/inputs/relative_date/picker'] = function(object){\nvar __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\n__p+='