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')