PageSourceSearch

https://members.chambermusicamerica.org/login

html chambermusicamerica.org collected 2026-09-24 20:30:57 UTC 96,486 bytes, 1,924 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/chambermusicamerica/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, #E1D7DD);
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, center left);
66        position: fixed;
67        z-index: 1;
68    }
69    .neonfront_element .form-check-input:checked {
70        background-color: var(--out-line-calibration-color, rgba(158, 14, 99, 1))!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, Source Sans Pro,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, rgba(158, 14, 99, 1))!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, rgba(158, 14, 99, 1))!important;
101        border-color: #D9DFE3;
102        box-shadow: none !important;
103    }
104
105    .neonfront-btn-link {
106        color: var(--out-line-calibration-color, rgba(158, 14, 99, 1))!important;
107    }
108    .neonfront_stepper.on,.neonfront_stepper_num {
109        background: var(--theme-color, rgba(158, 14, 99, 1))!important;
110    }
111
112    .neonfront_payment_tab .active {
113        border-bottom: 4px solid var(--out-line-calibration-color, rgba(158, 14, 99, 1)) !important;
114        color: var(--out-line-calibration-color, rgba(158, 14, 99, 1)) !important;
115    }
116
117    .neonfront-btn-primary, .btn-primary {
118        background-color: var(--theme-color, rgba(158, 14, 99, 1)) !important;
119        color: var(--primary-btn-text-color, #ffffff) !important;
120        border-color: var(--theme-color, rgba(158, 14, 99, 1)) !important;
121        box-shadow: none !important;
122    }
123    .secondary-exit-btn {
124        color: var(--theme-color, rgba(158, 14, 99, 1)) !important;
125        background-color: #FFF !important;
126        border: 1px solid var(--theme-color, rgba(158, 14, 99, 1)) !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, rgba(158, 14, 99, 1)) !important;
136    }
137
138    .neonfront-btn-outline-primary {
139        color: var(--out-line-calibration-color, rgba(158, 14, 99, 1))!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, rgba(158, 14, 99, 1)) !important;
144        border-color: var(--theme-color, rgba(158, 14, 99, 1)) !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, rgba(158, 14, 99, 1)) !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, #ffffff)!important;
167        background-color: var(--footer-bg-color, rgba(37, 0, 57, 1))!important;
168        padding-left: 0px;
169        padding-right: 0px;
170    }
171    .neonfront_footer_lv_info {
172        color: var(--footer-text-color, #ffffff)!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, #ffffff);
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, #ffffff)!important;
205    }
206
207    .neonfront-switch input:checked{
208        background-color: var(--theme-color, rgba(158, 14, 99, 1)) !important;
209    }
210
211    .popal-opportunities-tile .popal-opportunities-tile-col .pot-neon-round-img {
212        background-color: var(--theme-color, rgba(158, 14, 99, 1)) !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, rgba(158, 14, 99, 1)) !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:                   9,
692        logo:                 "https:\/\/chambermusicamerica.app.neoncrm.com\/np\/clients\/chambermusicamerica\/logo.gif",
693        logoAlt:              null,
694        logoWidth:            100,
695        logoUrl:              null,
696        footerColumns:        3,
697        footerCol1Text:       "<p><a href=\"http:\/\/www.chambermusicamerica.org\" target=\"_blank\" rel=\"noopener\">CMA Main Website Home<\/a><\/p>",
698        footerCol2Text:       "<p>P.O. Box 22248<br>Brooklyn, NY 11202<\/p>",
699        footerCol3Text:       "<p><a href=\"https:\/\/chambermusicamerica.org\/contact-us\/\" target=\"_self\">Contact Us<\/a><\/p>\n<p><a href=\"https:\/\/chambermusicamerica.org\/privacy-policy\/\" target=\"_self\">Privacy Policy<\/a><\/p>\n<p><a href=\"https:\/\/chambermusicamerica.org\/our-impact\/\" target=\"_blank\" rel=\"noopener\">CMA's Impact<\/a><\/p>",
700        displaySocialLink:    true,
701        socialLinkIconAlign:  "C",
702        socialLinkIconStyle: "L",
703        styles:               {"headerBgColor":"#FFFFFF","headerTextColor":"#222222","pageFont":"Source Sans Pro,sans-serif","pageBgColor":"#E1D7DD","pageBgMirror":"0","pageBgImage":"https:\/\/chambermusicamerica.app.neoncrm.com\/neon\/resource\/chambermusicamerica\/images\/CMA25-Sat-TrishBadger-075.jpg","themeJSSnippets":"","pageBgImageAlt":"CMA25-Sat-TrishBadger-075.jpg","themeColor":"rgba(158, 14, 99, 1)","footerBgColor":"rgba(37, 0, 57, 1)","pageBgAlign":"center left","pageFontColor":"#002D41","footerTextColor":"#ffffff","primaryBtnTextColor":"#ffffff","actBtnTextColor":"rgba(158, 14, 99, 1)"},
704        socialLinks:          [{"createBy":"Jenny Ouellette","createTime":"2026-01-21T14:31:12.000-06:00","lastModifyBy":"Jenny Ouellette","lastModifyTime":"2026-01-21T14:31:12.000-06:00","id":191,"templateId":9,"icon":"Website","url":"https:\/\/chambermusicamerica.org"},{"createBy":"Jenny Ouellette","createTime":"2026-01-21T14:31:12.000-06:00","lastModifyBy":"Jenny Ouellette","lastModifyTime":"2026-01-21T14:31:12.000-06:00","id":192,"templateId":9,"icon":"Facebook","url":"https:\/\/facebook.com\/chambermusicamerica"},{"createBy":"Jenny Ouellette","createTime":"2026-01-21T14:31:12.000-06:00","lastModifyBy":"Jenny Ouellette","lastModifyTime":"2026-01-21T14:31:12.000-06:00","id":193,"templateId":9,"icon":"Instagram","url":"https:\/\/instagram.com\/chambermusicamerica"},{"createBy":"Jenny Ouellette","createTime":"2026-01-21T14:31:12.000-06:00","lastModifyBy":"Jenny Ouellette","lastModifyTime":"2026-01-21T14:31:12.000-06:00","id":194,"templateId":9,"icon":"YouTube","url":"https:\/\/youtube.com\/chambermusicamerica"},{"createBy":"Jenny Ouellette","createTime":"2026-01-21T14:31:12.000-06:00","lastModifyBy":"Jenny Ouellette","lastModifyTime":"2026-01-21T14:31:12.000-06:00","id":195,"templateId":9,"icon":"LinkedIn","url":"https:\/\/linkedin.com\/company\/chamber-music-america"}],
705        naviLinks:            [{"createBy":"Jenny Ouellette","createTime":"2026-01-21T14:31:12.000-06:00","lastModifyBy":"Jenny Ouellette","lastModifyTime":"2026-01-21T14:31:12.000-06:00","id":124,"templateId":9,"url":"https:\/\/members.chambermusicamerica.org\/nx\/portal\/neonevents\/events?path=%2Fportal%2Fevents","name":"CMA Events","openInNewTab":1},{"createBy":"Jenny Ouellette","createTime":"2026-01-21T14:31:12.000-06:00","lastModifyBy":"Jenny Ouellette","lastModifyTime":"2026-01-21T14:31:12.000-06:00","id":125,"templateId":9,"url":"https:\/\/chambermusicamerica.org\/become-a-member\/","name":"Join Membership","openInNewTab":1},{"createBy":"Jenny Ouellette","createTime":"2026-01-21T14:31:12.000-06:00","lastModifyBy":"Jenny Ouellette","lastModifyTime":"2026-01-21T14:31:12.000-06:00","id":126,"templateId":9,"url":"https:\/\/chambermusicamerica.org\/news\/","name":"CMA News \u0026 Insights","openInNewTab":1}],
706        _homePageURL:         "https:\/\/chambermusicamerica.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                    <div class="col-md-12 neonfront_element">
800                        <div class="row g-2 justify-content-center">
801                            <div class="col login-portal-description">Thank you for logging into CMA's constituent portal.  Sign in here if you are a current member. If you are a current or returning member you already have an account with us. To create your login, please select "Having trouble logging in?" and you will be matched to your account. </div>
802                        </div>
803                    </div>
804                    <div class="col-md-12 neonfront_element mt-0">
805                        <div id="globalError" class="row mt-3 neonfront-portal neonfront_form_failed_container" v-show="globalError">
806                            <div class="col-2">
807                                <span class="fa fa-exclamation-circle mt-1" style="font-size: 2.25rem;"></span>
808                            </div>
809                            <div class="col-10">
810                                <h5 class="fs-6 mb-1" style="line-height: 42px">{{globalError}}</h5>
811                            </div>
812                        </div>
813                    </div>
814                    <div class="neonfront_element col-md-12">
815                        <label for="loginName" class="form-label">Username or Email</label>
816                        <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">
817                        <div class="invalid-feedback">
818                            <div><i class="fa fa-exclamation-triangle"></i>
819                                Username or Email is required.
820                            </div>
821                        </div>
822                    </div>
823                    <div class="neonfront_element col-md-12 login-password-field">
824                        <label for="password" class="form-label">Password</label>
825                        <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">
826                        <i class="fas fa-eye-slash presentation" v-if="!showPassword" @click="showPsd(true)"></i>
827                        <i class="fas fa-eye presentation" v-if="showPassword" @click="showPsd(false)"></i>
828                        <div class="invalid-feedback">
829                            <div><i class="fa fa-exclamation-triangle"></i>Password is required.</div>
830                        </div>
831                    </div>
832                    <div class="neonfront_element col-md-12 login-additional">
833                        <div class="form-check login-remember-me">
834                            <input class="form-check-input neonfront_checkbox" type="checkbox" value="yes" id="persistCookie" v-model="persistCookie">
835                            <label class="form-check-label neonfront_checkbox_label" for="persistCookie">
836                                Remember me for 2 weeks
837                            </label>
838                        </div>
839                        <div class="trouble-login">
840                            <a href="/requestPassword" data-testid="link-forgot-password">Having trouble logging in?</a>
841                            
842                        </div>
843                    </div>
844                    <div class="col-md-12 neonfront_element">
845                        <div class="row g-2 justify-content-center">
846                            <div class="col">
847                                <button type="button" class="btn neonfront-btn-primary btn-lg" @click="login" v-show="!loading" data-testid="bt
847n-login">Login</button>
848                                <button class="btn neonfront-btn-primary btn-lg" type="button" disabled v-show="loading">
849                                    Processing&nbsp; <span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
850                                </button>
851                            </div>
852                        </div>
853                    </div>
854                    <div class="col-md-12 neonfront_element login-separate">
855                        <div class="separate-line"></div>
856                        <div class="separate-text">or</div>
857                        <div class="separate-line"></div>
858                    </div>
859                    
860                    <div class="col-md-12 neonfront_element">
861                        <div class="create-account">
862                            Don't have an account?
863                            <a v-if="activeTab === 0 || autoMergeOn" href="/forms/createaccount">Create an account</a>
864                            <a v-else href="javascript:void(0)" @click="createFundraiser">Create an account</a>
865                        </div>
866                    </div>
867                    
868                </div>
869            </div>
870        </div>
871    </div>
872</div>
873<div class="modal fade neonfront-modal landing-page-login-modal" aria-hidden="true" id="fundraiserLoginModal" ref="createP2pAcctModal" data-bs-backdrop="static" tabindex="-1">
874    <div class="modal-dialog" style="max-width: 580px;">
875        <div class="modal-content px-sm-4 px-1">
876            <div class="modal-header">
877                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
878            </div>
879
880            <div class="modal-body" style="padding-top:0;padding-bottom:0">
881                <div class="row mt-1">
882                    <div class="col login-body-title p2p-login-body-title">
883                        Create an Account
884                    </div>
885                </div>
886                <div class="row mt-1">
887                    <div class="login-body-col">
888                        <div class="body-line" style="display: flex">
889                            <span>
890                                <label for="firstName" class="input-label">First Name *</label>
891                                <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)" />
892                                <div v-if="acctRules.fname.invalid" class="field-err-msg"><i class="fa fa-exclamation-triangle"></i>{{acctRules.fname.errMsg}}</div>
893                            </span>
894                            <span style="margin-left: 30px;">
895                                <label for="lastName" class="input-label">Last Name *</label>
896                                <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)" />
897                                <div v-if="acctRules.lname.invalid" class="field-err-msg"><i class="fa fa-exclamation-triangle"></i>{{acctRules.lname.errMsg}}</div>
898                            </span>
899                        </div>
900                        <div class="body-line">
901                            <label for="emailAddress" class="input-label" >Email Address *</label>
902                            <input type="text" v-model="acctData.email" class="form-control" id="emailAddress" name="emailAddress" :class="{'invalid': acctRules.email.invalid}" @blur="checkAcctFieldVal($event, acctRules.email)" />
903                            <div v-if="acctRules.email.invalid" class="field-err-msg"><i class="fa fa-exclamation-triangle"></i>{{acctRules.email.errMsg}}</div>
904                        </div>
905                        <div class="body-line">
906                            <label for="createPassword" class="input-label">Create Password *</label>
907                            <input type="password" v-model="acctData.password" class="form-control" id="createPassword" name="createPassword" :class="{'invalid': acctRules.psd1.invalid}" @blur="checkAcctFieldVal($event, acctRules.psd1)"/>
908                            <span>
909                                <i class="fa fa-info-circle"></i>
910                                <span class="info-message-text">Enter at least 8 characters, including at least one number. Spaces not allowed.</span>
911                            </span>
912                            <i class="fas fa-eye-slash presentation1" v-if="!showCreatePassword" @click="showCreatePsd(true)"></i>
913                            <i class="fas fa-eye presentation1" v-if="showCreatePassword" @click="showCreatePsd(false)"></i>
914                            <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>
915                        </div>
916                        <div class="body-line" style="margin-top: -20px;margin-bottom: 0px !important;">
917                            <label for="confirmPassword" class="input-label">Confirm Password *</label>
918                            <input type="password" class="form-control" id="confirmPassword" name="confirmPassword" :class="{'invalid': acctRules.psd2.invalid}" @blur="checkAcctFieldVal($event, acctRules.psd2)"/>
919                            <i class="fas fa-eye-slash presentation2" v-if="!showConfirmPassword" @click="showConfirmPsd(true)"></i>
920                            <i class="fas fa-eye presentation2" v-if="showConfirmPassword" @click="showConfirmPsd(false)"></i>
921                            <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>
922                        </div>
923
924                        <div v-if="gdprSettings.profileEnabled || gdprSettings.statementEnabled" class="body-line">
925                            <label class="input-label">Data Privacy & Consent</label>
926                            <div class="gdpr-setting-section">
927                                <div class="gdpr-setting-items">
928                                    <div v-if="gdprSettings.email" class="gdpr-setting-item neonfront_element">
929                                        <div class="checkbox-column">
930                                            <input v-model="gdprValues.email" type="checkbox" id="gdpr.email" name="gdpr.email" class="form-check-input neonfront_checkbox"/>
931                                        </div>
932                                        <label for="gdpr.email" class="setting-item-label">{{gdprSettings.email}}</label>
933                                    </div>
934                                    <div v-if="gdprSettings.phone" class="gdpr-setting-item neonfront_element">
935                                        <div class="checkbox-column">
936                                            <input v-model="gdprValues.phone" type="checkbox" id="gdpr.phone" name="gdpr.phone" class="form-check-input neonfront_checkbox"/>
937                                        </div>
938                                        <label for="gdpr.email" class="setting-item-label">{{gdprSettings.phone}}</label>
939                                    </div>
940                                    <div v-if="gdprSettings.mail" class="gdpr-setting-item neonfront_element">
941                                        <div class="checkbox-column">
942                                            <input v-model="gdprValues.mail" type="checkbox" id="gdpr.mail" name="gdpr.mail" class="form-check-input neonfront_checkbox"/>
943                                        </div>
944                                        <label for="gdpr.mail" class="setting-item-label">{{gdprSettings.mail}}</label>
945                                    </div>
946                                    <div v-if="gdprSettings.sms" class="gdpr-setting-item neonfront_element">
947                                        <div class="checkbox-column">
948                                            <input v-model="gdprValues.sms" type="checkbox" id="gdpr.sms" name="gdpr.sms" class="form-check-input neonfront_checkbox"/>
949                                        </div>
950                                        <label for="gdpr.sms" class="setting-item-label">{{gdprSettings.sms}}</label>
951                                    </div>
952                                    <div v-show="gdprValues.sms" class="smsPhoneNum-section" style="padding-left:25px">
953                                        <label for="smsPhoneNum" class="input-label">What's the best number for us to text you? *</label>
954                                        <input v-model="gdprValues.smsPhoneNum" type="text" class="form-control" id="smsPhoneNum" name="smsPhoneNum" :class="{'invalid': gdprValues.smsPhoneInvalid}" @blur="checkGdprSmsPhoneNum($event)"/>
955                                        <div v-if="gdprValues.smsPhoneInvalid" class="field-err-msg"><i class="fa fa-exclamation-triangle"></i>{{gdprValues.smsPhoneInvalidMsg}}</div>
956                                    </div>
957                                    <div v-if="gdprSettings.share" class="gdpr-setting-item neonfront_element">
958                                        <div class="checkbox-column">
959                                            <input v-model="gdprValues.share" type="checkbox" id="gdpr.share" name="gdpr.share" class="form-check-input neonfront_checkbox"/>
960                                        </div>
961                                        <label for="gdpr.share" class="setting-item-label">{{gdprSettings.share}}</label>
962                                    </div>
963                                </div>
964
965                                <div v-if="gdprSettings.statement" class="gdpr-statement">
966                                    {{gdprSettings.statement}}
967                                </div>
968                                <div v-if="gdprSettings.policyUrl" class="gdpr-policy">
969                                    <a :href="gdprSettings.policyUrl" target="_blank" style="text-decoration: none">{{gdprSettings.policyLabel}}</a>
970                                </div>
971                            </div>
972                        </div>
973                    </div>
974                </div>
975            </div>
976            <div class="login-footer p2p-login-footer">
977                <button v-if="!doCreateAcct" type="button"
978                        class="btn neonfront-btn-primary btn-lg p2p-btn"
979                        :disabled="checkCreateAcctField"
980                        @click="doP2pCreateAccount">Submit</button>
981                <button v-if="doCreateAcct" class="btn neonfront-btn-primary btn-lg p2p-btn" type="button" disabled>
982                    Processing&nbsp; <span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
983                </button>
984                <div class="col-md-12 neonfront_element login-separate p2p_login_separate">
985                    <div class="separate-line"></div>
986                    <div class="separate-text">or</div>
987                    <div class="separate-line"></div>
988                </div>
989                <div class="go_login_sty">Have an account? <a href="javascript:void(0)" data-bs-dismiss="modal">Log in</a></div>
990            </div>
991        </div>
992    </div>
993</div>
994
995</body>
996</html>
997
998              </div>
999            </div>
1000          </div>
1001        </div>
1002      </div>
1003    </div>
1004    <!DOCTYPE html>
1005<html>
1006<body>
1007    <div class="container-fluid neonfront_footer" id="footer-content">
1008        <div class="row neonfront_footer_lv_info">
1009            <div v-if="theme.footerColumns==1" class="col-md-12" style="padding-left:30px;padding-right:30px;">
1010                <div v-html="theme.footerCol1Text"></div>
1011            </div>
1012            <div v-if="theme.footerColumns==2" class="col-md-6" style="padding-left:30px;">
1013                <div class="float-md-start float-none" v-html="theme.footerCol1Text"></div>
1014            </div>
1015            <div v-if="theme.footerColumns==2" class="col-md-6" style="padding-right:30px;">
1016                <div class="float-md-end float-none" v-html="theme.footerCol2Text"></div>
1017            </div>
1018            <div v-if="theme.footerColumns==3" class="col-md-4" style="padding-left:30px;">
1019                <div class="float-md-start float-none" v-html="theme.footerCol1Text"></div>
1020            </div>
1021            <div v-if="theme.footerColumns==3" class="col-md-4 text-md-center footer_center_column">
1022                <div class="footer_center_column_content" style="margin:0 auto;" v-html="theme.footerCol2Text"></div>
1023            </div>
1024            <div v-if="theme.footerColumns==3" class="col-md-4" style="padding-right:30px;">
1025                <div class="float-md-end float-none" v-html="theme.footerCol3Text"></div>
1026            </div>
1027        </div>
1028        <div v-if="theme.displaySocialLink"
1029             :class="['neonfront_footer_lv_media',
1030                      theme.socialLinkIconAlign==='C' ? 'text-center' : (theme.socialLinkIconAlign==='R' ? 'text-end' : 'text-start'),
1031                      theme.socialLinkIconStyle==='L' ? 'text-white' : 'text-dark']">
1032            <template v-for="item in (theme.socialLinks||[])" :key="item.icon">
1033                <a v-if="item.url"
1034                   :href="item.icon==='Email' ? 'mailto:'+item.url : (item.url.startsWith('http://') || item.url.startsWith('https://') || item.url.startsWith('//') ? item.url : 'https://'+item.url)"
1035                   :style="theme._previewMode ? {'pointer-events': 'none'} : {}"
1036                   target="_blank"
1037                   class="text-reset social-link-icon">
1038                    <img width="24" height="24"
1039                         :src="getSocialMediaIcon(item.icon, theme.socialLinkIconStyle)">
1040                </a>
1041            </template>
1042        </div>
1043        
1044        <div class="footer-root">
1045            <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>
1046            <div class="footer-root-right">
1047                <a href="https://neonone.com/" target="_blank">
1048                <div class="footer-root-right-header"><img width="60" height="20" src="/nx/portal-static/img/logo-variations.svg"/></div>
1049                <div class="footer-root-right-footer"><img width="60" height="20" src="/nx/portal-static/img/merchant-copy.svg"/></div>
1050                </a>
1051            </div>
1052        </div>
1053    </div>
1054</body>
1055</html>
1056  </div>
1057  
1058  
1059    <!DOCTYPE html>
1060<html lang="en">
1061<body>
1062    <!DOCTYPE html>
1063<html lang="en">
1064<body>
1065<!-- Modal -->
1066<div class="modal fade neonfront-modal neonfront-confirm-modal" id="confirmSubMembershipEndModal" data-bs-backdrop="static" tabindex="-1">
1067    <div class="modal-dialog" style="max-width: 580px;">
1068        <div class="modal-content px-sm-4 px-1" style="z-index: 20000;">
1069            <div class="modal-header">
1070                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" @click="cancelSubMembershipEndTurnOff"></button>
1071            </div>
1072            <div class="modal-body">
1073                <div class="row mt-1">
1074                    <div class="col">
1075                        <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>
1076                    </div>
1077                </div>
1078            </div>
1079            <div class="modal-footer">
1080                <button type="button" class="btn btn-link neonfront-btn-l
1080ink" @click="cancelSubMembershipEndTurnOff" style="width: auto">Cancel</button>
1081                <button type="button" class="btn neonfront-btn-primary btn-lg px-4" @click="confirmSubMembershipEndTurnOff" style="width: auto">Confirm</button>
1082            </div>
1083        </div>
1084    </div>
1085</div>
1086</body>
1087</html>
1088    <!DOCTYPE html>
1089<html lang="en">
1090<body>
1091<!-- Modal -->
1092<div class="modal fade neonfront-modal neonfront-confirm-modal" id="addressListDeleteModal" data-bs-backdrop="static" tabindex="-1">
1093  <div class="modal-dialog" style="max-width: 580px;">
1094    <div class="modal-content px-sm-4 px-1" style="z-index: 20000;">
1095      <div class="modal-header">
1096        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" @click="cancelAddressListDeleteModal"></button>
1097      </div>
1098      <div class="modal-body">
1099        <div class="row mt-1">
1100          <div class="col">
1101            <p>Are you sure you want to delete this address?</p>
1102            <p class="desc">Your address will be deleted.</p>
1103          </div>
1104        </div>
1105      </div>
1106      <div class="modal-footer">
1107        <button type="button" class="btn btn-link neonfront-btn-link" @click="cancelAddressListDeleteModal" style="width: auto">Cancel</button>
1108        <button type="button" class="btn neonfront-btn-primary btn-lg px-4" @click="confirmAddressListDeleteModal" style="width: auto">Confirm</button>
1109      </div>
1110    </div>
1111  </div>
1112</div>
1113</body>
1114</html>
1115    <!DOCTYPE html>
1116<html lang="en">
1117<body>
1118<!-- Modal -->
1119<div class="modal fade neonfront-modal neonfront-confirm-modal" id="addressListCommonDeleteModal" data-bs-backdrop="static" tabindex="-1">
1120  <div class="modal-dialog" style="max-width: 580px;">
1121    <div class="modal-content px-sm-4 px-1" style="z-index: 20000;">
1122      <div class="modal-header">
1123        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" @click="cancelAddressListDeleteModal"></button>
1124      </div>
1125      <div class="modal-body">
1126        <div class="row mt-1">
1127          <div class="col">
1128            <p>Address cannot be deleted because of the following linked data:</p>
1129            <p class="desc" v-for="item in deleteInfo">
1130              {{decisionDeleteInfo(item)}}
1131            </p>
1132          </div>
1133        </div>
1134      </div>
1135      <div class="modal-footer">
1136        <button type="button" class="btn neonfront-btn-primary btn-lg px-4" @click="cancelAddressListCommonDeleteModal" style="width: auto;margin-left: auto">Close</button>
1137      </div>
1138    </div>
1139  </div>
1140</div>
1141</body>
1142</html>
1143</body>
1144</html>
1145  
1146  <div class="modal fade neonfront-modal" id="goP2pConfirmModal" data-bs-backdrop="static" tabindex="-1">
1147    <div class="modal-dialog">
1148        <div class="modal-content px-sm-4 px-1 go-p2p-confirm-modal">
1149            <div class="header">
1150                Leaving this Page
1151            </div>
1152            <div class="content">
1153                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.
1154                <br/><br/>Do you want to continue?
1155            </div>
1156            <div class="footer">
1157                <button type="button" class="btn btn-link neonfront-btn-link" @click="cancelP2pConfirmModal()"
1158                        style="width: auto; font-family: var(--page-font, Lato)">Cancel
1159                </button>
1160                <button type="button" class="btn neonfront-btn-primary btn-lg px-4" @click="confirmP2pConfirmModal"
1161                        style="width: auto; font-family: var(--page-font, Lato)">Continue
1162                </button>
1163            </div>
1164            <i class="fa-solid fa-xmark-large close_icon" @click="cancelP2pConfirmModal"></i>
1165        </div>
1166    </div>
1167</div>
1168
1169</div>
1170
1171<script src="/nx/portal-static/component/vue/vue-3.1.2.min.js"></script>
vendor: 1 bytes, line 1171
1171
1172<script src="/nx/portal-static/component/axios/axios.min.js"></script>
vendor: 1 bytes, line 1172
1172
1173<script src="/nx/portal-static/component/qs/qs.min.js"></script>
vendor: 1 bytes, line 1173
1173
1174<script src="/nx/portal-static/component/bootstrap/js/bootstrap.bundle.min.js"></script>
vendor: 1 bytes, line 1174
1174
1175<script src="/nx/portal-static/component/laydate/laydate.js"></script>
vendor: 1 bytes, line 1175
1175
1176<script src="/nx/portal-static/component/cropperjs/cropper.min.js"></script>
vendor: 1 bytes, line 1176
1176
1177<script src="/nx/portal-static/component/moment-js/moment-2.30.1.min.js"></script>
vendor: 1 bytes, line 1177
1177
1178<script src="/nx/portal-static/component/moment-js/moment-timezone.min.js"></script>
1178
1179
1180<script src="/nx/portal-static/js/portal.all.js"></script>
1180
1181<!--
1182<th:block th:if="${churnZeroEnabled && !#strings.isEmpty(churnZeroAccountExternalId)}">
1183<script src="https://neonone.us1app.churnzero.net/churnzero.js"></script>
vendor: 1 bytes, line 1183
1183
1184<script type="application/javascript">
1185    var ChurnZero = ChurnZero || []
1186
1187    ChurnZero.push(['setAppKey', '1!MFdwDlKbVUgVpSRtljcqdO9tOYWtqDKqGG7trJX5JYwtE02']) // AppKey from ChurnZero
1188    ChurnZero.push(['setContact', '', ''])
1189</script>
1189
1190</th:block>
1191-->
1192
1193<script type="application/javascript">
1194    // Common JS Code
1195</script>
1195
1196
1197
1198
1199
1200<script>
1201    // CLP embed: notify parent on DOMContentLoaded for re-postMessage
1202    (function () {
1203        const urlParams = new URLSearchParams(window.location.search);
1204        const clpEmbedEnv = sessionStorage.getItem('clpEmbedEnv') === '1'
1205            || urlParams.get('clpEmbedEnv') === '1';
1206        if (!clpEmbedEnv) return;
1207        const embedIframeId = urlParams.get('embedIframeId') || sessionStorage.getItem('clpEmbedIframeId');
1208        const embedIframeDomainUrl = urlParams.get('embedIframeDomainUrl') || sessionStorage.getItem('clpEmbedDomainUrl');
1209        if (!embedIframeId || !embedIframeDomainUrl) return;
1210        window.addEventListener('DOMContentLoaded', function () {
1211            parent.postMessage({type: 'DOMContentLoaded', embedIframeId: embedIframeId}, embedIframeDomainUrl);
1212        });
1213    }());
1214</script>
1214
1215
1216<script src="/nx/portal-static/js/clp-embed-content.js"></script>
vendor: 66 bytes, lines 1216-1219
1216
1217
1218
1219<script async src="https://www.googletagmanager.com/gtag/js?id=
1219G-E5PVK7VDYC
vendor: 12 bytes, line 1219
1219"></script>
1220<script>
1221    
vendor: 139 bytes, lines 1221-1224
1221window.dataLayer = window.dataLayer || [];
1222    function gtag(){dataLayer.push(arguments);}
1223    gtag('js', new Date());
1224    gtag('config', '
1224G-E5PVK7VDYC
vendor: 4 bytes, line 1224
1224');
1225</script>
vendor: 72 bytes, lines 1225-1228
1225
1226
1227
1228  <script src="https://www.google.com/recaptcha/enterprise.js?render=
12286Lciqi8rAAAAAG87Wg7usbkvyHVTXylFPgeSyHkM
vendor: 24 bytes, line 1228
1228" async defer></script>
1229<script type="text/javascript">
1230    const appData = {
1231        showCreatePassword: false,
1232        showConfirmPassword: false,
1233        doLoginLoading: false,
1234        doCreateAcct: false,
1235        acctRules: {
1236            fname: {type: 'name', invalid: null, errMsg: 'First Name is required.'},
1237            lname: {type: 'name', invalid: null, errMsg: 'Last Name is required.'},
1238            email: {type: 'email', invalid: null, errMsg: 'Email Address is required.'},
1239            psd1: {type: 'psd1', invalid: null, errMsg: 'Create Password is required.'},
1240            psd2: {type: 'psd2', invalid: null, errMsg: 'Confirm Password is required.'}
1241        },
1242        acctData: {
1243            firstName: '',
1244            lastName: '',
1245            email: '',
1246            password: ''
1247        },
1248        gdprSettings: {},
1249        autoMergeOn: false,
1250        gdprValues: {
1251            email: false,
1252            phone: false,
1253            mail: false,
1254            sms: false,
1255            share: false,
1256            smsPhoneNum: '',
1257            smsPhoneInvalid: null,
1258            smsPhoneInvalidMsg: ''
1259        },
1260        loginName: '',
1261        password: '',
1262        persistCookie: '',
1263        loading: false,
1264        loginNameError: false,
1265        passwordError: false,
1266        globalError: '',
1267        showPassword: false,
1268        source: '',
1269        // OAuth Authorization Server handoff: literal "as" == OauthConstant.LOGIN_SOURCE / LoginSourceConstants.OAUTH_AS
1270        targetUrl:'',
1271        redirectUrl: '',
1272        fromNeonWebSite: false,
1273        embeddedLogin: false,
1274        activeTab: 0,
1275        googleReCaptchaEnabled: true,
1276        googleReCaptchaEnabled0: true,
1277        enableModernLogin: false,
1278        fromLogout: false,
1279        recaptchaResponse: null
1280    }
1281
1282    const appMethods = {
1283        tabChange(index){
1284            this.activeTab = index
1285        },
1286        doLogin: function () {
1287            if (this.enableModernLogin) {
1288                if (this.embeddedLogin || this.source === 'embedded') {
1289                    const loginName = btoa(this.loginName)
1290                    const loginPassword = btoa(this.password)
1291                    const url = '/nx/auth/authenticate?source=embedded'
1292                        + '&loginName=' + encodeURIComponent(loginName)
1293                        + '&loginPassword=' + encodeURIComponent(loginPassword)
1294                        + (this.persistCookie === true ? '&rememberMe=true' : '')
1295                    window.location.href = url
1296                    return
1297                }
1298                // Align with legacy signIn.do: password login does not submit/verify reCAPTCHA.
1299                this.loading = true
1300                this.globalError = ''
1301                // Modern resolves the post-login redirect server-side, so the active tab has to
1302                // travel with the request (the legacy branch below picks /p2p/overview locally).
1303                // source=as owns targetUrl — ai= is appended to it — so never overwrite it there.
1304                let target = this.targetUrl || this.redirectUrl || ''
1305                if (!target && !this.source && this.activeTab === 1) {
1306                    target = '/p2p/overview'
1307                }
1308                let url = '/nx/auth/authenticate?targetUrl=' + encodeURIComponent(target);
1309                if (this.source) {
1310                    url += '&source=' + encodeURIComponent(this.source);
1311                }
1312                axios.post(url, {
1313                    loginName: this.loginName,
1314                    password: this.password,
1315                    rememberMe: this.persistCookie === true,
1316                    requestId: 'c962f264-c3df-4979-8b38-1044a8bda376',
1317                    source: this.source || ''
1318                }).then((res) => {
1319                    const body = res.data || {};
1320                    if (body.success && body.data) {
1321                        window.location = body.data.redirect;
1322                    } else {
1323                        this.loading = false
1324                        const msgs = body.messages || [];
1325                        this.globalError = (msgs[0] && msgs[0].message) || 'Login Name/Password is invali
1325d';
1326                    }
1327                }).catch(() => {
1328                    this.loading = false
1329                    this.globalError = 'Login Name/Password is invalid';
1330                });
1331                return
1332            }
1333            let $loginData = {
1334                loginName: this.loginName,
1335                loginPassword: this.password,
1336                persistCookie: this.persistCookie === true ? 'yes' : this.persistCookie,
1337                ajaxLogin: true,
1338                source: this.fromNeonWebSite? 'duda' : this.source,
1339                targetUrl: this.targetUrl || this.redirectUrl,
1340                requestId: 'c962f264-c3df-4979-8b38-1044a8bda376'
1341            }
1342            this.loading = true
1343            this.globalError = ''
1344            axios.post('/np/security/signIn.do', Qs.stringify($loginData)).then(response => {
1345                let $data = response.data
1346                if ($data.status === 'success') {
1347                    if (this.loginName === 'admin') {
1348                        window.location = '/np/admin/content/contentList.do'
1349                    } else if ($data.succeedRedirectPath) {
1350                        window.location = $data.succeedRedirectPath
1351                    } else if(this.activeTab === 1) {
1352                        window.location = '/p2p/overview'
1353                    } else {
1354                        window.location = '/constituent/dashboard'
1355                    }
1356                } else {
1357                    this.loading = false
1358                    this.globalError = $data.errors[0]
1359                }
1360            })
1361        },
1362        login: function () {
1363            let validatePass = true
1364            if (Validator.required.check(this.loginName)) {
1365                this.loginNameError = true
1366                validatePass = false
1367            }
1368            if (Validator.required.check(this.password)) {
1369                this.passwordError = true
1370                validatePass = false
1371            }
1372            if (!validatePass) {
1373                return
1374            }
1375            this.passwordError = false
1376            this.loginNameError = false
1377            this.doLogin()
1378        },
1379
1380        showPsd: function ($show) {
1381            if ($show) {
1382                this.showPassword = true
1383                document.querySelector('#password').type = 'text'
1384            } else {
1385                this.showPassword = false
1386                document.querySelector('#password').type = 'password'
1387            }
1388        },
1389        fn_openAuthWin: function (src) {
1390            if (this.enableModernLogin) {
1391                // Same sources as password login (targetUrl / redirectUrl), plus live ?redirect=
1392                // from the address bar — Thymeleaf injection alone has been unreliable for social.
1393                const params = new URLSearchParams(window.location.search || '');
1394                let path = (params.get('redirect')
1395                    || params.get('target')
1396                    || this.targetUrl
1397                    || this.redirectUrl
1398                    || '').trim();
1399                if (path) {
1400                    try {
1401                        path = decodeURIComponent(path);
1402                    } catch (e) { /* keep raw */ }
1403                }
1404                if (!path) {
1405                    path = window.location.pathname + window.location.search;
1406                    const pathOnly = (window.location.pathname || '').replace(/\/+$/, '') || '/';
1407                    if (pathOnly === '/login' || pathOnly === '/nx/login' || /\/login$/.test(pathOnly)) {
1408                        path = '/';
1409                    }
1410                }
1411                neonSocialLogin(src, {
1412                    targetUrl: path,
1413                    orgId: this.orgId,
1414                    // Literal "as" == OauthConstant.LOGIN_SOURCE / LoginSourceConstants.OAUTH_AS
1415                    custom: this.source === 'as' ? 'as' : undefined
1416                });
1417                return;
1418            }
1419            const path = "/np/clients/" + this.orgId + "/snOpenAuthWin.do?";
1420            const param = "src=" + src + "&da=login"+ (this.fromNeonWebSite?'&custom=duda':'');
1421            const url = path + param;
1422            const size = 'width=750,height=450,top=230,left=370,toolbar=no';
1423            window.location.href = url;
1424        },
1425        enterClick: function (e) {
1426            if (e.keyCode === 13) {
1427                if (e.target.closest('.neonfront_input')) {
1428                    this.login()
1429                }
1430                if (e.target.closest('.form-check-input')) {
1431                    this.login()
1432                }
1433            }
1434        },
1435        listenEnterKey: function () {
1436            document.addEventListener('keydown', this.enterClick)
1437        },
1438        createFundraiser: function () {
1439            this.doLoginLoading = false
1440            this.doCreateAcct = false
1441            this.p2pLoginErrorMessages = []
1442            this.acctRules.fname.invalid = null
1443            this.acctRules.lname.invalid = null
1444            this.acctRules.email.invalid = null
1445            this.acctRules.psd1.invalid = null
1446            this.acctRules.psd2.invalid = null
1447            this.$nextTick(() => {
1448                const modalElement = this.$refs.createP2pAcctModal;
1449                document.body.appendChild(modalElement);
1450                const modal = new bootstrap.Modal(this.$refs.createP2pAcctModal)
1451                modal.show()
1452            })
1453        },
1454        invisibleReCaptchaInit: function () {
1455            window.ReCaptchaLoaded = this.reCaptchaCallback;
1456            let tag = document.createElement("div")
1457            tag.className = 'g-recaptcha'
1458            document.body.appendChild(tag);
1459            let timer = setInterval(() => {
1460                if(window.grecaptcha && grecaptcha.enterprise && typeof window.grecaptcha.enterprise.render === 'function'){
1461                    window.grecaptcha.enterprise.render(tag,{
1462                        sitekey: '6Lciqi8rAAAAAG87Wg7usbkvyHVTXylFPgeSyHkM',
1463                        size: 'invisible'
1464                    });
1465                    clearInterval(timer);
1466                }
1467            },500);
1468        },
1469        reCaptchaCallback: function ($token) {
1470            this.recaptchaResponse = $token
1471            if(window.grecaptcha && window.grecaptcha.enterprise) {
1472                window.grecaptcha.enterprise.reset()
1473            }
1474            this.doP2pCreateAccountSendToServer()
1475        },
1476        validateReCaptcha:function () {
1477            if(window.grecaptcha && window.grecaptcha.enterprise) {
1478                this.recaptchaResponse = null
1479                window.grecaptcha.enterprise.execute().then(($token) => {
1480                    this.reCaptchaCallback($token)
1481                })
1482            } else {
1483                this.doP2pCreateAccountSendToServer()
1484            }
1485        },
1486        doP2pCreateAccount: function () {
1487            this.doCreateAcct = true
1488            this.validateReCaptcha()
1489        },
1490        doP2pCreateAccountSendToServer: function () {
1491            let formData = new FormData();
1492            formData.append('firstName', this.acctData.firstName.trim());
1493            formData.append('lastName', this.acctData.lastName.trim());
1494            formData.append('email', this.acctData.email.trim());
1495            formData.append('password', this.acctData.password.trim());
1496            formData.append('recaptchaResponse', this.recaptchaResponse);
1497            if (this.gdprSettings.profileEnabled) {
1498                formData.append('gdpr.email', this.gdprValues.email);
1499                formData.append('gdpr.phone', this.gdprValues.phone);
1500                formData.append('gdpr.mail', this.gdprValues.mail);
1501                formData.append('gdpr.sms', this.gdprValues.sms);
1502                formData.append('gdpr.share', this.gdprValues.share);
1503                formData.append('gdpr.smsPhoneNum', this.gdprValues.smsPhoneNum);
1504            }
1505            axios.post('/nx/portal/p2p-data/create-account', formData, {}).then(response => {
1506                if (response.data.success) {
1507                    this.doP2pLogin(this.acctData.email, this.acctData.password)
1508                } else {
1509                    alert(response.data.messages[0].message);
1510                    this.doCreateAcct = false
1511                }
1512            }).catch(error => {
1513                console.log(error)
1514                this.doCreateAcct = false
1515                alert('Create account failed, please try again.')
1516            })
1517        },
1518        doP2pLogin: function (name, password) {
1519            this.doLoginLoading = true
1520            if (this.enableModernLogin) {
1521                // Align with legacy: password login does not submit/verify reCAPTCHA.
1522                let url = '/nx/auth/authenticate?targetUrl=' + encodeURIComponent('/p2p/overview')
1523                axios.post(url, {
1524                    loginName: name,
1525                    password: password,
1526                    requestId: 'c962f264-c3df-4979-8b38-1044a8bda376'
1527                }).then((res) => {
1528                    const body = res.data || {};
1529                    if (body.success && body.data) {
1530                        window.location.href = body.data.redirect;
1531                    } else {
1532                        const msgs = body.messages || [];
1533                        this.globalError = (msgs[0] && msgs[0].message) || 'Login failed';
1534                        alert(this.globalError)
1535                    }
1536                    this.doCreateAcct = false
1537                    this.doLoginLoading = false
1538                }).catch((error) => {
1539                    console.error(error.response)
1540                    alert('Login error: ' + JSON.stringify(error.response))
1541                    this.doLoginLoading = false
1542                })
1543                return
1544            }
1545            let params = {
1546                loginName: name,
1547                loginPassword: password,
1548                orgId: 'chambermusicamerica',
1549                ajaxLogin: true,
1550                requestId: 'c962f264-c3df-4979-8b38-1044a8bda376'
1551            }
1552            axios.post('/np/security/encouragedSignIn.do', Qs.stringify(params)).then(response => {
1553                let result = response.data
1554                if (result.status === 'success') {
1555                    window.location.href = 'https://' + window.location.host + '/p2p/overview'
1556                } else {
1557                    alert('Login error: ' + JSON.stringify(result.errors))
1558                }
1559                this.doCreateAcct = false
1560                this.doLoginLoading = false
1561            }).catch(error => {
1562                console.error(error.response)
1563                alert('Login error: ' + JSON.stringify(error.response))
1564                this.doLoginLoading = false
1565            })
1566        },
1567        checkAcctFieldVal: function ($event, rule) {
1568            let val = $event.target.value.trim();
1569            if (rule.type === 'name' && !val) {
1570                rule.invalid = true
1571            } else if (rule.type === 'email' && !val) {
1572                rule.errMsg = 'Email Address is required.'
1573                rule.invalid = true
1574            } else if (rule.type === 'email' && Validator['email'].check(val)) {
1575                rule.errMsg = 'Email Address format is invalid.'
1576                rule.invalid = true
1577            } else if (rule.type === 'psd1' && !val) {
1578                rule.errMsg = 'Create Password is required.'
1579                rule.invalid = true
1580            } else if (rule.type === 'psd1' && Validator.password.check(val)) {
1581                rule.errMsg = 'Your password requires at least 8 characters, including at least one number. Spaces are not allowed.'
1582                rule.invalid = true
1583            } else if (rule.type === 'psd2') {
1584                if (val) {
1585                    let val1 = document.querySelector('#createPassword').value
1586                    rule.errMsg = 'Confirm Password does not match.'
1587                    rule.invalid = (val !== val1.trim())
1588                } else {
1589                    rule.errMsg = 'Confirm Password is required.'
1590                    rule.invalid = true
1591                }
1592            } else {
1593                rule.invalid = false
1594            }
1595        },
1596        showConfirmPsd: function ($show) {
1597            if ($show) {
1598                this.showConfirmPassword = true
1599                document.querySelector('#confirmPassword').type = 'text'
1600            } else {
1601                this.showConfirmPassword = false
1602                document.querySelector('#confirmPassword').type = 'password'
1603            }
1604        },
1605        showCreatePsd: function ($show) {
1606            if ($show) {
1607                this.showCreatePassword = true
1608                document.querySelector('#createPassword').type = 'text'
1609            } else {
1610                this.showCreatePassword = false
1611                document.querySelector('#createPassword').type = 'password'
1612            }
1613        },
1614        loadGdprSetting: function () {
1615            let previewMode = false
1616            if (previewMode) {
1617                return;
1618            }
1619            axios.get('/nx/portal/p2p-data/login-settings').then(response => {
1620                if (response.data.success) {
1621                    this.gdprSettings = response.data.data.gdprSettings
1622                    this.autoMergeOn = response.data.data.autoMergeOn
1623                }
1624            }).catch(error => {
1625                alert('Load GDPR settings failed, please try again.')
1626            })
1627        },
1628        checkGdprSmsPhoneNum: function ($event) {
1629            let val = $event.target.value.trim();
1630            if (!val) {
1631                this.gdprValues.smsPhoneInvalid = true
1632                this.gdprValues.smsPhoneInvalidMsg = 'The SMS/MMS Number is required.'
1633                return
1634            }
1635            let usPhoneReg = /^(\+?1[-\s]?)?\(?[2-9]\d{2}\)?[-\s]?[2-9](?!11)\d{2}[-\s]?\d{4}$/
1636            if (!usPhoneReg.test(val.replace(/\s+/g, ''))) {
1637                this.gdprValues.smsPhoneInvalid = true
1638                if (val.replace(/[\s\-()]/g, '').match(/^[^1]\d{10}$/)) {
1639                    this.gdprValues.smsPhoneInvalidMsg = '11-digit numbers must begin with the country code 1 (US/Canada).'
1640                } else {
1641                    this.gdprValues.smsPhoneInvalidMsg = 'Please enter a valid 10 or 11 digit mobile number.'
1642                }
1643                return
1644            }
1645            this.gdprValues.smsPhoneInvalid = false
1646            this.gdprValues.smsPhoneInvalidMsg = ''
1647        }
1648    }
1649
1650    const appComputed = {
1651        checkCreateAcctField: function () {
1652            return !(this.acctRules.fname.invalid === false && this.acctRules.lname.invalid === false
1653                && this.acctRules.email.invalid === false && this.acctRules.psd1.invalid === false
1654                && this.acctRules.psd2.invalid === false && (!this.gdprValues.sms || (this.gdprValues.smsPhoneNum && this.gdprValues.smsPhoneInvalid === false)))
1655        },
1656    }
1657
1658    const appWatch = {}
1659
1660    const appConfig = {
1661        data: function () {
1662            return appData
1663        },
1664        methods: appMethods,
1665        computed: appComputed,
1666        watch: appWatch,
1667        created: function() {
1668            const params = new URLSearchParams(window.location.search);
1669            const activeTab = params.get('activeTab');
1670            if(activeTab === '1') {
1671                this.activeTab = 1
1672            }
1673        },
1674        mounted: function () {
1675            if(this.googleReCaptchaEnabled || this.googleReCaptchaEnabled0) {
1676                this.invisibleReCaptchaInit()
1677            }
1678            this.listenEnterKey()
1679            this.loadGdprSetting()
1680        }
1681    }
1682</script>
1682
1683
1684
1685
1686<script type="application/javascript">
1687  appData.loggedIn = false
1688  appData.previewMode = false
1689  appData.orgId = 'chambermusicamerica'
1690  appData.currentAccountId = 
1691  appData.accountBasicInfo = ''
1692  appData.profilePhoto = {
1693    url: '',
1694    approved: false
1695  }
1696  appData.showMenu = false
1697  appData.formCurrency= '$'
1698  appData.formCurrencyCode= 'USD'
1699  appData.theme = window._neonThemeData || {}
1700  appMethods.formatAmount = function (amount) {
1701    return Formatter.numberFormat(amount, 2, this.formCurrency)
1702  }
1703  appMethods.showP2pConfirmModal = function() {
1704      bootstrap.Modal.getInstance(document.getElementById("goP2pConfirmModal")).show()
1705  }
1706  appMethods.cancelP2pConfirmModal = function(){
1707      bootstrap.Modal.getInstance(document.getElementById("goP2pConfirmModal")).hide()
1708  }
1709  appMethods.confirmP2pConfirmModal = function(){
1710      window.location.href='/p2p/overview'
1711  }
1712  appMethods.getSocialMediaIcon = function (name, style) {
1713    if (typeof getSocialIconURL === 'function') return getSocialIconURL(name, style)
1714    // legacy fallback
1715    return style === 'D'
1716      ? '/nx/static/img/icons/social-media/darkicons/' + name + '.svg'
1717      : '/nx/static/img/icons/social-media/lighticons/' + name + '.svg'
1718  }
1719  // Add interceptor for portal page
1720  axios.interceptors.request.use(function (config) {
1721    if(appData.previewMode){
1722      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')
1723    }
1724    return config
1725  }, function (error) {
1726    // ...
1727    return Promise.reject(error)
1728  });
1729
1730  (function (){
1731      if(window.location.href.includes('/constituent')){
1732          window.addEventListener( "pageshow", function ( $event ) {
1733              const performanceNavigationTiming = performance.getEntriesByType('navigation')[0];
1734              // performanceNavigationTiming.type === 'back_forward' for firefox
1735              const historyTraversal = $event.persisted || performanceNavigationTiming.type === 'back_forward';
1736              if ( historyTraversal ) {
1737                  window.location.reload();
1738              }
1739          });
1740      }
1741  })();
1742  const originalMounted = appConfig.mounted
1743  appConfig.mounted = function() {
1744      const result = originalMounted.apply(this);
1745      const $dom = document.getElementById("goP2pConfirmModal")
1746      if($dom) {
1747          new bootstrap.Modal($dom)
1748      }
1749      return result
1750  }
1751  appWatch.theme = {
1752    handler: function(newVal) {
1753      if (typeof applyThemeCSSVars === 'function') applyThemeCSSVars(newVal)
1754    },
1755    deep: true
1756  }
1757  const vueApp = Vue.createApp(appConfig)
1758  vueApp.config.errorHandler = function (err, vm, info) {
1759    if (handleVueJSError) {
1760      handleVueJSError(err, vm, info)
1761    }
1762    console.log(err)
1763  }
1764  vueApp.component('neon-multiselect', window.NeonMultiselect)
1765  vueApp.component('neon-search-box', window.NeonSearchBox)
1766  vueApp.component('neon-fileupload', window.NeonFileupload)
1767  vueApp.component('neon-advancedsearch', window.NeonAdvancedsearch)
1768  vueApp.component('neon-select', window.NeonSelect)
1769  vueApp.component('neon-img-cutter', window.NeonImgCutter)
1770  vueApp.component('neon-pagination', window.NeonPagination)
1771  vueApp.component('neon-pagination-plus', window.NeonPaginationPlus)
1772  vueApp.component('neon-share-modal', window.NeonShareModal)
1773  vueApp.component('neon-search-select', window.NeonSearchSelect)
1774  vueApp.component('neon-search-location', window.NeonSearchLocation)
1775  vueApp.component('neon-input', window.NeonInput)
1776  const app = vueApp.mount('#app')
1777  window.__neonApp = app
1778</script>
1778
1779
1780
1781
1782  
1782<script type="text/javascript">
1783    function setSliderBar(func , wait , immediate) {
1784        let timeout
1785        return function () {
1786            let context = this, args = arguments
1787            let later = function() {
1788                timeout = null
1789                if (!immediate) func.apply(context, args)
1790            }
1791            let callNow = immediate & !timeout
1792            clearTimeout(timeout);
1793            timeout = setTimeout(later, wait);
1794            if (callNow) func.apply(context, args)
1795        }
1796    }
1797    function fixSliderBar() {
1798        let maxWidth = document.body.clientWidth
1799        const leftBar = document.getElementById("left-nav")
1800        const content = document.getElementById("content")
1801        const rightContent = document.getElementById("right-content")
1802        const footContent = document.getElementById("footer-content")
1803        const fixedTitle = document.getElementById("fixed-title")
1804        const flexRow = rightContent.parentNode
1805        const clientHeight = window.innerHeight
1806        if (maxWidth >= 992) {
1807            let isClpEmbed = false
1808            try {
1809                isClpEmbed = sessionStorage.getItem('clpEmbedEnv') === '1'
1810                    || new URLSearchParams(window.location.search).get('clpEmbedEnv') === '1'
1811            } catch (e) {
1812            }
1813            if (isClpEmbed) {
1814                leftBar.style.position = 'static'
1815                leftBar.style.top = ''
1816                leftBar.style.bottom = ''
1817                leftBar.style.height = ''
1818                leftBar.style.minHeight = ''
1819                leftBar.style.left = ''
1820                leftBar.style.width = ''
1821                flexRow.style.paddingLeft = ''
1822                rightContent.style.marginLeft = ''
1823                rightContent.style.minHeight = '0'
1824                try {
1825                    fixedTitle.style.display = 'none'
1826                } catch (e) {
1827                }
1828                leftBar.style.maxHeight = ''
1829                leftBar.style.overflow = ''
1830                const contentHeight = rightContent.offsetHeight
1831                if (contentHeight > 0 && leftBar.scrollHeight > contentHeight) {
1832                    leftBar.style.maxHeight = contentHeight + 'px'
1833                    leftBar.style.overflow = 'hidden auto'
1834                }
1835                return
1836            }
1837            if (leftBar.getBoundingClientRect().top <= 0) {
1838                //float in top on footer
1839                const footerHidden = footContent.offsetHeight === 0
1840                let heightVary = footContent.getBoundingClientRect().bottom - clientHeight
1841                let varyHeight = footContent.offsetHeight - heightVary
1842                if (!footerHidden && varyHeight >= 0 && heightVary <= footContent.offsetHeight) {
1843                    //footer show
1844                    if(!leftBar.classList.contains('neonfront-p2p-portal-left-nav')) {
1845                        leftBar.style.bottom = varyHeight + 'px'
1846                        leftBar.style.height = (clientHeight - varyHeight) + 'px'
1847                    } else {
1848                        leftBar.style.bottom = '0px'
1849                        leftBar.style.height = clientHeight + 'px'
1850                    }
1851                    rightContent.style.minHeight = (clientHeight - varyHeight) + 'px'
1852                    leftBar.style.overflow = 'hidden auto'
1853                } else {
1854                    // footer hide
1855                    leftBar.style.height = clientHeight + 'px'
1856                    leftBar.style.bottom = ''
1857                    rightContent.style.minHeight = clientHeight + 'px'
1858                    leftBar.style.overflow = 'hidden auto'
1859                }
1860                if (leftBar.style.position !== 'fixed') {
1861                    const leftBarWidth = leftBar.offsetWidth || 260
1862                    leftBar.style.position = 'fixed'
1863                    leftBar.style.top = '0'
1864                    leftBar.style.width = leftBarWidth + 'px'
1865                    flexRow.style.paddingLeft = leftBarWidth + 'px'
1866                    rightContent.style.marginLeft = ''
1867                }
1868                leftBar.style.left = flexRow.getBoundingClientRect().left + 'px'
1869                try {
1870                    fixedTitle.style.display = ''
1871                } catch (e) {
1872                }
1873            }
1874            if (content.getBoundingClientRect().top > 0) {
1875                //roll back
1876                leftBar.style.position = 'static'
1877                leftBar.style.top = ''
1878                leftBar.style.bottom = ''
1879                leftBar.style.left = ''
1880                leftBar.style.width = ''
1881                flexRow.style.paddingLeft = ''
1882                rightContent.style.marginLeft = ''
1883                leftBar.style.minHeight = ''
1884                rightContent.style.minHeight = '0'
1885                try {
1886                    fixedTitle.style.display = 'none'
1887                } catch (e) {
1888                }
1889            }
1890        } else {
1891            //roll back
1892            leftBar.style.position = 'static'
1893            leftBar.style.top = ''
1894            leftBar.style.bottom = ''
1895            leftBar.style.left = ''
1896            leftBar.style.width = ''
1897            flexRow.style.paddingLeft = ''
1898            rightContent.style.marginLeft = ''
1899            leftBar.style.minHeight = ''
1900            rightContent.style.minHeight = '0'
1901            try {
1902                fixedTitle.style.display = 'none'
1903            } catch (e) {
1904            }
1905        }
1906    }
1907    const preFix = setSliderBar(function() {
1908        fixSliderBar();
1909    }, 4);
1910    try {
1911        const leftBar = document.getElementById("left-nav")
1912        leftBar.style.maxHeight = window.innerHeight + 'px'
1913        leftBar.style.overflow = 'hidden auto'
1914        window.addEventListener('scroll',preFix)
1915        window.addEventListener('resize',preFix)
1916    } catch (ignore) {
1917        //without sidebar not need fix
1918    }
1919</script>
1919
1920
1921
1922
1923</body>
1924</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.