{"version":3,"file":"consent_manager.min.js","sources":["https:\/\/lmsacelab.com\/m45\/filter\/consentmanager\/amd\/src\/consent_manager.js"],"sourcesContent":["\/\/ This file is part of Moodle - http:\/\/moodle.org\/\n\/\/\n\/\/ Moodle is free software: you can redistribute it and\/or modify\n\/\/ it under the terms of the GNU General Public License as published by\n\/\/ the Free Software Foundation, either version 3 of the License, or\n\/\/ (at your option) any later version.\n\/\/\n\/\/ Moodle is distributed in the hope that it will be useful,\n\/\/ but WITHOUT ANY WARRANTY; without even the implied warranty of\n\/\/ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\/\/ GNU General Public License for more details.\n\/\/\n\/\/ You should have received a copy of the GNU General Public License\n\/\/ along with Moodle. If not, see .\n\n\/**\n * JavaScript for consent manager.\n *\n * @module filter\/consentmanager\n * @copyright 2025 bdecent gmbh \n * @license http:\/\/www.gnu.org\/copyleft\/gpl.html GNU GPL v3 or later\n *\/\n\ndefine([\"core\/ajax\", \"core\/notification\", \"core\/str\", \"core\/modal_factory\", \"core\/modal_events\", \"core\/config\"], (\n Ajax,\n Notification,\n Str,\n ModalFactory,\n ModalEvents,\n Config,\n) => {\n var isModalOpen = false;\n var processedElements = new Set();\n var inactivePolicies = new Set();\n var displayWithdrawSetting;\n var modesetting;\n var mutationProcessedElements = new WeakSet();\n\n \/**\n * Initialize the module.\n *\n * @param {string} displaywithdraw - How to display withdraw option (\"disabled\", \"above\", \"below\").\n * @param {string} mode - Mode of operation (\"blacklist\" or \"whitelist\").\n * @returns {void}\n *\/\n var init = (displaywithdraw, mode) => {\n displayWithdrawSetting = displaywithdraw || \"disabled\";\n modesetting = mode || \"blacklist\";\n\n document.addEventListener(\"securitypolicyviolation\", handleCSPViolation);\n document.addEventListener(\"click\", handleDocumentClick);\n\n setTimeout(() => {\n document.querySelectorAll(\".filter-consentmanager-placeholder[data-policy-inactive]\").forEach((placeholder) => {\n const policyId = placeholder.getAttribute(\"data-policy-id\");\n if (policyId) {\n inactivePolicies.add(policyId);\n }\n });\n }, 300);\n\n setupMutationObserver();\n };\n\n \/**\n * Handle CSP violations.\n *\n * @param {SecurityPolicyViolationEvent} e - The CSP violation event.\n * @returns {void}\n *\/\n function handleCSPViolation(e) {\n const blockedElements = findBlockedElements(e.blockedURI);\n blockedElements.forEach(processBlockedElement);\n }\n\n \/**\n * Handle document click events.\n *\n * @param {MouseEvent} e - The click event.\n * @returns {void}\n *\/\n function handleDocumentClick(e) {\n if (isModalOpen) {\n return;\n }\n\n if (e.target.classList.contains(\"filter-consentmanager-withdraw-link\")) {\n e.preventDefault();\n e.stopPropagation();\n handleWithdrawClick(e.target);\n } else if (e.target.classList.contains(\"filter-consentmanager-consent-button\")) {\n e.preventDefault();\n e.stopPropagation();\n handleConsentClick(e.target);\n }\n }\n\n \/**\n * Handle withdraw consent click.\n *\n * @param {HTMLElement} link - The withdraw link element.\n * @returns {void}\n *\/\n function handleWithdrawClick(link) {\n const domain = link.getAttribute(\"data-domain\");\n const serviceId = link.getAttribute(\"data-service-id\");\n const policyId = link.getAttribute(\"data-policy-id\");\n\n if (policyId) {\n showWithdrawModal(domain, serviceId);\n }\n }\n\n \/**\n * Handle consent button click.\n *\n * @param {HTMLElement} button - The consent button element.\n * @returns {void}\n *\/\n function handleConsentClick(button) {\n const domain = button.getAttribute(\"data-domain\");\n const serviceId = button.getAttribute(\"data-service-id\");\n const policyId = button.getAttribute(\"data-policy-id\");\n const isPolicyInactive =\n button.closest(\".filter-consentmanager-placeholder\")?.hasAttribute(\"data-policy-inactive\") ||\n inactivePolicies.has(policyId);\n\n if (isPolicyInactive) {\n showInactivePolicyModal();\n return;\n }\n\n if (policyId) {\n showPolicyModal(policyId, domain, serviceId);\n } else {\n giveConsent(serviceId).then(handleConsentResponse).catch(Notification.exception);\n }\n }\n\n \/**\n * Handle consent response.\n *\n * @param {Object} response - The response object from the consent action.\n * @returns {void}\n *\/\n function handleConsentResponse(response) {\n if (response.error === \"policy_inactive\") {\n return;\n }\n\n if (response.success) {\n processPlaceholders();\n window.location.reload();\n }\n }\n\n \/**\n * Find elements blocked by CSP.\n *\n * @param {string} blockedURI - The URI that was blocked by CSP.\n *\/\n function findBlockedElements(blockedURI) {\n const elements = [];\n const domain = extractDomain(blockedURI);\n\n if (!domain) {\n return elements;\n }\n\n const selectors = [\"iframe\", \"img\", \"video\", \"audio\", \"object\", \"embed\"];\n selectors.forEach((tag) => {\n document.querySelectorAll(tag).forEach((element) => {\n const src = getElementSource(element);\n if (src && extractDomain(src) === domain) {\n elements.push(element);\n }\n });\n });\n\n return elements;\n }\n\n \/**\n * Process blocked element.\n *\n * @param {HTMLElement} element - The blocked element to process.\n * @returns {void}\n *\/\n function processBlockedElement(element) {\n const src = getElementSource(element);\n\n if (!src || element.hasAttribute(\"data-csp-processed\")) {\n return;\n }\n\n element.setAttribute(\"data-csp-processed\", \"true\");\n const domain = extractDomain(src);\n\n if (!domain) {\n return;\n }\n\n Ajax.call([\n {\n methodname: \"filter_consentmanager_get_service_by_domain\",\n args: { domain: domain },\n },\n ])[0].done((service) => {\n if (service?.name && service.status !== 0) {\n checkUserConsent(service.id).then((hasConsent) => {\n if (hasConsent) {\n showContentWithWithdrawOption(element, src, service);\n } else {\n showConsentPlaceholder(element, src, service);\n }\n });\n } else {\n if (modesetting === \"whitelist\") {\n showGenericBlockedPlaceholder(element, src, domain);\n } else {\n restoreBlockedContent(element, src);\n }\n }\n });\n }\n\n \/**\n * Get element source URL.\n *\n * @param {HTMLElement} element - The element to get the source from.\n * @return {string|null} - The source URL or null if not found.\n *\/\n function getElementSource(element) {\n const tagName = element.tagName.toLowerCase();\n return tagName === \"object\" ? element.getAttribute(\"data\") : element.getAttribute(\"src\");\n }\n\n \/**\n * Check user consent for service.\n *\n * @param {string} serviceId - The ID of the service to check consent for.\n * @return {Promise} - A promise that resolves to true if the user has consent, false otherwise.\n *\/\n function checkUserConsent(serviceId) {\n return Ajax.call([\n {\n methodname: \"filter_consentmanager_check_user_consent\",\n args: { serviceid: serviceId },\n },\n ])[0]\n .then((response) => response.hasConsent)\n .catch(() => false);\n }\n\n \/**\n * Show content with withdraw option.\n *\n * @param {HTMLElement} element - The element to show content for.\n * @param {string} src - The source URL of the content.\n * @param {Object} service - The service object containing id and policyid.\n * @return {void}\n *\/\n function showContentWithWithdrawOption(element, src, service) {\n if (displayWithdrawSetting === \"disabled\") {\n restoreBlockedContent(element, src);\n return;\n }\n\n const wrapper = document.createElement(\"div\");\n wrapper.className = \"filter-consentmanager-content-wrapper\";\n\n const contentElement = element.cloneNode(true);\n contentElement.removeAttribute(\"data-csp-processed\");\n setConsentAttributes(contentElement, src, service);\n\n Str.get_string(\"withdraw_consent_text\", \"filter_consentmanager\").then((withdrawText) => {\n const withdrawSection = createWithdrawSection(withdrawText, src, service);\n\n if (displayWithdrawSetting === \"above\") {\n wrapper.appendChild(withdrawSection);\n wrapper.appendChild(contentElement);\n } else {\n wrapper.appendChild(contentElement);\n withdrawSection.classList.add(\"mt-2\");\n wrapper.appendChild(withdrawSection);\n }\n\n element.parentNode.parentNode.replaceChild(wrapper, element.parentNode);\n });\n }\n\n \/**\n * Create withdraw section.\n *\n * @param {string} withdrawText - The text for the withdraw link.\n * @param {string} src - The source URL of the content.\n * @param {Object} service - The service object containing id and policyid.\n * @return {HTMLElement} - The withdraw section element.\n *\/\n function createWithdrawSection(withdrawText, src, service) {\n const withdrawLink = document.createElement(\"a\");\n withdrawLink.className = \"filter-consentmanager-withdraw-link\";\n withdrawLink.href = \"#\";\n withdrawLink.setAttribute(\"data-domain\", extractDomain(src));\n withdrawLink.setAttribute(\"data-service-id\", service.id);\n withdrawLink.setAttribute(\"data-policy-id\", service.policyid);\n withdrawLink.textContent = withdrawText;\n\n const withdrawSection = document.createElement(\"div\");\n withdrawSection.className = \"filter-consentmanager-withdraw-section\";\n withdrawSection.appendChild(withdrawLink);\n\n return withdrawSection;\n }\n\n \/**\n * Set consent attributes on element.\n *\n * @param {HTMLElement} element - The element to set attributes on.\n * @param {string} src - The source URL of the content.\n * @param {Object} service - The service object containing id and policyid.\n * @return {void}\n *\/\n function setConsentAttributes(element, src, service) {\n element.setAttribute(\"data-consent-domain\", extractDomain(src));\n element.setAttribute(\"data-consent-service-id\", service.id);\n element.setAttribute(\"data-consent-policy-id\", service.policyid);\n element.setAttribute(\"data-consent-type\", element.tagName.toLowerCase());\n element.classList.add(\"filter-consentmanager-content\");\n }\n\n \/**\n * Show consent placeholder.\n *\n * @param {HTMLElement} element - The element to show the placeholder for.\n * @param {string} src - The source URL of the content.\n * @param {Object} service - The service object containing id, name, policyid, and image..\n *\n *\/\n function showConsentPlaceholder(element, src, service) {\n const placeholder = createPlaceholder(element, src, service);\n const parentnode = element.parentElement.parentElement;\n parentnode.parentNode.replaceChild(placeholder, parentnode);\n }\n\n \/**\n * Show generic blocked placeholder.\n *\n * @param {HTMLElement} element - The element to show the placeholder for.\n * @param {string} src - The source URL of the content.\n * @param {string} domain - The domain of the content.\n *\n *\/\n function showGenericBlockedPlaceholder(element, src, domain) {\n const placeholder = createGenericPlaceholder(element, src, domain);\n const parentnode = element.parentElement.parentElement;\n parentnode.parentNode.replaceChild(placeholder, parentnode);\n }\n\n \/**\n * Create placeholder element.\n *\n * @param {HTMLElement} element - The element to create the placeholder for.\n * @param {string} src - The source URL of the content.\n * @param {Object} service - The service object containing id, name, policyid, and image.\n * @return {HTMLElement} - The created placeholder element.\n *\/\n function createPlaceholder(element, src, service) {\n const domain = extractDomain(src);\n\n const placeholder = document.createElement(\"div\");\n placeholder.className = \"filter-consentmanager-placeholder\";\n Object.assign(placeholder.style, {\n border: \"1px solid #ddd\",\n backgroundColor: \"#f8f9fa\",\n padding: \"20px\",\n textAlign: \"center\",\n });\n\n setPlaceholderAttributes(placeholder, src, domain, service);\n populatePlaceholder(placeholder, service);\n\n return placeholder;\n }\n\n \/**\n * Create generic placeholder.\n *\n * @param {HTMLElement} element - The element to create the placeholder for.\n * @param {string} src - The source URL of the content.\n * @param {string} domain - The domain of the content.\n *\/\n function createGenericPlaceholder(element, src, domain) {\n const placeholder = document.createElement(\"div\");\n placeholder.className = \"filter-consentmanager-placeholder\";\n Object.assign(placeholder.style, {\n border: \"1px solid #ddd\",\n backgroundColor: \"#f8f9fa\",\n padding: \"20px\",\n textAlign: \"center\",\n });\n\n setPlaceholderAttributes(placeholder, src, domain, null);\n\n const title = document.createElement(\"h4\");\n const description = document.createElement(\"p\");\n\n Str.get_string(\"blocked_content_title\", \"filter_consentmanager\").then((text) => (title.textContent = text));\n Str.get_string(\"blocked_content_desc\", \"filter_consentmanager\").then((text) => (description.textContent = text));\n\n placeholder.appendChild(title);\n placeholder.appendChild(description);\n\n return placeholder;\n }\n\n \/**\n * Set placeholder attributes.\n *\n * @param {HTMLElement} placeholder - The placeholder element to set attributes on.\n * @param {string} src - The source URL of the content.\n * @param {string} domain - The domain of the content.\n * @param {Object} service - The service object containing id, name, policyid, and image.\n * @return {void}\n *\n *\/\n function setPlaceholderAttributes(placeholder, src, domain, service) {\n placeholder.setAttribute(\"data-original-src\", src);\n placeholder.setAttribute(\"data-domain\", domain);\n placeholder.setAttribute(\"data-content-type\", placeholder.tagName?.toLowerCase() || \"div\");\n\n if (service) {\n placeholder.setAttribute(\"data-service-id\", service.id);\n placeholder.setAttribute(\"data-service-name\", service.name);\n placeholder.setAttribute(\"data-policy-id\", service.policyid);\n\n if (service.image) {\n placeholder.setAttribute(\"data-service-image\", service.image);\n }\n\n }\n }\n\n \/**\n * Populate placeholder content.\n *\n * @param {HTMLElement} placeholder - The placeholder element to populate.\n * @param {Object} service - The service object containing id, name, policyid, image, and description.\n * @return {void}\n *\/\n function populatePlaceholder(placeholder, service) {\n const title = document.createElement(\"h4\");\n const description = document.createElement(\"p\");\n\n Str.get_string(\"unlock_content_title\", \"filter_consentmanager\").then((text) => (title.textContent = text));\n placeholder.appendChild(title);\n\n if (service?.image) {\n const imageContainer = document.createElement(\"div\");\n imageContainer.className = \"service-image-container mb-3\";\n const img = document.createElement(\"img\");\n img.src = service.image.startsWith(\"http\")\n ? service.image\n : Config.wwwroot + \"\/\" + service.image.replace(\/^\\\/+\/, \"\");\n img.alt = service.name;\n img.className = \"service-image\";\n Object.assign(img.style, { maxWidth: \"100px\", maxHeight: \"50px\", objectFit: \"contain\" });\n imageContainer.appendChild(img);\n placeholder.appendChild(imageContainer);\n }\n\n if (service?.description) {\n const serviceDesc = document.createElement(\"p\");\n serviceDesc.className = \"service-description\";\n serviceDesc.textContent = service.description;\n placeholder.appendChild(serviceDesc);\n }\n\n Str.get_string(\"unlock_content_desc\", \"filter_consentmanager\", service?.name || \"\").then(\n (text) => (description.textContent = text),\n );\n\n\n placeholder.appendChild(description);\n\n if (service) {\n const button = document.createElement(\"button\");\n button.className = \"btn btn-primary filter-consentmanager-consent-button\";\n button.setAttribute(\"data-domain\", extractDomain(placeholder.getAttribute(\"data-original-src\")));\n button.setAttribute(\"data-service-id\", service.id);\n button.setAttribute(\"data-policy-id\", service.policyid);\n\n Str.get_string(\"give_consent_button\", \"filter_consentmanager\").then((text) => (button.textContent = text));\n placeholder.appendChild(button);\n }\n }\n\n \/**\n * Restore blocked content.\n *\n * @param {HTMLElement} element - The element to restore.\n * @param {string} src - The source URL of the content.\n * @return {void}\n *\/\n function restoreBlockedContent(element, src) {\n const tagName = element.tagName.toLowerCase();\n const attr = tagName === \"object\" ? \"data\" : \"src\";\n element.setAttribute(attr, src);\n element.removeAttribute(\"data-csp-processed\");\n }\n\n \/**\n * Extract domain from URL.\n *\n * @param {string} url - The URL to extract the domain from.\n * @return {string|null} - The extracted domain or null if invalid.\n *\/\n function extractDomain(url) {\n try {\n return new URL(url).hostname;\n } catch (e) {\n return null;\n }\n }\n\n \/**\n * Show inactive policy modal.\n *\n * @return {void}\n *\/\n var showInactivePolicyModal = () => {\n if (isModalOpen) { return; }\n isModalOpen = true;\n\n Promise.all([\n Str.get_string(\"policy_inactive_modal_title\", \"filter_consentmanager\"),\n Str.get_string(\"policy_inactive_modal_message\", \"filter_consentmanager\"),\n ])\n .then(([title, message]) => {\n const modalBody = `
\n <\/i> ${message}<\/strong><\/div>`;\n ModalFactory.create({\n type: ModalFactory.types.DEFAULT,\n title: title,\n body: modalBody,\n })\n .then((modal) => {\n modal.getRoot().on(ModalEvents.hidden, () => {\n isModalOpen = false;\n window.location.reload();\n });\n\n modal.show();\n })\n .catch(() => (isModalOpen = false));\n })\n .catch(() => (isModalOpen = false));\n };\n\n \/**\n * Show policy modal.\n *\n * @param {string} policyId - The ID of the policy to view.\n * @param {string} domain - The domain of the service.\n * @param {string} serviceId - The ID of the service.\n * @return {void}\n *\/\n var showPolicyModal = (policyId, domain, serviceId) => {\n if (inactivePolicies.has(policyId)) {\n showInactivePolicyModal();\n return;\n }\n\n isModalOpen = true;\n\n Promise.all([\n Str.get_string(\"view_policy_button\", \"filter_consentmanager\"),\n Str.get_string(\"accept\", \"moodle\"),\n Str.get_string(\"cancel\", \"moodle\"),\n Str.get_string(\"loading\", \"moodle\"),\n ])\n .then(([viewPolicyText, acceptText, cancelText, loadingText]) => {\n ModalFactory.create({\n type: ModalFactory.types.DEFAULT,\n title: viewPolicyText,\n body: `

${loadingText}...<\/p><\/div>`,\n }).then((loadingModal) => {\n loadingModal.show();\n loadingModal.getRoot().on(ModalEvents.hidden, () => (isModalOpen = false));\n\n fetchPolicyContent(policyId)\n .then((policyData) => {\n loadingModal.hide();\n\n if (!policyData?.name || !policyData.content) {\n Notification.alert(viewPolicyText, \"Unable to load policy content\", \"OK\");\n return;\n }\n\n if (policyData.inactive) {\n inactivePolicies.add(policyId);\n showInactivePolicyModal();\n return;\n }\n\n ModalFactory.create({\n type: ModalFactory.types.DEFAULT,\n title: `${viewPolicyText}: ${policyData.name}`,\n body: `

${policyData.content}<\/div>`,\n large: true,\n }).then((modal) => {\n modal.getRoot().on(ModalEvents.hidden, () => (isModalOpen = false));\n\n const footer = createModalFooter([\n { text: cancelText, class: \"btn-secondary\", action: () => modal.hide() },\n {\n text: acceptText,\n class: \"btn-primary\",\n action: (btn) => handlePolicyAccept(btn, acceptText, policyId, domain, serviceId, modal),\n },\n ]);\n\n modal.getRoot()[0].querySelector(\".modal-content\").appendChild(footer);\n modal.show();\n });\n })\n .catch(() => {\n loadingModal.hide();\n Notification.alert(viewPolicyText, \"Unable to load policy content\", \"OK\");\n });\n });\n }).catch(() => (isModalOpen = false));\n };\n\n \/**\n * Handle policy accept.\n *\n * @param {HTMLElement} button - The button that was clicked to accept the policy.\n * @param {string} acceptText - The text to display on the button while processing.\n * @param {string} policyId - The ID of the policy being accepted.\n * @param {string} domain - The domain of the service.\n * @param {string} serviceId - The ID of the service.\n * @param {Object} modal - The modal instance containing the policy content.\n * @return {void}\n *\/\n function handlePolicyAccept(button, acceptText, policyId, domain, serviceId, modal) {\n button.disabled = true;\n button.innerHTML = `<\/span> ${acceptText}`;\n\n const userId = Config.userId || 0;\n if (userId > 0) {\n directAcceptPolicy(userId, policyId)\n .then((response) => {\n if (response.success) {\n Notification.addNotification({ message: response.message, type: \"success\" });\n processPlaceholders();\n modal.hide();\n window.location.reload();\n } else if (response.error === \"policy_inactive\") {\n inactivePolicies.add(policyId);\n modal.hide();\n setTimeout(() => showInactivePolicyModal(), 300);\n } else {\n Notification.addNotification({ message: response.message, type: \"error\" });\n resetButton(button, acceptText);\n }\n })\n .catch((error) => {\n Notification.exception(error);\n resetButton(button, acceptText);\n });\n } else {\n giveConsent(serviceId)\n .then((response) => {\n if (response.error === \"policy_inactive\") {\n inactivePolicies.add(policyId);\n showInactivePolicyModal();\n modal.hide();\n } else {\n processPlaceholders();\n modal.hide();\n window.location.reload();\n }\n })\n .catch((error) => {\n Notification.exception(error);\n resetButton(button, acceptText);\n });\n }\n }\n\n \/**\n * Show withdraw modal.\n *\n * @param {string} domain - The domain of the service.\n * @param {string} serviceId - The ID of the service.\n * @return {void}\n *\/\n var showWithdrawModal = (domain, serviceId) => {\n isModalOpen = true;\n\n Promise.all([\n Str.get_string(\"withdraw_consent_title\", \"filter_consentmanager\"),\n Str.get_string(\"consent_withdrawal_confirm\", \"filter_consentmanager\"),\n Str.get_string(\"withdraw_user_consent\", \"filter_consentmanager\"),\n Str.get_string(\"cancel\", \"moodle\"),\n ])\n .then(([withdrawTitle, withdrawConfirm, withdrawButtonText, cancelText]) => {\n const serviceName = getServiceName(serviceId) || domain;\n const modalBody = `${withdrawConfirm.replace(\"{$a}\", serviceName)}<\/strong>`;\n\n ModalFactory.create({\n type: ModalFactory.types.DEFAULT,\n title: withdrawTitle,\n body: modalBody,\n })\n .then((modal) => {\n modal.getRoot().on(ModalEvents.hidden, () => (isModalOpen = false));\n\n const footer = createModalFooter([\n { text: cancelText, class: \"btn-secondary\", action: () => modal.hide() },\n {\n text: withdrawButtonText,\n class: \"btn-danger\",\n action: (btn) => handleWithdrawConsent(btn, withdrawButtonText, domain, serviceId, modal),\n },\n ]);\n\n modal.getRoot()[0].querySelector(\".modal-content\").appendChild(footer);\n modal.show();\n })\n .catch(() => (isModalOpen = false));\n })\n .catch(() => (isModalOpen = false));\n };\n\n \/**\n * Handle withdraw consent.\n *\n * @param {HTMLElement} button - The button that was clicked to withdraw consent.\n * @param {string} withdrawButtonText - The text to display on the button while processing.\n * @param {string} domain - The domain of the service.\n * @param {string} serviceId - The ID of the service.\n * @param {Object} modal - The modal instance containing the withdraw confirmation.\n * @return {void}\n *\/\n function handleWithdrawConsent(button, withdrawButtonText, domain, serviceId, modal) {\n button.disabled = true;\n button.innerHTML = `<\/span> ${withdrawButtonText}`;\n\n withdrawConsent(serviceId)\n .then(() => {\n Notification.addNotification({\n message: \"Your consent has been withdrawn successfully.\",\n type: \"success\",\n });\n modal.hide();\n window.location.reload();\n })\n .catch(() => {\n Notification.addNotification({\n message: \"There was an error withdrawing your consent.\",\n type: \"error\",\n });\n resetButton(button, withdrawButtonText);\n });\n }\n\n \/**\n * Create modal footer with buttons.\n *\n * @param {Array} buttons - Array of button configurations.\n * @return {HTMLElement} - The created footer element.\n *\/\n function createModalFooter(buttons) {\n const footer = document.createElement(\"div\");\n footer.className = \"modal-footer\";\n\n buttons.forEach((buttonConfig) => {\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.className = `btn ${buttonConfig.class}`;\n button.textContent = buttonConfig.text;\n button.addEventListener(\"click\", () => buttonConfig.action(button));\n footer.appendChild(button);\n });\n\n return footer;\n }\n\n \/**\n * Reset button state. This function resets the button to its original state after an action.\n *\n * @param {HTMLElement} button - The button to reset.\n * @param {string} originalText - The original text to restore.\n * @returns {void}\n *\/\n function resetButton(button, originalText) {\n button.disabled = false;\n button.textContent = originalText;\n }\n\n \/**\n * Get service name by ID.\n *\n * @param {string} serviceId - The ID of the service.\n * @return {string|null} - The name of the service or null if not found.\n *\/\n function getServiceName(serviceId) {\n const serviceElements = document.querySelectorAll(`[data-service-id=\"${serviceId}\"]`);\n return serviceElements.length ? serviceElements[0].getAttribute(\"data-service-name\") : null;\n }\n\n \/**\n * Process placeholders on page load.\n *\/\n var processPlaceholders = () => {\n processedElements.clear();\n\n document.querySelectorAll(\".filter-consentmanager-placeholder\").forEach((placeholder) => {\n const domain = placeholder.getAttribute(\"data-domain\");\n const originalSrc = placeholder.getAttribute(\"data-original-src\");\n const contentType = placeholder.getAttribute(\"data-content-type\");\n const isPolicyInactive = placeholder.getAttribute(\"data-policy-inactive\");\n\n if (!originalSrc || !domain) {\n return;\n }\n\n const elementId = `${originalSrc}-${domain}-${contentType}`;\n if (processedElements.has(elementId)) {\n return;\n }\n\n processedElements.add(elementId);\n\n if (!isPolicyInactive && hasPolicyConsent()) {\n replaceWithOriginalContent(placeholder, originalSrc, contentType);\n }\n });\n };\n\n \/**\n * Check if user has policy consent.\n *\n * @return {boolean} - Returns false by default, can be overridden to check actual\n *\/\n var hasPolicyConsent = () => false;\n\n \/**\n * Give consent for domain\/service.\n *\n * @param {string} serviceId - The ID of the service to give consent for.\n *\/\n var giveConsent = (serviceId) => {\n if (serviceId) {\n return Ajax.call([\n {\n methodname: \"filter_consentmanager_give_consent\",\n args: { serviceid: serviceId },\n },\n ])[0]\n .then((response) => {\n if (response.success) {\n processPlaceholders();\n return response;\n }\n })\n .catch((error) => ({ success: false, message: error.message }));\n }\n\n processPlaceholders();\n return Promise.resolve({ success: true });\n };\n\n \/**\n * Withdraw consent for domain\/service.\n *\n * @param {string} serviceId - The ID of the service to withdraw consent for.\n * @return {Promise} - A promise that resolves when the consent is withdrawn.\n *\/\n var withdrawConsent = (serviceId) => {\n if (serviceId) {\n return Ajax.call([\n {\n methodname: \"filter_consentmanager_withdraw_consent\",\n args: { serviceid: serviceId },\n },\n ])[0];\n }\n return Promise.resolve();\n };\n\n \/**\n * Replace placeholder with original content.\n *\n * @param {HTMLElement} placeholder - The placeholder element to replace.\n * @param {string} src - The source URL of the content.\n * @param {string} type - The type of content (e.g., \"image\", \"iframe\", \"video\").\n * @returns {void}\n *\/\n var replaceWithOriginalContent = (placeholder, src, type) => {\n\n if (placeholder.classList.contains(\"content-replaced\") || placeholder.hasAttribute(\"data-policy-inactive\")) {\n return;\n }\n\n placeholder.classList.add(\"content-replaced\");\n\n const dimensions = {\n width: placeholder.style.width,\n height: placeholder.style.height,\n };\n\n const serviceId = placeholder.getAttribute(\"data-service-id\");\n const policyId = placeholder.getAttribute(\"data-policy-id\");\n\n const contentElement = createContentElement(type, src, dimensions);\n setConsentAttributes(contentElement, src, { id: serviceId, policyid: policyId });\n\n if (displayWithdrawSetting !== \"disabled\" && serviceId && policyId) {\n const wrapper = document.createElement(\"div\");\n wrapper.className = \"filter-consentmanager-content-wrapper\";\n wrapper.appendChild(contentElement);\n\n Str.get_string(\"withdraw_consent_text\", \"filter_consentmanager\")\n .then((withdrawText) => {\n const withdrawSection = createWithdrawSection(withdrawText, src, { id: serviceId, policyid: policyId });\n\n if (displayWithdrawSetting === \"above\") {\n wrapper.insertBefore(withdrawSection, wrapper.firstChild);\n } else {\n withdrawSection.classList.add(\"mt-2\");\n wrapper.appendChild(withdrawSection);\n }\n\n placeholder.parentNode.replaceChild(wrapper, placeholder);\n })\n .catch(() => {\n placeholder.parentNode.replaceChild(contentElement, placeholder);\n });\n } else {\n placeholder.parentNode.replaceChild(contentElement, placeholder);\n }\n };\n\n \/**\n * Create content element based on type.\n *\n * @param {string} type - The type of content (e.g., \"image\", \"iframe\", \"video\").\n * @param {string} src - The source URL of the content.\n * @param {Object} dimensions - An object containing width and height properties.\n *\/\n function createContentElement(type, src, dimensions) {\n const element = document.createElement(type === \"image\" ? \"img\" : type);\n\n const srcAttr = type === \"object\" ? \"data\" : \"src\";\n element.setAttribute(srcAttr, src);\n\n if (dimensions.width) {\n element.style.width = dimensions.width;\n }\n\n if (dimensions.height) {\n element.style.height = dimensions.height;\n }\n\n if ([\"video\", \"audio\"].includes(type)) {\n element.controls = true;\n element.preload = \"metadata\";\n }\n\n if (type === \"iframe\") {\n element.frameBorder = \"0\";\n element.allowFullscreen = true;\n element.loading = \"lazy\";\n }\n\n if (type === \"img\") {\n element.loading = \"lazy\";\n }\n\n return element;\n }\n\n \/**\n * Setup mutation observer.\n *\/\n var setupMutationObserver = () => {\n if (!window.MutationObserver) {\n return;\n }\n\n const observer = new MutationObserver((mutations) => {\n mutations.forEach((mutation) => {\n if (mutation.addedNodes?.length) {\n mutation.addedNodes.forEach((node) => {\n if (node.nodeType === Node.ELEMENT_NODE) {\n processNewElement(node);\n }\n });\n }\n\n if (mutation.type === \"attributes\" && mutation.attributeName === \"src\") {\n const element = mutation.target;\n if (isExternalContentElement(element)) {\n processExternalContentElement(element);\n }\n }\n });\n });\n\n observer.observe(document.body, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: [\"src\", \"data\"],\n });\n\n processExistingExternalContent();\n };\n\n \/**\n * Process new element.\n *\n * @param {HTMLElement} element - The new element to process.\n * @return {void}\n *\/\n function processNewElement(element) {\n if (isExternalContentElement(element)) {\n processExternalContentElement(element);\n }\n\n element\n .querySelectorAll?.(\"iframe[src], img[src], video[src], audio[src], object[data], embed[src]\")\n .forEach(processExternalContentElement);\n }\n\n \/**\n * Check if element is external content.\n *\n * @param {HTMLElement} element - The element to check.\n * @return {boolean} - Returns true if the element is an external content element, false otherwise.\n *\/\n function isExternalContentElement(element) {\n if (!element?.tagName) {\n return false;\n }\n\n const tagName = element.tagName.toLowerCase();\n const supportedTags = [\"iframe\", \"img\", \"video\", \"audio\", \"object\", \"embed\"];\n\n if (!supportedTags.includes(tagName)) {\n return false;\n }\n\n const src = element.getAttribute(\"src\") || element.getAttribute(\"data\");\n return src && isExternalUrl(src);\n }\n\n \/**\n * Process external content element.\n *\n * @param {HTMLElement} element - The element to process.\n * @return {void}\n *\/\n function processExternalContentElement(element) {\n if (\n mutationProcessedElements.has(element) ||\n element.closest(\".filter-consentmanager-content-wrapper\") ||\n element.closest(\".filter-consentmanager-placeholder\")\n ) {\n return;\n }\n\n mutationProcessedElements.add(element);\n\n const src = element.getAttribute(\"src\") || element.getAttribute(\"data\");\n if (!src || !isExternalUrl(src)) {\n return;\n }\n\n const domain = extractDomain(src);\n if (!domain) {\n return;\n }\n\n Ajax.call([\n {\n methodname: \"filter_consentmanager_get_service_by_domain\",\n args: { domain: domain },\n },\n ])[0]\n .then((service) => {\n if (service?.id && service.policyid) {\n checkUserConsent(service.id).then((hasConsent) => {\n if (hasConsent) {\n addWithdrawOptionToLoadedContent(element, domain, service);\n }\n });\n }\n })\n .catch(() => { });\n }\n\n \/**\n * Add withdraw option to loaded content.\n *\n * @param {HTMLElement} element - The element to add the withdraw option to.\n * @param {string} domain - The domain of the content.\n * @param {Object} service - The service object containing id and policyid.\n * @return {void}\n *\/\n function addWithdrawOptionToLoadedContent(element, domain, service) {\n if (displayWithdrawSetting === \"disabled\" || element.hasAttribute(\"data-withdraw-added\")) {\n return;\n }\n\n element.setAttribute(\"data-withdraw-added\", \"true\");\n setConsentAttributes(element, element.getAttribute(\"src\") || element.getAttribute(\"data\"), service);\n\n Str.get_string(\"withdraw_consent_text\", \"filter_consentmanager\")\n .then((withdrawText) => {\n const withdrawSection = createWithdrawSection(\n withdrawText,\n element.getAttribute(\"src\") || element.getAttribute(\"data\"),\n service,\n );\n\n const parentNode = element.parentNode;\n if (displayWithdrawSetting === \"above\") {\n parentNode.parentNode.insertBefore(withdrawSection, parentNode);\n } else {\n withdrawSection.classList.add(\"mt-2\");\n if (parentNode.nextSibling) {\n parentNode.parentNode.insertBefore(withdrawSection, parentNode.nextSibling);\n } else {\n parentNode.parentNode.appendChild(withdrawSection);\n }\n }\n }).catch(() => { });\n }\n\n \/**\n * Process existing external content.\n *\n * @return {void}\n *\/\n function processExistingExternalContent() {\n document\n .querySelectorAll(\"iframe[src], img[src], video[src], audio[src], object[data], embed[src]\")\n .forEach((element) => {\n if (\n !element.closest(\".filter-consentmanager-content-wrapper\") &&\n !element.closest(\".filter-consentmanager-placeholder\")\n ) {\n processExternalContentElement(element);\n }\n });\n }\n\n \/**\n * Check if URL is external.\n *\n * @param {string} url - The URL to check.\n * @return {boolean} - Returns true if the URL is external, false otherwise.\n *\/\n function isExternalUrl(url) {\n if (\n !url ||\n url.startsWith(\"\/\") ||\n url.startsWith(\".\/\") ||\n url.startsWith(\"..\/\") ||\n url.startsWith(\"data:\") ||\n url.startsWith(\"blob:\")\n ) {\n return false;\n }\n\n try {\n const urlObj = new URL(url.startsWith(\"\/\/\") ? \"https:\" + url : url, window.location.href);\n return urlObj.hostname !== window.location.hostname;\n } catch (e) {\n return false;\n }\n }\n\n \/**\n * Fetch policy content.\n *\n * @param {string} policyId - The ID of the policy to fetch content for.\n * @return {Promise} - A promise that resolves with the policy content.\n *\/\n var fetchPolicyContent = (policyId) => {\n return Ajax.call([\n {\n methodname: \"filter_consentmanager_get_policy_content\",\n args: { policyid: policyId },\n },\n ])[0].then((response) => {\n if (response.success) {\n return response.policy;\n }\n throw new Error(response.message || \"Failed to fetch policy content\");\n });\n };\n\n \/**\n * Accept policy directly.\n *\n * @param {number} userId - The ID of the user accepting the policy.\n * @param {string} policyId - The ID of the policy to accept.\n * @return {Promise} - A promise that resolves with the acceptance result.\n *\/\n var directAcceptPolicy = (userId, policyId) => {\n return Ajax.call([\n {\n methodname: \"filter_consentmanager_accept_policy\",\n args: { userid: userId, policyid: policyId },\n },\n ])[0];\n };\n\n return {\n init: init,\n processPlaceholders: processPlaceholders,\n };\n});\n"],"names":["define","Ajax","Notification","Str","ModalFactory","ModalEvents","Config","displayWithdrawSetting","modesetting","isModalOpen","processedElements","Set","inactivePolicies","mutationProcessedElements","WeakSet","handleCSPViolation","e","blockedURI","elements","domain","extractDomain","forEach","tag","document","querySelectorAll","element","src","getElementSource","push","findBlockedElements","processBlockedElement","handleDocumentClick","target","classList","contains","preventDefault","stopPropagation","link","getAttribute","serviceId","showWithdrawModal","handleWithdrawClick","button","policyId","closest","hasAttribute","has","showInactivePolicyModal","showPolicyModal","giveConsent","then","handleConsentResponse","catch","exception","handleConsentClick","response","error","success","processPlaceholders","window","location","reload","setAttribute","call","methodname","args","done","service","name","status","checkUserConsent","id","hasConsent","restoreBlockedContent","wrapper","createElement","className","contentElement","cloneNode","removeAttribute","setConsentAttributes","get_string","withdrawText","withdrawSection","createWithdrawSection","appendChild","add","parentNode","replaceChild","showContentWithWithdrawOption","placeholder","Object","assign","style","border","backgroundColor","padding","textAlign","setPlaceholderAttributes","title","description","text","textContent","image","imageContainer","img","startsWith","wwwroot","replace","alt","maxWidth","maxHeight","objectFit","serviceDesc","policyid","populatePlaceholder","createPlaceholder","parentnode","parentElement","showConsentPlaceholder","createGenericPlaceholder","showGenericBlockedPlaceholder","tagName","toLowerCase","serviceid","withdrawLink","href","attr","url","URL","hostname","Promise","all","_ref","message","modalBody","create","type","types","DEFAULT","body","modal","getRoot","on","hidden","show","_ref2","viewPolicyText","acceptText","cancelText","loadingText","loadingModal","fetchPolicyContent","policyData","hide","content","inactive","large","footer","createModalFooter","class","action","btn","disabled","innerHTML","userId","directAcceptPolicy","addNotification","setTimeout","resetButton","handlePolicyAccept","querySelector","alert","_ref3","withdrawTitle","withdrawConfirm","withdrawButtonText","serviceName","serviceElements","length","getServiceName","withdrawConsent","handleWithdrawConsent","buttons","buttonConfig","addEventListener","originalText","clear","originalSrc","contentType","isPolicyInactive","elementId","hasPolicyConsent","replaceWithOriginalContent","resolve","dimensions","width","height","srcAttr","includes","controls","preload","frameBorder","allowFullscreen","loading","createContentElement","insertBefore","firstChild","setupMutationObserver","MutationObserver","mutations","mutation","addedNodes","_mutation$addedNodes","node","nodeType","Node","ELEMENT_NODE","isExternalContentElement","processExternalContentElement","processNewElement","attributeName","observe","childList","subtree","attributes","attributeFilter","isExternalUrl","nextSibling","addWithdrawOptionToLoadedContent","policy","Error","userid","init","displaywithdraw","mode"],"mappings":"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\/C7B,OAAOoB,aAAa,iBAAkBK,QAAQ+C,UAE9C\/G,IAAI8E,WAAW,sBAAuB,yBAAyB\/B,MAAMmD,MAAU3D,OAAO4D,YAAcD,OACpGX,YAAYL,YAAY3C,SAjH5ByE,CAAoBzB,YAAavB,SAE1BuB,YA1Ca0B,CAAkB3F,EAASC,IAAKyC,SAC9CkD,WAAa5F,QAAQ6F,cAAcA,cACzCD,WAAW9B,WAAWC,aAAaE,YAAa2B,YAjIhCE,CAAuB9F,QAASC,IAAKyC,YAIzB,cAAhB3D,qBAwIuBiB,QAASC,IAAKP,cAC3CuE,qBAsCwBjE,QAASC,IAAKP,cACtCuE,YAAcnE,SAASoD,cAAc,OAC3Ce,YAAYd,UAAY,oCACxBe,OAAOC,OAAOF,YAAYG,MAAO,CAC7BC,OAAQ,iBACRC,gBAAiB,UACjBC,QAAS,OACTC,UAAW,WAGfC,yBAAyBR,YAAahE,IAAKP,OAAQ,YAE7CgF,MAAQ5E,SAASoD,cAAc,MAC\/ByB,YAAc7E,SAASoD,cAAc,YAE3CxE,IAAI8E,WAAW,wBAAyB,yBAAyB\/B,MAAMmD,MAAUF,MAAMG,YAAcD,OACrGlG,IAAI8E,WAAW,uBAAwB,yBAAyB\/B,MAAMmD,MAAUD,YAAYE,YAAcD,OAE1GX,YAAYL,YAAYc,OACxBT,YAAYL,YAAYe,aAEjBV,YA3Da8B,CAAyB\/F,EAASC,IAAKP,QACrDkG,WAAa5F,QAAQ6F,cAAcA,cACzCD,WAAW9B,WAAWC,aAAaE,YAAa2B,YA1IpCI,CAA8BhG,QAASC,IAAKP,QAE5CsD,sBAAsBhD,QAASC,iBAYtCC,iBAAiBF,eAEH,WADHA,QAAQiG,QAAQC,cACFlG,QAAQa,aAAa,QAAUb,QAAQa,aAAa,gBAS7EgC,iBAAiB\/B,kBACftC,KAAK8D,KAAK,CACb,CACIC,WAAY,2CACZC,KAAM,CAAE2D,UAAWrF,cAExB,GACEW,MAAMK,UAAaA,SAASiB,aAC5BpB,OAAM,KAAM,aAgDZgC,sBAAsBF,aAAcxD,IAAKyC,eACxC0D,aAAetG,SAASoD,cAAc,KAC5CkD,aAAajD,UAAY,sCACzBiD,aAAaC,KAAO,IACpBD,aAAa\/D,aAAa,cAAe1C,cAAcM,MACvDmG,aAAa\/D,aAAa,kBAAmBK,QAAQI,IACrDsD,aAAa\/D,aAAa,iBAAkBK,QAAQ+C,UACpDW,aAAavB,YAAcpB,mBAErBC,gBAAkB5D,SAASoD,cAAc,cAC\/CQ,gBAAgBP,UAAY,yCAC5BO,gBAAgBE,YAAYwC,cAErB1C,yBAWFH,qBAAqBvD,QAASC,IAAKyC,SACxC1C,QAAQqC,aAAa,sBAAuB1C,cAAcM,MAC1DD,QAAQqC,aAAa,0BAA2BK,QAAQI,IACxD9C,QAAQqC,aAAa,yBAA0BK,QAAQ+C,UACvDzF,QAAQqC,aAAa,oBAAqBrC,QAAQiG,QAAQC,eAC1DlG,QAAQQ,UAAUqD,IAAI,0CAkGjBY,yBAAyBR,YAAahE,IAAKP,OAAQgD,kCACxDuB,YAAY5B,aAAa,oBAAqBpC,KAC9CgE,YAAY5B,aAAa,cAAe3C,QACxCuE,YAAY5B,aAAa,kDAAqB4B,YAAYgC,oEAASC,gBAAiB,OAEhFxD,UACAuB,YAAY5B,aAAa,kBAAmBK,QAAQI,IACpDmB,YAAY5B,aAAa,oBAAqBK,QAAQC,MACtDsB,YAAY5B,aAAa,iBAAkBK,QAAQ+C,UAE\/C\/C,QAAQoC,OACRb,YAAY5B,aAAa,qBAAsBK,QAAQoC,iBAmE1D9B,sBAAsBhD,QAASC,WAE9BqG,KAAmB,WADTtG,QAAQiG,QAAQC,cACI,OAAS,MAC7ClG,QAAQqC,aAAaiE,KAAMrG,KAC3BD,QAAQsD,gBAAgB,+BASnB3D,cAAc4G,gBAER,IAAIC,IAAID,KAAKE,SACtB,MAAOlH,UACE,UASX+B,wBAA0B,KACtBtC,cACJA,aAAc,EAEd0H,QAAQC,IAAI,CACRjI,IAAI8E,WAAW,8BAA+B,yBAC9C9E,IAAI8E,WAAW,gCAAiC,2BAE\/C\/B,MAAKmF,WAAElC,MAAOmC,oBACLC,sJACsED,2BAC5ElI,aAAaoI,OAAO,CAChBC,KAAMrI,aAAasI,MAAMC,QACzBxC,MAAOA,MACPyC,KAAML,YAELrF,MAAM2F,QACHA,MAAMC,UAAUC,GAAG1I,YAAY2I,QAAQ,KACnCvI,aAAc,EACdkD,OAAOC,SAASC,YAGpBgF,MAAMI,UAET7F,OAAM,IAAO3C,aAAc,OAEnC2C,OAAM,IAAO3C,aAAc,MAWhCuC,gBAAkB,CAACL,SAAUxB,OAAQoB,aACjC3B,iBAAiBkC,IAAIH,UACrBI,2BAIJtC,aAAc,EAEd0H,QAAQC,IAAI,CACRjI,IAAI8E,WAAW,qBAAsB,yBACrC9E,IAAI8E,WAAW,SAAU,UACzB9E,IAAI8E,WAAW,SAAU,UACzB9E,IAAI8E,WAAW,UAAW,YAEzB\/B,MAAKgG,YAAEC,eAAgBC,WAAYC,WAAYC,mBAC5ClJ,aAAaoI,OAAO,CAChBC,KAAMrI,aAAasI,MAAMC,QACzBxC,MAAOgD,eACPP,2CAAqCU,+BACtCpG,MAAMqG,eACLA,aAAaN,OACbM,aAAaT,UAAUC,GAAG1I,YAAY2I,QAAQ,IAAOvI,aAAc,IAEnE+I,mBAAmB7G,UACdO,MAAMuG,gBACHF,aAAaG,OAERD,MAAAA,YAAAA,WAAYrF,MAASqF,WAAWE,eAKjCF,WAAWG,UACXhJ,iBAAiB0E,IAAI3C,eACrBI,gCAIJ3C,aAAaoI,OAAO,CAChBC,KAAMrI,aAAasI,MAAMC,QACzBxC,gBAAUgD,4BAAmBM,WAAWrF,MACxCwE,2CAAqCa,WAAWE,kBAChDE,OAAO,IACR3G,MAAM2F,QACLA,MAAMC,UAAUC,GAAG1I,YAAY2I,QAAQ,IAAOvI,aAAc,UAEtDqJ,OAASC,kBAAkB,CAC7B,CAAE1D,KAAMgD,WAAYW,MAAO,gBAAiBC,OAAQ,IAAMpB,MAAMa,QAChE,CACIrD,KAAM+C,WACNY,MAAO,cACPC,OAASC,cA2BjBxH,OAAQ0G,WAAYzG,SAAUxB,OAAQoB,UAAWsG,OACzEnG,OAAOyH,UAAW,EAClBzH,OAAO0H,yFAAoFhB,kBAErFiB,OAAS\/J,OAAO+J,QAAU,EAC5BA,OAAS,EACTC,mBAAmBD,OAAQ1H,UACtBO,MAAMK,WACCA,SAASE,SACTvD,aAAaqK,gBAAgB,CAAEjC,QAAS\/E,SAAS+E,QAASG,KAAM,YAChE\/E,sBACAmF,MAAMa,OACN\/F,OAAOC,SAASC,UACU,oBAAnBN,SAASC,OAChB5C,iBAAiB0E,IAAI3C,UACrBkG,MAAMa,OACNc,YAAW,IAAMzH,2BAA2B,OAE5C7C,aAAaqK,gBAAgB,CAAEjC,QAAS\/E,SAAS+E,QAASG,KAAM,UAChEgC,YAAY\/H,OAAQ0G,gBAG3BhG,OAAOI,QACJtD,aAAamD,UAAUG,OACvBiH,YAAY\/H,OAAQ0G,eAG5BnG,YAAYV,WACPW,MAAMK,WACoB,oBAAnBA,SAASC,OACT5C,iBAAiB0E,IAAI3C,UACrBI,0BACA8F,MAAMa,SAENhG,sBACAmF,MAAMa,OACN\/F,OAAOC,SAASC,aAGvBT,OAAOI,QACJtD,aAAamD,UAAUG,OACvBiH,YAAY\/H,OAAQ0G,eApEiBsB,CAAmBR,IAAKd,WAAYzG,SAAUxB,EAAQoB,UAAWsG,UAI1FA,MAAMC,UAAU,GAAG6B,cAAc,kBAAkBtF,YAAYyE,QAC\/DjB,MAAMI,UA5BN\/I,aAAa0K,MAAMzB,eAAgB,gCAAiC,SA+B3E\/F,OAAM,KACHmG,aAAaG,OACbxJ,aAAa0K,MAAMzB,eAAgB,gCAAiC,eAGjF\/F,OAAM,IAAO3C,aAAc,UAmElC+B,kBAAoB,CAACrB,OAAQoB,aAC7B9B,aAAc,EAEd0H,QAAQC,IAAI,CACRjI,IAAI8E,WAAW,yBAA0B,yBACzC9E,IAAI8E,WAAW,6BAA8B,yBAC7C9E,IAAI8E,WAAW,wBAAyB,yBACxC9E,IAAI8E,WAAW,SAAU,YAExB\/B,MAAK2H,YAAEC,cAAeC,gBAAiBC,mBAAoB3B,wBAClD4B,qBAoGM1I,iBACd2I,gBAAkB3J,SAASC,6CAAsCe,wBAChE2I,gBAAgBC,OAASD,gBAAgB,GAAG5I,aAAa,qBAAuB,KAtG3D8I,CAAe7I,YAAcpB,OAC3CoH,4BAAuBwC,gBAAgBnE,QAAQ,OAAQqE,0BAE7D7K,aAAaoI,OAAO,CAChBC,KAAMrI,aAAasI,MAAMC,QACzBxC,MAAO2E,cACPlC,KAAML,YAELrF,MAAM2F,QACHA,MAAMC,UAAUC,GAAG1I,YAAY2I,QAAQ,IAAOvI,aAAc,UAEtDqJ,OAASC,kBAAkB,CAC7B,CAAE1D,KAAMgD,WAAYW,MAAO,gBAAiBC,OAAQ,IAAMpB,MAAMa,QAChE,CACIrD,KAAM2E,mBACNhB,MAAO,aACPC,OAASC,cAsBNxH,OAAQsI,mBAAoB7J,OAAQoB,UAAWsG,OAC1EnG,OAAOyH,UAAW,EAClBzH,OAAO0H,yFAAoFY,oBAE3FK,gBAAgB9I,WACXW,MAAK,KACFhD,aAAaqK,gBAAgB,CACzBjC,QAAS,gDACTG,KAAM,YAEVI,MAAMa,OACN\/F,OAAOC,SAASC,YAEnBT,OAAM,KACHlD,aAAaqK,gBAAgB,CACzBjC,QAAS,+CACTG,KAAM,UAEVgC,YAAY\/H,OAAQsI,uBAxCaM,CAAsBpB,IAAKc,mBAAoB7J,EAAQoB,UAAWsG,UAI3FA,MAAMC,UAAU,GAAG6B,cAAc,kBAAkBtF,YAAYyE,QAC\/DjB,MAAMI,UAET7F,OAAM,IAAO3C,aAAc,OAEnC2C,OAAM,IAAO3C,aAAc,cAyC3BsJ,kBAAkBwB,eACjBzB,OAASvI,SAASoD,cAAc,cACtCmF,OAAOlF,UAAY,eAEnB2G,QAAQlK,SAASmK,qBACP9I,OAASnB,SAASoD,cAAc,UACtCjC,OAAO+F,KAAO,SACd\/F,OAAOkC,wBAAmB4G,aAAaxB,OACvCtH,OAAO4D,YAAckF,aAAanF,KAClC3D,OAAO+I,iBAAiB,SAAS,IAAMD,aAAavB,OAAOvH,UAC3DoH,OAAOzE,YAAY3C,WAGhBoH,gBAUFW,YAAY\/H,OAAQgJ,cACzBhJ,OAAOyH,UAAW,EAClBzH,OAAO4D,YAAcoF,iBAiBrBhI,oBAAsB,KACtBhD,kBAAkBiL,QAElBpK,SAASC,iBAAiB,sCAAsCH,SAASqE,oBAC\/DvE,OAASuE,YAAYpD,aAAa,eAClCsJ,YAAclG,YAAYpD,aAAa,qBACvCuJ,YAAcnG,YAAYpD,aAAa,qBACvCwJ,iBAAmBpG,YAAYpD,aAAa,4BAE7CsJ,cAAgBzK,oBAIf4K,oBAAeH,wBAAezK,mBAAU0K,aAC1CnL,kBAAkBoC,IAAIiJ,aAI1BrL,kBAAkB4E,IAAIyG,YAEjBD,kBAAoBE,oBACrBC,2BAA2BvG,YAAakG,YAAaC,kBAU7DG,iBAAmB,KAAM,EAOzB\/I,YAAeV,WACXA,UACOtC,KAAK8D,KAAK,CACb,CACIC,WAAY,qCACZC,KAAM,CAAE2D,UAAWrF,cAExB,GACEW,MAAMK,cACCA,SAASE,eACTC,sBACOH,YAGdH,OAAOI,SAAaC,SAAS,EAAO6E,QAAS9E,MAAM8E,aAG5D5E,sBACOyE,QAAQ+D,QAAQ,CAAEzI,SAAS,KASlC4H,gBAAmB9I,WACfA,UACOtC,KAAK8D,KAAK,CACb,CACIC,WAAY,yCACZC,KAAM,CAAE2D,UAAWrF,cAExB,GAEA4F,QAAQ+D,UAWfD,2BAA6B,CAACvG,YAAahE,IAAK+G,WAE5C\/C,YAAYzD,UAAUC,SAAS,qBAAuBwD,YAAY7C,aAAa,+BAInF6C,YAAYzD,UAAUqD,IAAI,0BAEpB6G,WAAa,CACfC,MAAO1G,YAAYG,MAAMuG,MACzBC,OAAQ3G,YAAYG,MAAMwG,QAGxB9J,UAAYmD,YAAYpD,aAAa,mBACrCK,SAAW+C,YAAYpD,aAAa,kBAEpCuC,wBAoCoB4D,KAAM\/G,IAAKyK,kBAC\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\/D7I,WAAWA,WAAWF,YAAYF,qBAG3C\/B,OAAM,SA3COiL,CAAiC5M,QAASN,EAAQgD,eAKjEf,OAAM,kBAiEN+K,cAAcnG,SAEdA,KACDA,IAAItB,WAAW,MACfsB,IAAItB,WAAW,OACfsB,IAAItB,WAAW,QACfsB,IAAItB,WAAW,UACfsB,IAAItB,WAAW,gBAER,aAIQ,IAAIuB,IAAID,IAAItB,WAAW,MAAQ,SAAWsB,IAAMA,IAAKrE,OAAOC,SAASkE,MACtEI,WAAavE,OAAOC,SAASsE,SAC7C,MAAOlH,UACE,OAUXwI,mBAAsB7G,UACf1C,KAAK8D,KAAK,CACb,CACIC,WAAY,2CACZC,KAAM,CAAEiD,SAAUvE,aAEvB,GAAGO,MAAMK,cACJA,SAASE,eACFF,SAAS+K,aAEd,IAAIC,MAAMhL,SAAS+E,SAAW,qCAWxCgC,mBAAqB,CAACD,OAAQ1H,WACvB1C,KAAK8D,KAAK,CACb,CACIC,WAAY,sCACZC,KAAM,CAAEuK,OAAQnE,OAAQnD,SAAUvE,aAEvC,SAGA,CACH8L,KA3pCO,CAACC,gBAAiBC,QACzBpO,uBAAyBmO,iBAAmB,WAC5ClO,YAAcmO,MAAQ,YAEtBpN,SAASkK,iBAAiB,0BAA2B1K,oBACrDQ,SAASkK,iBAAiB,QAAS1J,qBAEnCyI,YAAW,KACPjJ,SAASC,iBAAiB,4DAA4DH,SAASqE,oBACrF\/C,SAAW+C,YAAYpD,aAAa,kBACtCK,UACA\/B,iBAAiB0E,IAAI3C,eAG9B,KAEHqK,yBA4oCAtJ,oBAAqBA"}