1/** 2 * The Header as a Service Loader 3 * 4 * The loader itself is a Thymeleaf template (note the stuff) to simplify tasks like 5 * - having configurable URLs 6 * - allowing the insertion of other resources 7 * Besides updating the DOM with the actual HaaS component HTML, it also loads the trackers if allowed. 8 * 9 * In case of SPAs trigger a 'haas-user-info-changed' event to update the header. Otherwise it will be loaded 10 * only once. 11 */ 12window.haas = window.haas || {}; 13/** 14 * Logout handler 15 */ 16window.haas.logout = function () { 17 if (window.Intercom) window.Intercom('shutdown'); 18 19 window.location.href = window.haas.isPublicPage === false 20 ? '' + "/logout" 21 : '' + "/logout?redirectUrl=" + encodeURIComponent(location.href); 22} 23 24/** 25 * Login handler 26 */ 27window.haas.login = function (redirectURL) { 28 if (redirectURL) { 29 window.location.href = '' + "/login?redirectUrl=" + encodeURIComponent(redirectURL); 30 } else { 31 window.location.href = '' + "/login?redirectUrl=" + encodeURIComponent(location.href); 32 } 33} 34 35window.haas.loader = (function () { 36 /** 37 * Renders the menu template literal 38 */ 39 const _renderMenu = userInfo => `<div id="haas-profile-section" class="haas-profile-menu-wrapper" style="display: none"> 40 <div id="haas-sign-in-up-links"> 41 <a id="haas-login-button" class="md:block haas-button haas-nav-button" href="javascript:void(0)" onclick="window.haas.login()">Sign in</a> 42 </div> 43 <button id="haas-profile-button" class="haas-profile-avatar" role="button" onclick="window.haas.profileMenuToggle()" 44 style="display: none"> 45 <span id="haas-profile-button-unread" class="haas-profile-avatar-unread">0</span> 46 <img class="haas-profile-avatar-img" id="haas-profile-menu-portrait-img" alt=""> 47 </button> 48 49 <div id="haas-profile-menu" class="haas-profile-menu"> 50 <div class="haas-profile-username"> 51 <div id="haas-profile-name" class="haas-profile-username-text"></div> 52 <div id="haas-profile-menu-account-role" class="haas-profile-account-role"></div> 53 <a router-link class="haas-profile-menu-link" href="${userInfo.linkBaseHref}/profile">Edit Profile</a> 54 </div> 55 56 <div class="haas-profile-menu-divider"></div> 57 58 <div id="haas-profile-menu-subscription-details" class="haas-profile-subscription"> 59 <div class="haas-profile-menu-title">Subscription</div> 60 <div class="subscription-name"> 61 <a id="haas-profile-menu-subscription-link" router-link href="${userInfo.linkBaseHref}/myaccount/subscriptions" 62 class="haas-profile-menu-link">Multiple</a> 63 </div> 64 </div> 65 66 <div class="haas-profile-menu-divider"></div> 67 68 <div> 69 <a id="haas-mypage-button" router-link href="${userInfo.linkBaseHref}/mypage" 70 class="haas-profile-menu-item"> 71 <div class="flex items-center"> 72 <i class="text-xl las las la-home"></i> 73 <span class="ml-1.5">My Page</span> 74 </div> 75 <span id="haas-myaccount-unread-messages" 76 class="ml-2 h-5 flex items-center bg-red-500 text-color px-1.5 text-sm text-white font-semibold rounded">0 unread</span> 77 </a> 78 <a router-link href="${userInfo.linkBaseHref}/myaccount/subscriptions" class="haas-profile-menu-item"> 79 <i class="text-xl las las la-user"></i> 80 <span class="ml-1.5">My Account</span> 81 </a> 82 <a router-link href="${userInfo.linkBaseHref}/myaccount/certifications" class="haas-profile-menu-item"> 83 <i class="text-xl las la-certificate"></i> 84 <span class="ml-1.5">My Certifications</span> 85 </a> 86 <a href="${userInfo.linkBaseHref}/directory/my-components" router-ignore class="haas-profile-menu-item"> 87 <i class="text-xl las la-layer-group"></i> 88 <span class="ml-1.5">My Components</span> 89 </a> 90 <div id="haas-profile-menu-vaadiner-section"> 91 <a href="javascript:void(0)" class="haas-profile-menu-item" onclick="window.haas.toggleAdminPanel()"> 92 <i class="text-xl las la-tachometer-alt"></i> 93 <span class="ml-1.5">Toggle admin panel</span> 94 </a> 95 </div> 96 97 <div class="haas-profile-menu-divider"></div> 98 99 <a href="javascript:void(0)" onclick="window.haas.logout()" class="haas-profile-menu-item"> 100 <i class="text-xl las la-sign-out-alt"></i>
101 <span class="ml-1.5">Log out</span> 102 </a> 103 </div> 104 <img id="pro-key-image" hidden src="" alt=""> 105 </div> 106</div> 107`; 108 109 /** 110 * Executes menu JS. 111 * 112 * NOTE: Must be executed only once per menu rendering. 113 */ 114 const _executeMenuJs = userInfo => { window.haas=window.haas||{},function(){function e(e,t){e.classList.add("haas-active"),t.setAttribute("aria-expanded",!0)}function t(e,t){e.classList.remove("haas-active"),t.setAttribute("aria-expanded",!1)}function n(n){var a=this.querySelector(".haas-dropdown-button");a&&((window.innerWidth||document.documentElement.clientWidth||document.body.clientWidth)<1192?"click"===n.type&&(this.classList.toggle("haas-expanded"),a.setAttribute("aria-expanded",this.classList.contains("haas-expanded"))):("mouseenter"!==n.type&&"mouseover"!==n.type||e(this,a),"mouseleave"===n.type&&t(this,a),"keyup"===n.type&&("Space"!==n.code&&"Enter"!==n.code||(n.preventDefault(),e(this,a),this.querySelector(".haas-menu").querySelector("a").focus()),"Escape"===n.code&&(t(this,a),a.focus()))))}window.haas.toggleMenu=function(){var e=document.getElementById("header-banner");e&&e.classList.toggle("header-banner-hidden"),document.getElementById("haas-nav").classList.toggle("haas-mobile-nav-open"),document.getElementById("haas-mobile-nav-button").classList.toggle("haas-mobile-nav-button-open")};var a=document.getElementById("haas-profile-section"),o=document.getElementById("haas-profile-button"),s=document.getElementById("haas-profile-menu"),i=document.getElementById("haas-sign-in-up-links"),c=document.getElementById("haas-profile-menu-portrait-img"),d=document.getElementById("haas-profile-button-unread"),l=document.getElementById("haas-myaccount-unread-messages"),r=document.getElementById("haas-profile-menu-vaadiner-section");function u(){s&&(s.classList.add("opacity-0","invisible"),s.classList.remove("opacity-100","visible"),document.removeEventListener("keyup",m))}function m(e){"Esc"!==e.key&&"Escape"!==e.key||(u(),o.focus())}window.haas.profileMenuToggle=function(){s&&s.classList.contains("visible")?u():s&&(s.classList.add("opacity-100","visible"),s.classList.remove("opacity-0","invisible"),document.addEventListener("keyup",m)),userInfo.unreadMessageCount>0?(l.style.display="",l.innerText="".concat(userInfo.unreadMessageCount," unread")):(l.style.display="none",d.style.display="none")},document.body.addEventListener("click",function(e){if(a){var t=e.target;a===t||a.contains(t)||u()}}),window.haas.toggleAdminPanel=function(){var e=new URLSearchParams(window.location.search).get("control-panel");window.location.href="".concat(window.location.pathname,"on"===e?"?control-panel=off":"?control-panel=on")};var p=new CustomEvent("haas-profile-updated",{detail:userInfo});window.haas.updateProfileSection=function(e){if(window.haas.userInfo=e,a){a.style.display="";var t=document.getElementById("pro-key-image");if(e.authenticated){i.style.display="none",c.addEventListener("load",function(){o.style.display=""}),t.src=(e.linkBaseHref?e.linkBaseHref:"")+"/_prokey?"+ +new Date;var n="".concat(e.firstname," ").concat(e.lastname);document.getElementById("haas-profile-name").innerText=n,c.src="https://vaadin.com"+e.portraitUrl,c.title=n,e.unreadMessageCount>0?(d.style.display="",d.innerText=e.unreadMessageCount):d.style.display="none";var s=document.getElementById("haas-profile-menu-subscription-link"),l=document.getElementById("haas-profile-menu-account-role");s.href=(e.linkBaseHref?e.linkBaseHref:"")+"/myaccount/subscriptions",e.productNames&&0!=e.productNames.length?s.innerText=1==e.productNames.length?e.productNames[0]:"Multiple":(s.href=(u="https://vaadin.com",window.location.origin.includes("preview.vaadin")&&(u="https://preview.vaadin.
114com"),u+"/pricing"),s.innerText="Subscribe now"),l.innerText=e.accountRole?e.accountRole:"",e.vaadiner?r.style.display="block":r.style.display="none"}else o.style.display="none",i.style.display="block",t.src="",c.src="";window.dispatchEvent(p)}var u},document.querySelectorAll(".haas-dropdown").forEach(function(e){["click","mouseover","mouseenter","mouseleave","keyup"].forEach(function(t){e.addEventListener(t,n)})}),document.addEventListener("keyup",function(e){if("Tab"===e.code){var t=document.querySelector(".haas-dropdown-button[aria-expanded='true']");t&&!t.nextElementSibling.contains(document.activeElement)&&(t.parentElement.classList.remove("haas-active"),t.setAttribute("aria-expanded",!1))}})}(); }; 115 116 /** 117 * Executes cookie JS 118 */ 119 const _executeCookieJs = () => { !function(){var e,o,n="privacyPolicy",t=(o=0,(e=document.cookie.match(new RegExp("(?:^|; )"+n.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g,"\\$1")+"=([^;]*)")))&&(o=e[1]),o);function a(e){Date.prototype.addDays=function(e){var o=new Date(this.valueOf());return o.setDate(o.getDate()+e),o};var o=(new Date).addDays(365).toUTCString(),t="".concat(encodeURIComponent(n),"=").concat(encodeURIComponent(e),"; domain=vaadin.com; path=/; expires=").concat(o,"; samesite=none; secure=true;");document.cookie=t}(!t||t<4)&&-1!=t&&window.location.pathname.indexOf("privacy-policy")<0&&window.navigator.userAgent.indexOf("HeadlessChrome")<0&&window.navigator.userAgent.indexOf("Chrome-Lighthouse")<0&&function(){var e=document.createElement("div");e.id="haas-cookie-dialog",e.className="haas-cookie-dialog";var o=document.createElement("div");o.id="cookie-dialog-container",o.className="cookie-dialog-container";var n=document.createElement("div");n.tabIndex="-1",n.className="content";var t=document.createElement("p");t.id="cookie-dialog-description",t.className="description",t.innerHTML='We use cookies to improve your experience on our site, ensure better functionality, and analyze traffic. By clicking "Accept," you consent to our use of cookies. <a href="https://vaadin.com/privacy-policy">About cookies</a>.';var i=document.createElement("div");i.id="cookie-buttons",i.className="cookie-buttons";var c=document.createElement("button");c.id="haas-cookie-dialog-decline-button",c.type="button",c.className="button primary cookie-button decline",c.innerText="Decline";var d=document.createElement("button");d.id="haas-cookie-dialog-button",d.type="button",d.className="button primary cookie-button accept",d.innerText="Accept",n.appendChild(t),i.appendChild(c),i.appendChild(d),o.appendChild(n),o.appendChild(i),e.appendChild(o),document.body&&document.body.appendChild(e),d&&d.addEventListener("click",function(){a(4),window.dispatchEvent(new Event("on-privacy-policy-confirmed")),e.remove()}),c&&c.addEventListener("click",function(){a(-1),window.dispatchEvent(new Event("on-privacy-policy-declined")),e.remove()})}()}(); }; 120 121 /** 122 * Checks for headless browser UA 123 */ 124 const _isHeadlessBrowser = () => navigator.userAgent && (navigator.userAgent.includes('HeadlessChrome') || navigator.userAgent.includes('Chrome-Lighthouse')); 125 126 /** 127 * Checks for headless browser UA 128 */ 129 const _isSsoSessionMarkerCookieAvailable = () => document.cookie.includes('SSO_SESSION_RUNNING='); 130 131 /** 132 * Only allow tracking if 133 * - user allows it 134 * - it's not a pre-rendering request 135 */ 136 const _isTrackingAllowed = () => "1" !== navigator.doNotTrack && !_isHeadlessBrowser(); 137 138 /** 139 * Only allow Intercom for non-headless browsers and if enabled in general 140 */ 141 const _isIntercomAllowed = () => { 142 return !_isHeadlessBrowser() && true; 143 }; 144 145 /** 146 * Renders the menu and executes the menu JS. 147 */ 148 const _loadMenu = (haasContainer, userInfo) => { 149 window.haas.isAuthenticated = userInfo.authenticated; 150 userInfo.loginUrl = 'https://sso.vaadin.com/login?service=https://vaadin.com/login/cas'; 151 userInfo.linkBaseHref = ''; 152 haasContainer.innerHTML = _renderMenu(userInfo); 153 _executeMenuJs(userInfo); 154 }; 155 156 const _updateProfileInfo = userInfo => { 157 window.haas.isAuthenticated = userInfo.authenticated; 158 if(!!window.haas.updateProfileSection) { 159 window.haas.updateProfileSection(userInfo); 160 } 161 } 162 163 /** 164 * Updates relative links in the menu to respect specified 'linkBaseHref' 165 */ 166 const _updateRelativeMenuLinks = () => { 167 const linkBaseHref = ''; 168 if(linkBaseHref) { 169 document.querySelectorAll('[relative-menu-link]').forEach(link => link.href = linkBaseHref + link.getAttribute("href")) 170 } 171 } 172 173 /** 174 * Public API 175 */ 176 return { 177 loadScript: (src, props = null, attrs = null, errorHandler = null) => { 178 const script = document.createElement('script'); 179 script.src = src; 180 script.type = 'text/javascript'; 181 for (const prop in props) { 182 script[prop] = props[prop]; 183 } 184 for (const attr in attrs) { 185 script.setAttribute(attr, attrs[attr]); 186 } 187 script.addEventListener('error', error => { 188 errorHandler 189 ? errorHandler() 190 : console.log(error); 191 }, false); /* eslint-disable-line no-console */ 192 document.head.appendChild(script); 193 }, 194 initPlaceholder: () => { 195 const haasContainer = document.getElementById("haas-container"); 196 if (haasContainer) { 197 var placeholder = document.createElement("div"); 198 placeholder.classList.add("haas-placeholder"); 199 placeholder.style.height = "var(--mainViewMenuHeight)"; 200 placeholder.style.width = "100%"; 201 placeholder.style.background = "var(--color-charcoal-darker)"; 202 haasContainer.appendChild(placeholder); 203 } else { 204 console.error("Unable to resolve HaaS container element (id = 'haas-container') after DOM is loaded. Check your index.html!"); 205 } 206 }, 207 initMenu: () => { 208 const haasContainer = document.getElementById("haas-container"); 209 if (haasContainer) { 210 _loadMenu(haasContainer, {"authenticated": false}); 211 _updateRelativeMenuLinks(); 212 haas.loader.startAuthenticationStateChecker(true); 213 } else { 214 console.error("Unable to resolve HaaS container element (id = 'haas-container') after DOM is loaded. Check your index.html!"); 215 } 216 }, 217 updateMenu: updateCache => { 218 if (_isSsoSessionMarkerCookieAvailable()) { 219 var userInfoUrl = 'https://vaadin.com/vaadincom/haas-service/v1/user-info'; 220 if (updateCache) { 221 userInfoUrl = userInfoUrl + '?updateCache=true'; 222 } 223 fetch(userInfoUrl, {credentials: 'include'}) 224 .then(response => { 225 if (response.status === 200) { 226 return response.json(); 227 } else if (response.status === 400) { 228 window.haas.logout(); 229 } 230 }) 231 .then(userInfo => { 232 if (!!userInfo) { 233 userInfo.authenticated = true; 234 userInfo.linkBaseHref = ''; 235 _updateProfileInfo(userInfo); 236 userInfoUrl = 'https://vaadin.com/vaadincom/haas-service/v1/user-info' + '?info=sub_details'; 237 fetch(userInfoUrl, {credentials: 'include'}) 238 .then(response => { 239 if (response.status === 200) { 240 return response.json(); 241 } else if (response.status === 400) { 242 window.haas.logout(); 243 } 244 }) 245 .then(userInfo => { 246 if (!!userInfo) { 247 userInfo.authenticated = true; 248 userInfo.linkBaseHref = ''; 249 _updateProfileInfo(userInfo); 250 } else { 251 _updateProfileInfo({"authenticated": false}); 252 } 253 }) 254 .catch(error => { 255 console.warn(error); 256 _updateProfileInfo({"authenticated": false}); 257 }); 258 } else { 259 _updateProfileInfo({"authenticated": false}); 260 } 261 }) 262 .catch(error => { 263 console.warn(error); 264 _updateProfileInfo({"authenticated": false}); 265 }); 266 } else { 267 _updateProfileInfo({"authenticated": false}); 268 } 269 }, 270 271 startAuthenticationStateChecker: forcedUpdate => { 272 let timeout = 5000; 273 const isAuthenticationStateSet = window.haas && typeof window.haas.isAuthenticated !== 'undefined'; 274 if (forcedUpdate) { 275 haas.loader.updateMenu(false); 276 } else if (isAuthenticationStateSet) { 277 // in case the authentication state is unclear, update the menu 278 if((!window.haas.isAuthenticated && _isSsoSessionMarkerCookieAvailable()) || 279 (window.haas.isAuthenticated && !_isSsoSessionMarkerCookieAvailable())) { 280 haas.loader.updateMenu(false); 281 // to prevent flooding the user info endpoint we double the refresh timeout when we expect an 282 // authentication state change 283 timeout = 10000; 284 } 285 } 286 287 setTimeout(() => haas.loader.startAuthenticationStateChecker(false), timeout); 288 }, 289
290 initCookie: () => { 291 try { 292 _executeCookieJs(); 293 } catch (e) { 294 console.error("Unable to initialize cookie dialog.") 295 } 296 }, 297 298 /* load tracker script if allowed */ 299 initTracker: () => { 300 if (_isTrackingAllowed()) { 301 haas.loader.loadScript('https://vaadin.com/vaadincom/haas-service/v1/tracker.js', 302 { 303 'defer': true, 304 }); 305 } 306 }, 307 308 /* load intercom script if allowed */ 309 initIntercom: () => { 310 if (_isIntercomAllowed()) { 311 haas.loader.loadScript('https://vaadin.com/vaadincom/haas-service/v1/intercom.js', 312 { 313 'defer': true, 314 }); 315 } 316 }, 317 }; 318})(); 319 320/** 321 * Initializes the menu when DOM is loaded and container div should be available. 322 */ 323if (document.readyState === 'interactive' || document.readyState === 'complete') { 324 console.log("initializing menu..."); 325 haas.loader.initPlaceholder(); 326 haas.loader.initMenu(); 327 haas.loader.initCookie(); 328 haas.loader.initTracker(); 329 haas.loader.initIntercom(); 330} else { 331 window.addEventListener("DOMContentLoaded", function () { 332 console.log("updating menu..."); 333 haas.loader.initPlaceholder(); 334 haas.loader.initMenu(); 335 haas.loader.initCookie(); 336 haas.loader.initTracker(); 337 haas.loader.initIntercom(); 338 }, false); 339} 340 341/** 342 * Updates the menu on user info changed events. 343 * 344 * TODO: It might make more sense to introduce a dedicated 'user-info-changed' event to update the menu. Otherwise 345 * TODO: we will trigger a lot of useless updates... 346 */ 347window.addEventListener('haas-user-info-changed', function () { 348 console.log("updating menu..."); 349 haas.loader.updateMenu(true); 350 /* TODO: Only the parts in the menu that changed should be updated (reactive). */ 351}, false);
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.