PageSourceSearch

https://www.marmomac.com/scripts/ai-chat-agent.js?v=202604291604

js marmomac.com collected 2026-09-30 11:49:27 UTC 6,609 bytes, 199 lines download raw bytes

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.