");vwo_$('head').append(_vwo_sel);return vwo_$('head')[0] && vwo_$('head')[0].lastChild;})("HEAD")}}, R_725969_176_1_2_0:{ fn:function(log,nonce=''){return (function(x) { if(!vwo_$.fn.vwoRevertHtml){ return; }; var ctx=vwo_$(x),el; /*vwo_debug log("Revert","content",""); vwo_debug*/; el=vwo_$('[vwo-element-id="1746468826117"]'); el.revertContentOp().remove();})("HEAD")}}, C_725969_176_1_2_1:{ fn:function(log,nonce=''){return (function(x) {var el,ctx=vwo_$(x); /*vwo_debug log("_clickElement",".btn"); vwo_debug*/(el=vwo_$(".btn")).addClass("_vwo_coal_1697827292277");})(".btn")}}, R_725969_176_1_2_1:{ fn:function(log,nonce=''){return (function(x) { if(!vwo_$.fn.vwoRevertHtml){ return; }; var el,ctx=vwo_$(x); /*vwo_debug log("Revert","_clickElement",".btn"); vwo_debug*/(el=vwo_$(".btn")).removeClass("_vwo_coal_1697827292277");})(".btn")}}, C_725969_176_1_2_2:{ fn:function(log,nonce=''){return (function(x) {})("dialog::backdrop,.dear-reader-dialog,.dear-reader,.dear-reader+hr,.dear-reader .btn,.dear-reader img,.dear-reader p,#book-img")}}, R_725969_176_1_2_2:{ fn:function(log,nonce=''){return (function(x) { if(!vwo_$.fn.vwoRevertHtml){ return; }; })("dialog::backdrop,.dear-reader-dialog,.dear-reader,.dear-reader+hr,.dear-reader .btn,.dear-reader img,.dear-reader p,#book-img")}}, C_725969_176_1_3_0:{ fn:function(log,nonce=''){return (function(x) { try{ var _vwo_sel = vwo_$("`); !vwo_$("head").find('#1746468826149').length && vwo_$('head').append(_vwo_sel);}catch(e) {VWO._.vAEH(e);} try{}catch(e) {VWO._.vAEH(e);} try{const dailyGoals=[{date:"April 29, 2025",value:50},{date:"May 1, 2025",value:5},{date:"May 2, 2025",value:4},{date:"May 3, 2025",value:5},{date:"May 4, 2025",value:6},{date:"May 5, 2025",value:5},{date:"May 6, 2025",value:6},{date:"May 7, 2025",value:5},{date:"May 8, 2025",value:6},{date:"May 9, 2025",value:5},{date:"May 10, 2025",value:5},{date:"May 11, 2025",value:5},{date:"May 12, 2025",value:6},{date:"May 13, 2025",value:5},{date:"May 14, 2025",value:6},{date:"May 15, 2025",value:4},{date:"May 16, 2025",value:5},{date:"May 17, 2025",value:7},{date:"May 18, 2025",value:5},{date:"May 19, 2025",value:6},{date:"May 20, 2025",value:15},{date:"May 21, 2025",value:10},{date:"May 22, 2025",value:7},{date:"May 23, 2025",value:9},{date:"May 24, 2025",value:6},{date:"May 25, 2025",value:9},{date:"May 26, 2025",value:8},{date:"May 27, 2025",value:10},{date:"May 28, 2025",value:25},{date:"May 29, 2025",value:50},{date:"May 30, 2025",value:40},{date:"May 31, 2025",value:60}],testDate=null;function getTodayFormatted(){return(new Date).toLocaleString("default",{month:"long",day:"numeric",year:"numeric"})}function getGoalForToday(){const e=getTodayFormatted(),t=dailyGoals.find(t=>t.date===e);return t?t.value:0}!function(){const e=Date.now(),t=localStorage.getItem("popupDismissedUntil");if(t&&e\n \n \n \n

Dear Reader,

\n

Have FamilyLife’s biblical resources helped strengthen your family and your walk with Christ?

\n
\n \n \n
\n ',document.body.appendChild(a);const o=document.createElement("dialog");function n(e){e instanceof HTMLDialogElement&&e.close(),e.style.display="none"}function l(){const e=getGoalForToday();var t=!1;window.nextafterPixel=function(a){t=!0;var n=0;a&&a.hits&&(n=parseInt(a.hits,10)||0);var l=e-n,r=document.querySelector(".dear-reader .counter");l<=0?r&&(r.style.display="none"):r&&(r.textContent=String(l)),setTimeout(()=>{void 0!==o&&o.showModal&&o.showModal()},100)};var a=document.createElement("script");a.src="https://pxl.nextafter.org/get.php?id=13&callback=nextafterPixel",a.id="nextafterPixelScript",document.body.appendChild(a),setTimeout(()=>{if(!t){console.warn("Pixel did not respond, showing fallback popup");var a=document.querySelector(".dear-reader .counter");a&&(a.textContent=String(e)),setTimeout(()=>{void 0!==o&&o.showModal&&o.showModal()},100)}},5e3)}o.classList.add("dear-reader-dialog"),o.innerHTML='\n
\n \n \n \n \n

Great!

\n

We’re blessed to know that our resources encouraged your family.

\n

We want you to have this inspirational new devotional from bestselling author Jackie Hill Perry, Upon Waking. It’s an invitation to step away from your to-do lists and anxiety and into a daily reminder of what truly matters.

\n

It is our gift to you when you become one of the X new monthly donors we\'re looking for today. As a FamilyLife partner, your gift will strengthen your family and other families all year. \n

\n

\n Get your devotional\n

