PageSourceSearch

https://secure.franciscanworks.org/login

html franciscanworks.org collected 2026-09-28 08:21:47 UTC 93,700 bytes, 1,920 lines download raw bytes

1<!DOCTYPE html>
2<html lang="en">
3<head>
4        <meta charset="UTF-8">
5        <meta http-equiv="X-UA-Compatible" content="IE=edge">
6        <meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
7
8        <!-- Add to home screen for Safari on iOS -->
9        <meta name="apple-mobile-web-app-capable" content="yes">
10        <meta name="apple-mobile-web-app-status-bar-style" content="black">
11
12        <html lang="en">
13<meta name="twitter:card" content="summary_large_image">
14<meta property="og:type" content="article"/>
15
16
17
18
19
20
21        <title>Login</title>
22
23        <link rel="shortcut icon" href="/neon/resource/franciscanworks/images/favicon.gif"/>
24        <!-- Common styles and scripts -->
25        <link href="https://fonts.googleapis.com/css?family=Lato:400,700|Open+Sans:400,700,900|Source+Sans+Pro:400,700,900|Montserrat:400,700,900|Roboto:100,100i,300,300i,400,400i,500,500i,700,700i,900,900i&amp;subset=cyrillic,cyrillic-ext,greek,greek-ext,latin-ext,vietnamese" rel="stylesheet">
26        
27        <link href="/nx/portal-static/component/bootstrap/css/bootstrap.min.css" rel="stylesheet">
28        <link href="/nx/portal-static/component/fontawesome-pro/css/all.min.css" rel="stylesheet">
29        <link href="/nx/portal-static/component/fontawesome-pro/css/sharp-solid.min.css" rel="stylesheet">
30        <link href="/nx/portal-static/component/cropperjs/cropper.min.css" rel="stylesheet">
31        
32        <link href="/nx/portal-static/css/portal/portal.all.css" rel="stylesheet">
33        
34
35        <!-- Custom Web Page only. Before customCss.css so org td/tr rules still win. -->
36        
37
38        
39
40        
41        
42
43
44
45
46
47<style>
48
49    .neon-body {
50        position: relative;
51    }
52    .neonfront {
53        position: relative;
54        z-index: 2;
55    }
56    .neon-background {
57        background: var(--page-bg-color, #F9FAFC);
58        background-image: var(--page-bg-image, url(""));
59        background-position: center; /* Center the image */
60        background-repeat: no-repeat; /* Do not repeat the image */
61        background-size: cover; /* Resize the background image to cover the entire container */
62        height: 100%;
63        width: 100%;
64        transform: var(--page-bg-mirror, );
65        background-position: var(--page-bg-align, 50%);
66        position: fixed;
67        z-index: 1;
68    }
69    .neonfront_element .form-check-input:checked {
70        background-color: var(--out-line-calibration-color, #F68E1E)!important;
71    }
72    .neonfront_content_wrap{
73        background: ;
74        backdrop-filter: blur(3px);
75    }
76    body {
77        font-family: var(--page-font);
78    }
79    .neonfront_content_title, .neonfront_content_desc {
80        font-family: var(--page-font, Arial,sans-serif) !important;
81        color: var(--page-font-color, #222222)!important;
82        word-break: break-word;
83    }
84    .neonfront_content_wrap.overlay-dark .neonfront_content_title,
85    .neonfront_content_wrap.overlay-dark .neonfront_content_desc {
86        color: #ffffff !important;
87    }
88    .neonfront_content_wrap.overlay-light .neonfront_content_title,
89    .neonfront_content_wrap.overlay-light .neonfront_content_desc {
90        color: #222222 !important;
91    }
92    .btn-outline-primary,.btn-outline-primary:hover {
93        color: var(--out-line-calibration-color, #F68E1E)!important;
94        background-color: transparent !important;
95        border-color: #D9DFE3;
96        box-shadow: none !important;
97    }
98    .btn-outline-secondary:hover,.btn-outline-secondary:active,.btn-outline-secondary:focus{
99        background-color: #fff!important;
100        color: var(--out-line-calibration-color, #F68E1E)!important;
101        border-color: #D9DFE3;
102        box-shadow: none !important;
103    }
104
105    .neonfront-btn-link {
106        color: var(--out-line-calibration-color, #F68E1E)!important;
107    }
108    .neonfront_stepper.on,.neonfront_stepper_num {
109        background: var(--theme-color, #F68E1E)!important;
110    }
111
112    .neonfront_payment_tab .active {
113        border-bottom: 4px solid var(--out-line-calibration-color, #F68E1E) !important;
114        color: var(--out-line-calibration-color, #F68E1E) !important;
115    }
116
117    .neonfront-btn-primary, .btn-primary {
118        background-color: var(--theme-color, #F68E1E) !important;
119        color: var(--primary-btn-text-color, #ffffff) !important;
120        border-color: var(--theme-color, #F68E1E) !important;
121        box-shadow: none !important;
122    }
123    .secondary-exit-btn {
124        color: var(--theme-color, #F68E1E) !important;
125        background-color: #FFF !important;
126        border: 1px solid var(--theme-color, #F68E1E) !important;
127        box-shadow: none !important;
128    }
129
130    .neonfront-btn-paypal {
131        background-color: #f7c457 !important;
132    }
133
134    .btn-primary, .form-check-input:checked {
135        border-color: var(--theme-color, #F68E1E) !important;
136    }
137
138    .neonfront-btn-outline-primary {
139        color: var(--out-line-calibration-color, #F68E1E)!important;
140    }
141
142    .btn-check:checked + .neonfront-btn-outline-primary, .donationFrequencyChecked, .btn-check:active + .neonfront-btn-outline-primary, .neonfront-btn-outline-primary.active, .neonfront-btn-outline-primary.dropdown-toggle.show {
143        background-color: var(--theme-color, #F68E1E) !important;
144        border-color: var(--theme-color, #F68E1E) !important;
145        color: var(--primary-btn-text-color, #ffffff) !important;
146        z-index: 0 !important;
147    }
148
149    .donation-frequency-default-select-icon-bg {
150        background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' %3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");
151    }
152
153    .neonfront-check-input-level:checked + .neonfront-btn-level, .neonfront-check-input-level:active + .neonfront-btn-level, .neonfront-btn-level:active, .neonfront-btn-level.active, .neonfront-btn-level.dropdown-toggle.show {
154        border-color: var(--theme-color, #F68E1E) !important;
155    }
156
157    .container-fluid.neonfront_header {
158        color: var(--header-text-color, #222222) !important;
159        background-color: var(--header-bg-color, #FFFFFF)!important;
160    }
161    .container-fluid.neonfront_header .navbar-toggler {
162        color: var(--header-text-color, #222222) !important;
163        z-index: 100;
164    }
165    .container-fluid.neonfront_footer {
166        color: var(--footer-text-color, #222222)!important;
167        background-color: var(--footer-bg-color, #FFFFFF)!important;
168        padding-left: 0px;
169        padding-right: 0px;
170    }
171    .neonfront_footer_lv_info {
172        color: var(--footer-text-color, #222222)!important;
173        font-family: var(--page-font, Lato);
174        font-style: normal;
175        font-weight: 400;
176        font-size: 14px;
177        padding-top: 21px;
178        padding-bottom: 3px;
179        word-break: break-word;
180        min-height: 30px;
181    }
182    .neonfront_footer_lv_info a{
183        color: var(--footer-text-color, #222222);
184        text-decoration: underline!important;
185    }
186    .neonfront_header .neonfront_link {
187        color: var(--header-text-color, #222222)!important;
188    }
189    .neonfront-profile-dropdown .fa-caret-down {
190        color: #3C4144!important;
191    }
192    .neonfront-profile-dropdown .name {
193        background-color: #3C4144!important;
194        color: #ffffff!important;
195    }
196    .neonfront_header .neonfront_link.login_button {
197        border-color: var(--header-text-color, #222222)!important;
198        width: 78px;
199        height: 32px;
200        padding: 0px 0px 2px 0px !important;
201        margin-left: 15px;
202    }
203    .neonfront_footer_lv_info .neonfront_link {
204        color: var(--footer-text-color, #222222)!important;
205    }
206
207    .neonfront-switch input:checked{
208        background-color: var(--theme-color, #F68E1E) !important;
209    }
210
211    .popal-opportunities-tile .popal-opportunities-tile-col .pot-neon-round-img {
212        background-color: var(--theme-color, #F68E1E) !important;
213        color: var(--primary-btn-text-color, #ffffff) !important;
214    }
215    .portal-volunteer-info-main .opportunity-app-col1 .module-icon-round {
216        background-color: var(--theme-color, #F68E1E) !important;
217        color: var(--primary-btn-text-color, #ffffff) !important;
218    }
219</style>
220<!-- Monthly Giving Suggestion (MGS) Styles -->
221
222
223<script type="text/javascript">
224    function setCalibrationColor(color) {
225        if (color) {
226            color = color.replace(/['"]/g, '')
227            let darkText = '#222222'
228            let lightText = '#ffffff'
229            color = this.contrastRatioColor(color, darkText) > this.contrastRatioColor(color, lightText) ? darkText : color
230            document.documentElement.style.setProperty("--out-line-calibration-color", color);
231        }
232    }
233    function contrastRatioColor(color1, color2) {
234        let color1luminanceColor = this.luminanceColor(color1)
235        let color2luminanceColor = this.luminanceColor(color2)
236        return color1luminanceColor > color2luminanceColor
237            ? ((color1luminanceColor + 0.05) / (color2luminanceColor + 0.05))
238            : ((color2luminanceColor + 0.05) / (color1luminanceColor + 0.05))
239    }
240    function luminanceColor(color) {
241        let rgbColor = /rgba?/.test(color) ? this.rgbaColorToRgbMap(color) : this.hexColorToRgb(color)
242        let rgb = [rgbColor.r, rgbColor.g, rgbColor.b].map(function (x) { x /= 255
243            return x <= 0.03928
244                ? x / 12.92
245                : Math.pow((x + 0.055) / 1.055, 2.4)
246        })
247        return rgb[0] * 0.2126 + rgb[1] * 0.7152 + rgb[2] * 0.0722
248    };
249    function rgbaColorToRgbMap(rgba) {
250        let result = rgba.match(/rgba?\((\d{1,3}), ?(\d{1,3}), ?(\d{1,3})\)?(?:, ?(\d(?:\.\d?))\))?/);
251        return result ? {r: parseInt(result[1]), g: parseInt(result[2]), b: parseInt(result[3])} : null
252    }
253    function hexColorToRgb(hex) {
254        let result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
255        return result ? {
256            r: parseInt(result[1], 16),
257            g: parseInt(result[2], 16),
258            b: parseInt(result[3], 16)
259        } : null
260    }
261</script>
vendor: 1 bytes, line 261
261
262<script>
263    /* ================================================================
264     * Portal Theme JS  (included via th:replace from theme/theme.html)
265     * Provides:
266     *   SOCIAL_ICON_CONFIG / getSocialIconURL(name, style)
267     *   applyThemeCSSVars(theme)
268     *   postMessage listener for live-preview iframe
269     * ================================================================ */
270
271    // ── Social Icons ──────────────────────────────────────────────────
272    const SOCIAL_ICON_BASE = '/nx/portal-static/img/social-icon';
273
274    const SOCIAL_ICON_CONFIG = {
275        Website: {color: 'website-color.svg', white: 'website-white.svg', black: 'website-black.svg'},
276        Email: {color: 'email-color.svg', white: 'email-white.svg', black: 'email-black.svg'},
277        Facebook: {color: 'facebook-color.png', white: 'facebook-white.png', black: 'facebook-color.png'}, // no black → brand rule
278        Flickr: {color: 'flickr-color.png', white: 'flickr-white.png', black: 'flickr-black.png'},
279        Instagram: {color: 'instagram-color.png', white: 'instagram-white.png', black: 'instagram-black.png'},
280        LinkedIn: {color: 'linkedin-color.png', white: 'linkedin-white.png', black: 'linkedin-black.png'},
281        Pinterest: {color: 'pinterest-color.png', white: 'pinterest-white.png', black: 'pinterest-black.png'},
282        Snapchat: {color: 'snapchat-color.png', white: 'snapchat-white.png', black: 'snapchat-black.png'},
283        SoundCloud: {color: 'soundcloud-white.png', white: 'soundcloud-white.png', black: 'soundcloud-black.png'}, // no color → reuses white
284        Spotify: {color: 'spotify-color.png', white: 'spotify-white.png', black: 'spotify-black.png'},
285        Vimeo: {color: 'vimeo-color.png', white: 'vimeo-white.png', black: 'vimeo-black.png'},
286        X: {color: 'x-white.png', white: 'x-white.png', black: 'x-black.png'}, // no color → reuses white
287        YouTube: {color: 'youtube-color.png', white: 'youtube-white.png', black: 'youtube-black.png'}
288    };
289
290    function getSocialIconURL(name, style) {
291        // A backend bug (BrandDataManager/PersistSystemSettingAction) previously saved 'Twitter'
292        // instead of 'X' for this icon; normalize so those existing records still render.
293        const key = name === 'Twitter' ? 'X' : name;
294        const cfg = SOCIAL_ICON_CONFIG[key];
295        if (!cfg) return ''
296        const styleMap = {C: 'color', L: 'white', D: 'black'};
297        const file = cfg[styleMap[style] || 'color'] || '';
298        return file ? SOCIAL_ICON_BASE + '/' + file : ''
299    }
300
301    // ── Hosted Brand Fonts ─────────────────────────────────────────────
302
303    const SYSTEM_FONT_NAMES = ['Arial', 'Comic Sans MS', 'Courier New', 'Georgia', 'Tahoma', 'Lucida Sans Unicode', 'Times New Roman', 'Trebuchet MS', 'Verdana'];
304
305    function getBrandFontName(fontFamilyValue) {
306        if (!fontFamilyValue || typeof fontFamilyValue !== 'string') return ''
307        return fontFamilyValue.split(',')[0].trim().replace(/^['"]|['"]$/g, '')
308    }
309
310    function loadHostedBrandFontFromFamily(fontFamilyValue) {
311        const fontName = getBrandFontName(fontFamilyValue);
312        if (!fontName || SYSTEM_FONT_NAMES.indexOf(fontName) > -1) return
313
314        const linkId = 'brand-font-' + fontName.toLowerCase().replace(/[^a-z0-9]+/g, '-');
315        if (document.getElementById(linkId)) return
316
317        const link = document.createElement('link');
318        link.id = linkId;
319        link.rel = 'stylesheet';
320        link.href = 'https://fonts.googleapis.com/css2?family=' + encodeURIComponent(fontName).replace(/%20/g, '+') + '&display=swap';
321        document.head.appendChild(link);
322    }
323
324    // ── CSS Variables ─────────────────────────────────────────────────
325
326    /**
327     * Parse a color string to {r,g,b,a}.
328     * Supports: #rgb / #rrggbb / #rrggbbaa / rgb(...) / rgba(...)
329     * Returns null if the input is not a recognized format.
330     */
331    function hexToRgba(color) {
332        if (!color || typeof color !== 'string') return null
333        var c = color.trim()
334        // rgb(r, g, b) or rgba(r, g, b, a)
335        var rgbMatch = c.match(/^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*([\d.]+))?\s*\)$/)
336        if (rgbMatch) {
337            return {
338                r: parseInt(rgbMatch[1]),
339                g: parseInt(rgbMatch[2]),
340                b: parseInt(rgbMatch[3]),
341                a: rgbMatch[4] !== undefined ? parseFloat(rgbMatch[4]) : 1
342            }
343        }
344        // #hex
345        if (c[0] !== '#') return null
346        var h = c.substring(1)
347        if (h.length === 3) { h = h[0]+h[0]+h[1]+h[1]+h[2]+h[2] }
348        if (h.length !== 6 && h.length !== 8) return null
349        return {
350            r: parseInt(h.substring(0, 2), 16),
351            g: parseInt(h.substring(2, 4), 16),
352            b: parseInt(h.substring(4, 6), 16),
353            a: h.length === 8 ? (parseInt(h.substring(6, 8), 16) / 25
3535) : 1
354        }
355    }
356
357    /**
358     * Compute accessible font color (#222222 or #ffffff) based on WCAG contrast ratio.
359     * Reuses the existing hexToRgba() helper defined above.
360     */
361    function computeInverseColor(bgColor) {
362        if (!bgColor) return '#222222'
363        function luminance(color) {
364            const rgba = hexToRgba(color);
365            if (!rgba) return 0
366            return [rgba.r, rgba.g, rgba.b].map(function(x) {
367                x /= 255
368                return x <= 0.03928 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4)
369            }).reduce(function(sum, v, i) { return sum + v * [0.2126, 0.7152, 0.0722][i] }, 0)
370        }
371        function contrastRatio(l1, l2) { return l1 > l2 ? (l1 + 0.05) / (l2 + 0.05) : (l2 + 0.05) / (l1 + 0.05) }
372
373        const lBg = luminance(bgColor);
374        return contrastRatio(lBg, luminance('#222222')) > contrastRatio(lBg, luminance('#ffffff')) ? '#222222' : '#ffffff'
375    }
376
377    /**
378     * Write theme config as :root CSS variables so the page can update
379     * in real-time (live preview via postMessage, or on initial load).
380     * Only sets a var when the corresponding value is non-empty.
381     */
382    function applyThemeCSSVars(theme) {
383        if (!theme) return
384        const styles = theme.styles || {};
385        const root = document.documentElement;
386
387        loadHostedBrandFontFromFamily(styles.pageFont)
388
389        // Helper: set only when value is truthy; clear the var when value is empty
390        function set(name, value) {
391            if (value !== null && value !== undefined && value !== '') {
392                root.style.setProperty(name, value)
393            } else if (value === '' || value === null) {
394                root.style.removeProperty(name)
395            }
396        }
397
398        function setColorOrTransparent(name, value) {
399            root.style.setProperty(name, (value !== null && value !== undefined && value !== '') ? value : 'transparent')
400        }
401
402        function setColorOrBlack(name, value) {
403            root.style.setProperty(name, (value !== null && value !== undefined && value !== '') ? value : '#000000')
404        }
405
406        function setOrKeepPrevious(name, value) {
407            if (value !== null && value !== undefined && value !== '') {
408                root.style.setProperty(name, value)
409            }
410        }
411
412        // ── Core theme vars (used by theme.html CSS) ──
413        setOrKeepPrevious('--theme-color',      styles.themeColor)
414        setColorOrTransparent('--page-bg-color', styles.pageBgColor)
415        set('--page-font',              styles.pageFont)
416        set('--page-font-color',        computeInverseColor(styles.pageBgColor))
417        setOrKeepPrevious('--primary-btn-text-color', styles.themeColor ? computeInverseColor(styles.themeColor) : null)
418        setColorOrTransparent('--header-bg-color', styles.headerBgColor)
419        setColorOrBlack('--header-text-color', styles.headerTextColor)
420        setColorOrTransparent('--footer-bg-color', styles.footerBgColor)
421        setColorOrBlack('--footer-text-color', styles.footerTextColor)
422
423        // bg-image: wrap in url() when non-empty, clear when empty
424        if (styles.pageBgImage && styles.pageBgImage !== '') {
425            root.style.setProperty('--page-bg-image', 'url("' + styles.pageBgImage + '")')
426        } else {
427            root.style.setProperty('--page-bg-image', '')
428        }
429
430        // bg-position: default 50% when absent
431        root.style.setProperty('--page-bg-align',
432            (styles.pageBgAlign && styles.pageBgAlign !== '') ? styles.pageBgAlign : '50%')
433
434        // bg-mirror: '1' → rotateY(180deg), else empty
435        root.style.setProperty('--page-bg-mirror',
436            styles.pageBgMirror === '1' ? 'rotateY(180deg)' : '')
437
438        // ── Campaign landing page vars (portal-landing-page.css, portal-constituent.css) ──
439        set('--landing-page-font-family',    styles.pageFont)
440        set('--landing-page-font-color',     computeInverseColor(styles.pageBgColor))
441        setOrKeepPrevious('--landing-page-theme-color',    styles.themeColor)
442        setOrKeepPrevious('--primary-theme-color',         styles.themeColor)
443        setOrKeepPrevious('--landing-page-btn-text-color', styles.themeColor ? computeInverseColor(styles.themeColor) : null)
444        // defaultBackgroundColor is resolved from themeColor (same value), used when no hero image
445        setOrKeepPrevious('--landing-page-hero-bg-color',  styles.themeColor)
446
447        // ── themeColor → RGBA decomposition ──
448        // Updates --campaign-progress-r/g/b/a (campaign-progress.css)
449        // and --goal-meter-widget-r/g/b/a and computes opacity variants for landing page
450        if (styles.themeColor) {
451            const rgba = hexToRgba(styles.themeColor);
452            if (rgba) {
453                root.style.setProperty('--campaign-progress-r', rgba.r)
454                root.style.setProperty('--campaign-progress-g', rgba.g)
455                root.style.setProperty('--campaign-progress-b', rgba.b)
456                root.style.setProperty('--campaign-progress-a', rgba.a)
457                root.style.setProperty('--goal-meter-widget-r', rgba.r)
458                root.style.setProperty('--goal-meter-widget-g', rgba.g)
459                root.style.setProperty('--goal-meter-widget-b', rgba.b)
460                root.style.setProperty('--goal-meter-widget-a', rgba.a)
461                root.style.setProperty('--landing-page-theme-color-opacity',
462                    'rgba(' + rgba.r + ',' + rgba.g + ',' + rgba.b + ',0.15)')
463                root.style.setProperty('--landing-page-theme-color-opacity-over',
464                    'rgba(' + rgba.r + ',' + rgba.g + ',' + rgba.b + ',0.7)')
465            }
466        }
467
468        // keep calibration in sync
469        if (styles.themeColor && typeof setCalibrationColor === 'function') {
470            setCalibrationColor(styles.themeColor)
471        }
472
473        let extraConfig = theme.extraConfig ? theme.extraConfig : {}
474        if (extraConfig && extraConfig.correctDashboard) {
475            if (extraConfig.useWhiteHeader) {
476                root.style.setProperty('--dashboard-header-color', '#fff')
477                root.style.setProperty('--dashboard-header-text-color', '#3C4144')
478                root.style.setProperty('--dashboard-header-hover-link-color', '#E2F2FE')
479            } else {
480                setColorOrTransparent('--dashboard-header-color', styles.headerBgColor)
481                setColorOrBlack('--dashboard-header-text-color', styles.headerTextColor)
482                setColorOrTransparent('--dashboard-header-hover-link-color', styles.headerBgColor)
483            }
484        } else {
485            // Clear dashboard-specific vars when not on dashboard to avoid unintended overrides
486            root.style.removeProperty('--dashboard-header-color')
487            root.style.removeProperty('--dashboard-header-text-color')
488            root.style.removeProperty('--dashboard-header-hover-link-color')
489        }
490
491        // ── Fullscreen preview: hide/show dashboard-specific elements ─
492        let fullscreenStyle = document.getElementById('_neonFullscreenStyle')
493        if (!fullscreenStyle) {
494            fullscreenStyle = document.createElement('style')
495            fullscreenStyle.id = '_neonFullscreenStyle'
496            document.head.appendChild(fullscreenStyle)
497        }
498        if (extraConfig && extraConfig.fullscreen === false) {
499            fullscreenStyle.textContent = '.neonfront-portal-left-nav { visibility: hidden !important; } .neonfront-portal-nav-bar { display: none !important; } .dashboard_welcome .sidebar-title { display: none !important; } .dashboard_welcome .welcome-text { margin-left: 16px; }'
500        } else {
501            fullscreenStyle.textContent = ''
502        }
503    }
504
505    // ── Footer column text and naviLinks processing ───────────────────
506    // Replace tokens in footer col texts and naviLink URLs via a single backend call.
507    // If no relevant data, calls onDone immediately without an API request.
508    function processFooterColTexts(theme, onDone) {
509        const hasFooter = theme.footerCol1Text || theme.footerCol2Text || theme.footerCol3Text;
510        const hasNaviToken = theme.naviLinks && theme.naviLinks.some(function (l) {
511            return l.url && l.url.indexOf('Organization URL') !== -1;
512        });
513        if (!hasFooter && !hasNaviToken) {
514            onDone(theme);
515            return;
516        }
517        axios.post('/nx/portal/theme/processColTexts', {
518            footerCol1Text: theme.footerCol1Text,
519            footerCol2Text: theme.footerCol2Text,
520            footerCol3Text: theme.footerCol3Text,
521            naviLinks:      theme.naviLinks,
522        }).then(function (res) {
523            var processed = theme;
524            if (res.data && res.data.success && res.data.data) {
525                processed = Object.assign({}, theme, res.data.data);
526            }
527            onDone(processed);
528        }).catch(function (e) {
529            onDone(theme);
530        });
531    }
532
533    // ── Footer request token: UUID-based guard against stale async callbacks ──
534    // Each DOMContentLoaded or postMessage generates a new UUID token.
535    // Any async callback whose token no longer matches the current one is discarded.
536    window._neonFooterToken = null;
537    window._neonFooterDebounceTimer = null;
538    window._neonPrevTheme = null;      // cache of last applied patchData (excludes footer cols)
539    window._neonPrevTokenSrc = null;   // cache of raw source for token-processed fields (naviLinks + footer cols)
540
541    // Deep-equal comparison: JSON for objects/arrays, strict for primitives.
542    function _themeValEqual(a, b) {
543        if (a === b) return true;
544        if (a == null || b == null) return a === b;
545        if (typeof a === 'object') return JSON.stringify(a) === JSON.stringify(b);
546        return false;
547    }
548
549    // Recursively patch only changed properties on a Vue reactive target.
550    // Plain objects are patched property-by-property; arrays/primitives are assigned directly.
551    function _patchReactive(target, source, prev) {
552        Object.keys(source).forEach(function(k) {
553            const nv = source[k], pv = prev ? prev[k] : undefined;
554            if (prev && _themeValEqual(pv, nv)) return;
555            const tIsObj = target[k] != null && typeof target[k] === 'object' && !Array.isArray(target[k]);
556            const nIsObj = nv != null && typeof nv === 'object' && !Array.isArray(nv);
557            if (tIsObj && nIsObj) _patchReactive(target[k], nv, pv || {});
558            else target[k] = nv;
559        });
560    }
561
562    // ── Initial page load: wait for DOM, then process footer ──────────
563    document.addEventListener('DOMContentLoaded', function () {
564
565        // ── CLP embed: set before Vue mounts so clpEmbedEnv is reactive ───
566        if (new URLSearchParams(window.location.search).get('clpEmbedEnv') === '1') {
567            sessionStorage.setItem('clpEmbedEnv', '1');
568        }
569        if (window.top !== window.self && sessionStorage.getItem('clpEmbedEnv') === '1') {
570            const _clpStyle = document.createElement('style');
571            _clpStyle.id = '_clpEmbedStyle';
572            _clpStyle.textContent = '#header-content, #footer-content { display: none !important; }';
573            document.head.appendChild(_clpStyle);
574        }
575
576        var theme = window._neonThemeData;
577        if (!theme) return;
578        loadHostedBrandFontFromFamily(theme.styles && theme.styles.pageFont);
579        var myToken = crypto.randomUUID();
580        window._neonFooterToken = myToken;
581        // Hide footer cols while processing to avoid showing raw tokens
582        if (window.__neonApp) {
583            window.__neonApp.theme = Object.assign({}, window.__neonApp.theme, {
584                footerCol1Text: '',
585                footerCol2Text: '',
586                footerCol3Text: '',
587            });
588        }
589        processFooterColTexts(theme, function (processed) {
590            if (window._neonFooterToken !== myToken) return;
591            if (window.__neonApp) {
592                window.__neonApp.theme = Object.assign({}, window.__neonApp.theme, {
593                    footerCol1Text: processed.footerCol1Text,
594                    footerCol2Text: processed.footerCol2Text,
595                    footerCol3Text: processed.footerCol3Text,
596                    naviLinks: processed.naviLinks,
597                });
598            }
599        });
600    });
601
602    // ── CLP embed: listen for parent postMessage (handles already-logged-in scenario) ──
603    window.addEventListener('message', function (event) {
604        if (!event.data || event.data.sharingType !== 'clpEmbed') return;
605        if (window.top === window.self) {
606            return;
607        }
608        sessionStorage.setItem('clpEmbedEnv', '1');
609        if (event.data.embedIframeId) sessionStorage.setItem('clpEmbedIframeId', event.data.embedIframeId);
610        if (event.data.domainUrl) sessionStorage.setItem('clpEmbedDomainUrl', event.data.domainUrl);
611        if (!document.getElementById('_clpEmbedStyle')) {
612            const _clpStyle = document.createElement('style');
613            _clpStyle.id = '_clpEmbedStyle';
614            _clpStyle.textContent = '#header-content, #footer-content { display: none !important; }';
615            document.head.appendChild(_clpStyle);
616        }
617    });
618
619    // ── postMessage listener (live preview) ───────────────────────────
620    window.addEventListener('message', function (event) {
621        if (!event.data || event.data.type !== 'NEON_THEME_PREVIEW') return
622        let newTheme = event.data.template;
623        if (!newTheme) return
624
625        console.log('NEON_THEME_PREVIEW received', newTheme);
626
627        const myToken = crypto.randomUUID();
628        window._neonFooterToken = myToken;
629
630        const ALIGN_MAP = {LEFT: 'L', CENTER: 'C', RIGHT: 'R', L: 'L', C: 'C', R: 'R'};
631        const COLOR_MAP = {COLOR: 'C', WHITE: 'L', BLACK: 'D', C: 'C', W: 'L', B: 'D', L: 'L', D: 'D'};
632
633        function enumVal(v, map, fb) { if (!v) return fb; if (typeof v === 'object') v = v.value || fb; return map[v] || fb }
634
635        newTheme = Object.assign({}, newTheme, {
636            extraConfig:          event.data.extraConfig || {},
637            socialLinkIconAlign:  enumVal(newTheme.socialLinkIconAlign,  ALIGN_MAP, 'L'),
638            socialLinkIconStyle: enumVal(newTheme.socialLinkIconStyle, COLOR_MAP, 'L'),
639        })
640
641        // ── Step 1: immediately patch only changed non-footer properties ──
642        // Footer cols are excluded: hidden while API processes to avoid showing raw tokens.
643        // naviLinks included: raw URL changes are shown immediately (same as before).
644        const patchData = Object.assign({}, newTheme);
645        delete patchData.footerCol1Text;
646        delete patchData.footerCol2Text;
647        delete patchData.footerCol3Text;
648
649        const prev = window._neonPrevTheme;
650        const changedKeys = Object.keys(patchData).filter(function (k) {
651            return !prev || !_themeValEqual(prev[k], patchData[k]);
652        });
653        if (changedKeys.length > 0) {
654            const target = window.__neonApp ? window.__neonApp.theme : window._neonThemeData;
655            if (target) _patchReactive(target, patchData, prev);
656            if (changedKeys.indexOf('styles') !== -1 || changedKeys.indexOf('extraConfig') !== -1) {
657                applyThemeCSSVars(patchData);
658            }
659            window._neonPrevTheme = JSON.parse(JSON.stringify(patchData));
660        }
661
662        // ── Step 2: debounce token API only when naviLinks or footer cols source changed ──
663        const tokenSrc = {
664            col1: newTheme.footerCol1Text,
665            col2: newTheme.footerCol2Text,
666            col3: newTheme.footerCol3Text,
667            nav: JSON.stringify(newTheme.naviLinks),
668        };
669        const ps = window._neonPrevTokenSrc;
670        if (ps && ps.col1 === tokenSrc.col1 && ps.col2 === tokenSrc.col2 && ps.col3 === tokenSrc.col3 && ps.nav === tokenSrc.nav) return;
671
672        clearTimeout(window._neonFooterDebounceTimer)
673        window._neonFooterDebounceTimer = setTimeout(function () {
674            const capturedToken = myToken;
675            processFooterColTexts(newTheme, function (processed) {
676                if (window._neonFooterToken !== capturedToken) return;
677                if (window.__neonApp) {
678                    window.__neonApp.theme['naviLinks']      = processed.naviLinks;
679                    window.__neonApp.theme['footerCol1Text'] = processed.footerCol1Text;
680                    window.__neonApp.theme['footerCol2Text'] = processed.footerCol2Text;
681                    window.__neonApp.theme['footerCol3Text'] = processed.footerCol3Text;
682                }
683                window._neonPrevTokenSrc = tokenSrc;
684                window.parent.postMessage({ type: 'NEON_THEME_READY' }, '*')
685            })
686        }, 300)
687    })
688</script>
vendor: 1 bytes, line 688
688
689<script>
690    window._neonThemeData = {
691        id:                   8,
692        logo:                 "https:\/\/franciscanworks.app.neoncrm.com\/np\/clients\/franciscanworks\/logo.gif",
693        logoAlt:              null,
694        logoWidth:            100,
695        logoUrl:              null,
696        footerColumns:        2,
697        footerCol1Text:       null,
698        footerCol2Text:       null,
699        footerCol3Text:       null,
700        displaySocialLink:    true,
701        socialLinkIconAlign:  "R",
702        socialLinkIconStyle: "D",
703        styles:               {"headerBgColor":"#FFFFFF","headerTextColor":"#222222","pageFont":"Arial,sans-serif","pageBgColor":"#F9FAFC","pageBgMirror":"","pageBgImage":null,"themeJSSnippets":"","pageBgImageAlt":"","themeColor":"#F68E1E","footerBgColor":"#FFFFFF","pageBgAlign":"","pageFontColor":"#002D41","footerTextColor":"#222222","primaryBtnTextColor":"#ffffff","actBtnTextColor":"#222222"},
704        socialLinks:          [{"createBy":"Organization Brand Reminder Schedule","createTime":"2026-05-10T07:10:25.000-05:00","lastModifyBy":"Organization Brand Reminder Schedule","lastModifyTime":"2026-05-10T07:10:25.000-05:00","id":351,"templateId":8,"icon":"Facebook","url":"https:\/\/www.facebook.com\/FranciscanWorks"},{"createBy":"Organization Brand Reminder Schedule","createTime":"2026-05-10T07:10:25.000-05:00","lastModifyBy":"Organization Brand Reminder Schedule","lastModifyTime":"2026-05-10T07:10:25.000-05:00","id":352,"templateId":8,"icon":"Twitter","url":"https:\/\/www.twitter.com\/FranciscanWorks"}],
705        naviLinks:            [{"createBy":"Organization Brand Reminder Schedule","createTime":"2026-05-10T07:10:25.000-05:00","lastModifyBy":"Organization Brand Reminder Schedule","lastModifyTime":"2026-05-10T07:10:25.000-05:00","id":277,"templateId":8,"url":"https:\/\/www.franciscanworks.org","name":"HOME","openInNewTab":1}],
706        _homePageURL:         "https:\/\/www.franciscanworks.org",
707        _previewMode:         false,
708    }
709    applyThemeCSSVars(window._neonThemeData)
710</script>
710
711
712
713    </head>
714<body>
715<div class="neon-body" id="app" v-cloak="">
716  <div class="neon-background"></div>
717  <div class="neonfront">
718    
719<!DOCTYPE html>
720<html>
721<body>
722    <nav class="navbar navbar-expand-lg pt-0" style="padding-bottom: 0!important;" id="header-content">
723        <div style="position: relative; width: 100%">
724        <div class="container-fluid neonfront_header">
725            <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
726                <i class="fas fa-bars"></i>
727            </button>
728            <div class="navbar-brand">
729                <div class="neonfront_logo">
730                    <!-- Case 1: logoUrl set -> link to logoUrl, open in new tab -->
731                    <a v-if="theme.logoUrl"
732                       :href="theme.logoUrl.startsWith('http://') || theme.logoUrl.startsWith('https://') || theme.logoUrl.startsWith('//') ? theme.logoUrl : 'https://' + theme.logoUrl"
733                       :style="theme._previewMode ? {'pointer-events': 'none'} : {}"
734                       target="_blank">
735                        <img v-if="theme.logo && theme.logoWidth !== 0" :src="theme.logo" :width="theme.logoWidth || undefined" :alt="theme.logoAlt" onerror="this.style.display='none';"/>
736                        <span v-else-if="!theme.logo">{{theme.logoAlt}}</span>
737                    </a>
738                    <!-- Case 2: no logoUrl but homePageURL -> original behavior -->
739                    <a v-else-if="theme._homePageURL"
740                       :href="theme._homePageURL.startsWith('http://') || theme._homePageURL.startsWith('https://') || theme._homePageURL.startsWith('//') ? theme._homePageURL : 'https://' + theme._homePageURL"
741                       :style="theme._previewMode ? {'pointer-events': 'none'} : {}"
742                       target="_blank">
743                        <img v-if="theme.logo && theme.logoWidth !== 0" :src="theme.logo" :width="theme.logoWidth || undefined" :alt="theme.logoAlt" onerror="this.style.display='none';"/>
744                        <span v-else-if="!theme.logo">{{theme.logoAlt}}</span>
745                    </a>
746                    <!-- Case 3: no link -->
747                    <template v-else>
748                        <img v-if="theme.logo && theme.logoWidth !== 0" :src="theme.logo" :width="theme.logoWidth || undefined" :alt="theme.logoAlt" onerror="this.style.display='none';"/>
749                        <span v-else-if="!theme.logo">{{theme.logoAlt}}</span>
750                    </template>
751                </div>
752            </div>
753            <div class="collapse navbar-collapse" id="navbarSupportedContent">
754                <ul class="neonfront_links navbar-nav mb-2 mb-lg-0">
755                    <template v-for="link in theme.naviLinks" :key="link.id">
756                        <li v-if="link.url && link.name" class="nav-item">
757                            <a class="neonfront_link" :href="!link.url ? '#' : link.url.startsWith('http://') || link.url.startsWith('https://') ? (link.url === 'http://' || link.url === 'https://' ? '#' : link.url) : 'https://' + link.url"
758                               :style="theme._previewMode ? {'pointer-events': 'none'} : {}"
759                               :target="link.openInNewTab==1 ? '_blank' : '_self'">{{link.name}}</a>
760                        </li>
761                    </template>
762                    
763                    
764                </ul>
765            </div>
766        </div>
767        </div>
768    </nav>
769</body>
770</html>
771
772    
773    <div class="neonfront_cover container-fluid">
774      
775      <div class="row flex-grow-1" id="content">
776        <div class="col">
777          <div class="d-flex neonfront-portal-content">
778            <!-- Left Menu -->
779            
780            
781            <!-- Right Content -->
782            <div class="flex-grow-1" id="right-content">
783              <div class="neonfront_constituent_body container-lg">
784                  <!DOCTYPE html>
785<html lang="en">
786<body>
787<div>
788    <div>
789        
790        <div class="neonfront_container neonfront-login">
791            <div class="col-12" id="neonj-page-1">
792                <div class="row g-3 neonfront-form">
793                    
794                    
795                    <div class="col-md-12 neonfront_element">
796                        <div class="login-portal-section-title">Welcome Back!</div>
797                        <label class="section-label fs-6 login-title">Log in to your account</label>
798                    </div>
799                    
800                    <div class="col-md-12 neonfront_element mt-0">
801                        <div id="globalError" class="row mt-3 neonfront-portal neonfront_form_failed_container" v-show="globalError">
802                            <div class="col-2">
803                                <span class="fa fa-exclamation-circle mt-1" style="font-size: 2.25rem;"></span>
804                            </div>
805                            <div class="col-10">
806                                <h5 class="fs-6 mb-1" style="line-height: 42px">{{globalError}}</h5>
807                            </div>
808                        </div>
809                    </div>
810                    <div class="neonfront_element col-md-12">
811                        <label for="loginName" class="form-label">Username or Email</label>
812                        <input id="loginName" v-model="loginName" name="currentPwd" type="text" @input="loginNameError = ''" class="form-control neonfront_input" :class="{'is-invalid': loginNameError}" data-testid="field-login-name">
813                        <div class="invalid-feedback">
814                            <div><i class="fa fa-exclamation-triangle"></i>
815                                Username or Email is required.
816                            </div>
817                        </div>
818                    </div>
819                    <div class="neonfront_element col-md-12 login-password-field">
820                        <label for="password" class="form-label">Password</label>
821                        <input id="password" v-model="password" name="password" type="password" @input="passwordError = ''" class="form-control neonfront_input" :class="{'is-invalid': passwordError}" data-testid="field-password">
822                        <i class="fas fa-eye-slash presentation" v-if="!showPassword" @click="showPsd(true)"></i>
823                        <i class="fas fa-eye presentation" v-if="showPassword" @click="showPsd(false)"></i>
824                        <div class="invalid-feedback">
825                            <div><i class="fa fa-exclamation-triangle"></i>Password is required.</div>
826                        </div>
827                    </div>
828                    <div class="neonfront_element col-md-12 login-additional">
829                        <div class="form-check login-remember-me">
830                            <input class="form-check-input neonfront_checkbox" type="checkbox" value="yes" id="persistCookie" v-model="persistCookie">
831                            <label class="form-check-label neonfront_checkbox_label" for="persistCookie">
832                                Remember me for 2 weeks
833                            </label>
834                        </div>
835                        <div class="trouble-login">
836                            <a href="/requestPassword" data-testid="link-forgot-password">Having trouble logging in?</a>
837                            
838                        </div>
839                    </div>
840                    <div class="col-md-12 neonfront_element">
841                        <div class="row g-2 justify-content-center">
842                            <div class="col">
843                                <button type="button" class="btn neonfront-btn-primary btn-lg" @click="login" v-show="!loading" data-testid="bt
843n-login">Login</button>
844                                <button class="btn neonfront-btn-primary btn-lg" type="button" disabled v-show="loading">
845                                    Processing&nbsp; <span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
846                                </button>
847                            </div>
848                        </div>
849                    </div>
850                    <div class="col-md-12 neonfront_element login-separate">
851                        <div class="separate-line"></div>
852                        <div class="separate-text">or</div>
853                        <div class="separate-line"></div>
854                    </div>
855                    
856                    <div class="col-md-12 neonfront_element">
857                        <div class="create-account">
858                            Don't have an account?
859                            <a v-if="activeTab === 0 || autoMergeOn" href="/forms/createaccount">Create an account</a>
860                            <a v-else href="javascript:void(0)" @click="createFundraiser">Create an account</a>
861                        </div>
862                    </div>
863                    
864                </div>
865            </div>
866        </div>
867    </div>
868</div>
869<div class="modal fade neonfront-modal landing-page-login-modal" aria-hidden="true" id="fundraiserLoginModal" ref="createP2pAcctModal" data-bs-backdrop="static" tabindex="-1">
870    <div class="modal-dialog" style="max-width: 580px;">
871        <div class="modal-content px-sm-4 px-1">
872            <div class="modal-header">
873                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
874            </div>
875
876            <div class="modal-body" style="padding-top:0;padding-bottom:0">
877                <div class="row mt-1">
878                    <div class="col login-body-title p2p-login-body-title">
879                        Create an Account
880                    </div>
881                </div>
882                <div class="row mt-1">
883                    <div class="login-body-col">
884                        <div class="body-line" style="display: flex">
885                            <span>
886                                <label for="firstName" class="input-label">First Name *</label>
887                                <input type="text" v-model="acctData.firstName" id="firstName" name="firstName" class="form-control half-line" :class="{'invalid': acctRules.fname.invalid}" @blur="checkAcctFieldVal($event, acctRules.fname)" />
888                                <div v-if="acctRules.fname.invalid" class="field-err-msg"><i class="fa fa-exclamation-triangle"></i>{{acctRules.fname.errMsg}}</div>
889                            </span>
890                            <span style="margin-left: 30px;">
891                                <label for="lastName" class="input-label">Last Name *</label>
892                                <input type="text" v-model="acctData.lastName" class="form-control half-line" id="lastName" name="lastName" :class="{'invalid': acctRules.lname.invalid}" @blur="checkAcctFieldVal($event, acctRules.lname)" />
893                                <div v-if="acctRules.lname.invalid" class="field-err-msg"><i class="fa fa-exclamation-triangle"></i>{{acctRules.lname.errMsg}}</div>
894                            </span>
895                        </div>
896                        <div class="body-line">
897                            <label for="emailAddress" class="input-label" >Email Address *</label>
898                            <input type="text" v-model="acctData.email" class="form-control" id="emailAddress" name="emailAddress" :class="{'invalid': acctRules.email.invalid}" @blur="checkAcctFieldVal($event, acctRules.email)" />
899                            <div v-if="acctRules.email.invalid" class="field-err-msg"><i class="fa fa-exclamation-triangle"></i>{{acctRules.email.errMsg}}</div>
900                        </div>
901                        <div class="body-line">
902                            <label for="createPassword" class="input-label">Create Password *</label>
903                            <input type="password" v-model="acctData.password" class="form-control" id="createPassword" name="createPassword" :class="{'invalid': acctRules.psd1.invalid}" @blur="checkAcctFieldVal($event, acctRules.psd1)"/>
904                            <span>
905                                <i class="fa fa-info-circle"></i>
906                                <span class="info-message-text">Enter at least 8 characters, including at least one number. Spaces not allowed.</span>
907                            </span>
908                            <i class="fas fa-eye-slash presentation1" v-if="!showCreatePassword" @click="showCreatePsd(true)"></i>
909                            <i class="fas fa-eye presentation1" v-if="showCreatePassword" @click="showCreatePsd(false)"></i>
910                            <div v-if="acctRules.psd1.invalid" class="field-err-msg" style="margin-top: -1.25rem"><i class="fa fa-exclamation-triangle"></i>{{acctRules.psd1.errMsg}}</div>
911                        </div>
912                        <div class="body-line" style="margin-top: -20px;margin-bottom: 0px !important;">
913                            <label for="confirmPassword" class="input-label">Confirm Password *</label>
914                            <input type="password" class="form-control" id="confirmPassword" name="confirmPassword" :class="{'invalid': acctRules.psd2.invalid}" @blur="checkAcctFieldVal($event, acctRules.psd2)"/>
915                            <i class="fas fa-eye-slash presentation2" v-if="!showConfirmPassword" @click="showConfirmPsd(true)"></i>
916                            <i class="fas fa-eye presentation2" v-if="showConfirmPassword" @click="showConfirmPsd(false)"></i>
917                            <div v-if="acctRules.psd2.invalid" class="field-err-msg" style="margin-top:-17px;"><i class="fa fa-exclamation-triangle"></i>{{acctRules.psd2.errMsg}}</div>
918                        </div>
919
920                        <div v-if="gdprSettings.profileEnabled || gdprSettings.statementEnabled" class="body-line">
921                            <label class="input-label">Data Privacy & Consent</label>
922                            <div class="gdpr-setting-section">
923                                <div class="gdpr-setting-items">
924                                    <div v-if="gdprSettings.email" class="gdpr-setting-item neonfront_element">
925                                        <div class="checkbox-column">
926                                            <input v-model="gdprValues.email" type="checkbox" id="gdpr.email" name="gdpr.email" class="form-check-input neonfront_checkbox"/>
927                                        </div>
928                                        <label for="gdpr.email" class="setting-item-label">{{gdprSettings.email}}</label>
929                                    </div>
930                                    <div v-if="gdprSettings.phone" class="gdpr-setting-item neonfront_element">
931                                        <div class="checkbox-column">
932                                            <input v-model="gdprValues.phone" type="checkbox" id="gdpr.phone" name="gdpr.phone" class="form-check-input neonfront_checkbox"/>
933                                        </div>
934                                        <label for="gdpr.email" class="setting-item-label">{{gdprSettings.phone}}</label>
935                                    </div>
936                                    <div v-if="gdprSettings.mail" class="gdpr-setting-item neonfront_element">
937                                        <div class="checkbox-column">
938                                            <input v-model="gdprValues.mail" type="checkbox" id="gdpr.mail" name="gdpr.mail" class="form-check-input neonfront_checkbox"/>
939                                        </div>
940                                        <label for="gdpr.mail" class="setting-item-label">{{gdprSettings.mail}}</label>
941                                    </div>
942                                    <div v-if="gdprSettings.sms" class="gdpr-setting-item neonfront_element">
943                                        <div class="checkbox-column">
944                                            <input v-model="gdprValues.sms" type="checkbox" id="gdpr.sms" name="gdpr.sms" class="form-check-input neonfront_checkbox"/>
945                                        </div>
946                                        <label for="gdpr.sms" class="setting-item-label">{{gdprSettings.sms}}</label>
947                                    </div>
948                                    <div v-show="gdprValues.sms" class="smsPhoneNum-section" style="padding-left:25px">
949                                        <label for="smsPhoneNum" class="input-label">What's the best number for us to text you? *</label>
950                                        <input v-model="gdprValues.smsPhoneNum" type="text" class="form-control" id="smsPhoneNum" name="smsPhoneNum" :class="{'invalid': gdprValues.smsPhoneInvalid}" @blur="checkGdprSmsPhoneNum($event)"/>
951                                        <div v-if="gdprValues.smsPhoneInvalid" class="field-err-msg"><i class="fa fa-exclamation-triangle"></i>{{gdprValues.smsPhoneInvalidMsg}}</div>
952                                    </div>
953                                    <div v-if="gdprSettings.share" class="gdpr-setting-item neonfront_element">
954                                        <div class="checkbox-column">
955                                            <input v-model="gdprValues.share" type="checkbox" id="gdpr.share" name="gdpr.share" class="form-check-input neonfront_checkbox"/>
956                                        </div>
957                                        <label for="gdpr.share" class="setting-item-label">{{gdprSettings.share}}</label>
958                                    </div>
959                                </div>
960
961                                <div v-if="gdprSettings.statement" class="gdpr-statement">
962                                    {{gdprSettings.statement}}
963                                </div>
964                                <div v-if="gdprSettings.policyUrl" class="gdpr-policy">
965                                    <a :href="gdprSettings.policyUrl" target="_blank" style="text-decoration: none">{{gdprSettings.policyLabel}}</a>
966                                </div>
967                            </div>
968                        </div>
969                    </div>
970                </div>
971            </div>
972            <div class="login-footer p2p-login-footer">
973                <button v-if="!doCreateAcct" type="button"
974                        class="btn neonfront-btn-primary btn-lg p2p-btn"
975                        :disabled="checkCreateAcctField"
976                        @click="doP2pCreateAccount">Submit</button>
977                <button v-if="doCreateAcct" class="btn neonfront-btn-primary btn-lg p2p-btn" type="button" disabled>
978                    Processing&nbsp; <span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
979                </button>
980                <div class="col-md-12 neonfront_element login-separate p2p_login_separate">
981                    <div class="separate-line"></div>
982                    <div class="separate-text">or</div>
983                    <div class="separate-line"></div>
984                </div>
985                <div class="go_login_sty">Have an account? <a href="javascript:void(0)" data-bs-dismiss="modal">Log in</a></div>
986            </div>
987        </div>
988    </div>
989</div>
990
991</body>
992</html>
993
994              </div>
995            </div>
996          </div>
997        </div>
998      </div>
999    </div>
1000    <!DOCTYPE html>
1001<html>
1002<body>
1003    <div class="container-fluid neonfront_footer" id="footer-content">
1004        <div class="row neonfront_footer_lv_info">
1005            <div v-if="theme.footerColumns==1" class="col-md-12" style="padding-left:30px;padding-right:30px;">
1006                <div v-html="theme.footerCol1Text"></div>
1007            </div>
1008            <div v-if="theme.footerColumns==2" class="col-md-6" style="padding-left:30px;">
1009                <div class="float-md-start float-none" v-html="theme.footerCol1Text"></div>
1010            </div>
1011            <div v-if="theme.footerColumns==2" class="col-md-6" style="padding-right:30px;">
1012                <div class="float-md-end float-none" v-html="theme.footerCol2Text"></div>
1013            </div>
1014            <div v-if="theme.footerColumns==3" class="col-md-4" style="padding-left:30px;">
1015                <div class="float-md-start float-none" v-html="theme.footerCol1Text"></div>
1016            </div>
1017            <div v-if="theme.footerColumns==3" class="col-md-4 text-md-center footer_center_column">
1018                <div class="footer_center_column_content" style="margin:0 auto;" v-html="theme.footerCol2Text"></div>
1019            </div>
1020            <div v-if="theme.footerColumns==3" class="col-md-4" style="padding-right:30px;">
1021                <div class="float-md-end float-none" v-html="theme.footerCol3Text"></div>
1022            </div>
1023        </div>
1024        <div v-if="theme.displaySocialLink"
1025             :class="['neonfront_footer_lv_media',
1026                      theme.socialLinkIconAlign==='C' ? 'text-center' : (theme.socialLinkIconAlign==='R' ? 'text-end' : 'text-start'),
1027                      theme.socialLinkIconStyle==='L' ? 'text-white' : 'text-dark']">
1028            <template v-for="item in (theme.socialLinks||[])" :key="item.icon">
1029                <a v-if="item.url"
1030                   :href="item.icon==='Email' ? 'mailto:'+item.url : (item.url.startsWith('http://') || item.url.startsWith('https://') || item.url.startsWith('//') ? item.url : 'https://'+item.url)"
1031                   :style="theme._previewMode ? {'pointer-events': 'none'} : {}"
1032                   target="_blank"
1033                   class="text-reset social-link-icon">
1034                    <img width="24" height="24"
1035                         :src="getSocialMediaIcon(item.icon, theme.socialLinkIconStyle)">
1036                </a>
1037            </template>
1038        </div>
1039        
1040        <div class="footer-root">
1041            <div class="footer-root-left"><a class="footer-root-link" href="https://neonone.com/privacypolicy/" target="_blank">Privacy Statement</a>   |  <a class="footer-root-link" href="https://neonone.com/productterms/" target="_blank">Terms of Service</a></div>
1042            <div class="footer-root-right">
1043                <a href="https://neonone.com/" target="_blank">
1044                <div class="footer-root-right-header"><img width="60" height="20" src="/nx/portal-static/img/logo-variations.svg"/></div>
1045                <div class="footer-root-right-footer"><img width="60" height="20" src="/nx/portal-static/img/merchant-copy.svg"/></div>
1046                </a>
1047            </div>
1048        </div>
1049    </div>
1050</body>
1051</html>
1052  </div>
1053  
1054  
1055    <!DOCTYPE html>
1056<html lang="en">
1057<body>
1058    <!DOCTYPE html>
1059<html lang="en">
1060<body>
1061<!-- Modal -->
1062<div class="modal fade neonfront-modal neonfront-confirm-modal" id="confirmSubMembershipEndModal" data-bs-backdrop="static" tabindex="-1">
1063    <div class="modal-dialog" style="max-width: 580px;">
1064        <div class="modal-content px-sm-4 px-1" style="z-index: 20000;">
1065            <div class="modal-header">
1066                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" @click="cancelSubMembershipEndTurnOff"></button>
1067            </div>
1068            <div class="modal-body">
1069                <div class="row mt-1">
1070                    <div class="col">
1071                        <div>Are you sure you want to end the selected sub-membership? This action will set the End Date to one day in the past.</div>
1072                    </div>
1073                </div>
1074            </div>
1075            <div class="modal-footer">
1076                <button type="button" class="btn btn-link neonfront-btn-l
1076ink" @click="cancelSubMembershipEndTurnOff" style="width: auto">Cancel</button>
1077                <button type="button" class="btn neonfront-btn-primary btn-lg px-4" @click="confirmSubMembershipEndTurnOff" style="width: auto">Confirm</button>
1078            </div>
1079        </div>
1080    </div>
1081</div>
1082</body>
1083</html>
1084    <!DOCTYPE html>
1085<html lang="en">
1086<body>
1087<!-- Modal -->
1088<div class="modal fade neonfront-modal neonfront-confirm-modal" id="addressListDeleteModal" data-bs-backdrop="static" tabindex="-1">
1089  <div class="modal-dialog" style="max-width: 580px;">
1090    <div class="modal-content px-sm-4 px-1" style="z-index: 20000;">
1091      <div class="modal-header">
1092        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" @click="cancelAddressListDeleteModal"></button>
1093      </div>
1094      <div class="modal-body">
1095        <div class="row mt-1">
1096          <div class="col">
1097            <p>Are you sure you want to delete this address?</p>
1098            <p class="desc">Your address will be deleted.</p>
1099          </div>
1100        </div>
1101      </div>
1102      <div class="modal-footer">
1103        <button type="button" class="btn btn-link neonfront-btn-link" @click="cancelAddressListDeleteModal" style="width: auto">Cancel</button>
1104        <button type="button" class="btn neonfront-btn-primary btn-lg px-4" @click="confirmAddressListDeleteModal" style="width: auto">Confirm</button>
1105      </div>
1106    </div>
1107  </div>
1108</div>
1109</body>
1110</html>
1111    <!DOCTYPE html>
1112<html lang="en">
1113<body>
1114<!-- Modal -->
1115<div class="modal fade neonfront-modal neonfront-confirm-modal" id="addressListCommonDeleteModal" data-bs-backdrop="static" tabindex="-1">
1116  <div class="modal-dialog" style="max-width: 580px;">
1117    <div class="modal-content px-sm-4 px-1" style="z-index: 20000;">
1118      <div class="modal-header">
1119        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" @click="cancelAddressListDeleteModal"></button>
1120      </div>
1121      <div class="modal-body">
1122        <div class="row mt-1">
1123          <div class="col">
1124            <p>Address cannot be deleted because of the following linked data:</p>
1125            <p class="desc" v-for="item in deleteInfo">
1126              {{decisionDeleteInfo(item)}}
1127            </p>
1128          </div>
1129        </div>
1130      </div>
1131      <div class="modal-footer">
1132        <button type="button" class="btn neonfront-btn-primary btn-lg px-4" @click="cancelAddressListCommonDeleteModal" style="width: auto;margin-left: auto">Close</button>
1133      </div>
1134    </div>
1135  </div>
1136</div>
1137</body>
1138</html>
1139</body>
1140</html>
1141  
1142  <div class="modal fade neonfront-modal" id="goP2pConfirmModal" data-bs-backdrop="static" tabindex="-1">
1143    <div class="modal-dialog">
1144        <div class="modal-content px-sm-4 px-1 go-p2p-confirm-modal">
1145            <div class="header">
1146                Leaving this Page
1147            </div>
1148            <div class="content">
1149                You are about to be redirected to another portal to manage your Peer-to-Peer Fundraising pages. You can return to this portal at any time.
1150                <br/><br/>Do you want to continue?
1151            </div>
1152            <div class="footer">
1153                <button type="button" class="btn btn-link neonfront-btn-link" @click="cancelP2pConfirmModal()"
1154                        style="width: auto; font-family: var(--page-font, Lato)">Cancel
1155                </button>
1156                <button type="button" class="btn neonfront-btn-primary btn-lg px-4" @click="confirmP2pConfirmModal"
1157                        style="width: auto; font-family: var(--page-font, Lato)">Continue
1158                </button>
1159            </div>
1160            <i class="fa-solid fa-xmark-large close_icon" @click="cancelP2pConfirmModal"></i>
1161        </div>
1162    </div>
1163</div>
1164
1165</div>
1166
1167<script src="/nx/portal-static/component/vue/vue-3.1.2.min.js"></script>
vendor: 1 bytes, line 1167
1167
1168<script src="/nx/portal-static/component/axios/axios.min.js"></script>
vendor: 1 bytes, line 1168
1168
1169<script src="/nx/portal-static/component/qs/qs.min.js"></script>
vendor: 1 bytes, line 1169
1169
1170<script src="/nx/portal-static/component/bootstrap/js/bootstrap.bundle.min.js"></script>
vendor: 1 bytes, line 1170
1170
1171<script src="/nx/portal-static/component/laydate/laydate.js"></script>
vendor: 1 bytes, line 1171
1171
1172<script src="/nx/portal-static/component/cropperjs/cropper.min.js"></script>
vendor: 1 bytes, line 1172
1172
1173<script src="/nx/portal-static/component/moment-js/moment-2.30.1.min.js"></script>
vendor: 1 bytes, line 1173
1173
1174<script src="/nx/portal-static/component/moment-js/moment-timezone.min.js"></script>
1174
1175
1176<script src="/nx/portal-static/js/portal.all.js"></script>
1176
1177<!--
1178<th:block th:if="${churnZeroEnabled && !#strings.isEmpty(churnZeroAccountExternalId)}">
1179<script src="https://neonone.us1app.churnzero.net/churnzero.js"></script>
vendor: 1 bytes, line 1179
1179
1180<script type="application/javascript">
1181    var ChurnZero = ChurnZero || []
1182
1183    ChurnZero.push(['setAppKey', '1!MFdwDlKbVUgVpSRtljcqdO9tOYWtqDKqGG7trJX5JYwtE02']) // AppKey from ChurnZero
1184    ChurnZero.push(['setContact', '', ''])
1185</script>
1185
1186</th:block>
1187-->
1188
1189<script type="application/javascript">
1190    // Common JS Code
1191</script>
1191
1192
1193
1194
1195
1196<script>
1197    // CLP embed: notify parent on DOMContentLoaded for re-postMessage
1198    (function () {
1199        const urlParams = new URLSearchParams(window.location.search);
1200        const clpEmbedEnv = sessionStorage.getItem('clpEmbedEnv') === '1'
1201            || urlParams.get('clpEmbedEnv') === '1';
1202        if (!clpEmbedEnv) return;
1203        const embedIframeId = urlParams.get('embedIframeId') || sessionStorage.getItem('clpEmbedIframeId');
1204        const embedIframeDomainUrl = urlParams.get('embedIframeDomainUrl') || sessionStorage.getItem('clpEmbedDomainUrl');
1205        if (!embedIframeId || !embedIframeDomainUrl) return;
1206        window.addEventListener('DOMContentLoaded', function () {
1207            parent.postMessage({type: 'DOMContentLoaded', embedIframeId: embedIframeId}, embedIframeDomainUrl);
1208        });
1209    }());
1210</script>
1210
1211
1212<script src="/nx/portal-static/js/clp-embed-content.js"></script>
vendor: 66 bytes, lines 1212-1215
1212
1213
1214
1215<script async src="https://www.googletagmanager.com/gtag/js?id=
1215G-E5PVK7VDYC
vendor: 12 bytes, line 1215
1215"></script>
1216<script>
1217    
vendor: 139 bytes, lines 1217-1220
1217window.dataLayer = window.dataLayer || [];
1218    function gtag(){dataLayer.push(arguments);}
1219    gtag('js', new Date());
1220    gtag('config', '
1220G-E5PVK7VDYC
vendor: 4 bytes, line 1220
1220');
1221</script>
vendor: 72 bytes, lines 1221-1224
1221
1222
1223
1224  <script src="https://www.google.com/recaptcha/enterprise.js?render=
12246Lciqi8rAAAAAG87Wg7usbkvyHVTXylFPgeSyHkM
vendor: 24 bytes, line 1224
1224" async defer></script>
1225<script type="text/javascript">
1226    const appData = {
1227        showCreatePassword: false,
1228        showConfirmPassword: false,
1229        doLoginLoading: false,
1230        doCreateAcct: false,
1231        acctRules: {
1232            fname: {type: 'name', invalid: null, errMsg: 'First Name is required.'},
1233            lname: {type: 'name', invalid: null, errMsg: 'Last Name is required.'},
1234            email: {type: 'email', invalid: null, errMsg: 'Email Address is required.'},
1235            psd1: {type: 'psd1', invalid: null, errMsg: 'Create Password is required.'},
1236            psd2: {type: 'psd2', invalid: null, errMsg: 'Confirm Password is required.'}
1237        },
1238        acctData: {
1239            firstName: '',
1240            lastName: '',
1241            email: '',
1242            password: ''
1243        },
1244        gdprSettings: {},
1245        autoMergeOn: false,
1246        gdprValues: {
1247            email: false,
1248            phone: false,
1249            mail: false,
1250            sms: false,
1251            share: false,
1252            smsPhoneNum: '',
1253            smsPhoneInvalid: null,
1254            smsPhoneInvalidMsg: ''
1255        },
1256        loginName: '',
1257        password: '',
1258        persistCookie: '',
1259        loading: false,
1260        loginNameError: false,
1261        passwordError: false,
1262        globalError: '',
1263        showPassword: false,
1264        source: '',
1265        // OAuth Authorization Server handoff: literal "as" == OauthConstant.LOGIN_SOURCE / LoginSourceConstants.OAUTH_AS
1266        targetUrl:'',
1267        redirectUrl: '',
1268        fromNeonWebSite: false,
1269        embeddedLogin: false,
1270        activeTab: 0,
1271        googleReCaptchaEnabled: true,
1272        googleReCaptchaEnabled0: true,
1273        enableModernLogin: false,
1274        fromLogout: false,
1275        recaptchaResponse: null
1276    }
1277
1278    const appMethods = {
1279        tabChange(index){
1280            this.activeTab = index
1281        },
1282        doLogin: function () {
1283            if (this.enableModernLogin) {
1284                if (this.embeddedLogin || this.source === 'embedded') {
1285                    const loginName = btoa(this.loginName)
1286                    const loginPassword = btoa(this.password)
1287                    const url = '/nx/auth/authenticate?source=embedded'
1288                        + '&loginName=' + encodeURIComponent(loginName)
1289                        + '&loginPassword=' + encodeURIComponent(loginPassword)
1290                        + (this.persistCookie === true ? '&rememberMe=true' : '')
1291                    window.location.href = url
1292                    return
1293                }
1294                // Align with legacy signIn.do: password login does not submit/verify reCAPTCHA.
1295                this.loading = true
1296                this.globalError = ''
1297                // Modern resolves the post-login redirect server-side, so the active tab has to
1298                // travel with the request (the legacy branch below picks /p2p/overview locally).
1299                // source=as owns targetUrl — ai= is appended to it — so never overwrite it there.
1300                let target = this.targetUrl || this.redirectUrl || ''
1301                if (!target && !this.source && this.activeTab === 1) {
1302                    target = '/p2p/overview'
1303                }
1304                let url = '/nx/auth/authenticate?targetUrl=' + encodeURIComponent(target);
1305                if (this.source) {
1306                    url += '&source=' + encodeURIComponent(this.source);
1307                }
1308                axios.post(url, {
1309                    loginName: this.loginName,
1310                    password: this.password,
1311                    rememberMe: this.persistCookie === true,
1312                    requestId: '04528c8e-3ab3-463d-9429-1dd412be25a9',
1313                    source: this.source || ''
1314                }).then((res) => {
1315                    const body = res.data || {};
1316                    if (body.success && body.data) {
1317                        window.location = body.data.redirect;
1318                    } else {
1319                        this.loading = false
1320                        const msgs = body.messages || [];
1321                        this.globalError = (msgs[0] && msgs[0].message) || 'Login Name/Password is invali
1321d';
1322                    }
1323                }).catch(() => {
1324                    this.loading = false
1325                    this.globalError = 'Login Name/Password is invalid';
1326                });
1327                return
1328            }
1329            let $loginData = {
1330                loginName: this.loginName,
1331                loginPassword: this.password,
1332                persistCookie: this.persistCookie === true ? 'yes' : this.persistCookie,
1333                ajaxLogin: true,
1334                source: this.fromNeonWebSite? 'duda' : this.source,
1335                targetUrl: this.targetUrl || this.redirectUrl,
1336                requestId: '04528c8e-3ab3-463d-9429-1dd412be25a9'
1337            }
1338            this.loading = true
1339            this.globalError = ''
1340            axios.post('/np/security/signIn.do', Qs.stringify($loginData)).then(response => {
1341                let $data = response.data
1342                if ($data.status === 'success') {
1343                    if (this.loginName === 'admin') {
1344                        window.location = '/np/admin/content/contentList.do'
1345                    } else if ($data.succeedRedirectPath) {
1346                        window.location = $data.succeedRedirectPath
1347                    } else if(this.activeTab === 1) {
1348                        window.location = '/p2p/overview'
1349                    } else {
1350                        window.location = '/constituent/dashboard'
1351                    }
1352                } else {
1353                    this.loading = false
1354                    this.globalError = $data.errors[0]
1355                }
1356            })
1357        },
1358        login: function () {
1359            let validatePass = true
1360            if (Validator.required.check(this.loginName)) {
1361                this.loginNameError = true
1362                validatePass = false
1363            }
1364            if (Validator.required.check(this.password)) {
1365                this.passwordError = true
1366                validatePass = false
1367            }
1368            if (!validatePass) {
1369                return
1370            }
1371            this.passwordError = false
1372            this.loginNameError = false
1373            this.doLogin()
1374        },
1375
1376        showPsd: function ($show) {
1377            if ($show) {
1378                this.showPassword = true
1379                document.querySelector('#password').type = 'text'
1380            } else {
1381                this.showPassword = false
1382                document.querySelector('#password').type = 'password'
1383            }
1384        },
1385        fn_openAuthWin: function (src) {
1386            if (this.enableModernLogin) {
1387                // Same sources as password login (targetUrl / redirectUrl), plus live ?redirect=
1388                // from the address bar — Thymeleaf injection alone has been unreliable for social.
1389                const params = new URLSearchParams(window.location.search || '');
1390                let path = (params.get('redirect')
1391                    || params.get('target')
1392                    || this.targetUrl
1393                    || this.redirectUrl
1394                    || '').trim();
1395                if (path) {
1396                    try {
1397                        path = decodeURIComponent(path);
1398                    } catch (e) { /* keep raw */ }
1399                }
1400                if (!path) {
1401                    path = window.location.pathname + window.location.search;
1402                    const pathOnly = (window.location.pathname || '').replace(/\/+$/, '') || '/';
1403                    if (pathOnly === '/login' || pathOnly === '/nx/login' || /\/login$/.test(pathOnly)) {
1404                        path = '/';
1405                    }
1406                }
1407                neonSocialLogin(src, {
1408                    targetUrl: path,
1409                    orgId: this.orgId,
1410                    // Literal "as" == OauthConstant.LOGIN_SOURCE / LoginSourceConstants.OAUTH_AS
1411                    custom: this.source === 'as' ? 'as' : undefined
1412                });
1413                return;
1414            }
1415            const path = "/np/clients/" + this.orgId + "/snOpenAuthWin.do?";
1416            const param = "src=" + src + "&da=login"+ (this.fromNeonWebSite?'&custom=duda':'');
1417            const url = path + param;
1418            const size = 'width=750,height=450,top=230,left=370,toolbar=no';
1419            window.location.href = url;
1420        },
1421        enterClick: function (e) {
1422            if (e.keyCode === 13) {
1423                if (e.target.closest('.neonfront_input')) {
1424                    this.login()
1425                }
1426                if (e.target.closest('.form-check-input')) {
1427                    this.login()
1428                }
1429            }
1430        },
1431        listenEnterKey: function () {
1432            document.addEventListener('keydown', this.enterClick)
1433        },
1434        createFundraiser: function () {
1435            this.doLoginLoading = false
1436            this.doCreateAcct = false
1437            this.p2pLoginErrorMessages = []
1438            this.acctRules.fname.invalid = null
1439            this.acctRules.lname.invalid = null
1440            this.acctRules.email.invalid = null
1441            this.acctRules.psd1.invalid = null
1442            this.acctRules.psd2.invalid = null
1443            this.$nextTick(() => {
1444                const modalElement = this.$refs.createP2pAcctModal;
1445                document.body.appendChild(modalElement);
1446                const modal = new bootstrap.Modal(this.$refs.createP2pAcctModal)
1447                modal.show()
1448            })
1449        },
1450        invisibleReCaptchaInit: function () {
1451            window.ReCaptchaLoaded = this.reCaptchaCallback;
1452            let tag = document.createElement("div")
1453            tag.className = 'g-recaptcha'
1454            document.body.appendChild(tag);
1455            let timer = setInterval(() => {
1456                if(window.grecaptcha && grecaptcha.enterprise && typeof window.grecaptcha.enterprise.render === 'function'){
1457                    window.grecaptcha.enterprise.render(tag,{
1458                        sitekey: '6Lciqi8rAAAAAG87Wg7usbkvyHVTXylFPgeSyHkM',
1459                        size: 'invisible'
1460                    });
1461                    clearInterval(timer);
1462                }
1463            },500);
1464        },
1465        reCaptchaCallback: function ($token) {
1466            this.recaptchaResponse = $token
1467            if(window.grecaptcha && window.grecaptcha.enterprise) {
1468                window.grecaptcha.enterprise.reset()
1469            }
1470            this.doP2pCreateAccountSendToServer()
1471        },
1472        validateReCaptcha:function () {
1473            if(window.grecaptcha && window.grecaptcha.enterprise) {
1474                this.recaptchaResponse = null
1475                window.grecaptcha.enterprise.execute().then(($token) => {
1476                    this.reCaptchaCallback($token)
1477                })
1478            } else {
1479                this.doP2pCreateAccountSendToServer()
1480            }
1481        },
1482        doP2pCreateAccount: function () {
1483            this.doCreateAcct = true
1484            this.validateReCaptcha()
1485        },
1486        doP2pCreateAccountSendToServer: function () {
1487            let formData = new FormData();
1488            formData.append('firstName', this.acctData.firstName.trim());
1489            formData.append('lastName', this.acctData.lastName.trim());
1490            formData.append('email', this.acctData.email.trim());
1491            formData.append('password', this.acctData.password.trim());
1492            formData.append('recaptchaResponse', this.recaptchaResponse);
1493            if (this.gdprSettings.profileEnabled) {
1494                formData.append('gdpr.email', this.gdprValues.email);
1495                formData.append('gdpr.phone', this.gdprValues.phone);
1496                formData.append('gdpr.mail', this.gdprValues.mail);
1497                formData.append('gdpr.sms', this.gdprValues.sms);
1498                formData.append('gdpr.share', this.gdprValues.share);
1499                formData.append('gdpr.smsPhoneNum', this.gdprValues.smsPhoneNum);
1500            }
1501            axios.post('/nx/portal/p2p-data/create-account', formData, {}).then(response => {
1502                if (response.data.success) {
1503                    this.doP2pLogin(this.acctData.email, this.acctData.password)
1504                } else {
1505                    alert(response.data.messages[0].message);
1506                    this.doCreateAcct = false
1507                }
1508            }).catch(error => {
1509                console.log(error)
1510                this.doCreateAcct = false
1511                alert('Create account failed, please try again.')
1512            })
1513        },
1514        doP2pLogin: function (name, password) {
1515            this.doLoginLoading = true
1516            if (this.enableModernLogin) {
1517                // Align with legacy: password login does not submit/verify reCAPTCHA.
1518                let url = '/nx/auth/authenticate?targetUrl=' + encodeURIComponent('/p2p/overview')
1519                axios.post(url, {
1520                    loginName: name,
1521                    password: password,
1522                    requestId: '04528c8e-3ab3-463d-9429-1dd412be25a9'
1523                }).then((res) => {
1524                    const body = res.data || {};
1525                    if (body.success && body.data) {
1526                        window.location.href = body.data.redirect;
1527                    } else {
1528                        const msgs = body.messages || [];
1529                        this.globalError = (msgs[0] && msgs[0].message) || 'Login failed';
1530                        alert(this.globalError)
1531                    }
1532                    this.doCreateAcct = false
1533                    this.doLoginLoading = false
1534                }).catch((error) => {
1535                    console.error(error.response)
1536                    alert('Login error: ' + JSON.stringify(error.response))
1537                    this.doLoginLoading = false
1538                })
1539                return
1540            }
1541            let params = {
1542                loginName: name,
1543                loginPassword: password,
1544                orgId: 'franciscanworks',
1545                ajaxLogin: true,
1546                requestId: '04528c8e-3ab3-463d-9429-1dd412be25a9'
1547            }
1548            axios.post('/np/security/encouragedSignIn.do', Qs.stringify(params)).then(response => {
1549                let result = response.data
1550                if (result.status === 'success') {
1551                    window.location.href = 'https://' + window.location.host + '/p2p/overview'
1552                } else {
1553                    alert('Login error: ' + JSON.stringify(result.errors))
1554                }
1555                this.doCreateAcct = false
1556                this.doLoginLoading = false
1557            }).catch(error => {
1558                console.error(error.response)
1559                alert('Login error: ' + JSON.stringify(error.response))
1560                this.doLoginLoading = false
1561            })
1562        },
1563        checkAcctFieldVal: function ($event, rule) {
1564            let val = $event.target.value.trim();
1565            if (rule.type === 'name' && !val) {
1566                rule.invalid = true
1567            } else if (rule.type === 'email' && !val) {
1568                rule.errMsg = 'Email Address is required.'
1569                rule.invalid = true
1570            } else if (rule.type === 'email' && Validator['email'].check(val)) {
1571                rule.errMsg = 'Email Address format is invalid.'
1572                rule.invalid = true
1573            } else if (rule.type === 'psd1' && !val) {
1574                rule.errMsg = 'Create Password is required.'
1575                rule.invalid = true
1576            } else if (rule.type === 'psd1' && Validator.password.check(val)) {
1577                rule.errMsg = 'Your password requires at least 8 characters, including at least one number. Spaces are not allowed.'
1578                rule.invalid = true
1579            } else if (rule.type === 'psd2') {
1580                if (val) {
1581                    let val1 = document.querySelector('#createPassword').value
1582                    rule.errMsg = 'Confirm Password does not match.'
1583                    rule.invalid = (val !== val1.trim())
1584                } else {
1585                    rule.errMsg = 'Confirm Password is required.'
1586                    rule.invalid = true
1587                }
1588            } else {
1589                rule.invalid = false
1590            }
1591        },
1592        showConfirmPsd: function ($show) {
1593            if ($show) {
1594                this.showConfirmPassword = true
1595                document.querySelector('#confirmPassword').type = 'text'
1596            } else {
1597                this.showConfirmPassword = false
1598                document.querySelector('#confirmPassword').type = 'password'
1599            }
1600        },
1601        showCreatePsd: function ($show) {
1602            if ($show) {
1603                this.showCreatePassword = true
1604                document.querySelector('#createPassword').type = 'text'
1605            } else {
1606                this.showCreatePassword = false
1607                document.querySelector('#createPassword').type = 'password'
1608            }
1609        },
1610        loadGdprSetting: function () {
1611            let previewMode = false
1612            if (previewMode) {
1613                return;
1614            }
1615            axios.get('/nx/portal/p2p-data/login-settings').then(response => {
1616                if (response.data.success) {
1617                    this.gdprSettings = response.data.data.gdprSettings
1618                    this.autoMergeOn = response.data.data.autoMergeOn
1619                }
1620            }).catch(error => {
1621                alert('Load GDPR settings failed, please try again.')
1622            })
1623        },
1624        checkGdprSmsPhoneNum: function ($event) {
1625            let val = $event.target.value.trim();
1626            if (!val) {
1627                this.gdprValues.smsPhoneInvalid = true
1628                this.gdprValues.smsPhoneInvalidMsg = 'The SMS/MMS Number is required.'
1629                return
1630            }
1631            let usPhoneReg = /^(\+?1[-\s]?)?\(?[2-9]\d{2}\)?[-\s]?[2-9](?!11)\d{2}[-\s]?\d{4}$/
1632            if (!usPhoneReg.test(val.replace(/\s+/g, ''))) {
1633                this.gdprValues.smsPhoneInvalid = true
1634                if (val.replace(/[\s\-()]/g, '').match(/^[^1]\d{10}$/)) {
1635                    this.gdprValues.smsPhoneInvalidMsg = '11-digit numbers must begin with the country code 1 (US/Canada).'
1636                } else {
1637                    this.gdprValues.smsPhoneInvalidMsg = 'Please enter a valid 10 or 11 digit mobile number.'
1638                }
1639                return
1640            }
1641            this.gdprValues.smsPhoneInvalid = false
1642            this.gdprValues.smsPhoneInvalidMsg = ''
1643        }
1644    }
1645
1646    const appComputed = {
1647        checkCreateAcctField: function () {
1648            return !(this.acctRules.fname.invalid === false && this.acctRules.lname.invalid === false
1649                && this.acctRules.email.invalid === false && this.acctRules.psd1.invalid === false
1650                && this.acctRules.psd2.invalid === false && (!this.gdprValues.sms || (this.gdprValues.smsPhoneNum && this.gdprValues.smsPhoneInvalid === false)))
1651        },
1652    }
1653
1654    const appWatch = {}
1655
1656    const appConfig = {
1657        data: function () {
1658            return appData
1659        },
1660        methods: appMethods,
1661        computed: appComputed,
1662        watch: appWatch,
1663        created: function() {
1664            const params = new URLSearchParams(window.location.search);
1665            const activeTab = params.get('activeTab');
1666            if(activeTab === '1') {
1667                this.activeTab = 1
1668            }
1669        },
1670        mounted: function () {
1671            if(this.googleReCaptchaEnabled || this.googleReCaptchaEnabled0) {
1672                this.invisibleReCaptchaInit()
1673            }
1674            this.listenEnterKey()
1675            this.loadGdprSetting()
1676        }
1677    }
1678</script>
1678
1679
1680
1681
1682<script type="application/javascript">
1683  appData.loggedIn = false
1684  appData.previewMode = false
1685  appData.orgId = 'franciscanworks'
1686  appData.currentAccountId = 
1687  appData.accountBasicInfo = ''
1688  appData.profilePhoto = {
1689    url: '',
1690    approved: false
1691  }
1692  appData.showMenu = false
1693  appData.formCurrency= '$'
1694  appData.formCurrencyCode= 'USD'
1695  appData.theme = window._neonThemeData || {}
1696  appMethods.formatAmount = function (amount) {
1697    return Formatter.numberFormat(amount, 2, this.formCurrency)
1698  }
1699  appMethods.showP2pConfirmModal = function() {
1700      bootstrap.Modal.getInstance(document.getElementById("goP2pConfirmModal")).show()
1701  }
1702  appMethods.cancelP2pConfirmModal = function(){
1703      bootstrap.Modal.getInstance(document.getElementById("goP2pConfirmModal")).hide()
1704  }
1705  appMethods.confirmP2pConfirmModal = function(){
1706      window.location.href='/p2p/overview'
1707  }
1708  appMethods.getSocialMediaIcon = function (name, style) {
1709    if (typeof getSocialIconURL === 'function') return getSocialIconURL(name, style)
1710    // legacy fallback
1711    return style === 'D'
1712      ? '/nx/static/img/icons/social-media/darkicons/' + name + '.svg'
1713      : '/nx/static/img/icons/social-media/lighticons/' + name + '.svg'
1714  }
1715  // Add interceptor for portal page
1716  axios.interceptors.request.use(function (config) {
1717    if(appData.previewMode){
1718      config.url = config.url.replace('/portal/constituent','/admin/portal/fake').replace('/portal/p2p','/admin/portal/fake/p2p').replace('/portal/event-list','/admin/portal/fake/event-list')
1719    }
1720    return config
1721  }, function (error) {
1722    // ...
1723    return Promise.reject(error)
1724  });
1725
1726  (function (){
1727      if(window.location.href.includes('/constituent')){
1728          window.addEventListener( "pageshow", function ( $event ) {
1729              const performanceNavigationTiming = performance.getEntriesByType('navigation')[0];
1730              // performanceNavigationTiming.type === 'back_forward' for firefox
1731              const historyTraversal = $event.persisted || performanceNavigationTiming.type === 'back_forward';
1732              if ( historyTraversal ) {
1733                  window.location.reload();
1734              }
1735          });
1736      }
1737  })();
1738  const originalMounted = appConfig.mounted
1739  appConfig.mounted = function() {
1740      const result = originalMounted.apply(this);
1741      const $dom = document.getElementById("goP2pConfirmModal")
1742      if($dom) {
1743          new bootstrap.Modal($dom)
1744      }
1745      return result
1746  }
1747  appWatch.theme = {
1748    handler: function(newVal) {
1749      if (typeof applyThemeCSSVars === 'function') applyThemeCSSVars(newVal)
1750    },
1751    deep: true
1752  }
1753  const vueApp = Vue.createApp(appConfig)
1754  vueApp.config.errorHandler = function (err, vm, info) {
1755    if (handleVueJSError) {
1756      handleVueJSError(err, vm, info)
1757    }
1758    console.log(err)
1759  }
1760  vueApp.component('neon-multiselect', window.NeonMultiselect)
1761  vueApp.component('neon-search-box', window.NeonSearchBox)
1762  vueApp.component('neon-fileupload', window.NeonFileupload)
1763  vueApp.component('neon-advancedsearch', window.NeonAdvancedsearch)
1764  vueApp.component('neon-select', window.NeonSelect)
1765  vueApp.component('neon-img-cutter', window.NeonImgCutter)
1766  vueApp.component('neon-pagination', window.NeonPagination)
1767  vueApp.component('neon-pagination-plus', window.NeonPaginationPlus)
1768  vueApp.component('neon-share-modal', window.NeonShareModal)
1769  vueApp.component('neon-search-select', window.NeonSearchSelect)
1770  vueApp.component('neon-search-location', window.NeonSearchLocation)
1771  vueApp.component('neon-input', window.NeonInput)
1772  const app = vueApp.mount('#app')
1773  window.__neonApp = app
1774</script>
1774
1775
1776
1777
1778  
1778<script type="text/javascript">
1779    function setSliderBar(func , wait , immediate) {
1780        let timeout
1781        return function () {
1782            let context = this, args = arguments
1783            let later = function() {
1784                timeout = null
1785                if (!immediate) func.apply(context, args)
1786            }
1787            let callNow = immediate & !timeout
1788            clearTimeout(timeout);
1789            timeout = setTimeout(later, wait);
1790            if (callNow) func.apply(context, args)
1791        }
1792    }
1793    function fixSliderBar() {
1794        let maxWidth = document.body.clientWidth
1795        const leftBar = document.getElementById("left-nav")
1796        const content = document.getElementById("content")
1797        const rightContent = document.getElementById("right-content")
1798        const footContent = document.getElementById("footer-content")
1799        const fixedTitle = document.getElementById("fixed-title")
1800        const flexRow = rightContent.parentNode
1801        const clientHeight = window.innerHeight
1802        if (maxWidth >= 992) {
1803            let isClpEmbed = false
1804            try {
1805                isClpEmbed = sessionStorage.getItem('clpEmbedEnv') === '1'
1806                    || new URLSearchParams(window.location.search).get('clpEmbedEnv') === '1'
1807            } catch (e) {
1808            }
1809            if (isClpEmbed) {
1810                leftBar.style.position = 'static'
1811                leftBar.style.top = ''
1812                leftBar.style.bottom = ''
1813                leftBar.style.height = ''
1814                leftBar.style.minHeight = ''
1815                leftBar.style.left = ''
1816                leftBar.style.width = ''
1817                flexRow.style.paddingLeft = ''
1818                rightContent.style.marginLeft = ''
1819                rightContent.style.minHeight = '0'
1820                try {
1821                    fixedTitle.style.display = 'none'
1822                } catch (e) {
1823                }
1824                leftBar.style.maxHeight = ''
1825                leftBar.style.overflow = ''
1826                const contentHeight = rightContent.offsetHeight
1827                if (contentHeight > 0 && leftBar.scrollHeight > contentHeight) {
1828                    leftBar.style.maxHeight = contentHeight + 'px'
1829                    leftBar.style.overflow = 'hidden auto'
1830                }
1831                return
1832            }
1833            if (leftBar.getBoundingClientRect().top <= 0) {
1834                //float in top on footer
1835                const footerHidden = footContent.offsetHeight === 0
1836                let heightVary = footContent.getBoundingClientRect().bottom - clientHeight
1837                let varyHeight = footContent.offsetHeight - heightVary
1838                if (!footerHidden && varyHeight >= 0 && heightVary <= footContent.offsetHeight) {
1839                    //footer show
1840                    if(!leftBar.classList.contains('neonfront-p2p-portal-left-nav')) {
1841                        leftBar.style.bottom = varyHeight + 'px'
1842                        leftBar.style.height = (clientHeight - varyHeight) + 'px'
1843                    } else {
1844                        leftBar.style.bottom = '0px'
1845                        leftBar.style.height = clientHeight + 'px'
1846                    }
1847                    rightContent.style.minHeight = (clientHeight - varyHeight) + 'px'
1848                    leftBar.style.overflow = 'hidden auto'
1849                } else {
1850                    // footer hide
1851                    leftBar.style.height = clientHeight + 'px'
1852                    leftBar.style.bottom = ''
1853                    rightContent.style.minHeight = clientHeight + 'px'
1854                    leftBar.style.overflow = 'hidden auto'
1855                }
1856                if (leftBar.style.position !== 'fixed') {
1857                    const leftBarWidth = leftBar.offsetWidth || 260
1858                    leftBar.style.position = 'fixed'
1859                    leftBar.style.top = '0'
1860                    leftBar.style.width = leftBarWidth + 'px'
1861                    flexRow.style.paddingLeft = leftBarWidth + 'px'
1862                    rightContent.style.marginLeft = ''
1863                }
1864                leftBar.style.left = flexRow.getBoundingClientRect().left + 'px'
1865                try {
1866                    fixedTitle.style.display = ''
1867                } catch (e) {
1868                }
1869            }
1870            if (content.getBoundingClientRect().top > 0) {
1871                //roll back
1872                leftBar.style.position = 'static'
1873                leftBar.style.top = ''
1874                leftBar.style.bottom = ''
1875                leftBar.style.left = ''
1876                leftBar.style.width = ''
1877                flexRow.style.paddingLeft = ''
1878                rightContent.style.marginLeft = ''
1879                leftBar.style.minHeight = ''
1880                rightContent.style.minHeight = '0'
1881                try {
1882                    fixedTitle.style.display = 'none'
1883                } catch (e) {
1884                }
1885            }
1886        } else {
1887            //roll back
1888            leftBar.style.position = 'static'
1889            leftBar.style.top = ''
1890            leftBar.style.bottom = ''
1891            leftBar.style.left = ''
1892            leftBar.style.width = ''
1893            flexRow.style.paddingLeft = ''
1894            rightContent.style.marginLeft = ''
1895            leftBar.style.minHeight = ''
1896            rightContent.style.minHeight = '0'
1897            try {
1898                fixedTitle.style.display = 'none'
1899            } catch (e) {
1900            }
1901        }
1902    }
1903    const preFix = setSliderBar(function() {
1904        fixSliderBar();
1905    }, 4);
1906    try {
1907        const leftBar = document.getElementById("left-nav")
1908        leftBar.style.maxHeight = window.innerHeight + 'px'
1909        leftBar.style.overflow = 'hidden auto'
1910        window.addEventListener('scroll',preFix)
1911        window.addEventListener('resize',preFix)
1912    } catch (ignore) {
1913        //without sidebar not need fix
1914    }
1915</script>
1915
1916
1917
1918
1919</body>
1920</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.