{"version":3,"file":"public-DRrxSwtj.js","sources":["../../../app/frontend/scripts/register-as-organization.js","../../../app/frontend/scripts/search.js","../../../app/frontend/scripts/status-message.js","../../../app/frontend/components/utils/flashMessage.js","../../../app/frontend/scripts/vote.js","../../../app/frontend/alpine/cookie-consent.js","../../../app/frontend/alpine/dropdown-public.js","../../../app/frontend/alpine/entries-search.js","../../../app/frontend/components/locales/translations.js","../../../app/frontend/alpine/entries-translation.js","../../../app/frontend/alpine/file-upload-public.js","../../../app/frontend/alpine/form-error-handler.js","../../../app/frontend/alpine/google-maps-public.js","../../../app/frontend/alpine/news-items-categories.js","../../../app/frontend/alpine/skiplinks.js","../../../app/frontend/alpine/social-media-sharer.js","../../../node_modules/mark.js/dist/mark.es6.min.js","../../../app/frontend/alpine/search-result-highlighter.js","../../../node_modules/js-sha3/src/sha3.js","../../../app/frontend/alpine/voter-rrn.js","../../../app/frontend/alpine/nudging.js","../../../app/frontend/entrypoints/public.js"],"sourcesContent":["(() => {\n const registrationOptions = document.querySelectorAll('input[name=\"participant-registers-as\"]');\n if (registrationOptions.length == 0) return;\n const organizationInputWrapper = document.querySelector('[data-participant-registers-as=\"organization-wrapper\"]');\n const organizationInput = organizationInputWrapper.querySelector('[data-participant-registers-as=\"organization\"]');\n const firstNameInput = document.querySelector('[data-participant-input=\"first-name\"]');\n\n registrationOptions.forEach((option) => {\n if (option.value == 'participant' && option.hasAttribute('checked')) {\n organizationInputWrapper.classList.add('hidden');\n organizationInput.setAttribute('value', '');\n firstNameInput.focus();\n }\n option.addEventListener('change', toggleOrganizationField, true);\n });\n})();\n\nfunction toggleOrganizationField(event) {\n const organizationInputWrapper = document.querySelector('[data-participant-registers-as=\"organization-wrapper\"]');\n const organizationInput = organizationInputWrapper.querySelector('[data-participant-registers-as=\"organization\"]');\n const firstNameInput = document.querySelector('[data-participant-input=\"first-name\"]');\n\n if (event.currentTarget.value == 'participant') {\n organizationInputWrapper.classList.add('hidden');\n organizationInput.setAttribute('value', '');\n firstNameInput.focus();\n }\n if (event.currentTarget.value == 'organization') {\n organizationInputWrapper.classList.remove('hidden');\n organizationInput.focus();\n }\n}\n","document.addEventListener('turbo:load', () => {\n const searchButton = document.querySelector(\"[data-js-selector='search-button']\");\n const searchWrapper = document.querySelector(\"[data-js-selector='search-bar']\");\n\n if (!searchButton || !searchWrapper) return;\n\n const observer = new MutationObserver(() => {\n if (searchWrapper.classList.contains('is-open')) {\n document.querySelector('header input[name=\"q\"]').focus();\n }\n });\n\n observer.observe(searchWrapper, { attributes: true });\n\n const searchInput = {\n open: () => {\n searchButton.classList.add('is-open');\n searchWrapper.classList.add('is-open');\n },\n close: () => {\n searchButton.classList.remove('is-open');\n searchWrapper.classList.remove('is-open');\n },\n };\n\n searchButton.addEventListener('click', () => {\n const isOpen = searchButton.classList.contains('is-open');\n\n if (isOpen) searchInput.close();\n else searchInput.open();\n });\n\n searchWrapper.addEventListener('keydown', (e) => {\n if (e.key === 'Escape') {\n searchInput.close();\n searchButton.focus();\n }\n });\n});\n","const flashWrapper = document.querySelector(\"[data-js-selector='flash-wrapper']\");\nconst closeButton = document.querySelector(\"[data-id='close-flash-message']\");\n\nconst removeFlashMessage = () => flashWrapper.removeChild(flashWrapper.children[0]);\n\nconst focusFirstElement = () => {\n const main = document.querySelector('#main');\n const focusable = main.querySelectorAll(\n \"button, [href], input:not([type='hidden']), select, textarea, [tabindex]:not([tabindex='-1'])\"\n );\n const firstFocusable = focusable[0];\n firstFocusable.focus();\n};\n\ncloseButton?.addEventListener('keypress', (e) => {\n if (e.key !== 'Enter') return;\n\n try {\n removeFlashMessage();\n focusFirstElement();\n } catch (error) {\n console.error(error);\n }\n});\n\ncloseButton?.addEventListener('click', () => {\n try {\n removeFlashMessage();\n } catch (error) {\n console.error(error);\n }\n});\n","export default (message) => {\n const alertElement = document.querySelector('[data-js-selector=\"flash-wrapper\"]');\n\n if (alertElement && message) {\n clearFlashMessages(alertElement);\n\n const jsAlert = document.createElement('div');\n const attentionIcon = document.createElement('i');\n jsAlert.classList.add('alert', 'alert-danger', 'alert--fixed');\n jsAlert.dataset.jsSelector = 'alert';\n attentionIcon.classList.add('fa', 'fa-exclamation-triangle');\n jsAlert.appendChild(attentionIcon);\n const messageNode = document.createTextNode(message);\n jsAlert.appendChild(messageNode);\n\n alertElement.appendChild(jsAlert);\n setTimeout(() => {\n jsAlert.remove();\n }, 3500);\n }\n};\n\nconst clearFlashMessages = (alertElement) => {\n const jsAlerts = alertElement.querySelectorAll('[data-js-selector=\"alert\"]');\n jsAlerts.forEach((alert) => {\n alert.remove();\n });\n};\n","import setFlashMessage from '~/components/utils/flashMessage';\nimport axios from 'axios';\n\n(() => {\n const entryTileLikeButtons = document.querySelectorAll('[data-js-selector=\"toggle-vote\"]');\n entryTileLikeButtons.forEach((el) => {\n el.addEventListener('click', (evt) => toggleVote(evt));\n });\n})();\n\nconst toggleVote = (evt) => {\n const entryId = evt.currentTarget.dataset.entryId;\n if (entryId) {\n axios\n .post(`entries/${entryId}/toggle_like.json`)\n .then((response) => {\n const data = response.data;\n if (data.action === 'voted') {\n hideVoteElements(entryId);\n } else if (data.action === 'voted_do_redirect') {\n hideVoteElements(entryId);\n window.location = `entries/${entryId}/thank_you_for_voting`;\n } else if (data.trigger === 'unvoted') {\n showVoteElements(entryId);\n }\n\n updateVotesCount(entryId, data.count);\n updateTopicVotesCount(data.topic_votes_count);\n if (data.flash_notice) setFlashMessage(data.flash_notice);\n })\n .catch((error) => {\n setFlashMessage('Something went wrong, please try again or contact support.');\n console.error(error);\n });\n } else {\n console.error('Could not retrieve entry id.');\n }\n};\n\nconst hideVoteElements = (entryId) => {\n document.querySelectorAll(`#entry_${entryId} .can-vote`).forEach((el) => el.classList.add('hidden'));\n\n document.querySelectorAll(`#entry_${entryId} .cannot-vote`).forEach((el) => el.classList.remove('hidden'));\n};\n\nconst showVoteElements = (entryId) => {\n document.querySelectorAll(`#entry_${entryId} .can-vote`).forEach((el) => el.classList.remove('hidden'));\n\n document.querySelectorAll(`#entry_${entryId} .cannot-vote`).forEach((el) => el.classList.add('hidden'));\n};\n\nconst updateVotesCount = (entryId, count) => {\n const counters = document.querySelectorAll(`#entry_${entryId} .votes-count, #counters_${entryId} .votes-count`);\n counters.forEach((el) => (el.innerText = count));\n};\n\nconst updateTopicVotesCount = (count) => {\n const counter = document.querySelector('[data-js-selector=\"topic-likes-counter\"]');\n if (counter) counter.innerText = count;\n};\n\nexport { updateVotesCount };\n","export default function cookieConsent() {\n const COOKIE_NAME = 'bpart_cookie_consent_analytics';\n const COOKIE_EXPIRE = 31536000; // 1 year\n\n return {\n show: null,\n consented: null,\n\n init() {\n this.$watch('consented', (value) => this.consentChanged(value));\n this.show = !this.isAnalyticsCookieSet();\n this.consented = this.isAnalyticsAllowed();\n },\n\n consentChanged(consent) {\n switch (consent) {\n case true:\n this.setAnalyticsCookieGranted();\n break;\n case false:\n this.setAnalyticsCookieDenied();\n break;\n }\n },\n\n handleClick(consent = { consented: false }) {\n this.consented = consent.consented;\n this.show = false;\n },\n\n setAnalyticsCookieGranted() {\n if (this.isAnalyticsCookieGranted()) return;\n document.cookie = `${COOKIE_NAME}=granted; max-age=${COOKIE_EXPIRE}; path=/`;\n this.$dispatch('gtag-consent', { analytics_storage: 'granted' });\n },\n\n setAnalyticsCookieDenied() {\n if (this.isAnalyticsCookieDenied()) return;\n document.cookie = `${COOKIE_NAME}=denied; max-age=${COOKIE_EXPIRE}; path=/`;\n this.$dispatch('gtag-consent', { analytics_storage: 'denied' });\n },\n\n isAnalyticsAllowed() {\n if (this.isAnalyticsCookieDenied()) return false;\n if (this.isAnalyticsCookieGranted()) return true;\n return null;\n },\n\n isAnalyticsCookieSet() {\n return document.cookie.split(';').some((item) => item.trim().startsWith(COOKIE_NAME));\n },\n\n isAnalyticsCookieDenied() {\n return document.cookie.split(';').some((item) => item.trim().startsWith(`${COOKIE_NAME}=denied`));\n },\n\n isAnalyticsCookieGranted() {\n return document.cookie.split(';').some((item) => item.trim().startsWith(`${COOKIE_NAME}=granted`));\n },\n };\n}\n","export default function dropdown() {\n return {\n currentFocus: -1,\n items: this.$el.querySelectorAll('[role=\"menuitem\"]'),\n trigger: this.$el.querySelector('summary'),\n\n init() {\n this.$el.parentElement.addEventListener('mouseleave', () => {\n this.$el.open = false;\n });\n },\n\n previousDropdownItem(items) {\n if (this.currentFocus > 0) {\n items[this.currentFocus].classList.remove('dropdown__item--focus');\n\n this.currentFocus--;\n\n items[this.currentFocus].classList.add('dropdown__item--focus');\n }\n },\n\n nextDropdownItem(items) {\n if (this.trigger.matches(':focus')) this.trigger.classList.add('summary--unfocused');\n if (this.currentFocus < items.length - 1) {\n if (this.currentFocus > -1) items[this.currentFocus].classList.remove('dropdown__item--focus');\n\n this.currentFocus++;\n\n items[this.currentFocus].classList.add('dropdown__item--focus');\n }\n },\n\n resetFocus(items) {\n if (items[this.currentFocus]) items[this.currentFocus].classList.remove('dropdown__item--focus');\n this.trigger.classList.remove('summary--unfocused');\n this.currentFocus = -1;\n },\n\n dropdown: {\n ['@mouseover']() {\n window.innerWidth > 1024 ? (this.$el.open = true) : null;\n },\n ['@click.outside']() {\n this.$el.open = false;\n },\n ['@keydown.arrow-up.prevent']() {\n this.previousDropdownItem(this.items);\n },\n ['@keydown.arrow-down.prevent']() {\n this.nextDropdownItem(this.items);\n },\n ['@keydown.tab']() {\n this.resetFocus(this.items);\n this.$el.open = false;\n },\n ['@keydown.space']() {\n if (this.$el.open) {\n this.resetFocus(this.items);\n }\n },\n ['@keydown.enter']() {\n if (this.currentFocus > -1) {\n this.items[this.currentFocus].click();\n this.resetFocus(this.items);\n }\n },\n },\n };\n}\n","export default function entriesSearch(translations) {\n return {\n translations: JSON.parse(translations),\n // For search and topics, the first element to get focus using the arrow keys is the crosshair (to remove current input).\n // But by default this crosshair is not visible since there's no input yet.\n firstElementVisible: { search: false, topics: false, sort: true },\n selected: { topics: [], sort: 'random' },\n currentFocus: -1,\n items: {\n topics: this.$refs.topicFilter.querySelectorAll('[role=\"menuitem\"]'),\n sort: this.$refs.sortFilter.querySelectorAll('[role=\"menuitem\"]'),\n },\n trigger: {\n topics: this.$refs.topicFilter.querySelector('summary'),\n sort: this.$refs.sortFilter.querySelector('summary'),\n },\n activeView: 'grid',\n\n init() {\n // the search field\n this.$refs.searchInput.addEventListener('input', () => {\n this.firstElementVisible.search = this.$refs.searchInput.value.length > 0;\n });\n\n // the topics dropdown\n this.$refs.topicFilter.addEventListener('toggle', () => {\n if (!this.$refs.topicFilter.open) this.search();\n });\n this.$refs.topicFilter.addEventListener('input', () => {\n this.firstElementVisible.topics = this.selected.topics.length > 0;\n });\n\n // the sort dropdown\n this.$refs.sortFilter.addEventListener('change', () => {\n this.selected.sort = this.$refs.sortFilter.querySelector('input:checked').value;\n this.search();\n this.$refs.sortFilter.open = false;\n });\n },\n\n search() {\n this.$refs.searchForm.requestSubmit();\n },\n\n clearSearch() {\n this.$refs.searchInput.value = '';\n this.$refs.searchInput.focus();\n this.search();\n },\n\n clearTopics() {\n this.$refs.topicFilter.open = false;\n this.selected.topics.forEach((topic) => {\n topic.checked = false;\n });\n this.firstElementVisible.topics = false;\n this.search();\n },\n\n selectItem(type) {\n let item = this.items[type][this.currentFocus].querySelector('input');\n if (this.currentFocus > -1) item.checked = !item.checked;\n this.selected[type] = type === 'topics' ? this.$refs.topicFilter.querySelectorAll('input:checked') : item.value;\n this.firstElementVisible[type] = type === 'topics' ? this.selected[type].length > 0 : true;\n if (type === 'sort') this.$refs.sortFilter.open = !this.$refs.sortFilter.open;\n },\n\n deselectAllTopics() {\n this.selected.topics.forEach((topic) => {\n topic.checked = false;\n });\n this.firstElementVisible.topics = false;\n this.resetFocus('topics');\n this.$refs.topicFilter.open = false;\n },\n\n selectedTopicsText() {\n return `${this.translations.selected} (${this.selected.topics.length})`;\n },\n\n previousDropdownItem(type) {\n if (this.currentFocus > (this.firstElementVisible[type] ? 0 : 1)) {\n this.items[type][this.currentFocus].classList.remove('dropdown__item--focus');\n this.currentFocus--;\n this.items[type][this.currentFocus].classList.add('dropdown__item--focus');\n }\n },\n\n nextDropdownItem(type) {\n if (this.trigger[type].matches(':focus')) this.trigger[type].classList.add('summary--unfocused');\n\n if (this.currentFocus === -1 && !this.firstElementVisible[type]) {\n this.currentFocus = 1;\n this.items[type][this.currentFocus].classList.add('dropdown__item--focus');\n } else {\n if (this.currentFocus < this.items[type].length - 1) {\n if (this.currentFocus > -1) this.items[type][this.currentFocus].classList.remove('dropdown__item--focus');\n this.currentFocus++;\n this.items[type][this.currentFocus].classList.add('dropdown__item--focus');\n }\n }\n },\n\n resetFocus(type) {\n if (this.items[type][this.currentFocus]) {\n this.items[type][this.currentFocus].classList.remove('dropdown__item--focus');\n }\n this.trigger[type].classList.remove('summary--unfocused');\n this.currentFocus = -1;\n },\n\n // Pressing Tab (9), Enter (13), Shift (16) or Space (32) should not trigger a search after 750ms.\n // Enter is here to prevent triggering search when pressing Enter in the skiplinks.\n // Pressing Enter in the search input will still work as expected (= post the search form).\n searchInput: {\n ['@keydown.debounce.750ms']() {\n if ([9, 13, 16, 32].includes(this.$event.keyCode)) return;\n this.search();\n },\n },\n\n clearInputButton: {\n ['@click']() {\n this.clearSearch();\n },\n ['@keydown.enter.stop']() {\n this.clearSearch();\n },\n ['x-show']() {\n return this.firstElementVisible['search'];\n },\n },\n\n clearTopicsButton: {\n ['@click.prevent']() {\n this.clearTopics();\n },\n ['x-show']() {\n return this.firstElementVisible.topics;\n },\n },\n\n topicsPlaceholder: {\n ['x-text']() {\n return this.firstElementVisible.topics ? this.selectedTopicsText() : this.translations.placeholder;\n },\n ['x-bind:class']() {\n return this.firstElementVisible.topics ? 'topic-filter__selected-topics' : '';\n },\n ['@keyup.tab']() {\n this.$refs.topicFilter.open = false;\n this.search();\n },\n },\n\n sortPlaceholder: {\n ['x-text']() {\n return this.translations.filters[this.selected.sort];\n },\n ['@click']() {\n this.$refs.sortFilter.open = false;\n },\n ['@keyup.enter']() {\n this.search();\n },\n ['@keyup.tab']() {\n this.search();\n },\n },\n\n topicDropdown: {\n ['@click']() {\n this.selected.topics = this.$refs.topicFilter.querySelectorAll('input:checked');\n },\n ['@click.outside']() {\n this.$refs.topicFilter.open = false;\n },\n ['@keydown.arrow-up.prevent']() {\n if (this.$refs.topicFilter.open) this.previousDropdownItem('topics');\n },\n ['@keydown.arrow-down.prevent']() {\n if (this.$refs.topicFilter.open) this.nextDropdownItem('topics');\n },\n ['@keydown.tab']() {\n this.resetFocus('topics');\n this.$refs.topicFilter.open = false;\n },\n ['@keydown.space.prevent']() {\n if (this.$refs.topicFilter.open) this.resetFocus('topics');\n this.$refs.topicFilter.open = !this.$refs.topicFilter.open;\n },\n ['@keydown.enter.prevent.stop']() {\n if (this.currentFocus !== 0) {\n this.selectItem('topics');\n } else if (this.currentFocus === 0) {\n this.deselectAllTopics();\n }\n },\n },\n\n sortDropdown: {\n ['x-cloak']() {\n return true;\n },\n ['x-transition']() {\n return true;\n },\n ['x-show']() {\n return this.activeView === 'grid';\n },\n ['@click.outside']() {\n this.$refs.sortFilter.open = false;\n },\n ['@keydown.arrow-up.prevent']() {\n if (this.$refs.sortFilter.open) this.previousDropdownItem('sort');\n },\n ['@keydown.arrow-down.prevent']() {\n if (this.$refs.sortFilter.open) this.nextDropdownItem('sort');\n },\n ['@keydown.tab']() {\n this.resetFocus('sort');\n this.$refs.sortFilter.open = false;\n },\n ['@keydown.space.prevent']() {\n if (this.$refs.sortFilter.open) this.resetFocus('sort');\n this.$refs.sortFilter.open = !this.$refs.sortFilter.open;\n },\n ['@keydown.enter.stop.prevent']() {\n if (this.currentFocus > -1) {\n this.selectItem('sort');\n this.resetFocus('sort');\n }\n },\n },\n\n shuffleTrigger: {\n ['@click']() {\n this.search();\n },\n ['@keydown.enter']() {\n this.search();\n },\n },\n };\n}\n","import EN_translations from './translation_files/EN_translations.json';\nimport NL_translations from './translation_files/NL_translations.json';\nimport FR_translations from './translation_files/FR_translations.json';\nimport DE_translations from './translation_files/DE_translations.json';\nimport IT_translations from './translation_files/IT_translations.json';\n\nexport const translate = (app, label, lang) => {\n switch (lang) {\n case 'en':\n return EN_translations[app][label];\n case 'nl':\n return NL_translations[app][label];\n case 'fr':\n return FR_translations[app][label];\n case 'de':\n return DE_translations[app][label];\n case 'it':\n return IT_translations[app][label];\n default:\n return EN_translations[app][label];\n }\n};\n","import { translate } from '~/components/locales/translations';\nimport axios from 'axios';\n\nexport default function entriesTranslation(googleTranslateApiKey, source) {\n return {\n isTranslated: false,\n translationSaved: false,\n error: false,\n currentLocale: document.querySelector('html').getAttribute('lang'),\n metaComponents: document.querySelectorAll('.entry__meta'),\n entryFields: null,\n\n init() {\n // There are two entry__meta components on the page:\n // one in the entry (for mobile) and one in the sidebar (for desktop)\n this.metaComponents.forEach((metaComponent) => {\n if (metaComponent.querySelector('.entry__icon--translation')) {\n this.setTranslationButtonText();\n this.entryFields = this.createEntryFields();\n\n // save the original text in localStorage\n this.entryFields.forEach((field, index) => localStorage.setItem(index, field.innerHTML));\n\n metaComponent.querySelector('.entry__translate-text').addEventListener('click', () => {\n this.handleTranslate();\n });\n }\n });\n },\n\n setTranslationButtonText() {\n // use translate to get the different messages in the correct language\n const backToOriginalMessage = translate('entry-translation', 'show_original', this.currentLocale);\n const toTranslationMessage = translate('entry-translation', 'translate', this.currentLocale);\n const translationByMessage = translate('entry-translation', 'translated_by_google', this.currentLocale);\n const errorMessage = translate('entry-translation', 'error', this.currentLocale);\n\n // put the relevant messages on the button for both entry__meta components\n this.metaComponents.forEach((metaComponent) => {\n metaComponent.querySelector('.entry__icon--translation').classList.remove('hidden');\n if (this.isTranslated) {\n metaComponent.querySelector('.entry__translate-text').innerText = backToOriginalMessage;\n metaComponent.querySelector('.translated-by--entry-meta').innerText = translationByMessage;\n } else {\n metaComponent.querySelector('.entry__translate-text').innerText = toTranslationMessage;\n metaComponent.querySelector('.translated-by--entry-meta').innerText = '';\n }\n if (this.error) {\n metaComponent.querySelector('.error-message--entry-meta').innerText = errorMessage;\n }\n });\n },\n\n // creates an array with all DOM nodes inside\n createEntryFields() {\n const entryFields = [];\n const entryContent = this.$refs.entry;\n const entryTitle = entryContent.querySelector(\"[data-id='title']\");\n const fields = entryContent.querySelectorAll('[data-field-type]');\n entryFields.push(entryTitle);\n fields.forEach((field) => entryFields.push(field));\n return entryFields;\n },\n\n handleTranslate() {\n if (this.isTranslated) {\n // restore original text in DOM from localStorage\n this.entryFields.forEach((field, index) => (field.innerHTML = localStorage.getItem(index)));\n this.isTranslated = false;\n } else if (this.translationSaved) {\n // restore translations in DOM from localStorage\n this.entryFields.forEach((field, index) => (field.innerHTML = localStorage.getItem(`translated-${index}`)));\n this.isTranslated = true;\n } else {\n // get translations from google\n this.fetchGoogleTranslation();\n this.isTranslated = true;\n this.translationSaved = true;\n }\n this.setTranslationButtonText();\n },\n\n async fetchGoogleTranslation() {\n axios\n .post(`https://translation.googleapis.com/language/translate/v2?key=${googleTranslateApiKey}`, {\n q: this.entryFields.map((text) => text.innerHTML),\n source: source,\n target: this.currentLocale,\n format: 'text',\n })\n .then((response) => {\n const googleTranslations = response.data.data.translations;\n googleTranslations.forEach((translation, index) => {\n // update the DOM with the translated text by changing the DOM nodes in entryFields\n this.entryFields[index].innerHTML = translation.translatedText;\n\n // save the translated text in localStorage\n localStorage.setItem(`translated-${index}`, translation.translatedText);\n });\n\n this.error = false;\n })\n .catch(() => {\n this.error = true;\n });\n },\n };\n}\n","import ImageBlobReduce from 'image-blob-reduce';\n\n// utils\nimport fireSwal from '~/scripts/fire-swal';\nimport { translate } from '~/scripts/locales/translations';\n\nconst MAX_IMAGE_SIZE = 10485760; // 10 MB\nconst MAX_IMAGE_DIMENSION = 1200; // px\n\nconst reduce = new ImageBlobReduce();\nconst locale = document.querySelector('html').getAttribute('lang');\n\nexport default function fileUpload({ originalUrl = '', originalFilename = '', isPreviewActive }) {\n return {\n originalUrl,\n originalFilename,\n isPreviewActive,\n destroyFile: '',\n\n isImageAttribute() {\n if (this.$refs.imagePreview) {\n return true;\n } else {\n return false;\n }\n },\n\n isFileAttribute() {\n if (this.$refs.filenamePreview) {\n return true;\n } else {\n return false;\n }\n },\n\n handleInput() {\n const input = this.$refs.input;\n const file = input.files[0];\n\n if (this.isFileAttribute()) {\n this.$refs.filenamePreview.innerText = file.name;\n this.$refs.filenamePreview.removeAttribute('href');\n this.isPreviewActive = true;\n this.destroyFile = '';\n } else if (this.isImageAttribute()) {\n const preview = this.$refs.imagePreview;\n const filenameEl = this.$refs.imageFilename;\n\n // check if the file is an image, if not return\n if (file.type.includes('image')) {\n fireSwal.loading({ title: translate('img-resize', 'analyzing', locale) });\n if (file.size > MAX_IMAGE_SIZE) {\n fireSwal.error({\n title: translate('img-resize', 'size-too-large__title', locale),\n text: translate('img-resize', 'size-too-large__text', locale),\n });\n input.value = null;\n return;\n }\n } else {\n return;\n }\n\n const reader = new FileReader();\n const img = new Image();\n reader.readAsDataURL(file);\n\n // we wait for the reader to load the file\n reader.onload = () => {\n img.src = reader.result;\n // we wait for the image to read the reader\n img.onload = () => {\n // validate dimension\n if (img.width > MAX_IMAGE_DIMENSION || img.height > MAX_IMAGE_DIMENSION) {\n fireSwal.loading({ title: translate('img-resize', 'loading', locale) });\n\n reduce\n .toBlob(file, { max: MAX_IMAGE_DIMENSION })\n .then((blob) => {\n // transfer reduced image back to input\n let transfer = new DataTransfer();\n const reducedImg = new File([blob], file.name);\n transfer.items.add(reducedImg);\n input.files = transfer.files;\n\n fireSwal.success({ title: translate('img-resize', 'success', locale) });\n })\n .catch(() =>\n fireSwal.error({\n title: translate('img-resize', 'error__title', locale),\n text: translate('img-resize', 'error__text', locale),\n })\n );\n } else {\n fireSwal.success({ title: translate('img-resize', 'size-ok', locale) });\n }\n };\n // if there is a preview image (e.g. carousel)\n if (preview) preview.src = reader.result;\n if (filenameEl) filenameEl.innerText = file.name;\n this.isPreviewActive = true;\n this.destroyFile = '';\n };\n } else {\n console.log('Something went wrong, please check the fileUpload component');\n }\n },\n\n // delete new file:\n // - if there was an original file present and there is a new file selected, reset the preview to the old file and clear the input\n // - if not, set the destroyFile flag and clear the preview\n deleteFile() {\n if (this.originalFilename != '' && this.$refs.input.files[0]) {\n this.$refs.input.value = '';\n this.resetPreview();\n } else {\n this.destroyFile = true;\n this.clearPreview();\n }\n },\n\n resetPreview() {\n if (this.isImageAttribute()) {\n this.$refs.imagePreview.src = this.originalUrl;\n this.$refs.imageFilename.innerText = this.originalFilename;\n }\n if (this.isFileAttribute()) {\n this.$refs.filenamePreview.href = this.originalUrl;\n this.$refs.filenamePreview.innerText = this.originalFilename;\n }\n this.isPreviewActive = true;\n },\n\n clearPreview() {\n if (this.isImageAttribute()) {\n this.$refs.imagePreview.removeAttribute('src');\n this.$refs.imageFilename.innerText = '';\n }\n if (this.isFileAttribute()) this.$refs.filenamePreview.removeAttribute('href');\n this.isPreviewActive = false;\n },\n\n handleDrop(event) {\n // Ignore if dataTransfer doesn't contain exactly one item\n if (event.dataTransfer.files.length !== 1) return;\n this.$refs.input.files = event.dataTransfer.files;\n this.handleInput();\n },\n\n dropzone: {\n ['@drop.prevent'](event) {\n this.handleDrop(event);\n },\n ['@dragover.prevent']() {\n // Just here to prevent the file from being opened\n },\n },\n };\n}\n","export default function formErrorHandler() {\n return {\n\n init() {\n document.addEventListener('turbo:before-stream-render', (e) => {\n // targetId is the target dom id of the turbo_stream\n const targetId = e.detail.newStream.target;\n if (targetId == 'input__error') {\n this.removeStyling();\n this.removeInputErrorMessages();\n } else {\n this.addStyling(targetId);\n this.focusFirstError();\n }\n });\n },\n\n addStyling(targetId) {\n const element = document.getElementById(targetId);\n const type = Array.from(element.classList).filter(klass => !/^form/.test(klass))[0];\n\n this.findInputElement(element, type)?.classList.add('form__input--invalid');\n\n if (['string', 'text'].includes(type) && element.querySelector('.tox-tinymce')) {\n element.querySelector('.tox-tinymce').style.borderColor = 'red';\n }\n },\n\n removeStyling() {\n const stylingElements = document.querySelectorAll('.form__input--invalid');\n\n stylingElements.forEach((element) => {\n element.classList.remove('form__input--invalid');\n if (element.querySelector('.tox-tinymce')) {\n element.parentNode.querySelector('.tox-tinymce').style.borderColor = '';\n };\n });\n },\n\n removeInputErrorMessages() {\n const messages = document.querySelectorAll('.input__error');\n\n messages.forEach((message) => {\n message.remove();\n });\n },\n\n focusFirstError() {\n const errorElement = document.querySelector('.form__input--invalid');\n\n if (errorElement) {\n if (errorElement.parentNode.classList.contains('form-field__file-upload') ||\n errorElement.classList.contains('map-entry') ||\n errorElement.classList.contains(\"string__input\") ||\n errorElement.classList.contains(\"text__input\")) {\n errorElement.parentNode.scrollIntoView();\n } else {\n errorElement.focus();\n }\n }\n },\n\n findInputElement(element, type) {\n switch (type) {\n case 'devise':\n return element.getElementsByTagName('input')[0];\n case 'string':\n return element.querySelector('input[type=text]') ||\n element.querySelector('input[type=password]') ||\n element.querySelector('input[type=email]') ||\n element.querySelector('input[type=confirmation]');\n case 'text':\n return element.getElementsByTagName('textarea')[0];\n case 'select':\n return element.getElementsByTagName('select')[0];\n case 'checkbox__wrapper':\n return element.querySelector('input[type=checkbox]');\n case 'checkboxes':\n return element.querySelector('input[type=checkbox]');\n case 'radio':\n return element.querySelector('input[type=radio]');\n case 'file-upload':\n return element.querySelector('input[type=file]');\n case 'map-select':\n return element.getElementsByClassName('map-entry')[0];\n }\n },\n };\n}\n","export default function googleMapsPublic(originLatLng, mapParams) {\n const topics = mapParams.topics;\n const mapSettings = mapParams.mapSettings;\n const mapOverlay = mapParams.mapOverlay;\n const geoJson = mapParams.geoJson;\n const latInput = topics\n ? null\n : this.$refs.map.closest('[data-selector=\"form-field__map\"').querySelector('[data-map-input=\"lat\"]');\n const lngInput = topics\n ? null\n : this.$refs.map.closest('[data-selector=\"form-field__map\"').querySelector('[data-map-input=\"lng\"]');\n\n return {\n showOverlay: mapOverlay.mapOverlayVisible,\n showError: false,\n overlayURL: mapOverlay.mapOverlayImageUrl,\n overlay: {\n north: mapOverlay.overlayBoundNorth,\n south: mapOverlay.overlayBoundSouth,\n east: mapOverlay.overlayBoundEast,\n west: mapOverlay.overlayBoundWest,\n },\n groundOverlay: null,\n map: null,\n markerURL:\n mapParams.markerUrl || 'https://bpart-default-assets.s3.eu-central-1.amazonaws.com/img/map-default-marker.png',\n intersectionObserver: null,\n\n init() {\n // Create an IntersectionObserver to animate the markers\n this.intersectionObserver = new IntersectionObserver((entries) => {\n for (const entry of entries) {\n if (entry.isIntersecting) {\n entry.target.classList.add('drop');\n this.intersectionObserver.unobserve(entry.target);\n }\n }\n });\n\n // Initialize the map (asynchronously)\n this.initMap();\n },\n\n async initMap() {\n // Load the Google Maps API libraries\n const { Map } = await google.maps.importLibrary('maps');\n const { AdvancedMarkerElement } = await google.maps.importLibrary('marker');\n const { Autocomplete } = await google.maps.importLibrary('places');\n\n // Create the map and set its options\n const mapOptions = {\n zoom: mapSettings.zoomLevel || 14,\n center: this.centerLatLng(),\n mapTypeId: mapSettings.mapType || 'roadmap',\n mapTypeControl: true,\n mapTypeControlOptions: {\n style: google.maps.MapTypeControlStyle.DROPDOWN_MENU,\n },\n gestureHandling: 'cooperative',\n mapId: 'MAP_ID',\n };\n let map = new Map(this.$refs.map, mapOptions);\n\n // Add a search box\n if (this.$refs.searchBox) {\n const placeAutocomplete = new Autocomplete(this.$refs.searchBox);\n\n placeAutocomplete.addListener('place_changed', async (ev) => {\n let value = this.$refs.searchBox.value;\n await this.findPlaces(value, map);\n });\n }\n\n // Add all entry markers (on homepage)\n if (google && map && topics) {\n topics.collection.forEach((topic) => {\n topic.entries.forEach((entry) => {\n const icon = document.createElement('img');\n icon.src = topic.iconUrl;\n\n const markerLatLng = new google.maps.LatLng(entry.position.lat, entry.position.lng);\n const title = entry.title.replace(/<[^>]*>?/gm, '');\n\n const marker = new AdvancedMarkerElement({\n map: map,\n position: markerLatLng,\n content: icon,\n title: title,\n });\n\n if (topics.type === 'topics') {\n const contentString =\n '