1/** 2 * @file 3 * Defines Javascript behaviors for the cookies module. 4 * These are invoked once the user performs a cookie consent 5 * action like: accept / deny / revoke to immediately 6 * react on that with loading a script, video, iframe, etc. 7 * or if consent is revoked, removing existing cookies, 8 * stop tracking or similar. 9 * 10 * This is NOT invoked on each page load, only immediately, when the user 11 * takes concrete action in the COOKiES banner. 12 */ 13 14(function (Drupal) { 15 "use strict"; 16 17 /** 18 * Define defaults. 19 */ 20 Drupal.behaviors.cookies_open_readspeaker = { 21 // id corresponding to the cookies_service.schema->id. 22 id: "open_readspeaker", 23 cssNodeId: "rsmod_Styles", 24 blockSelector: "div[data-block-plugin-id=\"open_readspeaker_block\"]", 25 scriptSelector: "script[data-sid=\"open_readspeaker_external\"]", 26 27 /** 28 * Called when consent was given. 29 */ 30 consentGiven: function (context, observer) { 31 // Select the node that will be observed for mutations 32 const targetNode = document.getElementById(this.cssNodeId)?.parentNode ?? document.head; 33 // Start observing the target node 34 observer.observe(targetNode, {childList: true}); 35 36 // Find all external script tags for open readspeaker. 37 document.querySelectorAll(this.scriptSelector) 38 .forEach((nodeScript) => { 39 if (nodeScript.getAttribute("type") === "text/javascript") { 40 // No need to replace node because it is already from type text/javascript. 41 return; 42 } 43 // Build new script node. 44 const newNodeScript = document.createElement(nodeScript.tagName); 45 Array.from(nodeScript.attributes) 46 .forEach((attr) => { 47 newNodeScript.setAttribute(attr.name, attr.name === "type" ? "text/javascript" : attr.value); 48 }); 49 newNodeScript.innerHTML = nodeScript.innerHTML; 50 // Replace script node so that the javascript is evaluated. 51 nodeScript.replaceWith(newNodeScript); 52 }); 53 54 // Show the block 55 document.querySelectorAll(this.blockSelector).forEach((element) => element.style.display = ""); 56 }, 57 58 /** 59 * Called when consent was denied / revoked. 60 */ 61 consentDenied: function (context) { 62 /** 63 * Get an array of all cookie names. Can optionally be filtered by the parameter 'names'. 64 * @param {string[]} names If provided, only check for these cookies. 65 * @returns {string[]} Array with cookie names. 66 */ 67 const getCookieNames = (names = []) => decodeURIComponent(document.cookie).split(";") 68 .map(cookie => cookie.trimStart().split("=", 1).toString()) 69 .filter(cookieName => names.length > 0 ? names.includes(cookieName) : true); 70 let needReload = false; 71 // Hide the block 72 document.querySelectorAll(this.blockSelector).forEach((element) => element.style.display = "none"); 73 // Remove cookies 74 const cookieNames = ["_rspkrLoadCore", "ReadSpeakerSettings"]; 75 const paths = ['/', window.location.pathname]; 76 getCookieNames(cookieNames).forEach((name) => { 77 paths.forEach(path => { 78 document.cookie = `${name}=;path=${path};max-age=0;`; 79 const domainParts = window.location.hostname.split('.'); 80 while (domainParts.length > 1) { 81 const domain = domainParts.join('.'); 82 document.cookie = `${name}=;path=${path};domain=${domain};max-age=0;`; 83 domainParts.shift(); 84 } 85 }); 86 needReload = true; 87 }); 88 // Check script types if there were no cookies to delete
89 if (!needReload) { 90 document.querySelectorAll(this.scriptSelector) 91 .forEach((nodeScript) => { 92 if (nodeScript.getAttribute("type") === "text/javascript") { 93 needReload = true; 94 } 95 }); 96 } 97 // Reload page 98 if (needReload) { 99 location.reload(); 100 } 101 }, 102 103 attach: function (context) { 104 const self = this; 105 // Observer to replace readspeaker css if it is set with wrong href link 106 const observer = new MutationObserver((mutationList, observer) => { 107 for (const mutation of mutationList) { 108 if (mutation.type === 'childList' && mutation.addedNodes.length > 0) { 109 mutation.addedNodes.forEach(node => { 110 if (node.type === "text/css" && node.nodeName === "LINK" && node.id === self.cssNodeId) { 111 let scriptNode = document.querySelector(self.scriptSelector); 112 if (scriptNode != null) { 113 let src = scriptNode.src.substring(0, scriptNode.src.lastIndexOf("/") + 1); 114 let href = node.href; 115 let newHref = href.replace(/.*(?=\w\/\w\d+\/ReadSpeaker)/, src); 116 observer.disconnect(); 117 if (newHref !== href) { 118 let newNode = document.createElement(node.tagName); 119 Array.from(node.attributes) 120 .forEach((attr) => { 121 newNode.setAttribute(attr.name, attr.name === "href" ? newHref : attr.value); 122 }); 123 newNode.innerHTML = node.innerHTML; 124 node.replaceWith(newNode); 125 } 126 } 127 } 128 }); 129 } 130 } 131 }); 132 document.addEventListener("cookiesjsrUserConsent", function (event) { 133 const service = (typeof event.detail.services === "object") ? event.detail.services : {}; 134 if (typeof service[self.id] !== "undefined" && service[self.id]) { 135 // Consent was given: 136 self.consentGiven(context, observer); 137 } else { 138 // Consent was denied/revoked. 139 self.consentDenied(context); 140 } 141 }); 142 }, 143 }; 144})(Drupal);
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.