1(function () { 2 "use strict"; 3 4 /** 5 * Borlabs owns consent; this module owns presentation and query relay only. 6 * Never grants consent directly â uses ContentBlocker.unblockServices. 7 */ 8 var defaults = { 9 serviceId: "neurabot-chatbot", 10 serviceGroupId: "external-media", 11 dialogId: "cosmema-ki-search-consent", 12 /** Hash-Key (nicht Query-Param): Fragment wird nie an den Server gesendet. */ 13 hashKey: "cosmema_ki_q", 14 widgetWaitMs: 12000, 15 debug: false, 16 }; 17 18 var cfg = {}; 19 var i18n = {}; 20 var override = window.cosmemaKiSearchBorlabsConfig || {}; 21 var pendingQuery = null; 22 var dialogOpen = false; 23 var listenersBound = false; 24 25 for (var key in defaults) { 26 cfg[key] = Object.prototype.hasOwnProperty.call(override, key) ? override[key] : defaults[key]; 27 } 28 29 if (override.i18n) { 30 i18n = override.i18n; 31 } 32 33 function log() { 34 if (!cfg.debug || !window.console) { 35 return; 36 } 37 console.log.apply(console, ["[cosmema-ki-search/borlabs]"].concat([].slice.call(arguments))); 38 } 39 40 function borlabsReady() { 41 return !!(window.BorlabsCookie && window.BorlabsCookie.Consents); 42 } 43 44 function hasConsent() { 45 try { 46 return ( 47 borlabsReady() && 48 window.BorlabsCookie.Consents.hasConsent(cfg.serviceId, cfg.serviceGroupId) === true 49 ); 50 } catch (error) { 51 return false; 52 } 53 } 54 55 function widgetReady() { 56 return !!( 57 window.neurabot && 58 typeof window.neurabot.openWindowAndTrySendMessage === "function" 59 ); 60 } 61 62 function sendQuery(query) { 63 if (typeof window.neurabot.openWindowAndTrySendMessage === "function") { 64 window.neurabot.openWindowAndTrySendMessage(query); 65 return; 66 } 67 if (typeof window.neurabot.openWindowAndSendMessage === "function") { 68 window.neurabot.openWindowAndSendMessage(query); 69 return; 70 } 71 throw new Error("neurabot send methods are not available"); 72 } 73 74 function writePendingHash(query) { 75 var base = window.location.pathname + window.location.search; 76 var next = query 77 ? base + "#" + cfg.hashKey + "=" + encodeURIComponent(query) 78 : base; 79 window.history.replaceState(null, "", next); 80 } 81 82 function readPendingHash() { 83 var hash = window.location.hash.replace(/^#/, ""); 84 if (!hash) { 85 return null; 86 } 87 88 var value = new URLSearchParams(hash).get(cfg.hashKey); 89 return value && value.trim() ? value.trim() : null; 90 } 91 92 function persistPendingQuery(query) { 93 pendingQuery = query || null; 94 writePendingHash(pendingQuery); 95 } 96 97 function clearPendingQuery() { 98 persistPendingQuery(null); 99 } 100 101 function restorePendingQuery() { 102 if (pendingQuery) { 103 return pendingQuery; 104 } 105 106 var fromHash = readPendingHash(); 107 if (fromHash) { 108 pendingQuery = fromHash; 109 log("restored pending query from URL hash"); 110 } 111 112 return pendingQuery; 113 } 114 115 function restoreInputValue(query) { 116 if (!query) { 117 return; 118 } 119 120 var inputs = document.querySelectorAll(".cosmema-ki-search input.chatbot-search"); 121 for (var i = 0; i < inputs.length; i++) { 122 if (!inputs[i].value) { 123 inputs[i].value = query; 124 } 125 } 126 } 127 128 function openBorlabsConsent() { 129 try { 130 var contentBlocker = window.BorlabsCookie && window.BorlabsCookie.ContentBlocker; 131 if (contentBlocker && typeof contentBlocker.unblockServices === "function") { 132 contentBlocker.unblockServices([cfg.serviceId]); 133 log("consent opened via ContentBlocker.unblockServices"); 134 return true; 135 } 136 } catch (error) { 137 log("unblockServices failed", error); 138 } 139 140 var fallbackButton = document.querySelector( 141 ".brlbs-cmpnt-content-blocker [data-borlabs-cookie-unblock]" 142 ); 143 if (fallbackButton) { 144 fallbackButton.click(); 145 return true; 146 } 147 148 if (window.console) {
149 console.warn("[cosmema-ki-search/borlabs] no Borlabs consent path available"); 150 } 151 return false; 152 } 153 154 function removeDialog() { 155 var dialog = document.getElementById(cfg.dialogId); 156 if (dialog && dialog.parentNode) { 157 dialog.parentNode.removeChild(dialog); 158 } 159 dialogOpen = false; 160 } 161 162 function showConsentDialog() { 163 if (dialogOpen || document.getElementById(cfg.dialogId)) { 164 return; 165 } 166 167 dialogOpen = true; 168 169 var overlay = document.createElement("div"); 170 overlay.id = cfg.dialogId; 171 overlay.className = "cosmema-ki-search-consent"; 172 overlay.setAttribute("role", "presentation"); 173 174 var card = document.createElement("div"); 175 card.className = "cosmema-ki-search-consent__card"; 176 card.setAttribute("role", "dialog"); 177 card.setAttribute("aria-modal", "true"); 178 card.setAttribute("aria-label", i18n.title || "KI-Chatbot"); 179 180 var title = document.createElement("p"); 181 title.className = "cosmema-ki-search-consent__title"; 182 title.textContent = i18n.title || "KI-Chatbot"; 183 184 var text = document.createElement("p"); 185 text.className = "cosmema-ki-search-consent__text"; 186 text.textContent = 187 i18n.text || 188 "Der KI-Chatbot beantwortet Ihre Fragen. Zum Aktivieren ist Ihre Einwilligung nötig."; 189 190 var cta = document.createElement("button"); 191 cta.type = "button"; 192 cta.className = "cosmema-ki-search-consent__cta"; 193 cta.textContent = i18n.cta || "KI-Chatbot aktivieren"; 194 195 var close = document.createElement("button"); 196 close.type = "button"; 197 close.className = "cosmema-ki-search-consent__close"; 198 close.textContent = i18n.close || "SchlieÃen"; 199 200 card.appendChild(title); 201 card.appendChild(text); 202 card.appendChild(cta); 203 card.appendChild(close); 204 overlay.appendChild(card); 205 document.body.appendChild(overlay); 206 207 function closeDialog(clearPending) { 208 if (clearPending) { 209 clearPendingQuery(); 210 } 211 removeDialog(); 212 } 213 214 overlay.addEventListener("click", function (event) { 215 if (event.target === overlay) { 216 closeDialog(true); 217 } 218 }); 219 220 close.addEventListener("click", function () { 221 closeDialog(true); 222 }); 223 224 cta.addEventListener("click", function () { 225 // Persist before Borlabs may reload the page after unblock. 226 restorePendingQuery(); 227 removeDialog(); 228 openBorlabsConsent(); 229 }); 230 231 log("consent dialog shown"); 232 } 233 234 function flushPendingQuery() { 235 var query = restorePendingQuery(); 236 if (!query) { 237 return; 238 } 239 240 try { 241 sendQuery(query); 242 clearPendingQuery(); 243 } catch (error) { 244 log("sendQuery failed", error); 245 clearPendingQuery(); 246 } 247 } 248 249 function waitAndSend() { 250 if (!restorePendingQuery()) { 251 return; 252 } 253 254 var start = Date.now(); 255 var timer = setInterval(function () { 256 if (hasConsent() && widgetReady()) { 257 clearInterval(timer); 258 flushPendingQuery(); 259 } else if (Date.now() - start > cfg.widgetWaitMs) { 260 clearInterval(timer); 261 log("widget not ready within timeout - dropping held query"); 262 clearPendingQuery(); 263 } 264 }, 250); 265 } 266 267 function onConsentStateChange() { 268 if (!borlabsReady() || !hasConsent() || !restorePendingQuery()) { 269 return; 270 } 271 waitAndSend(); 272 } 273 274 function resumeAfterReload() { 275 var query = restorePendingQuery(); 276 if (!query) { 277 return; 278 } 279 280 restoreInputValue(query); 281 bindConsentListeners(); 282 283 if (!borlabsReady()) { 284 return; 285 } 286 287 if (hasConsent()) { 288 log("resuming pending query after reload"); 289 waitAndSend(); 290 return; 291 } 292 293 // Consent not yet readable (init race) â listeners will pick it up. 294 log("pending query present, waiting for Borlabs consent init"); 295 } 296 297 function bindConsentListeners() { 298 if (listenersBound) { 299 return; 300 } 301 listenersBound = true; 302 document.addEventListener("borlabs-cookie-after-init", onConsentStateChange); 303 document.addEventListener("borlabs-cookie-consent-saved", onConsentStateChange); 304 } 305 306 function shouldGateSend() { 307 return borlabsReady() && !hasConsent(); 308 } 309 310 function requestConsentAndSend(query) { 311 bindConsentListeners(); 312 313 if (!borlabsReady()) { 314 sendQuery(query); 315 return; 316 } 317 318 if (hasConsent() && widgetReady()) { 319 sendQuery(query); 320 return; 321 } 322 323 persistPendingQuery(query); 324 325 if (!hasConsent()) { 326 log("search without consent - showing dialog, holding query"); 327 showConsentDialog(); 328 return; 329 } 330 331 waitAndSend(); 332 } 333 334 window.cosmemaKiSearchBorlabs = { 335 isActive: borlabsReady, 336 hasConsent: hasConsent, 337 shouldGateSend: shouldGateSend, 338 requestConsentAndSend: requestConsentAndSend, 339 }; 340 341 // After consent + page reload, pick up the stored query and send it. 342 if (document.readyState === "loading") { 343 document.addEventListener("DOMContentLoaded", resumeAfterReload); 344 } else { 345 resumeAfterReload(); 346 } 347 document.addEventListener("borlabs-cookie-after-init", resumeAfterReload); 348})();
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.