1/* 2 * Hands the logged in member over to the 8wave chat agent widget. 3 * 4 * The custom element is injected by the tag manager, so we can't count on it being in the markup: 5 * the data is exposed on window.aiChatAgent for the tag to use while rendering the element and, 6 * as a fallback, the DOM is watched to sync whatever element shows up later on. 7 */ 8(function (window, document) { 9 "use strict"; 10 11 var configElement = document.getElementById("ai-chat-agent-config"); 12 if (!configElement) { 13 return; 14 } 15 16 var config; 17 try { 18 config = JSON.parse(configElement.textContent || "{}"); 19 } catch (error) { 20 return; 21 } 22 23 if (!config || !config.elementName) { 24 return; 25 } 26 27 var elementName = String(config.elementName).toLowerCase(); 28 var hasUser = !!(config.isAuthenticated && config.externalToken); 29 30 function warn(error) { 31 if (window.console && window.console.warn) { 32 window.console.warn("ai chat agent: sync of the widget failed", error); 33 } 34 } 35 36 function applyState(widget) { 37 try { 38 if (hasUser) { 39 // the attribute is the source of truth of the session on the widget side 40 widget.setAttribute("external-token", config.externalToken); 41 42 if (config.profile && typeof widget.setContext === "function") { 43 widget.setContext(config.profile); 44 } 45 } else { 46 clearState(widget); 47 } 48 } catch (error) { 49 warn(error); 50 } 51 } 52 53 /* 54 * The widget restores its own session from a cookie, so a logout done on another tab or 55 * before this page load has to be propagated or the chat would keep answering with the 56 * profile of the member who was connected before. 57 */ 58 function clearState(widget) { 59 try { 60 widget.removeAttribute("external-token"); 61 62 if (typeof widget.signOut === "function") { 63 widget.signOut(); 64 } 65 66 if (typeof widget.setContext === "function") { 67 widget.setContext({}); 68 } 69 } catch (error) { 70 warn(error); 71 } 72 } 73 74 /* 75 * The widget renders its launcher only once it manages to resolve the agent, and stays an 76 * empty element otherwise. Reporting what it emits saves the next debugging session: a 77 * failure to resolve the agent is silent apart from these events. 78 */ 79 function watchWidgetEvents(widget) { 80 if (widget.aiChatAgentEventsBound) { 81 return; 82 } 83 84 widget.aiChatAgentEventsBound = true; 85 86 widget.addEventListener("error", function (event) { 87 if (window.console && window.console.warn) { 88 window.console.warn("ai chat agent: the widget reported an error", event.detail); 89 } 90 }); 91 } 92 93 function sync(widget) { 94 if (!widget) { 95 return; 96 } 97 98 watchWidgetEvents(widget); 99 100 // attributes are read when the element connects, so setting them before the upgrade is fine 101 applyState(widget); 102 103 // the methods instead only exist once the 8wave library has upgraded the element 104 if (window.customElements && typeof window.customElements.whenDefined === "function") { 105 window.customElements.whenDefined(elementName).then(function () { 106 applyState(widget); 107 })["catch"](function () { 108 /* the library never loaded, there is nothing to sync */ 109 }); 110 } 111 } 112 113 function forEachWidget(root, callback) { 114 var widgets = (root || document).querySelectorAll(elementName); 115 for (var i = 0; i < widgets.length; i++) { 116 callback(widgets[i]); 117 } 118 } 119 120 function syncAll(root) { 121 forEachWidget(root, sync); 122 } 123 124 function signOutAll() { 125 forEachWidget(document, clearState); 126 } 127 128 function watchForWidget() { 129 if (!window.MutationObserver) { 130 return; 131 } 132 133 var observer = new window.MutationObserver(function (mutations) { 134 for (var i = 0; i < mutations.length; i++) { 135 var addedNodes = mutations[i].addedNodes; 136 for (var j = 0; j < addedNodes.length; j++) { 137 var node = addedNodes[j]; 138 if (node.nodeType !== 1) { 139 continue; 140 } 141 142 if (node.localName === elementName) { 143 sync(node); 144 } else if (node.querySelectorAll) { 145 syncAll(node); 146 } 147 } 148 } 149 }); 150 151 // the tag manager can inject the widget at any moment, even on an interaction, 152 // so the observer is kept for the whole life of the page 153 observer.observe(document.documentElement, { childList: true, subtree: true });
154 } 155 156 window.aiChatAgent = { 157 elementName: config.elementName, 158 organizationSlug: config.organizationSlug, 159 agentId: config.agentId, 160 isAuthenticated: hasUser, 161 externalToken: config.externalToken || null, 162 profile: config.profile || null, 163 sync: sync, 164 syncAll: syncAll, 165 signOut: signOutAll 166 }; 167 168 /* 169 * Only the flag is published on the data layer: the access token and the profile are personal 170 * data and must not be readable by every other tag configured on the tag manager. 171 */ 172 window.dataLayer = window.dataLayer || []; 173 window.dataLayer.push({ 174 event: "aiChatAgent.ready", 175 aiChatAgentAuthenticated: hasUser 176 }); 177 178 watchForWidget(); 179 syncAll(document); 180 181 document.addEventListener("DOMContentLoaded", function () { 182 syncAll(document); 183 }); 184 185 // anticipates the reconciliation instead of waiting for the page we land on after the logout 186 document.addEventListener("click", function (event) { 187 var target = event.target; 188 if (target && target.closest && target.closest(".js-ai-chat-agent-signout")) { 189 signOutAll(); 190 } 191 }); 192 193 // pages restored from the back/forward cache don't run the scripts again 194 window.addEventListener("pageshow", function (event) { 195 if (event.persisted) { 196 syncAll(document); 197 } 198 }); 199})(window, document);
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.