1<!DOCTYPE html> 2<html lang="en" id="ng-app" ng-app="ziftAppModule"> 3<head> 4 5 <title ng-bind="(pageTitle ? pageTitle + ' - ' : '') + (siteBrand && siteBrand.marcomName ? siteBrand.marcomName : isPartnerPortalDomain ? '' : 'Partner portal')"></title> 6 7 <meta charset="utf-8"> 8 <meta name="description" content="Unifyr" /> 9 <meta name="author" content="Unifyr" /> 10 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> 11 <!-- Fallback cache control if server/CDN-level headers are not configured --> 12 <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate"> 13 <meta http-equiv="Pragma" content="no-cache"> 14 <meta http-equiv="Expires" content="0"> 15 <link rel="shortcut icon" brand-favicon /> 16 <!-- Safari --> 17 <meta property="og:image" og-image-logo> 18 <link rel="mask-icon" ng-href="{{getFaviconUrl()}}" color="#000000"> 19 <link rel="apple-touch-icon" ng-href="{{getFaviconUrl()}}"> 20 <link rel="apple-touch-icon" sizes="152x152" ng-href="{{getFaviconUrl()}}"> 21 <link rel="apple-touch-icon" sizes="180x180" ng-href="{{getFaviconUrl()}}"> 22 <link rel="apple-touch-icon" sizes="167x167" ng-href="{{getFaviconUrl()}}"> 23 <link rel="apple-touch-startup-image" ng-href="{{getFaviconUrl()}}"> 24 <!-- Safari --> 25 <!-- WOW-5179 / SUPP-4670: the canonical href is set by the inline script below, NOT by an Angular 26 binding. Crawlers read the raw <head> before Angular runs, so an Angular placeholder here was 27 indexed literally as a relative URL that 404s. Hash-routed views are all one page to crawlers, 28 so the canonical is the portal URL without query string or #/route. --> 29 <link rel="canonical" /> 30
30<script> 31 (function () { 32 var canonical = document.querySelector('link[rel="canonical"]'); 33 if (canonical) { 34 canonical.setAttribute('href', window.location.origin + window.location.pathname); 35 } 36 })(); 37 </script>
37 38 <!-- no clickjacking --> 39
39<script>if (top != self && top.location.hostname != self.location.hostname) top.location=location</script>
39 40 <!-- WOW-4574 / ZLS-2859: expose URL for portal CMS SSO deep-links. 41 DO NOT REMOVE â CMS-authored content reads this global. --> 42
42<script>window.currentUrl = window.location.href;</script>
42 43
43<script defer src="js/config.js?rel=1790958502168"></script>
43 44 45 <!-- FontAwesome Pro 7.2.0 self-hosted (SUPP-5535 / DEVOPS-6558) --> 46 <!-- Preconnect to both possible FA domains (browser ignores unused one) --> 47 <link rel="preconnect" href="https://fontawesome.unifyr.com" crossorigin /> 48 <link rel="preconnect" href="https://fontawesome.ziftone.com" crossorigin /> 49
49<script> 50 (function() { 51 var faHost = window.location.hostname.includes('unifyr') 52 ? 'fontawesome.unifyr.com' 53 : 'fontawesome.ziftone.com'; 54 var kitId = 'af436fe6a2'; 55 var baseUrl = 'https://' + faHost + '/' + kitId; 56 57 // Preload critical font files (starts download immediately, before CSS is parsed) 58 var fonts = [ 59 '/webfonts/fa-solid-900.woff2', 60 '/webfonts/fa-regular-400.woff2', 61 '/webfonts/fa-brands-400.woff2', 62 '/webfonts/fa-v4compatibility.woff2' 63 ]; 64 fonts.forEach(function(f) { 65 var preload = document.createElement('link'); 66 preload.rel = 'preload'; 67 preload.as = 'font'; 68 preload.type = 'font/woff2'; 69 preload.href = baseUrl + f; 70 preload.crossOrigin = 'anonymous'; 71 document.head.appendChild(preload); 72 }); 73 74 // Load stylesheets with SRI 75 var sheets = [ 76 { href: baseUrl + '/css/all.min.css', integrity: 'sha384-DaaNrXcuLeA/PSKXCil6CDOeHUkcIViXIN5xuIn27doItvbpRV1OGt0Uebt+4Rdm' }, 77 { href: baseUrl + '/css/v4-shims.min.css', integrity: 'sha384-uCn24gWD1LsuzrwWwQXd9XY+E0d0o8fIfNWRNq8xyTHX+yL0PYi++LXltsu0jvz5' } 78 ]; 79 sheets.forEach(function(s) { 80 var link = document.createElement('link'); 81 link.rel = 'stylesheet'; 82 link.href = s.href; 83 link.integrity = s.integrity; 84 link.crossOrigin = 'anonymous'; 85 document.head.appendChild(link); 86 }); 87 })(); 88 </script>
88 89 90 <!-- Bootstrap 5 bundle (includes Popper.js) --> 91
91<script defer src="lib/bootstrap/bootstrap.bundle.min.js?rel=1791145067968"></script>
91 92
92<script> 93 // Disable Bootstrap 5's native Offcanvas click handling to prevent conflict 94 // with the jQuery offcanvas plugin (loaded later via app.js) which provides 95 // content-sliding behavior instead of Bootstrap 5's overlay behavior 96 document.addEventListener('DOMContentLoaded', function() { 97 if (typeof bootstrap !== 'undefined' && bootstrap.Offcanvas) { 98 bootstrap.Offcanvas.getOrCreateInstance = function() { return { toggle: function() {} }; }; 99 bootstrap.Offcanvas.getInstance = function() { return null; }; 100 } 101 }); 102 </script>
102 103 104 <!-- Google Tag Manager --> 105
105<script id="google-tag-manager-script"> 106 // Google Tag Manager script will be loaded dynamically after configuration is ready 107 </script>
107 108 <!-- End Google Tag Manager --> 109 110 <!-- many scripts previously imported by GTM now injected via app.js --> 111 <!-- Bootstrap loaded directly above (was previously loaded via GTM) --> 112 113 <!-- Always loads the bridge - feature flag check is done in UnifiedCampaignsController --> 114
114<script> 115 (function() { 116 // Derive the unifyr-r URL from the current hostname 117 // Examples: 118 // hp.local.ziftone.com -> hp.local.unifyr.com:3000 119 // hp.sandbox.ziftone.com -> hp.sandbox.unifyr.com 120 // hp.staging.ziftone.com -> hp.staging.unifyr.com 121 // hp.ziftone.com -> hp.unifyr.com 122 var currentHost = window.location.hostname; 123 var bridgeBaseUrl; 124 125 if (currentHost.includes('local')) { 126 // Local development - unifyr-r runs on port 3000 127 128 var unifyrHost = currentHost.replace('ziftone.com', 'unifyr.com'); 129 bridgeBaseUrl = window.location.protocol + `//${unifyrHost}:3000`; 130 } else { 131 132 133 // Chris is using sbox.zift123.com bucket right now. 134 // This bit of code needs to change depending on which s3 bucket we decide to use 135 136 // Replace ziftone.com with unifyr.com for all other environments 137 // var unifyrHost = currentHost.replace('ziftone.com', 'unifyr.com'); 138 bridgeBaseUrl = window.location.protocol + '//' + currentHost; 139 } 140 141 var cacheBuster = '?v=' + new Date().getTime(); 142 var cssUrl = bridgeBaseUrl + '/css-bridge/unifyr-bridge-style.css' + cacheBuster; 143 var jsUrl = bridgeBaseUrl + '/js-bridge/unifyr-bridge.js' + cacheBuster; 144 145 // Load CSS via dynamic link element (avoids document.write which blocks parsing) 146 var link = document.createElement('link'); 147 link.rel = 'stylesheet'; 148 link.href = cssUrl; 149 document.head.appendChild(link); 150 151 // Load JS with error handling and timeout 152 var script = document.createElement('script'); 153 script.type = 'module'; 154 script.src = jsUrl; 155 script.async = false; 156 157 // Track if bridge loaded successfully 158 window.unifyrBridgeLoadFailed = false; 159 160 // Set a timeout for bridge loading (5 seconds) 161 var loadTimeout = setTimeout(function() { 162 if (!window.UnifyrBridge) { 163 window.unifyrBridgeLoadFailed = true; 164 } 165 }, 5000); 166 167 script.onload = function() { 168 clearTimeout(loadTimeout); 169 }; 170 171 script.onerror = function() { 172 clearTimeout(loadTimeout); 173 window.unifyrBridgeLoadFailed = true; 174 }; 175 176 document.head.appendChild(script); 177 })(); 178 </script>
178 179 180
180<script src="/rest/z123clients/portal/jquerylibs"></script>
180 181 182 <!-- Browser Update JS - previously imported by GTM but I don't think it was firing - Paul says if we weren't seeing it before don't show it hence commented out for now --> 183 <!-- 184
184<script> 185 var $buoop = {reminder: 24,reminderClosed: 150}; 186 function $buo_f(){ 187 var e = document.createElement("script"); 188 e.src = "//browser-update.org/update.min.js?rel=1791145067968"; 189 document.body.appendChild(e); 190 }; 191 try {document.addEventListener("DOMContentLoaded", $buo_f,false)} 192 catch(e){window.attachEvent("onload", $buo_f)} 193 </script>
193 194 --> 195 196 <!-- ?v= must be updated manually when these files change --> 197
197<script src="../common/lib/third-party.js?v=20251222"></script>
197 <!-- to use the constituent parts esp for performance profiling replace this line with code in third-party-js-for-dev.html --> 198 <!-- Looker Embed SDK (UMD, exposes window.LookerEmbedSDK) for cookieless advanced reporting embed (WOW-4599) --> 199
199<script src="../common/lib/looker-embed-sdk.min.js?v=20260716"></script>
199 200 <link href="css/third-party.min.css?v=20260506" rel="stylesheet" /> 201 <!-- 202 I removed third-party.min.css from cachebreaker in gruntfile.js so the suite (inc bootstrap) are handled consistently with v= cache-busting not rel= 203 we use v= for rarely-updated third party libraries but we could consider adding them all to the gruntfile in case the eng forgets to update the v 204 --> 205 206 <!-- Bootstrap 5 overrides --> 207 <link href="css/bootstrap-5-overrides.css?rel=1791145067968" rel="stylesheet" /> 208 209 <link rel="stylesheet" href="dist/css/base.min.8774b08b.css?rel=1791145067968"/> 210 211 <style> 212 .feedback-container { margin-top:15px; } 213 .feedback-link { margin-left:20px; margin-top:-3px; } 214 .messages-header { padding-top:10px; } 215 </style> 216 <link rel="stylesheet" type="text/css" href="../common/css/toast.css?rel=1791145067968"> 217 <!--[if lt IE 9]> 218
218<script src="lib/bootstrap/html5shiv.js?rel=1791145067968"></script>
218 219
219<script src="lib/bootstrap/respond.min.js?rel=1791145067968"></script>
219 220 <![endif]--> 221 222 <!--setting the default document domain--> 223 224
224<script type="text/javascript"> 225 226 var currentHost = location.host; 227 var targetDomain = "zift123.com"; 228 if (currentHost.indexOf(targetDomain) === -1) { 229 targetDomain = currentHost; 230 } 231 document.domain = targetDomain; 232 233 // during the first round of testing it would be as well that everyone sees jMigrate console log messages 234 // once we are happy then we can uncomment the line below so that converted clients don't see them and we only see them if you are testing a site using jQuery 3.5 via the url param 235 jQuery.migrateMute = !hasJquery35UrlParam; 236 </script>
236 237
237<script defer src="https://checkout.stripe.com/checkout.js?rel=1791145067968" integrity="sha384-wdG19eCFQhAyeABAI6CVPNs72Y7xS4SqrdYIquAWdZZ3Na8ZXk5bJEriDCGRUJ65" crossorigin="anonymous"></script>
237 238
238<script defer src="https://cdn.auth0.com/js/auth0-spa-js/2.0/auth0-spa-js.production.js?rel=1791145067968" integrity="sha384-zVM3YoOThNVWy1csEJq5Hbk+eeS0XrkL2lCADHb8HnFew6raF21cbOCcMgedS7HO" crossorigin="anonymous"></script>
238 239
239<script defer src="https://cdn.jsdelivr.net/npm/@brightcove/[email protected]/dist/brightcove-player-loader.min.js?rel=1791145067968" integrity="sha384-OCprk9rOs2ZPvq5NZWczLmxlNCnbZD+Per8SPuQl6ah1KY7GUsiZexX7Vb23UUs9" crossorigin="anonymous"></script>
239 240 241 <link rel="stylesheet" type="text/css" href="css/headless.css?rel=1791145067968" ng-if="headless"> 242</head> 243<body ng-style="bodyStyle" ng-class="{'no-vertical-scroll' : isBeeFreeFullPage(), 'hide-body': isUnifyrLoginPageLoading()}" modal-close-handler> 244<!-- Toast Container --> 245<div class="toast-wrapper"> 246 <div class="container container-main"> 247 <div id="toast-container" class="toast-top-right"></div> 248 </div> 249</div> 250<!-- Google Tag Manager (noscript) --> 251<noscript id="google-tag-manager-noscript"> 252 <!-- Google Tag Manager noscript will be loaded dynamically after configuration is ready --> 253</noscript> 254<!-- End Google Tag Manager (noscript) --> 255 256 257<div brand-styles site-brand="siteBrand" ng-if="showAuthenticated()"></div> 258 259<!-- Loading spinner shown while dictionaries load. Uses ng-hide (not ng-if) 260 so it renders immediately before Angular bootstraps, improving LCP. 261 Auth handoff routes render their own wait template instead (WOW-5295). --> 262<div class="app-loading-spinner" ng-hide="allDictionariesLoaded || isAuthHandoffRoute()"> 263 <div class="spinner"></div> 264</div> 265 266<div ng-if="showAuthenticated() && allDictionariesLoaded && !isBeeFreeFullPage()" ng-controller="NavbarCtrl" 267 class="{{quickNav || headless?(showSubnav()?'masthead-wrapper-bg':''):''}}" 268 ng-class="{'masthead-wrapper-sm-agency':getCurrentAgencyOrderId() && showReturnToParentAccount(), 'masthead-wrapper-sm-agency_order':getCurrentAgencyOrderId() && !showReturnToParentAccount()}" 269> 270 <div ng-include src="'partials/common/mastHeadTop.html'" ng-if="!headless"></div> 271 <div ng-include src="'partials/common/mastHeadBottom.html'" ng-if="!headless"></div> 272</div> 273 274<div class="container-fluid {{isBeeFreeFullPage()?'':'remove-padding'}} {{isTokenLogin?'container-token':''}}" 275 ng-if="(allDictionariesLoaded || isAuthHandoffRoute()) && (!showAuthenticated() || layout.promise.$$state.status)" id="outer-container" style="min-height: 100vh" data-ng-attr-style="{{isBeeFreeFullPage() || headless ? 'min-height: 100vh' : 'padding-top: ' + n
275avBarOffset + '; min-height: 100vh' }}"> 276 277 <div ng-class="{'adjacent-to-side-nav':useSideNav(), 'container container-main remove-padding-top-and-bottom':!isFullWidthPage(), 'unifyr-alterations':isUnifyr()}"> 278 279 <div class="row remove-padding-top-and-bottom" ng-if="showAuthenticated() && checkCollectFeedback()" ng-cloak> 280 <div class="col col-md-12"> 281 <div class="alert alert-info text-center feedback-container container"> 282 {{'feedback.message'|i18n}} <a class="btn btn-sm btn-primary feedback-link" 283 ng-click="showFeedbackPanel()" 284 href="">{{'feedback.message.link'|i18n}}</a> 285 </div> 286 </div> 287 </div> 288 289 <div ng-if="!isTokenLogin" class="row remove-padding-top-and-bottom"> 290 <div class="col col-md-12" ng-class="isUnifyrClient ? 'unifyr-alert-container' : ''"> 291 <div id="alert-section" ng-class="isUnifyrClient ? 'unifyr-alert' : ''"> 292 293 </div> 294 </div> 295 </div> 296 <div zift-flash-messages ng-if="!headless"></div> 297 298 <div class="row remove-padding-top-and-bottom"> 299 <div ng-class="isFullWidthPage() && !headless ? 'remove-row-padding' : 'col col-md-12'" ng-style="{'padding-top' : getInnerContainerPaddingTop() }"> 300 301 <!-- global error bar --> 302 <div class="alert alert-danger" id="global-error-bar" style="display: none"> 303 <div class="row"> 304 <div class="col-md-12"> 305 <div class="global-error-bar-content"> 306 <div class="global-error-bar-content-left pull-left"> 307 <div class="global-error-bar-content-left-text" id="global-error-bar-text" role="alert" aria-live="assertive" aria-atomic="true"> 308 </div> 309 </div> 310 <div class="global-error-bar-content-right pull-right"> 311 <div class="global-error-bar-content-right-text"> 312 <span class="padding-10"> 313 <span id="global-error-bar-code"></span> 314 <span id="global-error-bar-ticket"></span> 315 </span> 316 <button type="button" class="close ng-scope" aria-label="{{ 'common.close' | i18n }}" onclick="$('#global-error-bar').hide()">Ã</button> 317 </div> 318 </div> 319 </div> 320 </div> 321 </div> 322 </div> 323 324 <div id="mainAngularView" ng-style="getMainAngularViewStyle()" ng-view></div> 325 <!-- Legacy chat: AI Expert enabled, NOT Ask IQ, NOT onboarding-only --> 326 <div ng-if="showAIExpert() && !showAskIQ() && !isOnboardingOnly()" ng-include="'partials/common/ai-expert-drawer/ai-expert-drawer.html'"></div> 327 <!-- AIChatV2: Ask IQ enabled OR onboarding-only mode --> 328 <div ng-if="showAskIQ() || isOnboardingOnly()" ng-include="'partials/common/ai-expert-drawer/ai-chat-v2-drawer.html'"></div> 329 <!-- Used to perform Zendesk SSO form submission--> 330 <div ng-include="'partials/zendesk/zendesk-form.html'"></div> 331 332 <!-- Onboarding Popup Adapter - React component that handles showing popup on first login --> 333 <!-- Placed at root level (not in navbar) to ensure visibility across all portal configurations --> 334 <unifyr-react 335 component="onboarding-popup-adapter" 336 props='{}'> 337 </unifyr-react> 338 </div> 339 </div> 340 </div> 341 342 <div id="customFooterContainer" ng-show="showCustomFooter()" ng-class="{'adjacent-to-side-nav':useSideNav()}"> 343 <div class="row remove-padding-top-and-bottom">
344 <span custom-footer id="page_footer"></span> 345 </div> 346 </div> 347 348 <div class="zift-footer-container" ng-class="{'adjacent-to-side-nav':useSideNav(), 'bee-free-full-page-footer-container' : isBeeFreeFullPage()}"> 349 <div class="container-main container" ng-if="allDictionariesLoaded" > 350 <div ng-show="showAuthenticated()"> 351 <div ng-include src="'partials/common/footer.html'"></div> 352 </div> 353 </div> 354 </div> 355 356 <!--<div class="adblock-modal" ng-show="!isGoogleTagManagerLoaded && !hasAdblockNotificationBeenAccepted()" ng-cloak> 357 <div class="adblock-modal-content"> 358 <div class="modal-header"> 359 <h4 class="modal-title">{{ 'adblock.modal.header' | i18n }}</h4> 360 </div> 361 <div class="modal-body"> 362 <div class="alert alert-danger"> 363 <span>{{ 'adblock.modal.body' | i18n }}</span> 364 </div> 365 </div> 366 <div class="modal-footer"> 367 <button type="button" class="btn btn-default" ng-click="setAdblockModalAccept()">{{ 'common.continue'|i18n }}</button> 368 </div> 369 </div> 370 </div>--> 371 372</div> 373
374<script defer src="dist/js/app.min.2ef2084a.js?rel=1791145067968"></script>
374 375
376<script async src="../common/lib/calendar/js/calendar.min.js?rel=1791145067968"></script>
376 377 378<div style="display:none; color:#000000" class="modal" data-bs-backdrop="static" id="globalFeedbackModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> 379 <div class="modal-dialog"> 380 <div class="modal-content"> 381 <div class="modal-header"><button type="button" class="close" data-bs-dismiss="modal" aria-hidden="true">×</button> 382 <h4 class="modal-title">Feedback</h4> 383 </div> 384 <div class="modal-body" ng-if="feedbackModal.extraMessage">{{feedbackModal.extraMessage|i18n}}</div> 385 <div id="globalFeedbackModalContent"> 386 <!-- Feedback panel script will be loaded dynamically after configuration is ready --> 387 </div> 388 </div> 389 </div> 390</div> 391 392<div ng-if="allDictionariesLoaded" class="modal" data-bs-backdrop="static" id="portalRedirectModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> 393 <div class="modal-dialog"> 394 <div class="modal-content"> 395 <div class="modal-header"><button type="button" class="close" data-bs-dismiss="modal" aria-hidden="true">×</button> 396 <h4 class="modal-title" id="global-dashboard-title"></h4> 397 </div> 398 <div class="modal-body" id="global-dashboard-url"></div> 399 <div class="modal-footer"> 400 <button class="btn btn-default" type="button" data-bs-dismiss="modal">{{ 'common.cancel'|i18n }}</button> 401 <button class="btn btn-success" type="button" ng-click="redirectToPortal()">{{ 'common.continue'|i18n }}</button> 402 </div> 403 </div> 404 </div> 405</div> 406 407<div ng-if="allDictionariesLoaded" class="modal" data-bs-backdrop="static" id="helpModal" tabindex="-1" role="dialog" aria-hidden="strue" > 408 <div class="modal-dialog"> 409 <div class="modal-content"> 410 <div class="modal-header"><button type="button" class="close" data-bs-dismiss="modal" aria-hidden="true">×</button> 411 <h4 class="modal-title">{{ 'nav.help'|i18n }}</h4> 412 </div> 413 <div class="modal-body" id="help-modal-body"> 414 415 </div> 416 <div class="modal-footer"> 417 <button type="button" class="btn btn-default" data-bs-dismiss="modal">{{ 'common.close'|i18n }}</button> 418 </div> 419 </div> 420 </div> 421</div> 422 423<div ng-if="allDictionariesLoaded" ng-controller="InternationalizationCtrl"> 424 <div class="modal" data-bs-backdrop="static" id="i18nModal" tabindex="-1" role="dialog" aria-hidden="true"> 425 <div class="modal-dialog modal-sm"> 426 <div class="modal-content"> 427 <div class="modal-header"> 428 <button type="button" class="close" data-bs-dismiss="modal" aria-hidden="true">×</button> 429 <h4 class="modal-title">{{ 'common.language' | i18n }}</h4> 430 </div> 431 <div class="modal-body"> 432 <form name="languageForm" class="row"> 433 <div class="col-md-12"> 434 <select class="form-control" ng-model="currentLanguage" required ng-options="('language.'+lang|i18n) for lang in languages | orderBy:'name' track by lang" ></select> 435 </div> 436 </form> 437 </div> 438 <div class="modal-footer"> 439 <button class="btn btn-default" type="button" data-bs-dismiss="modal">{{ 'common.close'|i18n }}</button> 440 <button class="btn btn-success" type="button" ng-disabled="languageForm.$invalid" ng-click="updateLanguage()">{{ 'common.save'|i18n }}</button> 441 </div> 442 </div> 443 </div> 444 </div> 445</div> 446 447
448<script src="/dist/partials/app.templates.bf0fe77d.js?rel=1791145067968"></script>
448 449
450<script> 451 $(document).on('click','.navbar-collapse.in',function(e) { 452 if( $(e.target).is('a') && $(e.target).attr('class') != 'dropdown-toggle' ) { 453 $(this).collapse('hide'); 454 } 455 }); 456</script>
456 457 458</body> 459 460</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.