Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save whatnickcodes/ad493722222b177abb96b4b391379b9d to your computer and use it in GitHub Desktop.

Select an option

Save whatnickcodes/ad493722222b177abb96b4b391379b9d to your computer and use it in GitHub Desktop.
<style>
#action-network-embed [data-action-network-src] {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
#action-network-embed [data-action-network-target] {
min-height: 180px;
}
#action-network-embed .action-network-status {
display: none;
border: 1px solid currentColor;
padding: 1rem;
color: var(--color-primary-900);
background: color-mix(in oklab, var(--color-accent-300) 14%, white);
}
#action-network-embed .is-invalid .action-network-status {
display: block;
}
</style>
<div id="action-network-embed" class="wait bg-gray-100 px-[5%] py-[10%] lg:py-[6%]">
<div class="max-w-[680px] mx-auto">
<div field-tab="Copy" class="text-center">
<div field="Eyebrow" class="heading-6 !text-secondary-700 motion-preset-slide-up-xs">Take action</div>
<h2 field="Heading" class="heading-3 mt-2 motion-preset-slide-up-sm motion-delay-200">Join the campaign</h2>
<p field-textarea="Description" class="p mt-4 max-w-[58ch] mx-auto motion-preset-slide-up-sm motion-delay-300">Add your name and stay connected.</p>
</div>
<div field-tab="Action Network" class="contents">
<div
class="mt-8 motion-preset-slide-up-sm motion-delay-400"
data-action-network-embed
data-action-network-style="standard"
data-action-network-layout="standard">
<div field-textarea="Action Network URL" data-action-network-src>
https://actionnetwork.org/forms/demo-the-action-network-toolset
</div>
<div class="action-network-status mb-4" data-action-network-status role="status"></div>
<div data-action-network-target></div>
</div>
</div>
</div>
</div>
<script>
(function () {
var ACTION_TYPES = {
form: 'form',
forms: 'form',
petition: 'petition',
petitions: 'petition',
event: 'event',
events: 'event',
letter: 'letter',
letters: 'letter',
letter_campaign: 'letter_campaign',
letter_campaigns: 'letter_campaign',
call_campaign: 'call_campaign',
call_campaigns: 'call_campaign',
fundraising: 'fundraising',
fundraiser: 'fundraising',
fundraisers: 'fundraising',
fundraising_page: 'fundraising',
fundraising_pages: 'fundraising',
ticketed_event: 'ticketed_event',
ticketed_events: 'ticketed_event',
}
var STYLE_URLS = {
standard: 'https://actionnetwork.org/css/style-embed-v3.css',
'default': 'https://actionnetwork.org/css/style-embed-v3.css',
'layout-only': 'https://actionnetwork.org/css/style-embed-whitelabel.css',
layout: 'https://actionnetwork.org/css/style-embed-whitelabel.css',
none: '',
}
function clean(value) {
return String(value || '').replace(/^\s+|\s+$/g, '')
}
function own(object, key) {
return Object.prototype.hasOwnProperty.call(object, key)
}
function isActionNetworkHost(hostname) {
hostname = clean(hostname).toLowerCase()
return hostname === 'actionnetwork.org' || /(^|\.)actionnetwork\.org$/.test(hostname)
}
function stripTrailingSlash(value) {
return String(value || '').replace(/\/+$/, '')
}
function decodeHtml(value) {
var textarea = document.createElement('textarea')
textarea.innerHTML = value
return textarea.value
}
function urlFromMaybeRelative(value) {
value = clean(value)
if (!value) return null
if (value.charAt(0) === '/') value = 'https://actionnetwork.org' + value
if (/^actionnetwork\.org\//i.test(value)) value = 'https://' + value
if (/^(forms|petitions|events|letter_campaigns|call_campaigns|fundraising|fundraisers|ticketed_events)\//i.test(value)) {
value = 'https://actionnetwork.org/' + value
}
try {
var anchor = document.createElement('a')
anchor.href = value
if (!isActionNetworkHost(anchor.hostname)) return null
var pathname = anchor.pathname || '/'
if (pathname.charAt(0) !== '/') pathname = '/' + pathname
return {
href: anchor.href,
hostname: anchor.hostname,
pathname: pathname,
search: anchor.search || '',
}
} catch (error) {
return null
}
}
function extractSrcFromEmbed(value) {
var decoded = decodeHtml(value)
var scriptMatch = decoded.match(/<script\b[^>]*\bsrc\s*=\s*(['"])(.*?)\1/i)
if (scriptMatch) return scriptMatch[2]
var urlMatch = decoded.match(/https?:\/\/(?:[a-z0-9-]+\.)?actionnetwork\.org[^\s"'<>]+/i)
if (urlMatch) return urlMatch[0]
var pathMatch = decoded.match(/\/(?:forms|petitions|events|letter_campaigns|call_campaigns|fundraising|fundraisers|ticketed_events)\/[^\s"'<>]+/i)
return pathMatch ? pathMatch[0] : decoded
}
function decodeQueryPart(value) {
try {
return decodeURIComponent(String(value || '').replace(/\+/g, ' '))
} catch (error) {
return String(value || '')
}
}
function encodeQueryPart(value) {
return encodeURIComponent(String(value || ''))
}
function queryPairs(search) {
search = String(search || '').replace(/^\?/, '')
if (!search) return []
var pairs = []
var chunks = search.split('&')
for (var i = 0; i < chunks.length; i++) {
if (!chunks[i]) continue
var eq = chunks[i].indexOf('=')
var key = eq === -1 ? chunks[i] : chunks[i].slice(0, eq)
var value = eq === -1 ? '' : chunks[i].slice(eq + 1)
pairs.push([decodeQueryPart(key), decodeQueryPart(value)])
}
return pairs
}
function getQueryParam(search, key) {
var pairs = queryPairs(search)
for (var i = 0; i < pairs.length; i++) {
if (pairs[i][0] === key) return pairs[i][1]
}
return ''
}
function setQueryParam(pairs, key, value) {
var found = false
for (var i = 0; i < pairs.length; i++) {
if (pairs[i][0] === key) {
if (!found) {
pairs[i][1] = value
found = true
} else {
pairs.splice(i, 1)
i--
}
}
}
if (!found) pairs.push([key, value])
}
function hasQueryParam(pairs, key) {
for (var i = 0; i < pairs.length; i++) {
if (pairs[i][0] === key) return true
}
return false
}
function serializeQuery(pairs) {
var out = []
for (var i = 0; i < pairs.length; i++) {
if (pairs[i][0] === 'url') continue
out.push(encodeQueryPart(pairs[i][0]) + '=' + encodeQueryPart(pairs[i][1]))
}
return out.join('&')
}
function pathParts(pathname) {
var raw = stripTrailingSlash(pathname).split('/')
var out = []
for (var i = 0; i < raw.length; i++) {
if (raw[i]) out.push(raw[i])
}
return out
}
function widgetFromCleanUrl(url, layout) {
var parts = pathParts(url.pathname)
var actionType = ACTION_TYPES[parts[0]]
var slug = parts[1]
if (!actionType || !slug) return null
var params = queryPairs(url.search)
setQueryParam(params, 'format', 'js')
if (!hasQueryParam(params, 'source')) setQueryParam(params, 'source', 'widget')
if (layout === 'full' && !hasQueryParam(params, 'style')) setQueryParam(params, 'style', 'full')
return {
type: actionType,
slug: slug,
src: 'https://actionnetwork.org/widgets/v6/' + actionType + '/' + slug + '?' + serializeQuery(params),
targetId: 'can-' + actionType + '-area-' + slug
}
}
function widgetFromScriptUrl(url) {
var parts = pathParts(url.pathname)
var widgetsIndex = -1
for (var i = 0; i < parts.length; i++) {
if (parts[i] === 'widgets') {
widgetsIndex = i
break
}
}
if (widgetsIndex === -1) return null
var maybeVersion = parts[widgetsIndex + 1]
var typeIndex = /^v\d+$/i.test(maybeVersion || '') ? widgetsIndex + 2 : widgetsIndex + 1
var actionType = ACTION_TYPES[parts[typeIndex]] || parts[typeIndex]
var slug = parts[typeIndex + 1]
if (!actionType || !slug) return null
var params = queryPairs(url.search)
setQueryParam(params, 'format', 'js')
if (!hasQueryParam(params, 'source')) setQueryParam(params, 'source', 'widget')
return {
type: actionType,
slug: slug,
src: 'https://actionnetwork.org/' + parts.join('/') + '?' + serializeQuery(params),
targetId: 'can-' + actionType + '-area-' + slug
}
}
function widgetFromOembedUrl(url, layout) {
if (stripTrailingSlash(url.pathname) !== '/oembed') return null
var embedded = getQueryParam(url.search, 'url')
if (!embedded) return null
var actionUrl = urlFromMaybeRelative(embedded)
if (!actionUrl) return null
actionUrl.search = url.search
return widgetFromCleanUrl(actionUrl, layout)
}
function normalizeWidget(raw, layout) {
var source = extractSrcFromEmbed(raw)
var url = urlFromMaybeRelative(source)
if (!url) return null
return widgetFromOembedUrl(url, layout)
|| widgetFromScriptUrl(url)
|| widgetFromCleanUrl(url, layout)
}
function requestedStyle(wrap) {
var value = clean(wrap.getAttribute('data-action-network-style')).toLowerCase()
return own(STYLE_URLS, value) ? value : 'standard'
}
function requestedLayout(wrap) {
var value = clean(wrap.getAttribute('data-action-network-layout')).toLowerCase()
return value === 'full' ? 'full' : 'standard'
}
function ensureStylesheet(style) {
var href = STYLE_URLS[style]
if (!href) return
if (document.getElementById('mf-action-network-css-' + style.replace(/[^a-z0-9_-]/gi, '-'))) return
var link = document.createElement('link')
link.rel = 'stylesheet'
link.href = href
link.id = 'mf-action-network-css-' + style.replace(/[^a-z0-9_-]/gi, '-')
link.setAttribute('data-action-network-css', style)
document.head.appendChild(link)
}
function clearChildren(element) {
while (element.firstChild) element.removeChild(element.firstChild)
}
function setInvalid(wrap, invalid) {
var className = (' ' + (wrap.className || '') + ' ').replace(/\s+is-invalid\s+/g, ' ')
if (invalid) className += ' is-invalid '
wrap.className = className.replace(/^\s+|\s+$/g, '').replace(/\s+/g, ' ')
}
function setStatus(wrap, message) {
var status = wrap.querySelector('[data-action-network-status]')
setInvalid(wrap, Boolean(message))
if (status) status.textContent = message || ''
}
function renderActionNetworkEmbed(wrap) {
var sourceEl = wrap.querySelector('[data-action-network-src]')
var targetEl = wrap.querySelector('[data-action-network-target]')
if (!sourceEl || !targetEl) return
var raw = clean(sourceEl.textContent)
if (!raw) {
clearChildren(targetEl)
setStatus(wrap, '')
return
}
var style = requestedStyle(wrap)
var layout = requestedLayout(wrap)
var widget = normalizeWidget(raw, layout)
if (!widget) {
clearChildren(targetEl)
setStatus(wrap, 'Use an Action Network action URL or widget embed.')
return
}
ensureStylesheet(style)
setStatus(wrap, '')
clearChildren(targetEl)
var target = document.createElement('div')
target.id = widget.targetId
target.style.width = '100%'
targetEl.appendChild(target)
var script = document.createElement('script')
script.src = widget.src
script.async = true
script.setAttribute('data-action-network-widget', widget.type + ':' + widget.slug)
targetEl.appendChild(script)
}
function init(root) {
var embeds = root.querySelectorAll('[data-action-network-embed]')
for (var i = 0; i < embeds.length; i++) renderActionNetworkEmbed(embeds[i])
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function () { init(document) })
} else {
init(document)
}
})()
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment