function formValue(id) { const el = document.getElementById(`form-${id}`) return el ? el.value : null } function getMetaContent(property) { const el = document.querySelector(`meta[property="${property}"]`) if (el) { return el.getAttribute('content') } else { return '' } } const app = Vue.createApp({ components: window.VUE_COMPONENTS || {}, data() { return { hasSigned: false, hasCalled: false, hasSubscribed: false, anHasLoaded: false, sendsToCongress: false, sendsToHouse: false, sendsToSenate: false, sendsToBioguideIds: null, congressEmailSubject: null, firstName: null, lastName: null, email: null, street: null, zipCode: null, country: null, comments: null, phone: null, requiresOptIn: null, sendsToRegulations: false, regulationsDoc: null, hideComment: false, sendsToFCC: false, fccDocket: null } }, watch: { hasSigned(newValue) { document.body.classList.toggle('has-signed', newValue) } }, computed: { fullName() { return [ this.firstName || '', this.lastName || '' ].map(s => s.trim()).join(' ').trim() }, usesCommentsAPI() { return this.sendsToCongress || this.sendsToRegulations || this.sendsToFCC }, sharingURL() { const el = document.querySelector('meta[property="og:url"]') if (el) { return el.getAttribute('content') } return location.href } }, mounted() { document.addEventListener('can_embed_loaded', this.anEmbedLoaded) document.addEventListener('can_embed_submitted', this.anEmbedSubmitted) if (location.search.match(/signed/)) { this.hasSigned = true this.showCallFormModal() } else if (location.search.match(/call=(1|true)/)) { this.showCallFormModal() } else if (location.search.match(/hasCalled/)) { this.hasCalled = true } // add search params to any link with this class document.querySelectorAll('a.js-add-search-params').forEach(a => { const sep = a.href.includes('?') ? '&' : '?' a.href += window.location.search.replace('?', sep) }) // set up airtable modal links document.querySelectorAll('.js-airtable-modal').forEach(el => { // let this work with WP button divs or actual links const a = el.href ? el : el.querySelector('a') if (a) { a.setAttribute('data-bs-toggle', 'modal') a.setAttribute('data-bs-target', '#airtable-modal') a.addEventListener('click', event => { const { href, dataset } = event.target const embedSrc = href.match(/embed/) ? href : href.replace('https://airtable.com/', 'https://airtable.com/embed/') const modalBody = document.querySelector(`${dataset.bsTarget} .modal-body`) if (modalBody) { modalBody.innerHTML = ` ` } }) } }) document.querySelectorAll('.js-share-modal').forEach(el => { // let this work with WP button divs or actual links const a = el.href ? el : el.querySelector('a') // just pick the first share modal on the page const shareModal = document.querySelector('.share-modal') if (a && shareModal) { a.addEventListener('click', event => { event.preventDefault() this.share(shareModal.id) }) } }) }, beforeDestroy() { document.removeEventListener('can_embed_loaded', this.anEmbedLoaded) document.removeEventListener('can_embed_submitted', this.anEmbedSubmitted) }, methods: { trackEvent(eventName) { if (window.fathom) { window.fathom.trackEvent(eventName) } }, anEmbedLoaded() { console.log('AN Loaded') if (this.anHasLoaded) { return false } const form = document.getElementById('new_signature') if (!form) { console.error('Missing #new_signature form') return false } // grab embedded data from DOM const dataElement = form.closest('.js-petition-data') if (dataElement) { const { dataset } = dataElement this.requiresOptIn = dataset.requiresOptIn this.comments = dataset.defaultComment this.hideComment = !!dataset.hideComment if (dataset.sendsToCongress) { this.sendsToCongress = true this.sendsToHouse = !!dataset.sendsToHouse this.sendsToSenate = !!dataset.sendsToSenate this.sendsToBioguideIds = dataset.sendsToBioguideIds this.congressEmailSubject = dataset.congressEmailSubject } if (dataset.sendsToRegulations) { this.sendsToRegulations = true this.regulationsDoc = dataset.regulationsDoc } if (dataset.sendsToFcc) { this.sendsToFCC = true this.fccDocket = dataset.fccDocket } } // make country selector work with our custom CSS // (these won't be here if the user is logged in to Action Network) const countryLink = document.querySelector('.js-international_link') const countryField = document.getElementById('form-country') if (countryLink) { countryLink.addEventListener('click', this.showCountrySelector) } if (countryField) { countryField.addEventListener('change', this.handleCountryChange) // also run this immediately in case a non-US country was pre-selected this.handleCountryChange() } // add our privacy policy to the form this.addPrivacyPolicy() // save form data so we can use it for our after-actions form.addEventListener('submit', event => { this.firstName = formValue('first_name') this.lastName = formValue('last_name') this.email = formValue('email') this.street = formValue('street') this.zipCode = formValue('zip_code') this.country = formValue('country') this.comments = formValue('comments') }) const commentBox = document.getElementById('form-comments') if (commentBox) { // load default comment if (this.comments) { commentBox.value = this.comments } // hide comment if asked if (this.hideComment) { commentBox.style.display = 'none' } } if (this.requiresOptIn) { document.querySelectorAll('#d_sharing input[type="checkbox"]').forEach(box => { box.checked = false }) } this.anHasLoaded = true }, anEmbedSubmitted() { // if there's an airtable embed, prefill the name and email fields if (this.$refs.airtableEmbed) { this.$refs.airtableEmbed.src = `https://airtable.com/embed/${this.$refs.airtableEmbed.dataset.embedId}?backgroundColor=purple&prefill_Name=${encodeURIComponent(this.fullName || '')}&prefill_Email=${encodeURIComponent(this.email || '')}` } this.hasSigned = true this.trackEvent('signPetition') // if there's a call modal, show that next if (this.$refs.callFormModal) { this.showCallFormModal() } // if we're on desktop, show the share modal else if (!navigator.share) { this.share() } if (this.usesCommentsAPI) { this.sendToCommentsAPI() } }, addPrivacyPolicy() { document.querySelectorAll('#d_sharing li label').forEach(label => { // add privacy policy link to Fight for the Future box if (label.innerText.match(/Fight for the Future/i)) { const el = document.createElement('span') el.innerHTML = 'Privacy Policy' label.appendChild(el) } // remove FFTF Joint Actions from the list since it's not a user-facing group if (label.innerText.match(/FFTF Joint Actions(, | and)/)) { label.innerHTML = label.innerHTML.replace(/FFTF Joint Actions(, | and)/, '') } }) }, showCountrySelector() { document.getElementById('can_embed_form').classList.toggle('is-international') }, handleCountryChange() { const country = document.getElementById('form-country').value if (country !== 'US') { document.getElementById('name_optin1').removeAttribute('checked') } }, share(modalId) { if (navigator.share) { const title = getMetaContent('og:title') || document.title const text = getMetaContent('og:description') const url = getMetaContent('og:url') || location.href navigator.share({ title, text, url }) } else if (modalId) { const shareModal = document.getElementById(modalId) if (shareModal) { new bootstrap.Modal(shareModal, { keyboard: true, focus: true }).show() } } }, showCallFormModal() { if (this.$refs.callFormModal) { new bootstrap.Modal(this.$refs.callFormModal, { keyboard: true, focus: true }).show() } }, submitCallForm($event) { // just assume it worked this.hasCalled = true // make AJAX request to CallPower API const form = $event.target fetch(form.action, { method: form.method, body: new URLSearchParams(new FormData(form)) }) this.trackEvent('makeCall') const modal = form.closest('.modal'); if (modal) { modal.addEventListener('hidden.bs.modal', event => { this.hasCalled = false }) } }, async sendToCommentsAPI() { // extract base domain name to use as source let source = location.hostname.replace(/^www\./, '') // if domain is fightforthefuture.org, add the path if (source === 'fightforthefuture.org') { source += location.pathname.replace(/\/$/, '') } const params = { first_name: this.firstName, last_name: this.lastName, email: this.email, address: this.street, postal_code: this.zipCode, country: this.country, comment: this.comments, destinations: [], source } if (this.sendsToCongress) { params.subject = this.congressEmailSubject if (this.sendsToHouse) { params.destinations.push('house') } if (this.sendsToSenate) { params.destinations.push('senate') } if (this.sendsToBioguideIds) { params.bioguide_ids = this.sendsToBioguideIds } } if (this.sendsToRegulations) { params.destinations.push('regulations') params.regulations_doc = this.regulationsDoc } if (this.sendsToFCC) { params.destinations.push('fcc') params.fcc_docket = this.fccDocket } // temporary hack to add a disclaimer to the comment // TODO: make this a block param if (typeof (window.COMMENT_DISCLAIMER) !== 'undefined' && window.COMMENT_DISCLAIMER) { params.comment += `\n\n(${window.COMMENT_DISCLAIMER})` } const response = await fetch('https://comments-api.fftf.cat/comments', { method: 'POST', body: JSON.stringify(params), headers: { 'Content-Type': 'application/json' } }) if (response.ok) { const data = await response.json() console.log(data) } }, submitSubscribeForm($event) { this.hasSubscribed = true const form = $event.target fetch(form.action, { method: form.method, headers: { 'Accept': 'application/json', 'Content-type': 'application/json' }, body: JSON.stringify({ email: this.email, phone: this.phone, status: 'subscribed' }) }) } } }) app.mount('#page')