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 Function('consent', 'service', service.callbackCode)(consent, service); 172 } 173 174 // Do the behaviors only for consent. 175 if (!consent) { 176 return; 177 } 178 179 // Rewrite data-id to id. 180 var elements = document.querySelectorAll('[data-name="' + service.name + '"][data-id]'); 181 if (elements.length > 0) { 182 Array.prototype.forEach.call(elements, function(element) { 183 element.setAttribute('id', element.getAttribute('data-id')); 184 }); 185 if (Drupal.behaviors.klaro.ready) { 186 Drupal.attachBehaviors(document, drupalSettings); 187 } 188 else { 189 Drupal.behaviors.klaro.needCallBehaviors = true; 190 } 191 } 192 193 let knownBehaviors = Object.keys(Drupal.behaviors); 194 var elements = document.querySelectorAll('[data-name="' + service.name + '"][src]'); 195 if (!elements) { 196 return; 197 } 198 199 // #3459261 Fix HtmlMediaElements not loading correctly. 200 var source_tags = document.querySelectorAll('source[data-name="' + service.name + '"]'); 201 if (source_tags.length > 0) { 202 var loading_state; 203 for (var i = 0; i < source_tags.length; i++) { 204 loading_state = source_tags[i].parentElement.readyState; 205 if (loading_state == 0 || loading_state == 1) { 206 source_tags[i].parentElement.load(); 207 } 208 } 209 } 210 211 function processBehaviors() { 212 for (let name in Drupal.behaviors) { 213 if (knownBehaviors.indexOf(name) === -1) { 214 // Execute newly added behaviors. This is somewhat guesswork, as 215 // the order of execution is undetermined based on file load order. 216 Drupal.behaviors[name].attach(document, drupalSettings); 217 knownBehaviors.push(name); 218 } 219 } 220 } 221 222 Array.prototype.forEach.call(elements, function(element) { 223 if (element.complete) { 224 processBehaviors(); 225 } 226 else { 227 element.addEventListener('load', processBehaviors); 228 } 229 }); 230 }, 231 232 /** 233 * Decorate wrapperIdentifiers in context. 234 */ 235 klaroDecorateWrapper: function(context, settings) { 236 // Iterate services and wrapperIdentifier.
237 settings.klaro.config.services.forEach(function(service, a_index) { 238 service.wrapperIdentifier.forEach(function(wrapperIdentifier) { 239 var elements = once('klaro-wrap', wrapperIdentifier, context); 240 Array.prototype.forEach.call(elements, function(el) { 241 if (el) { 242 var wrapper = document.createElement('div'); 243 wrapper.setAttribute('data-name', service.name); 244 wrapper.setAttribute('data-original-display', getComputedStyle(el).display); 245 el.parentNode.insertBefore(wrapper, el); 246 wrapper.appendChild(el); 247 } 248 }); 249 }); 250 }); 251 }, 252 253 /** 254 * Watch for DOMchanges on klaro-element. 255 */ 256 klaroElementObserver: function() { 257 var targetNode = document.getElementById("klaro"); 258 if (Drupal.behaviors.klaro.observing || !targetNode) { 259 return; 260 } 261 Drupal.behaviors.klaro.observing = true; 262 263 const config = { childList: true, subtree: true }; 264 const callback = (mutationList, observer) => { 265 for (const mutation of mutationList) { 266 if (mutation.type === "childList") { 267 Drupal.behaviors.klaro.klaroElementMutator(); 268 } 269 } 270 }; 271 // Create an observer instance linked to the callback function 272 const observer = new MutationObserver(callback); 273 observer.observe(targetNode, config); 274 Drupal.behaviors.klaro.klaroElementMutator(); 275 }, 276 277 /** 278 * Observer klaro element to adapt customizations. 279 */ 280 klaroElementMutator: function() { 281 282 // Add minimal accessibility features to the customize consent dialog. 283 var labels = document.querySelectorAll('#klaro label') 284 if (labels.length > 0){ 285 for (var i = 0; i < labels.length; i++) { 286 labels[i].setAttribute('onkeydown', 'Drupal.behaviors.klaro.KlaroToggleService(event)'); 287 labels[i].setAttribute('aria-labelledby', labels[i].getAttribute('for') + '-title'); 288 labels[i].setAttribute('aria-describedby', labels[i].getAttribute('for') + '-description'); 289 if (labels[i].previousElementSibling && labels[i].previousElementSibling.tagName == 'INPUT') { 290 labels[i].previousElementSibling.setAttribute('tabindex', '0'); 291 labels[i].removeAttribute('tabindex'); 292 } 293 } 294 // Set focus to first possible label. 295 document.querySelector('#klaro input:not(:disabled)+label')?.focus(); 296 } 297 // Add accessibility features to the preferences dialog : role dialog, aria-modal and aria-labelledby. 298 if (document.querySelector('.cm-modal.cm-klaro')) { 299 var elem = document.querySelector('.cm-modal.cm-klaro'); 300 elem.setAttribute('role', 'dialog'); 301 elem.setAttribute('aria-modal', 'true'); 302 elem.querySelector('.cm-header .title').setAttribute('id', 'cm-modal-title'); 303 elem.setAttribute('aria-labelledby', 'cm-modal-title'); 304 } 305 306 // Handle close button X. 307 if (drupalSettings.klaro.show_close_button) { 308 if (document.querySelector('#klaro-cookie-notice')) { 309 var elem = document.querySelector('#klaro-cookie-notice'); 310 } 311 else if (document.querySelector('.cm-modal.cm-klaro')) { 312 var elem = document.querySelector('.cm-modal.cm-klaro'); 313 if ((Drupal.behaviors.klaro.manager.confirmed) && (!Drupal.behaviors.klaro.config.mustConsent)) { 314 elem = false; 315 } 316 } 317 318 if (elem && !document.querySelector('.klaro-close')) { 319 var close_label = Drupal.t("Close dialog and decline all", {},{context: 'klaro'}); 320 var close_html = '<button title="' + close_label + '" type="button" class="cn-decline klaro-close" tabindex="0" aria-label="' + close_label + '"></button>'; 321 elem.insertAdjacentHTML('beforeend', close_html);; 322 document.querySelector('.klaro-close')?.addEventListener('click', (e) => { 323 Drupal.behaviors.klaro.manager.changeAll(false); 324 Drupal.behaviors.klaro.manager.saveAndApplyConsents(); 325 }, false); 326 document.querySelector('.klaro').classList.add('klaro-close-enabled'); 327 document.querySelector('.klaro .cookie-modal .cm-modal .hide')?.remove(); 328 } 329 } 330 331 }, 332 KlaroToggleService: function(event) { 333 if (event.key === "Enter") { 334 event.target.click();
335 event.target.focus(); 336 } 337 } 338 }; 339 340 /** 341 * Open the Klaro! consent notice from links & buttons. 342 */ 343 Drupal.behaviors.klaroLink = { 344 attach: function (context, settings) { 345 var elements = once('klaroLink', '[rel*="open-consent-manager"], [href*="#klaro"], .open-consent-manager', context); 346 Array.prototype.forEach.call(elements, function(element) { 347 element.addEventListener('click', function (event) { 348 klaro.show(Drupal.behaviors.klaro.config); 349 event.preventDefault(); 350 return false; 351 }); 352 }); 353 } 354 }; 355})(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.