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