PageSourceSearch

https://vaadin.com/vaadincom/haas-service/v2/haas-loader.js?theme=login

js vaadin.com collected 2026-09-24 08:39:47 UTC 20,395 bytes, 351 lines download raw bytes

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.