1(function (Drupal, drupalSettings) { 2 3 'use strict'; 4 5 /** 6 * Initialize Klaro! with custom settings. 7 */ 8 Drupal.behaviors.klaro = { 9 config: false, 10 manager: false, 11 observing: false, 12 needCallBehaviors: false, 13 ready: false, 14 attach: function (context, settings) { 15 if (!settings.klaro) { 16 return; 17 } 18 if (typeof klaro === 'undefined') { 19 document.querySelector('#klaro-js').addEventListener("load", () => { 20 Drupal.behaviors.klaro.proceed(context, settings); 21 }); 22 } 23 else { 24 Drupal.behaviors.klaro.proceed(context, settings); 25 } 26 }, 27 proceed: function(context, settings) { 28 // Bootstrap config once. 29 if (!Drupal.behaviors.klaro.config) { 30 settings.klaro.config.services.forEach(function (service, a_index) { 31 // Set callback for each service. 32 settings.klaro.config.services[a_index].callback = Drupal.behaviors.klaro.serviceCallback; 33 // Create regular expressions from config. 34 service.cookies.forEach(function (cookie, c_index) { 35 settings.klaro.config.services[a_index]['cookies'][c_index] = [new RegExp(cookie[0], 'i'), cookie[1] || '/', cookie[2] || location.host]; 36 }); 37 }); 38 39 Drupal.behaviors.klaro.config = settings.klaro.config; 40 } 41 42 // Decorate wrapperIdentifiers in context. 43 Drupal.behaviors.klaro.klaroDecorateWrapper(context, settings); 44 45 // Remove unused data attributes. 46 context.querySelectorAll('a[data-name]')?.forEach((item) => { 47 item.removeAttribute('data-href'); 48 }); 49 50 // Store reference to manager once. 51 if (!Drupal.behaviors.klaro.manager) { 52 Drupal.behaviors.klaro.manager = klaro.getManager(Drupal.behaviors.klaro.config); 53 } 54 55 // Loading klaro less intrusive. 56 if (settings.klaro.dialog_mode === 'silent') { 57 Drupal.behaviors.klaro.manager.confirmed = true; 58 } 59 60 // Setup klaro on each attach (support ajax insert commands). 61 klaro.setup(Drupal.behaviors.klaro.config); 62 63 // Observe klaro-element to add aria-features. 64 Drupal.behaviors.klaro.klaroElementObserver(); 65 66 // Add accessibility features to the learn more link. 67 let learn_more_link = document.querySelector('a.cm-link.cn-learn-more'); 68 if (learn_more_link) { 69 // Add title to learn more link. 70 let label_open_consent_dialog = Drupal.t("Open consent dialog", {},{context: 'klaro'}); 71 learn_more_link.setAttribute('title', label_open_consent_dialog); 72 // Add the "button" role to the learn more link. 73 learn_more_link.setAttribute('role', 'button'); 74 // Add the attribute aria-haspopup="dialog". 75 learn_more_link.setAttribute('aria-haspopup', 'dialog'); 76 } 77 78 // Store reference to manager once. 79 if (!Drupal.behaviors.klaro.manager) { 80 Drupal.behaviors.klaro.manager = klaro.getManager(Drupal.behaviors.klaro.config); 81 } 82 83 if(context === document) { 84 var elements = once('klaro', 'body', context); 85 Array.prototype.forEach.call(elements, function () { 86 // Add toggle dialog button. 87 if (settings.klaro.show_toggle_button && !document.getElementById('klaro_toggle_dialog')) { 88 var button_label = Drupal.t("Manage consents", {},{context: 'klaro'}); 89 var button_html = '<button id="klaro_toggle_dialog" aria-label="' + button_label + '" aria-haspopup="dialog" title="'+ button_label + '" type="button" class="klaro_toggle_dialog klaro_toggle_dialog_override" rel="open-consent-manager"></button>'; 90 document.body.insertAdjacentHTML('afterbegin', button_html); 91 } 92 }); 93 } 94 else { 95 for (var app in Drupal.behaviors.klaro.manager.executedOnce) { 96 if (Drupal.behaviors.klaro.manager.executedOnce[app]) { 97 // (Re-)apply consents on each attach (ajax add_js support) 98 // only if there are elements with data-src left. 99 if (document.querySelectorAll('[data-name="' + app + '"][data-src]:not([src])').length > 0) { 100 Drupal.behaviors.klaro.manager.applyConsents(); 101 } 102 } 103 } 104 } 105 106 // Set preview image for contextual consent. 107 var elements = once('klaro-thumbnail', '.klaro.cm-as-context-notice', context); 108 Array.prototype.forEach.call(elements, function (el) { 109 let klaro_elem = (el.closest('.field'))?.querySelector('[data-modified-by-klaro]'); 110 let thumbnail = klaro_elem?.getAttribute('data-thumbnail'); 111 let title = klaro_elem?.getAttribute('title'); 112 113 if (thumbnail) { 114 el.parentElement.style.backgroundImage = 'url(' + thumbnail + ')'; 115 el.parentElement.style.backgroundSize = 'cover'; 116 el.parentElement.style.backgroundPosition = 'center'; 117 el.firstChild.style.backgroundColor = 'rgba(250, 250, 250, 0.75)'; 118 } 119 120 if (title) { 121 let title_elem = document.createElement('p'); 122 title_elem.textContent = title; 123 el.firstChild.prepend(title_elem); 124 } 125 }); 126 127 // Iterate services and replace placeholder texts.
128 Drupal.behaviors.klaro.config.services.forEach(function(service, a_index) { 129 if (service.contextualConsentText) { 130 var elements = once('klaro-text', '[data-type="placeholder"][data-name="' + service.name + '"] div.context-notice > p:first-child', context); 131 if (elements.length > 0) { 132 let title_elem = document.createElement('p'); 133 title_elem.innerHTML = service.contextualConsentText; 134 Array.prototype.forEach.call(elements, function (el) { 135 el.parentNode.replaceChild(title_elem.cloneNode(true), el); 136 }); 137 } 138 } 139 }); 140 141 // Add link to consent manager for contextual consents. 142 var elements = once('klaro-consent-link', '[data-type="placeholder"] div.context-notice'); 143 var title = Drupal.t("Open the Consent Management Dialog", {}, {context: 'klaro'}); 144 var linktext = Drupal.t("Manage privacy settings", {}, {context: 'klaro'}); 145 Array.prototype.forEach.call(elements, function (el) { 146 el.insertAdjacentHTML('beforeend', `<p class="cm-dialog-link"><a href="#" title="${title}" rel="open-consent-manager">${linktext}</a></p>`); 147 }); 148 149 // Call Behaviors if needed. 150 if (Drupal.behaviors.klaro.needCallBehaviors && context === document) { 151 Drupal.behaviors.klaro.needCallBehaviors = false; 152 Drupal.attachBehaviors(document, drupalSettings); 153 } 154 155 // Set ready. 156 Drupal.behaviors.klaro.ready = true; 157 }, 158 159 /** 160 * A callback for each service that will run at the beginning and on consent change. 161 */ 162 serviceCallback: function (consent, service) { 163 164 // If onlyOnce, abort if already executed. 165 if (Drupal.behaviors.klaro.manager && service.onlyOnce && Drupal.behaviors.klaro.manager.executedOnce[service.name]) { 166 return; 167 } 168 169 // Execute callbackCode from config form. 170 if (service.callbackCode.length > 0) { 171 try { 172 Function('consent', 'service', service.callbackCode)(consent, service); 173 } 174 catch (error) { 175 console.error('Error executing callback code for service "' + service.name + '":', error); 176 } 177 } 178 179 // Do the behaviors only for consent. 180 if (!consent) { 181 return; 182 } 183 184 // Rewrite data-id to id. 185 var elements = document.querySelectorAll('[data-name="' + service.name + '"][data-id]'); 186 if (elements.length > 0) { 187 Array.prototype.forEach.call(elements, function(element) { 188 element.setAttribute('id', element.getAttribute('data-id')); 189 }); 190 if (Drupal.behaviors.klaro.ready) { 191 Drupal.attachBehaviors(document, drupalSettings); 192 } 193 else { 194 Drupal.behaviors.klaro.needCallBehaviors = true; 195 } 196 } 197 198 let knownBehaviors = Object.keys(Drupal.behaviors); 199 var elements = document.querySelectorAll('[data-name="' + service.name + '"][src]'); 200 if (!elements) { 201 return; 202 } 203 204 // #3459261 Fix HtmlMediaElements not loading correctly. 205 var source_tags = document.querySelectorAll('source[data-name="' + service.name + '"]'); 206 if (source_tags.length > 0) { 207 var loading_state; 208 for (var i = 0; i < source_tags.length; i++) { 209 loading_state = source_tags[i].parentElement.readyState; 210 if (loading_state == 0 || loading_state == 1) { 211 source_tags[i].parentElement.load(); 212 } 213 } 214 } 215 216 function processBehaviors() { 217 for (let name in Drupal.behaviors) { 218 if (knownBehaviors.indexOf(name) === -1) { 219 // Execute newly added behaviors. This is somewhat guesswork, as 220 // the order of execution is undetermined based on file load order. 221 Drupal.behaviors[name].attach(document, drupalSettings); 222 knownBehaviors.push(name); 223 } 224 } 225 } 226 227 Array.prototype.forEach.call(elements, function(element) { 228 if (element.complete) { 229 processBehaviors(); 230 } 231 else { 232 element.addEventListener('load', processBehaviors); 233 } 234 }); 235 }, 236 237 /** 238 * Decorate wrapperIdentifiers in context. 239 */ 240 klaroDecorateWrapper: function(context, settings) { 241 // Iterate services and wrapperIdentifier.
242 settings.klaro.config.services.forEach(function(service, a_index) { 243 service.wrapperIdentifier.forEach(function(wrapperIdentifier) { 244 var elements = once('klaro-wrap', wrapperIdentifier, context); 245 Array.prototype.forEach.call(elements, function(el) { 246 if (el) { 247 var wrapper = document.createElement('div'); 248 wrapper.setAttribute('data-name', service.name); 249 wrapper.setAttribute('data-original-display', getComputedStyle(el).display); 250 el.parentNode.insertBefore(wrapper, el); 251 wrapper.appendChild(el); 252 } 253 }); 254 }); 255 }); 256 }, 257 258 /** 259 * Watch for DOMchanges on klaro-element. 260 */ 261 klaroElementObserver: function() { 262 var targetNode = document.getElementById("klaro"); 263 if (Drupal.behaviors.klaro.observing || !targetNode) { 264 return; 265 } 266 Drupal.behaviors.klaro.observing = true; 267 268 const config = { childList: true, subtree: true }; 269 const callback = (mutationList, observer) => { 270 for (const mutation of mutationList) { 271 if (mutation.type === "childList") { 272 Drupal.behaviors.klaro.klaroElementMutator(); 273 } 274 } 275 }; 276 // Create an observer instance linked to the callback function 277 const observer = new MutationObserver(callback); 278 observer.observe(targetNode, config); 279 Drupal.behaviors.klaro.klaroElementMutator(); 280 }, 281 282 /** 283 * Observer klaro element to adapt customizations. 284 */ 285 klaroElementMutator: function() { 286 287 // Add minimal accessibility features to the customize consent dialog. 288 var labels = document.querySelectorAll('#klaro label') 289 if (labels.length > 0){ 290 for (var i = 0; i < labels.length; i++) { 291 labels[i].setAttribute('onkeydown', 'Drupal.behaviors.klaro.KlaroToggleService(event)'); 292 labels[i].setAttribute('aria-labelledby', labels[i].getAttribute('for') + '-title'); 293 labels[i].setAttribute('aria-describedby', labels[i].getAttribute('for') + '-description'); 294 if (labels[i].previousElementSibling && labels[i].previousElementSibling.tagName == 'INPUT') { 295 labels[i].previousElementSibling.setAttribute('tabindex', '0'); 296 labels[i].removeAttribute('tabindex'); 297 } 298 } 299 // Set focus to first possible label. 300 document.querySelector('#klaro input:not(:disabled)+label')?.focus(); 301 } 302 // Add accessibility features to the preferences dialog : role dialog, aria-modal and aria-labelledby. 303 if (document.querySelector('.cm-modal.cm-klaro')) { 304 var elem = document.querySelector('.cm-modal.cm-klaro'); 305 elem.setAttribute('role', 'dialog'); 306 elem.setAttribute('aria-modal', 'true'); 307 elem.querySelector('.cm-header .title').setAttribute('id', 'cm-modal-title'); 308 elem.setAttribute('aria-labelledby', 'cm-modal-title'); 309 } 310 311 // Handle close button X. 312 if (drupalSettings.klaro.show_close_button) { 313 if (document.querySelector('#klaro-cookie-notice')) { 314 var elem = document.querySelector('#klaro-cookie-notice'); 315 } 316 else if (document.querySelector('.cm-modal.cm-klaro')) { 317 var elem = document.querySelector('.cm-modal.cm-klaro'); 318 if ((Drupal.behaviors.klaro.manager.confirmed) && (!Drupal.behaviors.klaro.config.mustConsent)) { 319 elem = false; 320 } 321 } 322 323 if (elem && !document.querySelector('.klaro-close')) { 324 var close_label = Drupal.t("Close dialog and decline all", {},{context: 'klaro'}); 325 var close_html = '<button title="' + close_label + '" type="button" class="cn-decline klaro-close" tabindex="0" aria-label="' + close_label + '"></button>'; 326 elem.insertAdjacentHTML('beforeend', close_html);; 327 document.querySelector('.klaro-close')?.addEventListener('click', (e) => { 328 Drupal.behaviors.klaro.manager.changeAll(false); 329 Drupal.behaviors.klaro.manager.saveAndApplyConsents(); 330 }, false); 331 document.querySelector('.klaro').classList.add('klaro-close-enabled'); 332 document.querySelector('.klaro .cookie-modal .cm-modal .hide')?.remove(); 333 } 334 } 335 336 }, 337 KlaroToggleService: function(event) { 338 if (event.key === "Enter") { 339 event.target.click();
340 event.target.focus(); 341 } 342 } 343 }; 344 345 /** 346 * Open the Klaro! consent notice from links & buttons. 347 */ 348 Drupal.behaviors.klaroLink = { 349 attach: function (context, settings) { 350 var elements = once('klaroLink', '[rel*="open-consent-manager"], [href*="#klaro"], .open-consent-manager', context); 351 Array.prototype.forEach.call(elements, function(element) { 352 element.addEventListener('click', function (event) { 353 klaro.show(Drupal.behaviors.klaro.config); 354 event.preventDefault(); 355 return false; 356 }); 357 }); 358 } 359 }; 360})(Drupal, drupalSettings);
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.