\n
',document.body.appendChild(o),a.querySelector("svg").onclick=()=>n(a),o.querySelector("svg").onclick=()=>n(o),a.querySelector("#popup-no").onclick=()=>{const e=Date.now()+6048e5;localStorage.setItem("popupDismissedUntil",String(e)),n(a)},a.querySelector("#popup-yes").onclick=()=>{n(a),setTimeout(l,300)},setTimeout(()=>a.showModal(),3e3)}();}catch(e) {VWO._.vAEH(e);} return vwo_$('head')[0] && vwo_$('head')[0].lastChild;})("head")}}, R_725969_176_1_3_0:{ fn:function(log,nonce=''){return (function(x) { try{ var ctx=vwo_$(x),el; /*vwo_debug log("Revert","content",""); vwo_debug*/; el=vwo_$('[vwo-element-id="1746468826149"]'); el.revertContentOp().remove(); } catch(e) {VWO._.vAEH(e);} try{ var el,ctx=vwo_$(x); /*vwo_debug log("Revert","addElement","body"); vwo_debug*/(el=vwo_$('[vwo-element-id="1746468826150"]')).remove(); } catch(e) {VWO._.vAEH(e);} return vwo_$('head')[0] && vwo_$('head')[0].lastChild;})("head")}}, C_725969_176_1_3_1:{ fn:function(log,nonce=''){return (function(x) {var el,ctx=vwo_$(x); /*vwo_debug log("_clickElement",".btn"); vwo_debug*/(el=vwo_$(".btn")).addClass("_vwo_coal_1697827292277");})(".btn")}}, R_725969_176_1_3_1:{ fn:function(log,nonce=''){return (function(x) { if(!vwo_$.fn.vwoRevertHtml){ return; }; var el,ctx=vwo_$(x); /*vwo_debug log("Revert","_clickElement",".btn"); vwo_debug*/(el=vwo_$(".btn")).removeClass("_vwo_coal_1697827292277");})(".btn")}}, C_725969_183_1_2_0:{ fn:function(log,nonce=''){return (function(x) { try{ var _vwo_sel = vwo_$("`); !vwo_$("head").find('#1746479885395').length && vwo_$('head').append(_vwo_sel);}catch(e) {VWO._.vAEH(e);} try{}catch(e) {VWO._.vAEH(e);} try{function getCurrentDate (d = new Date()) { return d.toISOString().split('T')[0] } function vwoCustomEvent (labelValue) { window.VWO = window.VWO || []; VWO.event = VWO.event || function () {VWO.push(["event"].concat([].slice.call(arguments)))}; VWO.event("customEvent", { "label": labelValue.toString() }); } function vwoDonationInterrupter2 (id, type) { window.VWO = window.VWO || []; VWO.event = VWO.event || function () {VWO.push(["event"].concat([].slice.call(arguments)))}; if (type === "shown" || type === "error") { VWO.event("donationInterrupter2", { "id": id?.toString(), "type": type?.toString(), }); } else { VWO.event("donationInterrupter2action", { "id": id?.toString(), "type": type?.toString(), }); } } class SortOfSimpleCountdownTimer{constructor(t,i=document.createElement("div"),e={onEnd:function(t){t.hide()},onChange:function(t){}}){return console.log(t),this._time={end:t instanceof Date?t:new Date(t).getTime(),now:new Date().getTime()},this._time.remaining=this._time.end-this._time.now,this._data=null,i.classList.contains("countdown")||i.classList.add("countdown"),this.element=i,this.callbacks=e,setTimeout(()=>this.init(),50),this}init(){console.log("Countdown Timer initialized."),this._interval=setInterval(t=>t.update(),1e3,this),this.update()}update(){try{this.now=new Date().getTime(),this.remaining=this.end-this.now,this.now;let t=this.remaining;if(!this.remaining||"NaN"==this.remaining){console.warn("Remaining time is not a number or is falsy.");return}if(this.remaining<=0)this.finish(),this.hide();else{let i={days:Math.floor(t/864e5),hours:Math.floor(t%864e5/36e5),minutes:Math.floor(t%36e5/6e4),seconds:Math.floor(t%6e4/1e3)};this.data=i;let e=[];Object.entries(i).filter(([t,i])=>"seconds"==t).forEach(([t,i])=>e.push(`
${i||0}
`)),this.element.innerHTML=e.join("\n")}this.callbacks&&"function"==typeof this.callbacks.onChange&&this.callbacks.onChange.call(this,this.element)}catch(n){this.element.style.setProperty("display","none"),console.error(n)}}finish(){this._interval?(clearInterval(this._interval),console.info("Countdown finished.")):console.error("Failed to clear interval."),this.callbacks&&"function"==typeof this.callbacks.onEnd&&this.callbacks.onEnd.call(this,this.element)}hide(){this.element.style.setProperty("display","none")}get now(){return this._time.now}set now(t){this._time.now=t||new Date().getTime()}get end(){return this._time.end}set end(t){this._time.end=t}get remaining(){return this._time.remaining}set remaining(t){this._time.remaining=t||this._time.end-this._time.now}get data(){return this._data}set data(t){this._data=t}} class NextAfterDonationMicrosite_GiftArrayButton { static activeClass = 'active'; constructor (element) { if (!(element instanceof HTMLElement) || !(element.classList.contains('amt-button') || element.classList.contains('option'))) throw new TypeError("Argument 0 must be an instance of HTMLElement with a class of '.amt-button' or '.option'."); this.button = element; } get value () { return parseInt(this.button.dataset.amt); } set value (newValue) { this.button.dataset.amt = parseInt(newValue); } get text () { return this.button.textContent; } set text (newText) { this.button.textContent = newText; } get active () { return this.button.classList.contains(NextAfterDonationMicrosite_GiftArrayButton.activeClass) ? true : false; } set active (boolean) { if (boolean) { this.select(); if (!this.active) // if for some reason the button was selected but still does not have the active class this.button.classList.add(NextAfterDonationMicrosite_GiftArrayButton.activeClass); } else { this.button.classList.remove(NextAfterDonationMicrosite_GiftArrayButton.activeClass); } } click () { this.button.click() } select () { this.click() } addEventListener (eventType, eventHandlerCallback) { this.button.addEventListener(eventType, eventHandlerCallback); } get amount () { return this.value; } set amount (newAmount) { if (Number.isNaN(parseInt(newAmount)) || parseInt(newAmount) < 0) throw new Error("New amount must be a valid number greater than 0."); newAmount = parseInt(newAmount); this.text = parseFloat(newAmount).toLocaleString('en-US', { format: 'currency', currency: 'USD', minimumFractionDigits: 0, maximumFractionDigits: 0 }); this.value = parseInt(newAmount); } } class NextAfterDonationMicrosite_GiftArrayButtonRadio extends NextAfterDonationMicrosite_GiftArrayButton { constructor (element) { super(element); this.radio = element.matches('input[type="radio"]') ? element : element.querySelector('input[type="radio"]'); } get value () { return parseInt(this.radio.value); } set value (newValue) { this.radio.value = parseInt(newValue); } set text (newText) { const textNode = this.radio.nextSibling.nodeType === Node.TEXT_NODE ? this.radio.nextSibling : [...this.element.childNodes].find(({ nodeType, nodeValue }) => nodeType === Node.TEXT_NODE && nodeValue.length > 0); textNode.nodeValue = newText.toString(); } addEventListener (eventType, eventHandlerCallback) { if (eventType == 'click') { this.button.addEventListener(eventType, eventHandlerCallback); } else { this.radio.addEventListener(eventType, eventHandlerCallback); } } } class NextAfterDonationMicrosite_GiftArrayOtherAmount { constructor (element) { if (!(element instanceof HTMLInputElement) || !(element.type == 'text')) throw new TypeError("Argument 0 must be an instance of HTMLInputElement with a 'type' of 'text'."); this.input = element; } get value () { return this.input.value; } set value (newValue) { this.input.dispatchEvent(new Event('click')); this.input.dispatchEvent(new Event('focus')); this.input.value = newValue; // this.input.dispatchEvent(new Event('keyup')); this.input.dispatchEvent(new Event('input')); this.input.dispatchEvent(new Event('change')); this.input.dispatchEvent(new Event('blur')); } get text () { return this.input.placeholder; } set text (newPlaceholderText) { this.input.placeholder = newPlaceholderText; } get amount () { return parseFloat(this.value); } set amount (newAmount) { if (Number.isNaN(parseInt(newAmount)) || parseInt(newAmount) <= 0) throw new Error("New amount must be a valid number greater than 0."); newAmount = parseFloat(newAmount); this.value = newAmount; } addEventListener (eventType, eventHandlerCallback) { this.input.addEventListener(eventType, eventHandlerCallback); } } class NextAfterDonationMicrosite_GiftArray extends Array { constructor (items) { if (!Array.isArray(items) && items.length === 0) { throw !Array.isArray(items) ? new TypeError("GiftArray: Arugment 1 is not an instance of Array:" + items.join(', ')) : new RangeError("GiftArray: Arugment 1 is not an instance of Array with a length greater than 0:" + items.join(', ')); } if (items.every((item) => item instanceof NextAfterDonationMicrosite_GiftArrayButton || item instanceof NextAfterDonationMicrosite_GiftArrayOtherAmount)) { if (items.find((item) => item instanceof NextAfterDonationMicrosite_GiftArrayOtherAmount)) { // if the array is mixed (both GiftArrayButton and GiftArrayButtonOtherAmount) let temp = items.find((item) => item instanceof NextAfterDonationMicrosite_GiftArrayOtherAmount); // temporarily hold the other amount items = items.filter((item) => item instanceof NextAfterDonationMicrosite_GiftArrayButton); // assign items to be only the gift array button temporarily (removed the other amount) items.push(temp); // add back the other amount to items so that it's always the last entry in the array } } else if (items.every((item) => item instanceof HTMLElement)) { items = items.map((item) => { if (item.matches('.stripe-donation-other-amt, .option-other-text')) return new NextAfterDonationMicrosite_GiftArrayOtherAmount(item); return item.querySelector('input[type="radio"]') ? new NextAfterDonationMicrosite_GiftArrayButtonRadio(item) : new NextAfterDonationMicrosite_GiftArrayButton(item); }); } else { throw new Error("GiftArray: Arugment 1 is not of type HTMLElement[], or GiftArrayButton[]:" + items.join(', ')); } super(...items); this.Buttons = items.filter((item) => item instanceof NextAfterDonationMicrosite_GiftArrayButton) ?? []; this.OtherAmountInput = items.find((item) => item instanceof NextAfterDonationMicrosite_GiftArrayOtherAmount) ?? null; } get amount () { const activeButton = this.Buttons.find((item) => item.active); if (activeButton) { // active button was found in the gift array return activeButton.amount; } else { return this.OtherAmountInput.amount; } } set amount (newAmount) { if (Number.isNaN(parseInt(newAmount)) || parseInt(newAmount) <= 0) throw new Error("New amount must be a valid number greater than 0."); newAmount = parseFloat(newAmount); const matchingButton = this.find((item) => parseFloat(item.value) === newAmount); if (matchingButton) { matchingButton.click(); } else { this.OtherAmountInput.amount = newAmount; const activeButton = this.Buttons.find((item) => item.active); if (activeButton) activeButton.active = false; } } addEventListeners (eventType, eventHandlerCallback) { this.forEach((item) => item.addEventListener(eventType, eventHandlerCallback)); } } class NextAfterDonationMicrosite_FrequencyToggle { constructor (element) { if (!(element instanceof HTMLInputElement) || !(element.type == 'checkbox')) throw new TypeError("Argument 0 must be an instance of HTMLInputElement with a 'type' of 'checkbox'."); this.checkbox = element; } get frequency () { return this.checkbox.checked ? true : false; } set frequency (boolean) { if (boolean !== this.frequency) { // only if the arugment is different from what is already set if (boolean === true) this.checkbox.checked = true; if (boolean === false) this.checkbox.checked = true; } } get recurring () { return this.frequency ? true : false; } set recurring (bool) { this.frequency = bool; } addEventListener (eventType, eventHandlerCallback) { this.checkbox.addEventListener(eventType, eventHandlerCallback); } } class NextAfterDonationMicrosite_FrequencyRadio { constructor (element) { if (!(element instanceof HTMLInputElement) || !(element.type == 'radio')) throw new TypeError("Argument 0 must be an instance of HTMLInputElement with a 'type' of 'radio'."); this.radio = element; } get value () { return this.radio.value; } set value (newValue) { this.radio.value = newValue; } get frequency () { return this.radio.checked ? true : false; } set frequency (boolean) { if (boolean !== this.frequency) { // only if the arugment is different from what is already set if (boolean === true) this.radio.checked = true; if (boolean === false) this.radio.checked = true; } } get recurring () { return this.frequency ? true : false; } set recurring (bool) { this.frequency = bool; } get checked () { return this.radio.checked; } set checked (bool) { this.radio.checked = bool ? true : false; } click () { this.radio.click(); } addEventListener (eventType, eventHandlerCallback) { this.radio.addEventListener(eventType, eventHandlerCallback); } } class NextAfterDonationMicrosite_FrequencyArray extends Array { constructor (items) { if (!Array.isArray(items) && items.length === 0) { throw !Array.isArray(items) ? new TypeError("FrequencyArray: Arugment 1 is not an instance of Array:" + items.join(', ')) : new RangeError("FrequencyArray: Arugment 1 is not an instance of Array with a length greater than 0:" + items.join(', ')); } if (items.every((item) => item instanceof NextAfterDonationMicrosite_FrequencyRadio)) { items = items; } else if (items.every((item) => item instanceof HTMLElement)) { items = items.map((item) => (item instanceof HTMLInputElement && item.type === 'radio') ? new NextAfterDonationMicrosite_FrequencyRadio(item) : undefined); } else { throw new Error("FrequencyArray: Arugment 1 is not of type HTMLInputElement[]|HTMLElement[], or FrequencyRadio[]:" + items.join(', ')); } super(...items); this.Options = items.filter((item) => item instanceof NextAfterDonationMicrosite_FrequencyRadio) ?? []; } get frequency () { const checkedRadio = this.Options.find(({ radio }) => radio.checked); if (checkedRadio) { // active button was found in the gift array return checkedRadio.value; } else { return undefined; } } set frequency (newFrequency) { const matchingRadio = this.Options.find(({ value }) => value === newFrequency); if (matchingRadio) { matchingRadio.click(); } else { console.warn("Invalid frequency: ", newFrequency); } } addEventListener (eventType, eventHandlerCallback) { this.Options.forEach((option) => option.addEventListener(eventType, eventHandlerCallback)); } } class NextAfterDonationMicrosite_FrequencyTab { constructor (element) { if (!(element instanceof HTMLElement)) throw new TypeError("Argument 0 must be an instance of HTMLElement."); this.element = element; } get text () { return this.element.textContent; } set text (newText) { this.element.textContent = newText; } click () { this.element.click(); } addEventListener (eventType, eventHandlerCallback) { this.radio.addEventListener(eventType, eventHandlerCallback); } } class NextAfterDonationMicrosite_FrequencyTabs extends Array { constructor (items) { if (!Array.isArray(items) && items.length === 0) { throw !Array.isArray(items) ? new TypeError("FrequencyTabs: Arugment 1 is not an instance of Array:" + items.join(', ')) : new RangeError("FrequencyTabs: Arugment 1 is not an instance of Array with a length greater than 0:" + items.join(', ')); } if (items.every((item) => item instanceof NextAfterDonationMicrosite_FrequencyTab)) { items = items; } else if (items.every((item) => item instanceof HTMLElement)) { items = items.map((item) => new NextAfterDonationMicrosite_FrequencyTab(item) ?? undefined); } else { throw new Error("FrequencyTabs: Arugment 1 is not of type HTMLElement[], or FrequencyTab[]:" + items.join(', ')); } super(...items); this.Buttons = items.filter((item) => item instanceof NextAfterDonationMicrosite_FrequencyTab) ?? []; } select (value) { const matchingTab = this.Buttons.find(({ text }) => text.match(value) || text.includes(value)); if (matchingTab) { matchingTab.click(); } else { console.warn("Invalid value: ", value); } } addEventListener (eventType, eventHandlerCallback) { this.Buttons.forEach((option) => option.addEventListener(eventType, eventHandlerCallback)); } } // const lockedProperty = { writable: false, configurable: false, enumerable: true }; function DonationFormAPI (elements, options = {}) { const defaultOptions = { min: 1.00, max: 999999.99, makeTabbed: false, fakeSubmit: true, overrideGiftArrayValues: false, }; options = { ...defaultOptions, ...options }; // const { recurringOptions, amountButtons, submitButton, root } = elements; const debug = { log: (...args) => window.NA.DonationForm.DEBUG_MODE && console.log(...args), info: (...args) => window.NA.DonationForm.DEBUG_MODE && console.log(...args), warn: (...args) => window.NA.DonationForm.DEBUG_MODE && console.log(...args), error: (...args) => window.NA.DonationForm.DEBUG_MODE && console.log(...args), }; // const api = new Object(); Object.defineProperty(api, 'root', { value: root, writable: false, configurable: true, enumerable: true, }); Object.defineProperties(api, { FORM_MINIMUM: { // constant value: options.min || 0, ...lockedProperty }, FORM_MAXIMUM: { // constant value: options.max || Infinity, ...lockedProperty }, }); Object.defineProperties(api, { GiftArray: { // variable value: new NextAfterDonationMicrosite_GiftArray(amountButtons), writable: false, configurable: true, enumerable: true, }, Frequency: { // variable value: Array.isArray(recurringOptions) ? new NextAfterDonationMicrosite_FrequencyArray(recurringOptions) : new NextAfterDonationMicrosite_FrequencyToggle(recurringOptions), writable: false, configurable: true, enumerable: true, }, SubmitButton: { // variable value: submitButton, writable: false, configurable: false, enumerable: true, } }); if (elements.tabs && Array.isArray(elements.tabs) && elements.tabs.length >= 2 && elements.tabs.every((item) => item instanceof HTMLElement)) { Object.defineProperty(api, 'Tabs', { value: new NextAfterDonationMicrosite_FrequencyTabs(elements.tabs), writable: false, configurable: true, enumerable: true, }); } Object.defineProperties(api, { getFrequency: { // function value: async function () { if (!this || this === null) throw new Error("validate: Unable to read API context."); /*if (this.Tabs && this.Tabs instanceof NextAfterDonationMicrosite_FrequencyTabs) { // tabs } else*/ if (this.Frequency instanceof NextAfterDonationMicrosite_FrequencyArray) { // radios return new Promise((resolve, reject) => { try { resolve(this.Frequency.frequency); } catch (error) { reject(error); } }); } else if (this.Frequency instanceof NextAfterDonationMicrosite_FrequencyToggle) { // checkbox return new Promise((resolve, reject) => { try { resolve(this.Frequency.frequency); } catch (error) { reject(error); } }); } }, ...lockedProperty }, setFrequency: { // function value: async function (frequency) { if (!this || this === null) throw new Error("validate: Unable to read API context."); if (this.Frequency instanceof NextAfterDonationMicrosite_FrequencyArray) { // radios if (this.Tabs && this.Tabs instanceof NextAfterDonationMicrosite_FrequencyTabs) { // tabs let index = this.Frequency.Options.findIndex(({ value }) => value.trim().match(new RegExp(frequency, 'i')) || value.trim().includes(frequency)) ?? -1; if (index > -1 && index < this.Tabs.Buttons.length) { this.Tabs.Buttons[index]?.click(); } else { this.Tabs.select(frequency); } } return new Promise(async (resolve, reject) => { try { this.Frequency.frequency = frequency; if (await this.getFrequency() === frequency) resolve(frequency); } catch (error) { reject(error); } }); } if (this.Frequency instanceof NextAfterDonationMicrosite_FrequencyToggle) { // checkbox if (this.Tabs && this.Tabs instanceof NextAfterDonationMicrosite_FrequencyTabs) { // tabs let index = this.Frequency.Options.findIndex(({ value }) => value.trim().match(new RegExp(frequency, 'i')) || value.trim().includes(frequency)) ?? -1; if (index > -1 && index < this.Tabs.Buttons.length) { this.Tabs.Buttons[index]?.click(); } else { this.Tabs.select(frequency); } } return new Promise(async (resolve, reject) => { try { this.Frequency.frequency = frequency; if (await this.getFrequency() === frequency) resolve(frequency); } catch (error) { reject(error); } }); } }, ...lockedProperty }, getAmount: { // function value: async function () { if (!this || this === null) throw new Error("validate: Unable to read API context."); return new Promise((resolve, reject) => { try { resolve(this.GiftArray.amount); } catch (error) { reject(error); } }); }, ...lockedProperty }, setAmount: { // function value: async function (amount, frequency = undefined) { if (!this || this === null) throw new Error("validate: Unable to read API context."); return new Promise(async (resolve, reject) => { try { this.GiftArray.amount = amount; if (await this.getAmount() === amount) resolve(amount); } catch (error) { reject(error); } }); }, ...lockedProperty }, getRecurring: { // function value: async function () { if (!this || this === null) throw new Error("validate: Unable to read API context."); return new Promise((resolve, reject) => { try { resolve(this.Frequency.recurring); } catch (error) { reject(error); } }); }, ...lockedProperty }, setRecurring: { // function value: async function (bool) { if (!this || this === null) throw new Error("validate: Unable to read API context."); return new Promise(async (resolve, reject) => { try { this.Frequency.frequency = bool ? true : false; if (await this.getRecurring() === bool) resolve(bool); } catch (error) { reject(error); } }); }, ...lockedProperty }, freqency: { get () { return this.getFrequency() }, set (value) { this.setFrequency(value) }, enumerable: true, configurable: true, }, amount: { get () { return this.getAmount() }, set (value) { this.setAmount(value) }, enumerable: true, configurable: true, }, recurring: { get () { return this.getRecurring() }, set (value) { this.setRecurring(value) }, enumerable: true, configurable: true, }, }); Object.defineProperties(api, { submit: { // function value: async function (condition = this.validate||function(){return true}) { //this.hooks['onBeforeSubmit'].forEach((callback) => callback.call(this)); let result; const isAsyncFunction = (func) => func.constructor.name === "AsyncFunction"; if (Array.isArray(condition)) { if (condition.every((c) => typeof c === 'function' && isAsyncFunction(c))) { result = await Promise.all(condition.map(async (c) => await c.call(this))); } else if (condition.every((c) => typeof c === 'function')) { result = condition.every((c) => c.call(this)); } else if (condition.every((c) => c === true || c === false)) { result = condition.every((c) => c); } } else if (typeof condition === 'function' && isAsyncFunction(condition)) { result = await condition.call(this); } else if (typeof condition === 'function') { result = condition.call(this); } else if (condition === true || condition === false) { result = condition; } else { console.error("Unknown error."); debugger; } // if (result === true) { if (window.NA.DonationForm.hasOwnProperty("DEBUG_MODE") && window.NA.DonationForm["DEBUG_MODE"] == true) return console.log("Submit aborted (debug mode is enabled)."); this.SubmitButton.click(), this.hooks['onSubmit'].forEach((callback) => callback.call(this)); //this.hooks['onAfterSubmit'].forEach((callback) => callback.call(this)); } else { return console.log("Submit failed (conditions did not evaluate to true)."); } }, ...lockedProperty }, interceptSubmit: { // function value: function (handleInterceptedSubmit = () => { return new Promise((resolve) => resolve(undefined)) }) { try { window.NA.DonationForm.SubmitButtonCopy = window.NA.DonationForm.SubmitButtonCopy || createNewSubmitButton(window.NA.DonationForm.SubmitButton, { cloneOriginal: false, hideOriginal: true, observeOriginal: false }); window.NA.DonationForm.SubmitButtonCopy.addEventListener('click', async (event) => { event.preventDefault(), event.stopPropagation(); const shouldRetryInterrupterAfterFailedSubmit = false; const shouldFormSubmit = await handleInterceptedSubmit.call(this, event); if (shouldFormSubmit) { console.info("Submit allowed by initial interceptSubmit callback function resulting in a truthy evaluation."); const formIsValid = await window.NA.DonationForm.validate(); if (!formIsValid) { // if the the form SHOULD submit (based on handleInterceptedSubmit return value) and if the form IS NOT valid (there are known errors in the form found by calling the form API's validate function) console.warn("Form has known errors. Attempting to submit to show errors then retrying."); console.log("Submitting..."); window.NA.DonationForm.submit(true); // submit anyway to trigger the error to be shown if (shouldRetryInterrupterAfterFailedSubmit) { // if true, the fake button that will intercept submit will be shown again after a failed submit, otherwise it will show the original to ensure it submits the next time window.NA.DonationForm.SubmitButton.style.setProperty("display", "none"), debug.info("SubmitButton hidden."); // hide the original submit button again // window.NA.DonationForm.SubmitButtonCopy.style.setProperty("display", "none"), debug.info("SubmitButtonCopy hidden."); // hide the copy of the submit button again window.NA.DonationForm.SubmitButtonCopy.style.removeProperty("display"), debug.info("SubmitButtonCopy unhidden."); // show the copy of the submit button } else { window.NA.DonationForm.SubmitButtonCopy.style.setProperty("display", "none"), debug.info("SubmitButtonCopy hidden."); // hide the copy of the submit button again // window.NA.DonationForm.SubmitButton.style.setProperty("display", "none"), debug.info("SubmitButton hidden."); // hide the copy of the submit button again window.NA.DonationForm.SubmitButton.style.removeProperty("display"), debug.info("SubmitButton unhidden."); // hide the original submit button again } } else { // if the the form SHOULD submit (based on handleInterceptedSubmit return value) and if the form IS valid console.log("Submitting..."); window.NA.DonationForm.submit(true); } } else { // if the form SHOULD NOT submit (based on handleInterceptedSubmit return value) -- usually means show donation interrupter after preventing submit action console.log("Submit prevented."), console.info("Next submit will be allowed."); window.NA.DonationForm.SubmitButton.style.removeProperty("display"), debug.info("SubmitButton unhidden."); // show the original submit button so that if something goes wrong the user can still click the submit button window.NA.DonationForm.SubmitButtonCopy.style.setProperty("display", "none"), debug.info("SubmitButtonCopy hidden."); // hide the copy of the submit button that intercepts submit attempts so that there aren't two buttons } }); console.log("Submit intercept added.\nButton:", window.NA.DonationForm.SubmitButtonCopy); } catch (error) { console.error("Failed to add submit intercept:", error); } }, ...lockedProperty }, validate: { // function value: async function (root = undefined) { if (!this || this === null) throw new Error("validate: Unable to read API context."); root = root || this.root; const flattenArray = (array) => array.reduce((flat, toFlatten) => flat.concat(Array.isArray(toFlatten) ? flattenArray(toFlatten) : toFlatten), []); try { const isRecurring = await this.getFrequency(), amount = await this.getAmount(); if (isRecurring === null || isRecurring === undefined) return false; if (!amount) return false; if (amount < this.FORM_MINIMUM || amount > this.FORM_MAXIMUM) return console.error("validate:", "Gift amount is invalid:", amount), false; const chosenPaymentMethod = root.querySelector('.payment-option-holder .active')?.textContent.trim(); const requiredFields = Array.from(root.querySelectorAll('input[required]')) const valid = requiredFields.every((input) => { const type = input.tagName.toLowerCase() === 'select' ? 'select' : input.type; const { name, value, id } = input; //console.log(type, name, value); if (input.classList.contains('stripe-donation-card-number')) { // if the required input is the CC field AND the selected payment is CC if (chosenPaymentMethod == 'Credit Card') { if (input.value.trim().length === 16) // if the card number is 16 digits return true; // mark as valid return false; } else { // CC isn't even selected so don't even need to validate it return true; } } if (input.classList.contains('stripe-donation-routing-number') || input.classList.contains('stripe-donation-account-number') || input.classList.contains('stripe-donation-account-number-check')) { // if the required input is the CC field AND the selected payment is CC if (chosenPaymentMethod == 'Bank Account') { const trimmedValue = input.value.trim(); if (input.classList.contains('stripe-donation-routing-number')) { // routing number if (trimmedValue.length === 9) // if the routing number is 9 digits return true; // mark as valid return false; } else { // account number if (trimmedValue.length >= 8 && trimmedValue.length <= 17) // if the routing number is 9 digits return true; // mark as valid return false; } } else { // ACH isn't even selected so don't even need to validate it return true; } } switch (type) { case 'email': const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; if (!emailRegex.test(value)) return console.error("validate:", name+':', "Email address is invalid.\n", input, value), false; return true; case 'tel': if (!value || value.length < 10) return console.error("validate:", name+':', "Phone number is invalid.\n", input, value), false; return true; case 'select': case 'radio': case 'text': if (!value || value.length === 0) return console.error("validate:", name+':', "Field is invalid.\n", input, value), false; return true; default: debug.log("default"); return true; } /*if (!value || value.length === 0) return false;*/ }); return valid; } catch (error) { console.error(error); return false; } }, ...lockedProperty }, DonationInterrupter: { value: { init: initDonationInterrupter.bind(api) }, enumerable: true, configurable: true, writable: true, } }); initHooks(api, ['onFrequencyChange', 'onAmountChange', 'onTrySubmit', 'onSubmit']); api.Frequency.addEventListener('change', (event) => { if (event.target.checked) { api.hooks['onFrequencyChange'].forEach((callback) => { callback.call(api, event.target.value); }); } }); api.GiftArray.addEventListeners('change', (event) => { if (event.target.checked) { api.hooks['onAmountChange'].forEach((callback) => { callback.call(api, event.target.value); }); } }); api.SubmitButton.addEventListener('click', (event) => { api.hooks['onTrySubmit'].forEach((callback) => callback.call(api, event)); }); api.root.addEventListener('submit', (event) => { api.hooks['onSubmit'].forEach((callback) => callback.call(api, event)); }); if (options.makeTabbed) api.makeTabbed(); /*if (options.fakeSubmit) window.NA.DonationForm.SubmitButtonCopy = window.NA.DonationForm.SubmitButtonCopy || createNewSubmitButton(window.NA.DonationForm.SubmitButton, { cloneOriginal: false, hideOriginal: true, observeOriginal: false });*/ return api; } function createNewSubmitButton (originalSubmitButton = window.NA.DonationForm.SubmitButton, options = {}) { const defaultOptions = { cloneOriginal: true, hideOriginal: true, observeOriginal: true, }; options = { ...defaultOptions, ...options }; const newSubmitButton = document.createElement('button'); newSubmitButton.id = "submit-button-copy"; newSubmitButton.classList.add(...originalSubmitButton.classList.values()); newSubmitButton.textContent = originalSubmitButton.value; originalSubmitButton.after(newSubmitButton); options.hideOriginal && originalSubmitButton.style.setProperty("display", "none"); return newSubmitButton; } function initHooks (api, hookNames = []) { const hooks = Object.fromEntries(hookNames.map((hookName) => ([hookName, new Array()]))); Object.defineProperty(api, 'hooks', { value: hooks, ...lockedProperty }); } function initDonationInterrupter (options = {}) { const getExpId = (matching = /Donation Interrupter/) => { let experiments = window._vwo_exp; experiments = Object.entries(window._vwo_exp); let id = experiments.find(([id, data]) => { const name = data.name, status = data.status, variation = data.hasOwnProperty('combination_chosen') ? data.combination_chosen : null; return variation && status === 'RUNNING' && name.match(matching); })[1]?.id; return id; }; const getExpVariation = (id) => { let experiment = window._vwo_exp[id]; return experiment.combination_chosen || experiment.combination_selected; }; const defaultOptions = { id: [ 'VWO', getExpId(), getExpVariation(getExpId()) ].join('-'), tokenName: ("NA__THF_DonationInterrupter:" + [ 'VWO', getExpId(), getExpVariation(getExpId()) ].join('-')), min: 10, max: 100, askAmount: (originalAmount) => { if (originalAmount > 500) // $500+ return false; // don't show if (originalAmount >= 400) // $400-$500 return 50; if (originalAmount >= 300) // $300-$399 return 40; if (originalAmount >= 200) // $200-$299 return 30; if (originalAmount >= 100) // $100-$199 return 15; if (originalAmount < 100) // $100- return 10; return false; }, askFrequency: (originalFrequency) => { return true; }, popupHTML: { headingHTML: (` `), bodyHTML: (` `), footerHTML: (``), }, countdownSeconds: null, }; options = { ...defaultOptions, ...options }; console.log("Initializing donation interrupter."); return new Promise((resolve, reject) => { try { const dialogElement = document.createElement('dialog'); dialogElement.id = options.id; dialogElement.classList.add("popup", "donation-interrupter", "NA"); dialogElement.innerHTML = `
${options.popupHTML.headingHTML} ${options.popupHTML.bodyHTML} ${options.popupHTML.footerHTML}
`; document.body.appendChild(dialogElement); // // const api = new Object({ askAmount: options.askAmount, askFrequency: options.askFrequency, }); Object.defineProperties(api, { id: { value: options.id, writable: false, enumerable: true, configurable: false, }, tokenName: { value: options.tokenName, writable: false, enumerable: true, configurable: false, }, Dialog: { value: dialogElement, writable: false, enumerable: true, configurable: true, }, show: { value: function () { this.update(), this.Dialog.showModal(), // vwoCustomEvent(`${this.id}:DonationInterrupter:shown`); vwoDonationInterrupter2(this.id, 'shown'); this.storedState.updateTokenProperty("lastShown", getCurrentDate()); this.hooks['onShow'].forEach((callback) => callback.call(this)); }, ...lockedProperty }, hide: { value: function () { this.Dialog.close(), this.hooks['onHide'].forEach((callback) => callback.call(this)); }, ...lockedProperty }, update: { value: function () { this.Dialog.dispatchEvent(new CustomEvent('update'), { bubbles: false }); }, ...lockedProperty }, autoSubmitCountdownSeconds: { value: options.countdownSeconds ?? null, writable: true, configurable: true, enumerable: true, } }); Object.defineProperty(api, 'storedState', { value: { storageApi: localStorage, getToken: (function () { const tokenName = this.tokenName, storageApi = this.storedState.storageApi; return JSON.parse(storageApi.getItem(tokenName)) || null; }).bind(api), setToken: (function (tokenValue) { const tokenName = this.tokenName, storageApi = this.storedState.storageApi; return storageApi.setItem(tokenName, JSON.stringify(tokenValue)); }).bind(api), updateTokenProperty: (function (tokenPropertyName, tokenPropertyValue) { let state = this.storedState.getToken() || {}; state[tokenPropertyName] = tokenPropertyValue; this.storedState.setToken(state); return (this.storedState.getToken() || {})[tokenPropertyName] || undefined; }).bind(api), } }); initHooks(api, ['onShow', 'onHide', 'onUpdate', 'onYes', 'onNo']); // const abortCountdown = (function () { if (this.SubmitCountdown) { this.SubmitCountdown.callbacks.onEnd = []; this.SubmitCountdown.finish(); console.log("Countdown cancelled."); } }).bind(api); const handleYes = (async function () { // vwoCustomEvent(`${this.id}:DonationInterrupter:yes`); vwoDonationInterrupter2(this.id, 'yes'); const currentAmount = await window.NA.DonationForm.getAmount(), currentFrequency = await window.NA.DonationForm.getFrequency(); const catchAsyncError = (error) => { console.error("An error occured:", error); debugger; this.hide(); }; abortCountdown(); window.NA.DonationForm.setFrequency(this.askFrequency(currentFrequency)).then((frequency) => { console.log("Updated frequency:", frequency); window.NA.DonationForm.setAmount(this.askAmount(currentAmount)).then((amount) => { console.log("Updated amount:", amount); setTimeout(() => { console.log("Submitting..."); try { window.NA.DonationForm.submit(); } catch (error) { vwoDonationInterrupter2(this.id, 'error'); console.error("🛑 " + "Error when submitting."); } finally { this.hide(); } }, 100); }).catch(catchAsyncError); }).catch(catchAsyncError); this.storedState.updateTokenProperty("lastConverted", getCurrentDate()); }).bind(api); const handleNo = (function () { // vwoCustomEvent(`${this.id}:DonationInterrupter:no`); vwoDonationInterrupter2(this.id, 'no'); abortCountdown(); setTimeout(() => { console.log("Submitting..."); try { window.NA.DonationForm.submit(); } catch (error) { vwoDonationInterrupter2(this.id, 'error'); console.error("🛑 " + "Error when submitting."); } finally { this.hide(); } }, 100); this.storedState.updateTokenProperty("lastDismissed", getCurrentDate()); }).bind(api); const handleCancel = (function () { // vwoDonationInterrupter2(this.id, 'cancel'); abortCountdown(); this.hide(); this.storedState.updateTokenProperty("lastDismissed", getCurrentDate()); }).bind(api); // function handleDialogUpdate (event) { // update dynamic text in the dialog Array.from(this.Dialog.querySelectorAll('[data-value]')).forEach(async (el) => { const attributeValue = el.getAttribute('data-value'); const currentAmount = await window.NA.DonationForm.getAmount(), currentFrequency = await window.NA.DonationForm.getFrequency(); if (attributeValue.match("askAmount")) { el.textContent = this.askAmount(currentAmount); } else if (attributeValue.match("askFrequency")) { el.textContent = this.askFrequency(currentFrequency) === true ? "monthly" : "one-time"; } else if (attributeValue.match("originalAmount") || attributeValue.match("amount")) { el.textContent = currentAmount; } else if (attributeValue.match("originalFrequency") || attributeValue.match("frequency")) { el.textContent = currentFrequency === true ? "monthly" : "one-time"; } }); // const countdownTimerElement = this.Dialog.querySelector('.countdown-timer') || null; if (countdownTimerElement && this.autoSubmitCountdownSeconds && this.autoSubmitCountdownSeconds > 0) { let newEndDateTimeString = new Date((new Date).getTime() + ((this.autoSubmitCountdownSeconds + 1) * 1000)).toISOString();//.split('.')[0]; countdownTimerElement.setAttribute("data-endDateTime", newEndDateTimeString), console.log("Countdown timer expiration set:", newEndDateTimeString); this.SubmitCountdown = initCountdown(countdownTimerElement, function onCountdownEnd (element) { console.log("Countdown expired!"); handleNo(); }); } // this.hooks['onUpdate'].forEach((callback) => callback.call(this)); } api.Dialog.addEventListener('update', handleDialogUpdate.bind(api)); // async function handleDialogButtonClick (event) { event.preventDefault(); if (event.target.hasAttribute('data-action')) { if (event.target.getAttribute('data-action').match("yes")) { await handleYes.call(this); this.hooks['onYes'].forEach((callback) => callback.call(this)); } if (event.target.getAttribute('data-action').match("no")) { await handleNo.call(this); this.hooks['onNo'].forEach((callback) => callback.call(this)); } } } Array.from(api.Dialog.querySelectorAll('.popup__actions button')).forEach((button) => button.addEventListener('click', handleDialogButtonClick.bind(api))); // if (api.Dialog.querySelector('.btn-dismiss')) api.Dialog.querySelector('.btn-dismiss').onclick = handleCancel; window.NA.DonationForm.DonationInterrupter = api; // function initCountdown (element, callback = (element) => {}) { const countdownTarget = element; if (countdownTarget && countdownTarget.getAttribute("data-endDateTime")) { const handleCountdownTimeChange = async function (element) { console.log(`Countdown: auto-submitting original $${await window.NA.DonationForm.getAmount()} donation in`, this.data.seconds, "seconds...") }; try { const countdownEndDateTime = new Date(countdownTarget.getAttribute("data-endDateTime")); const countdownTimer = new SortOfSimpleCountdownTimer(countdownEndDateTime, undefined, { onEnd: callback, onChange: handleCountdownTimeChange }); countdownTarget.append(countdownTimer.element); return countdownTimer; } catch (error) { countdownTarget.style.setProperty("display", "none"); console.error(error); } } } async function shouldDonationInterrupterShow () { return new Promise(async (resolve, reject) => { const shouldSubmit = true, shouldNotSubmit = false; const shouldShow = () => { this.show(), resolve(shouldNotSubmit) }, shouldNotShow = () => resolve(shouldSubmit); try { const formIsValid = await window.NA.DonationForm.validate(); if (!formIsValid) return console.log("🟠 One or more donation form fields are invalid; donation interrupter will not be shown."), shouldNotShow(); const currentFrequency = await window.NA.DonationForm.getFrequency(), currentAmount = await window.NA.DonationForm.getAmount(), askFrequency = this.askFrequency(currentFrequency), askAmount = this.askAmount(currentAmount); if (currentFrequency === undefined || currentFrequency === null || askFrequency == currentFrequency) return console.log("🟠 Ask frequency returned undefined or null; donation interrupter will not be shown."), shouldNotShow(); if (askAmount == false || askAmount <= 0) return console.log("🟠 Ask amount returned undefined, null, false, or invalid; donation interrupter will not be shown."), shouldNotShow(); } catch (error) { return console.error("🔴 " + error), shouldNotShow(); } try { /// Summary: shows when not seen before at all, or if seen and dismissed on a day that is not the current day (e.g. yesterday) const storedState = this.storedState.getToken(); if (!storedState || !storedState.hasOwnProperty("lastShown")) { // has not been seen before; first time return console.log("🟢 Donation interrupter not seen yet; donation interrupter will be shown."), shouldShow(); } else { // returning visitors if (storedState.hasOwnProperty("lastConverted")) { // the user has converted from the popup before return console.log("🟡 Already converted; donation interrupter will not be shown."), shouldNotShow(); } else if (storedState.hasOwnProperty("lastDismissed") && storedState['lastDismissed'] !== getCurrentDate()) { // if the popup has been dismissed before but the last time it was dismissed is NOT today return console.log("🟢 Donation interrupter dismissed, but not today; donation interrupter will be shown."), shouldShow(); } else { return console.log("🟡 Donation interrupter already seen and/or dismissed today; donation interrupter will not be shown."), shouldNotShow(); } } } catch (error) { return console.error("🔴 " + error), shouldNotShow(); } }); } window.NA.DonationForm.interceptSubmit(shouldDonationInterrupterShow.bind(api)); resolve(api); } catch (error) { console.error(error); } }); } window.NA = window.NA || {}; window.NA.DonationForm = window.NA.DonationForm || {}; window.NA.DonationForm.init = async function init () { console.log("Initializing donation form API. Waiting for required elements...."); return new Promise((resolve, reject) => { const asyncWaitForElement=async function(e,r=100,t=1e4){r=Number.isInteger(r)&&r>0&&r<=100?r:parseInt(r);let n="Array";if("NaN"==r)return console.error("Invalid refresh interval:",r);Array.isArray(e)||"string"!=typeof e||(n="string",e=[e]);let l=e=>document.querySelector(e),i=e=>e.every(e=>!!l(e));return new Promise((R,a)=>{let m=(e,r=null)=>(r&&clearInterval(r),R("Array"==n||e.length>1?e.map(e=>l(e)):l(e[0]))),o=n=>{console.error(`${n.name}: ${n.message}`);let l=()=>asyncWaitForElement(e,r=100,t=1e4);return a(n,l)};try{if(i(e))return m(e);let s=setInterval(()=>{if(i(e))return m(e,s)},1e3/r);setTimeout(()=>{try{if(!i(e)){clearInterval(s);let r=Error(`Failed to find matching elements within ${t}ms`);throw r.name="Timed Out",r}}catch(n){return o(n)}},t)}catch(u){return o(u)}})}; asyncWaitForElement([ '.stripe-donation-form, .donation-form', '.stripe-donation-form .amt-holder, .donation-form .gift-array', '.stripe-donation-form .amt-holder, .donation-form .gift-frequency', '.stripe-donation-form input[type="submit"], .donation-form #btnSubmit' ]).then(([ form, formAmountSection, formFrequencySection, formSubmitButton ]) => { const recurringCheckbox = formAmountSection.querySelector('#recurring, input[name="DonationFrequency"][value="Recurring"]') ?? null; const recurringRadios = formFrequencySection.querySelectorAll('input[name="DonationFrequency"]').length > 0 ? [...formFrequencySection.querySelectorAll('input[name="DonationFrequency"]')] : null; const amountButtons = formAmountSection.querySelectorAll('.amt-button, .stripe-donation-other-amt, .option, .option-other-text'); // gift array buttons and other amount input !formSubmitButton.classList.contains("submit-button") && formSubmitButton.classList.add("submit-button"); const tabs = document.querySelector('.tabs') ? [...document.querySelectorAll('.tabs a')] : undefined; const api = DonationFormAPI({ root: form, recurringOptions: recurringCheckbox || recurringRadios, amountButtons: [...amountButtons], submitButton: formSubmitButton, tabs: tabs, }); window.NA.DonationForm = { ...window.NA.DonationForm, ...api }; resolve(window.NA.DonationForm); }).catch((error) => reject(error)); }); }; }catch(e) {VWO._.vAEH(e);} return vwo_$('head')[0] && vwo_$('head')[0].lastChild;})("head")}}, C_725969_183_1_2_1:{ fn:function(log,nonce=''){return (function(x) { try{ var _vwo_sel = vwo_$("`); !vwo_$("head").find('#1746479885398').length && vwo_$('head').append(_vwo_sel);}catch(e) {VWO._.vAEH(e);} try{; /*vwo_debug log("addElement","body"); vwo_debug*/; el=vwo_$("body") ; !el.find('[vwo-op-1746479885400=""]').length && el.vwoElement({"position":"append","html":"\n
\n \n
\n \n
\n
\n

\n Even A Small Gift Makes a Lasting Impact on Struggling Families\n

\n

\n For just $10 a month, you can bring Christ-centered resources to \n 76 families in need of strength, restoration, \n and hope.\n

\n \n \n \n
\n
\n
\n
\n
"});}catch(e) {VWO._.vAEH(e);} try{const ARM_EXIT_INTENT_AFTER_SECONDS = 5; const IDLE_TIMER_COUNTDOWN_SECONDS = 90; // // // // // const TOKEN_NAME = "NA__FL_ExitIntentPopup_Apr2025"; const getToken = (t) => JSON.parse(localStorage.getItem(t)) || null; const setToken = (t, v) => localStorage.setItem(t, JSON.stringify(v)); function getCurrentDate (d = new Date()) { return d.toISOString().split('T')[0] } function vwoCustomEvent (labelValue) { window.VWO = window.VWO || []; VWO.event = VWO.event || function () {VWO.push(["event"].concat([].slice.call(arguments)))}; VWO.event("customEvent", { "label": labelValue.toString() }); } const onExitIntent=(e,t=5,...q)=>{let n=t=>{if(!t.toElement&&!t.relatedTarget){document.removeEventListener("mouseout",n),window.onblur=()=>{},e.call();let o=e=>{"dialog"==e.target.tagName.toLowerCase()&&(e.target.close(),window.onclick=()=>{})};window.onclick=o}};/*console.log(`exit intent trigger will be armed in ${t} seconds`),*/setTimeout(()=>{console.log("exit intent trigger armed"),document.addEventListener("mouseout",n),window.onblur=n},1e3*t,...q)}; const isAsyncFunction = (func) => typeof func === 'function' && func.constructor.name === "AsyncFunction"; function onIdleTimer (callbackFn, timeoutMs = 60000, ...args) { const RESET_TIMER_ON_EVENTS = [ 'click', 'scroll' ]; // these events set the timer back to the original value const CANCEL_TIMER_ON_EVENTS = [ /*'input'*/ ]; // these events will clear the idle timer let timer; const removeTimer = function () { const interval = this; clearTimeout(interval); }.bind(timer); const resetTimer = () => { clearTimeout(timer); timer = setTimeout(() => { try { callbackFn.call(this, ...args); } catch (error) { console.error(error); } finally { RESET_TIMER_ON_EVENTS.forEach((eventName) => document.removeEventListener(eventName, eventHandler)); CANCEL_TIMER_ON_EVENTS.forEach((eventName) => document.removeEventListener(eventName, eventHandler)); } }, timeoutMs); console.log("Idle timer reset.", (new Date).toTimeString()); }; const eventHandler = () => resetTimer(); RESET_TIMER_ON_EVENTS.forEach((eventName) => document.addEventListener(eventName, eventHandler)); CANCEL_TIMER_ON_EVENTS.forEach((eventName) => document.addEventListener(eventName, removeTimer)); resetTimer(); } function showExitIntent () { let status = getToken(TOKEN_NAME) || {}; if (status.hasOwnProperty("lastShown") && status.lastShown === getCurrentDate()) // if last shown today return console.info("showExitIntent:", "Last shown today already (maybe from idle timer trigger). Exit intent will NOT be shown."), false; // const dialog = this; dialog.dispatchEvent(new CustomEvent('update')); // trigger an update vwoCustomEvent(TOKEN_NAME+":show"); // Array.from(dialog.querySelectorAll('[data-target="window.NA.DonationForm"]')).forEach((button) => { const eventType = (this.dataset.trigger || button.getAttribute('data-trigger')) ?? 'click'; button.addEventListener(eventType, function updateDonationForm (event) { const instructionQueue = (this.dataset.action || this.getAttribute('data-action')).split(';').map((instruction) => instruction.split('=')).map((instruction) => { const [ functionName, argumentsString ] = instruction; let argumentsArray = argumentsString.split(',').map((argument) => { argument = argument.replace(/[\'\"]/gm,''); argument = !Number.isNaN(parseInt(argument)) ? parseInt(argument) : argument; return argument; }); // split, map on argumentsString return [ functionName.trim(), argumentsArray ]; }); // split, map, map on instructionQueue instructionQueue.forEach(async ([ donationFormApiFunction, args ]) => { if (window.NA.DonationForm.hasOwnProperty(donationFormApiFunction) && typeof window.NA.DonationForm[donationFormApiFunction] === 'function') { if (isAsyncFunction(typeof window.NA.DonationForm[donationFormApiFunction])) { await window.NA.DonationForm[donationFormApiFunction](...args); } else { window.NA.DonationForm[donationFormApiFunction](...args); } } else { console.warn("window.NA.DonationForm does not have property", donationFormApiFunction); } }) // forEach of instructionQueue }); // addEventListener to button button.addEventListener('click', function pushState () { const params = new URLSearchParams(window.location.search); params.set('src', 'exitIntentPopup'); // adds or updates const newUrl = `${window.location.pathname}?${params.toString()}`; history.pushState(null, '', newUrl); }); button.addEventListener('click', function scroll () { const scrollTarget = document.querySelector('.donation-form .row-option-other'); if (scrollTarget) { let marginTop = '16px'; scrollTarget.style.setProperty('scroll-margin-block-start', marginTop); scrollTarget.scrollIntoView({ behavior: 'smooth', block: 'start' }); } }); }); // forEach [data-target="window.NA.DonationForm"] // const dialogButton1 = dialog.querySelector('.btn'); dialogButton1?.addEventListener('click', (e) => { vwoCustomEvent(TOKEN_NAME+":click"); let status = getToken(TOKEN_NAME) || {}; status.converted = true; // mark as converted setToken(TOKEN_NAME, status); }); // dialog.showModal(); // status.lastShown = getCurrentDate(); status.lastDismissed = status.lastDismissed || false; status.totalDismissed = status.totalDismissed || 0; status.totalShown = status.totalShown && status.totalShown >= 1 ? status.totalShown + 1 : 1; status.converted = status.converted || status.converted === false ? status.converted : false; setToken(TOKEN_NAME, status); // return dialog; } function main () { const dialog = this; // const onExitIntentCallback = (function () { console.info("onExitIntent"); const dialog = this; showExitIntent.call(dialog); }).bind(dialog); if (shouldShowExitIntent() === true) { console.info("main:", "should show, arm in " + ARM_EXIT_INTENT_AFTER_SECONDS + " seconds"); if (window.innerWidth < 768) { console.log("Idle timer started."), onIdleTimer(onExitIntentCallback, IDLE_TIMER_COUNTDOWN_SECONDS * 1000); } onExitIntent(onExitIntentCallback, ARM_EXIT_INTENT_AFTER_SECONDS); // arm exit intent popup after N seconds } else { dialog.style.setProperty("display", "none", "important"); } } function shouldShowExitIntent () { dismissHandler(); const status = getToken(TOKEN_NAME) || {}; //console.log(status); if ( !status.hasOwnProperty("lastShown") ) { // first time; token is not defined return console.info("shouldShowExitIntent:", "Exit intent popup has not been seen. Exit intent will be shown."), true; // show } else if ( status.hasOwnProperty("converted") && status.converted === true ) { // if the user has already converted (clicked the link) return console.info("shouldShowExitIntent:", "Exit intent popup has already been seen and the user converted from it. Exit intent will be NOT shown."), false; // do not show } else if ( status.hasOwnProperty("lastDismissed") && status.lastDismissed !== getCurrentDate() ) { // returning; token has data, last seen NOT today, and user has NOT signed up yet return console.info("shouldShowExitIntent:", "Exit intent popup has been seen, but not today AND they have not converted from it yet. Exit intent will be shown."), true; // show } else { return console.info("shouldShowExitIntent: Exit intent popup has been seen, the user has not converted from it yet, and the popup was last dismissed TODAY. Exit intent will be NOT shown."), false; // do not show } } window.NA.DonationForm.init({ makeTabbed: false }).then((donationFormReference) => { console.log("Donation Form API: %c READY %c", 'background-color: MediumSeaGreen; color: white; font-weight: bold;', 'background-color: unset; color: unset; font-weight: unset;'); const asyncWaitForElement=(e,t=10)=>{return new Promise((n,r)=>{if(document.querySelector(e)&&document.querySelector(e).isConnected)return n(document.querySelector(e));let o,c=new MutationObserver(t=>{for(let r of t)for(let i of r.addedNodes)if(i instanceof HTMLElement&&i.matches(e)&&i.isConnected)return clearInterval(o),c.disconnect(),n(i)});c.observe(document.documentElement,{childList:!0,subtree:!0}),o=setInterval(e=>{if(document.querySelector(e)&&document.querySelector(e).isConnected)return clearInterval(o),c.disconnect(),n(document.querySelector(e))},50,e),setTimeout(t=>{t.disconnect(),r(Error(`Element with selector "${e}" not found within the time limit.`))},1e3*t,c)})},asyncWaitForElements=(...S)=>{return Promise.all(S.map((s)=>asyncWaitForElement(s)));}; asyncWaitForElement('#'+TOKEN_NAME).then((dialogElement) => main.call(dialogElement)).catch((error) => { throw (error instanceof Error ? error : new Error(error)) }); }); function dismissHandler (dismiss = (target) => target.tagName.toLowerCase() === 'dialog' ? target.close() : target.style.setProperty("display", "none")) { document.querySelectorAll('[data-action="dismiss"]').forEach((element) => { let targets = Array.from(document.querySelectorAll(element.getAttribute('data-target'))) || []; targets.length > 0 && element.addEventListener('click', (e) => Array.from(document.querySelectorAll(e.srcElement.getAttribute('data-target'))).forEach((target) => { dismiss(target); let status = getToken(TOKEN_NAME); status.lastDismissed = getCurrentDate(); status.totalDismissed += 1; setToken(TOKEN_NAME, status); })); }); } }catch(e) {VWO._.vAEH(e);} return vwo_$('head')[0] && vwo_$('head')[0].lastChild;})("head")}}, R_725969_183_1_2_1:{ fn:function(log,nonce=''){return (function(x) { try{ var ctx=vwo_$(x),el; /*vwo_debug log("Revert","content",""); vwo_debug*/; el=vwo_$('[vwo-element-id="1746479885398"]'); el.revertContentOp().remove(); } catch(e) {VWO._.vAEH(e);} try{ var el,ctx=vwo_$(x); /*vwo_debug log("Revert","addElement","body"); vwo_debug*/(el=vwo_$('[vwo-element-id="1746479885399"]')).remove(); } catch(e) {VWO._.vAEH(e);} return vwo_$('head')[0] && vwo_$('head')[0].lastChild;})("head")}}, C_725969_177_1_2_0:{ fn:function(log,nonce=''){return (function(x) {;var _vwo_sel=vwo_$("");vwo_$('head').append(_vwo_sel);return vwo_$('head')[0] && vwo_$('head')[0].lastChild;})("HEAD")}}, R_725969_177_1_2_0:{ fn:function(log,nonce=''){return (function(x) { if(!vwo_$.fn.vwoRevertHtml){ return; }; var ctx=vwo_$(x),el; /*vwo_debug log("Revert","content",""); vwo_debug*/; el=vwo_$('[vwo-element-id="1746468897295"]'); el.revertContentOp().remove();})("HEAD")}}, C_725969_177_1_2_1:{ fn:function(log,nonce=''){return (function(x) {var el,ctx=vwo_$(x); /*vwo_debug log("_clickElement",".btn"); vwo_debug*/(el=vwo_$(".btn")).addClass("_vwo_coal_1697827292277");})(".btn")}}},rules:[{"tags":[{"data":{"campaigns":[{"c":176,"g":3}],"type":"g"},"id":"metric","metricId":0}],"triggers":["13865268"]},{"tags":[{"id":"runCampaign","priority":4,"data":"campaigns.183"}],"triggers":["13865409"]},{"tags":[{"data":{"priority":2,"samplingRate":0},"id":"sampleVisitor"}],"triggers":["9591582"]},{"tags":[{"id":"runCampaign","priority":4,"data":"campaigns.187"}],"triggers":["13612542"]},{"tags":[{"data":{"campaigns":[{"c":176,"g":4}],"type":"g"},"id":"metric","metricId":0}],"triggers":["13865271"]},{"tags":[{"data":{"campaigns":[{"c":183,"g":2}],"type":"m"},"id":"metric","metricId":171541},{"data":{"campaigns":[{"c":177,"g":2}],"type":"m"},"id":"metric","metricId":171541},{"data":{"campaigns":[{"c":176,"g":2}],"type":"m"},"id":"metric","metricId":171541}],"triggers":["1327296"]},{"tags":[{"data":{"priority":1,"samplingRate":-2},"id":"sampleVisitor"}],"triggers":["13391538"]},{"tags":[{"id":"runCampaign","priority":4,"data":"campaigns.177"},{"id":"runCampaign","priority":4,"data":"campaigns.176"}],"triggers":["1895897"]},{"tags":[{"id":"runCampaign","priority":4,"data":"campaigns.3"}],"triggers":["13391544"]},{"tags":[{"data":{"campaigns":[{"c":183,"g":5}],"type":"m"},"id":"metric","metricId":208690},{"data":{"campaigns":[{"c":177,"g":5}],"type":"m"},"id":"metric","metricId":208690},{"data":{"campaigns":[{"c":176,"g":5}],"type":"m"},"id":"metric","metricId":208690}],"triggers":["1520999"]},{"tags":[{"data":{"campaigns":[{"c":177,"g":4}],"type":"g"},"id":"metric","metricId":0}],"triggers":["13865310"]},{"tags":[{"data":{"campaigns":[{"c":177,"g":3}],"type":"g"},"id":"metric","metricId":0}],"triggers":["13865307"]},{"tags":[{"id":"runCampaign","priority":4,"data":"campaigns.2"}],"triggers":["13391556"]},{"tags":[{"data":{"campaigns":[{"c":183,"g":6}],"type":"m"},"id":"metric","metricId":208693},{"data":{"campaigns":[{"c":177,"g":6}],"type":"m"},"id":"metric","metricId":208693},{"data":{"campaigns":[{"c":176,"g":6}],"type":"m"},"id":"metric","metricId":208693}],"triggers":["1520996"]},{"tags":[{"data":{"campaigns":[{"c":183,"g":1}],"type":"m"},"id":"metric","metricId":171544},{"data":{"campaigns":[{"c":177,"g":1}],"type":"m"},"id":"metric","metricId":171544},{"data":{"campaigns":[{"c":176,"g":1}],"type":"m"},"id":"metric","metricId":171544}],"triggers":["1327293"]},{"tags":[{"data":{"campaigns":[{"c":187,"g":11}],"type":"g"},"id":"metric","metricId":0}],"triggers":["13612644"]},{"tags":[{"id":"visibilityService","priority":2}],"triggers":["9"]},{"tags":[{"id":"runTestCampaign"}],"triggers":["2"]},{"tags":[{"id":"urlChange"}],"triggers":["75"]},{"tags":[{"id":"checkEnvironment"}],"triggers":["5"]},{"tags":[{"id":"prePostMutation","priority":3},{"id":"groupCampaigns","priority":2}],"triggers":["8"]}],pages:{"ec":[{"1674516":{"inc":["o",["url","urlReg","(?i).*"]]}},{"2223906":{"inc":["o",["url","urlReg","(?i).*https?\\:\\\/\\\/(w{3}\\.)?donate\\.familylife\\.com.*"],["url","urlReg","(?i).*https?\\:\\\/\\\/(w{3}\\.)?familylife\\.com\\\/weekend\\-to\\-remember.*"],["url","urlReg","(?i).*https?\\:\\\/\\\/(w{3}\\.)?shop\\.familylife\\.com.*"],["url","urlReg","(?i).*https?\\:\\\/\\\/(w{3}\\.)?familylife\\.com\\\/familylife\\-equip.*"]]}}]},pagesEval:{"ec":[1674516,2223906]},stags:{}}})(); ;;var commonWrapper=function(argument){if(!argument){argument={valuesGetter:function(){return{}},valuesSetter:function(){},verifyData:function(){return{}}}}const getVisitorUuid=function(){if(window._vwo_acc_id>=1037725){return window.VWO&&window.VWO.get("visitor.id")}else{return window.VWO._&&window.VWO._.cookies&&window.VWO._.cookies.get("_vwo_uuid")}};var pollInterval=100;var timeout=6e4;return function(){var accountIntegrationSettings={};var _interval=null;function waitForAnalyticsVariables(){try{accountIntegrationSettings=argument.valuesGetter();accountIntegrationSettings.visitorUuid=getVisitorUuid()}catch(error){accountIntegrationSettings=undefined}if(accountIntegrationSettings&&argument.verifyData(accountIntegrationSettings)){argument.valuesSetter(accountIntegrationSettings);return 1}return 0}var currentTime=0;_interval=setInterval((function(){currentTime=currentTime||performance.now();var result=waitForAnalyticsVariables();if(result||performance.now()-currentTime>=timeout){clearInterval(_interval)}}),pollInterval)}}; commonWrapper({valuesGetter:function(){return {"ga4s":0}},valuesSetter:function(data){var singleCall=data["ga4s"]||0;if(singleCall){window.sessionStorage.setItem("vwo-ga4-singlecall",true)}var ga4_device_id="";if(typeof window.VWO._.cookies!=="undefined"){ga4_device_id=window.VWO._.cookies.get("_ga")||""}if(ga4_device_id){window.vwo_ga4_uuid=ga4_device_id.split(".").slice(-2).join(".")}},verifyData:function(data){if(typeof window.VWO._.cookies!=="undefined"){return window.VWO._.cookies.get("_ga")||""}else{return false}}})(); var pushBasedCommonWrapper=function(argument){var firedCamp={};if(!argument){argument={integrationName:"",getExperimentList:function(){},accountSettings:function(){},pushData:function(){}}}return function(){window.VWO=window.VWO||[];const getVisitorUuid=function(){if(window._vwo_acc_id>=1037725){return window.VWO&&window.VWO.get("visitor.id")}else{return window.VWO._&&window.VWO._.cookies&&window.VWO._.cookies.get("_vwo_uuid")}};var sendDebugLogsOld=function(expId,variationId,errorType,user_type,data){try{var errorPayload={f:argument["integrationName"]||"",a:window._vwo_acc_id,url:window.location.href,exp:expId,v:variationId,vwo_uuid:getVisitorUuid(),user_type:user_type};if(errorType=="initIntegrationCallback"){errorPayload["log_type"]="initIntegrationCallback";errorPayload["data"]=JSON.stringify(data||"")}else if(errorType=="timeout"){errorPayload["timeout"]=true}if(window.VWO._.customError){window.VWO._.customError({msg:"integration debug",url:window.location.href,lineno:"",colno:"",source:JSON.stringify(errorPayload)})}}catch(e){window.VWO._.customError&&window.VWO._.customError({msg:"integration debug failed",url:"",lineno:"",colno:"",source:""})}};var sendDebugLogs=function(expId,variationId,errorType,user_type){var eventName="vwo_debugLogs";var eventPayload={};try{eventPayload={intName:argument["integrationName"]||"",varId:variationId,expId:expId,type:errorType,vwo_uuid:getVisitorUuid(),user_type:user_type};if(window.VWO._.event){window.VWO._.event(eventName,eventPayload,{enableLogs:1})}}catch(e){eventPayload={msg:"integration event log failed",url:window.location.href};window.VWO._.event&&window.VWO._.event(eventName,eventPayload)}};const callbackFn=function(data){if(!data)return;var expId=data[1],variationId=data[2],repeated=data[0],singleCall=0,debug=0;var experimentList=argument.getExperimentList();var integrationName=argument["integrationName"]||"vwo";if(typeof argument.accountSettings==="function"){var accountSettings=argument.accountSettings();if(accountSettings){singleCall=accountSettings["singleCall"];debug=accountSettings["debug"]}}if(debug){sendDebugLogs(expId,variationId,"intCallTriggered",repeated)}if(singleCall&&(repeated==="vS"||repeated==="vSS")||firedCamp[expId]){return}window.expList=window.expList||{};var expList=window.expList[integrationName]=window.expList[integrationName]||[];if(expId&&variationId&&["VISUAL_AB","VISUAL","SPLIT_URL"].indexOf(_vwo_exp[expId].type)>-1){if(experimentList.indexOf(+expId)!==-1){firedCamp[expId]=variationId;var visitorUuid=getVisitorUuid();var pollInterval=100;var currentTime=0;var timeout=6e4;var user_type=_vwo_exp[expId].exec?"vwo-retry":"vwo-new";var interval=setInterval((function(){if(expList.indexOf(expId)!==-1){clearInterval(interval);return}currentTime=currentTime||performance.now();var toClearInterval=argument.pushData(expId,variationId,visitorUuid);if(debug&&toClearInterval){sendDebugLogsOld(expId,variationId,"",user_type);sendDebugLogs(expId,variationId,"intDataPushed",user_type)}var isTimeout=performance.now()-currentTime>=timeout;if(isTimeout&&debug){sendDebugLogsOld(expId,variationId,"timeout",user_type);sendDebugLogs(expId,variationId,"intTimeout",user_type)}if(toClearInterval||isTimeout){clearInterval(interval)}if(toClearInterval){window.expList[integrationName].push(expId)}}),pollInterval||100)}}};window.VWO.push(["onVariationApplied",callbackFn]);window.VWO.push(["onVariationShownSent",callbackFn])}}; var validationUtils={exists:function(path){try{const parts=path.split(".");let current=window;for(const part of parts){if(current[part]===undefined){return false}current=current[part]}return true}catch(e){return false}},safeExecute:function(path,args=[]){try{const parts=path.split(".");let current=window;for(const part of parts){if(current[part]===undefined){return false}current=current[part]}if(typeof current!=="function"){return false}return current(...args)}catch(e){return false}},validateAll:function(paths){return paths.every(path=>this.exists(path))}}; var surveyDataCommonWrapper=function(argument){if(!argument){argument={getCampaignList:function(){return[]},surveyStatusChange:function(){},answerSubmitted:function(){}}}return function(){window.VWO=window.VWO||[];function getValuesFromAnswers(answers){var values=[];for(var i=0;i=timeout;if(toClearInterval||isTimeout){clearInterval(interval)}}),pollInterval)}}window.VWO.push(["onSurveyShown",function(data){commonSurveyCallback(data,argument.surveyStatusChange,"surveyShown")}]);window.VWO.push(["onSurveyCompleted",function(data){commonSurveyCallback(data,argument.surveyStatusChange,"surveyCompleted")}]);window.VWO.push(["onSurveyAnswerSubmitted",function(data){commonSurveyCallback(data,argument.answerSubmitted,"surveySubmitted")}])}}; (function(){var VWOOmniTemp={};window.VWOOmni=window.VWOOmni||{};for(var key in VWOOmniTemp)Object.prototype.hasOwnProperty.call(VWOOmniTemp,key)&&(window.VWOOmni[key]=VWOOmniTemp[key]);window._vwoIntegrationsLoaded=1;pushBasedCommonWrapper({integrationName:"GA4",getExperimentList:function(){return [176,177,183]},accountSettings:function(){var accountIntegrationSettings={"dataVariable":"dataLayer","setupVia":"gtm","manualSetup":true};if(accountIntegrationSettings["debugType"]=="ga4"&&accountIntegrationSettings["debug"]){accountIntegrationSettings["debug"]=1}else{accountIntegrationSettings["debug"]=0}return accountIntegrationSettings},pushData:function(expId,variationId){var accountIntegrationSettings={"dataVariable":"dataLayer","setupVia":"gtm","manualSetup":true};var ga4Setup=accountIntegrationSettings["setupVia"]||"gtag";if(ga4Setup==="gtag"){validationUtils.safeExecute("gtag",["event","VWO",{vwo_campaign_name:window._vwo_exp[expId].name+":"+expId,vwo_variation_name:window._vwo_exp[expId].comb_n[variationId]+":"+variationId}]);validationUtils.safeExecute("gtag",["event","experience_impression",{exp_variant_string:"VWO-"+expId+"-"+variationId}]);return true}return false}})();pushBasedCommonWrapper({integrationName:"GA4-GTM",getExperimentList:function(){return [176,177,183]},accountSettings:function(){var accountIntegrationSettings={"dataVariable":"dataLayer","setupVia":"gtm","manualSetup":true};if(accountIntegrationSettings["debugType"]=="gtm"&&accountIntegrationSettings["debug"]){accountIntegrationSettings["debug"]=1}else{accountIntegrationSettings["debug"]=0}return accountIntegrationSettings},pushData:function(expId,variationId){var accountIntegrationSettings={"dataVariable":"dataLayer","setupVia":"gtm","manualSetup":true};var ga4Setup=accountIntegrationSettings["setupVia"]||"gtm";var dataVariable=accountIntegrationSettings["dataVariable"]||"dataLayer";if(ga4Setup==="gtm"&&validationUtils.exists(dataVariable)&&Array.isArray(window[dataVariable])){window[dataVariable].push({event:"vwo-data-push-ga4",vwo_exp_variant_string:"VWO-"+expId+"-"+variationId});return true}return false}})(); ;})();(function(){window.VWO=window.VWO||[];var pollInterval=100;var _vis_data={};var intervalObj={};var analyticsTimerObj={};var experimentListObj={};window.VWO.push(["onVariationApplied",function(data){if(!data){return}var expId=data[1],variationId=data[2];if(expId&&variationId&&["VISUAL_AB","VISUAL","SPLIT_URL"].indexOf(window._vwo_exp[expId].type)>-1){}}])})();; ;var vD=VWO.data||{};VWO.data={content:{"fns":{"list":{"vn":1,"args":{"1":{}}}}},as:"r4.visualwebsiteoptimizer.com",dacdnUrl:"https://dev.visualwebsiteoptimizer.com",accountJSInfo:{"pc":{"t":0,"a":0},"url":{},"ts":1746570975,"rp":60,"pvn":0,"noSS":false}};for(var k in vD){VWO.data[k]=vD[k]};;var gcpfb=function(a,loadFunc,status,err,success){function vwoErr() {_vwo_err({message:"Google_Cdn failing for " + a + ". Trying Fallback..",code:"cloudcdnerr",status:status});} if(a.indexOf("/cdn/")!==-1){loadFunc(a.replace("cdn/",""),err,success); vwoErr(); return true;} else if(a.indexOf("/dcdn/")!==-1&&a.indexOf("evad.js") !== -1){loadFunc(a.replace("dcdn/",""),err,success); vwoErr(); return true;}};window.VWO=window.VWO || [];window.VWO._= window.VWO._ || {};window.VWO._.gcpfb=gcpfb;;var d={cookie:document.cookie,URL:document.URL,referrer:document.referrer};var w={VWO:{_:{}},location:{href:window.location.href,search:window.location.search},_vwoCc:window._vwoCc};;window._vwo_cdn="https://dev.visualwebsiteoptimizer.com/cdn/";window._vwo_apm_debug_cdn="https://dev.visualwebsiteoptimizer.com/cdn/";window.VWO._.useCdn=true;window.vwo_eT="br";window._VWO=window._VWO||{};window._VWO.fSeg={};window._VWO.dcdnUrl="/dcdn/settings.js";window.VWO.sTs=1746546005;window._VWO._vis_nc_lib=window._vwo_cdn+"edrv/nc-54cfde37c5a3743bc2f6e91bb1c31abfbr.js";var loadWorker=function(url){_vwo_code.load(url, { dSC: true, onloadCb: function(xhr,a){window._vwo_wt_l=true;if(xhr.status===200 ||xhr.status===304){var code="var window="+JSON.stringify(w)+",document="+JSON.stringify(d)+";window.document=document;"+xhr.responseText;var blob=new Blob([code||"throw new Error('code not found!');"],{type:"application/javascript"}),url=URL.createObjectURL(blob);window.mainThread={webWorker:new Worker(url)};window.vwoChannelFW=new MessageChannel();window.vwoChannelToW=new MessageChannel();window.mainThread.webWorker.postMessage({vwoChannelToW:vwoChannelToW.port1,vwoChannelFW:vwoChannelFW.port2},[vwoChannelToW.port1, vwoChannelFW.port2]);if(!window._vwo_mt_f)return window._vwo_wt_f=true;_vwo_code.addScript({text:window._vwo_mt_f});delete window._vwo_mt_f}else{if(gcpfb(a,loadWorker,xhr.status)){return;}_vwo_code.finish("&e=loading_failure:"+a)}}, onerrorCb: function(a){if(gcpfb(a,loadWorker)){return;}window._vwo_wt_l=true;_vwo_code.finish("&e=loading_failure:"+a);}})};loadWorker("https://dev.visualwebsiteoptimizer.com/cdn/edrv/worker-aa7ffe63a93031e335ac2a7c312d4377br.js");;var _vis_opt_file;var _vis_opt_lib;if(window.VWO._.allSettings.dataStore.previewExtraSettings!=undefined&&window.VWO._.allSettings.dataStore.previewExtraSettings.isSurveyPreviewMode){var surveyHash=window.VWO._.allSettings.dataStore.plugins.LIBINFO.SURVEY_DEBUG_EVENTS.HASH;var param1="evad.js?va=";var param2="&d=debugger_new";var param3="&sp=1&a=725969&sh="+surveyHash;_vis_opt_file=vwoCode.use_existing_jquery&&typeof vwoCode.use_existing_jquery()!=="undefined"?vwoCode.use_existing_jquery()?param1+"vanj"+param2:param1+"va_gq"+param2:param1+"edrv/va_gq-df0ecc1bb02a7e73cd2e2a895835ec5cbr.js"+param2;_vis_opt_file=_vis_opt_file+param3;_vis_opt_lib="https://dev.visualwebsiteoptimizer.com/dcdn/"+_vis_opt_file}else if(window.VWO._.allSettings.dataStore.mode!=undefined&&window.VWO._.allSettings.dataStore.mode=="PREVIEW"){ var path1 = 'edrv/pd_'; var path2 = window.VWO._.allSettings.dataStore.plugins.LIBINFO.EVAD.HASH + ".js"; ;_vis_opt_file=vwoCode.use_existing_jquery&&typeof vwoCode.use_existing_jquery()!=="undefined"?vwoCode.use_existing_jquery()?path1+"vanj"+path2:path1+"va_gq"+path2:path1+"edrv/va_gq-df0ecc1bb02a7e73cd2e2a895835ec5cbr.js"+path2;_vis_opt_lib="https://dev.visualwebsiteoptimizer.com/cdn/"+_vis_opt_file}else{_vis_opt_file=vwoCode.use_existing_jquery&&typeof vwoCode.use_existing_jquery()!=="undefined"?vwoCode.use_existing_jquery()?"edrv/vanj-e70040e7d2d675ff50eeed490c144b1dbr.js":"edrv/va_gq-df0ecc1bb02a7e73cd2e2a895835ec5cbr.js":"edrv/va_gq-df0ecc1bb02a7e73cd2e2a895835ec5cbr.js"}window._vwo_library_timer=setTimeout((function(){vwoCode.removeLoaderAndOverlay&&vwoCode.removeLoaderAndOverlay();vwoCode.finish()}),vwoCode.library_tolerance&&typeof vwoCode.library_tolerance()!=="undefined"?vwoCode.library_tolerance():2500),_vis_opt_lib=typeof _vis_opt_lib=="undefined"?window._vwo_cdn+_vis_opt_file:_vis_opt_lib;var loadLib=function(url){_vwo_code.load(url, { dSC: true, onloadCb:function(xhr,a){window._vwo_mt_l=true;if(xhr.status===200 || xhr.status===304){if(!window._vwo_wt_f)return window._vwo_mt_f=xhr.responseText;_vwo_code.addScript({text:xhr.responseText});delete window._vwo_wt_f;}else{if(gcpfb(a,loadLib,xhr.status)){return;}_vwo_code.finish("&e=loading_failure:"+a);}}, onerrorCb: function(a){if(gcpfb(a,loadLib)){return;}window._vwo_mt_l=true;_vwo_code.finish("&e=loading_failure:"+a);}})};loadLib(_vis_opt_lib);VWO.load_co=function(u,opts){return window._vwo_code.load(u,opts);};;;}}catch(e){_vwo_code.finish();_vwo_code.removeLoaderAndOverlay&&_vwo_code.removeLoaderAndOverlay();_vwo_err(e);window.VWO.caE=1}})();

The Mindful Marriage: Create Your Best Relationship Through Understanding and Managing Yourself

MSRP: $27.00

$21.99

Struggling to break free from the same old conflicts in your relationships? Backed by neuroscience and biblical wisdom, The Mindful Marriage offers a proven, grace-filled approach to healing patterns of pain and strengthening your connection. Whether you’re dating, engaged, married, or in a blended family, it’s ideal for both small-group discussions and couples study.

110 in stock (can be backordered)

View All Available Bulk Discounts

Available Discounts

Quantity Discount
1 - 9 0%
10 - 24 10%
25 - 49 15%
50 + 20%
The offer will automatically be applied in cart.
+
-
Free standard shipping on all orders $75 and up.
Overview

Strengthen Your Connection at Any Stage

Backed by neuroscience and biblical principles, The Mindful Marriage delivers a therapeutic model for couples who want to stop the painful patterns in troubled relationships—or strengthen already healthy ones. Whether you’re dating, engaged, married, or in a blended family, this resource offers practical tools for building deeper connections.

Transformative Restoration Therapy: When Ron and Nan Deal faced the devastating loss of their child, their marriage was nearly shattered by destructive cycles. Seeking help, they turned to pioneering therapists Dr. Terry Hargrave and Sharon Hargrave, LMFT, whose groundbreaking Restoration Therapy has helped millions through the practice of emotional mindfulness. This proven method equips couples to understand themselves in moments of emotional distress and self-regulate for healthier relationships.

Rooted in Science and Biblical Principles: Ron and Nan Deal offer a thoughtful balance of scientifically backed techniques and Scripturally sound teaching, providing practical and spiritual tools to cultivate more loving and faithful marriages.

Great for Small Groups:  No extra guide is needed—just read, discuss, and dive into the included exercises together. Share insights, encourage one another, and put the principles into practice. Free bonus materials, including an optional workbook for personal reflection, extra exercises, and articles, are available at RonDeal.org/TMM.

Buy a copy for each couple in your group and start building stronger relationships today!

About the Author

Nan and Ron Deal are coauthors (with Terry & Sharon Hargrave) of the book The Mindful Marriage: Create Your Best Relationship Through Understanding and Managing Yourself. Nan retired from teaching after 25 years in public and private schools. Ron is the author of more than a dozen books and resources, a licensed marriage and family therapist, and a podcaster. Together, Ron and Nan work with various ministries and present marriage enrichment seminars around the country. They have three sons, a daughter-in-law, a grandson, and live in Little Rock, Arkansas.

Ron L. Deal is Director of FamilyLife Blended™ and President of SmartStepfamilies.com. He is a family ministry consultant and conducts marriage and family seminars around the country; he specializes in marriage and stepfamily enrichment. Ron is author of the bestselling book and DVD curriculum The Smart Stepfamily and author/coauthor of The Smart Stepmom, The Smart Stepdad, and The Remarriage Checkup.

He has extensive experience training pastors, ministry leaders, and counselors and has appeared on dozens of national radio and TV broadcasts in the US, Canada, and the UK. Ron is considered a leading voice in stepfamily education and ministry and in the prevention of redivorce. He and his wife, Nan, have been married since 1986 and have three boys. For more information visit RonDeal.org.

Product Details
SKU: BKH21896
ISBN: 978-1546007388
Publisher: Worthy Publisher
Language: English
Page Count: 240
Publication Date: 01/07/2025
Size: 9 × 6 × 0.88 in
Author: Nan Deal, Ron Deal
Format: Hardcover
Shipping & Returns

We offer free shipping for orders with subtotals of $75 or greater. You’ll find shipping options and delivery dates at checkout. Please allow 7-10 business days for standard shipping, expedited shipping is available for an additional fee. Orders received after 12 p.m. CST on Fridays will not be processed until the following Monday. You can view our return policy here.

Reviews

There are no reviews yet.

Be the first to review “The Mindful Marriage: Create Your Best Relationship Through Understanding and Managing Yourself”

Got questions?

Our friendly specialists are here to help. Give us a call now or chat with us.