1const userConsentCookieExists = document.cookie.split('; ').some((item) => item.trim().startsWith('mdu-user-consent=')); 2const userConsentCookie = userConsentCookieExists ? document.cookie.split('; ').find((item) => item.trim().startsWith('mdu-user-consent=')) : null; 3const acceptedCookieCategories = userConsentCookie ? userConsentCookie.split('=')[1].split(':') : null; 4 5const TYPE_ATTRIBUTE = 'javascript/blocked' 6 7var blacklist = [ 8 {src: /\.facebook\.com/, category: 'analytics'}, 9 {src: /\.facebook\.net/, category: 'analytics'}, 10 {src: /\.rek\.ai/, category: 'analytics'}, 11 {src: /\.adform\.net/, category: 'marketing'} 12]; 13 14 15// Backup list containing the original blacklisted script elements 16const backupScripts = { 17 blacklisted: [] 18} 19 20//Creates an observer for added scripts in the DOM. 21const observer = new MutationObserver(mutations => { 22 for (let i = 0; i < mutations.length; i++) { 23 const { addedNodes } = mutations[i]; 24 for(let i = 0; i < addedNodes.length; i++) { 25 const node = addedNodes[i] 26 // For each added script tag 27 if(node.nodeType === 1 && node.tagName === 'SCRIPT') { 28 const src = node.src 29 const type = node.type 30 // If the src is inside the blacklist and is not inside the whitelist 31 if(isOnBlacklist(src, type)) { 32 console.log('BLOCKED: ' + src); 33 // We backup the node 34 backupScripts.blacklisted.push([node, node.type]) 35 36 // Blocks inline script execution in Safari & Chrome 37 node.type = TYPE_ATTRIBUTE 38 39 // Firefox has this additional event which prevents scripts from being executed 40 const beforeScriptExecuteListener = function (event) { 41 // Prevent only marked scripts from executing 42 if(node.getAttribute('type') === TYPE_ATTRIBUTE) 43 event.preventDefault() 44 node.removeEventListener('beforescriptexecute', beforeScriptExecuteListener) 45 } 46 node.addEventListener('beforescriptexecute', beforeScriptExecuteListener) 47 48 // Remove the node from the DOM 49 node.parentElement && node.parentElement.removeChild(node) 50 } 51 } 52 } 53 } 54}) 55 56// Blacklist 57const isOnBlacklist = (src, type) => ( 58 src && 59 (!type || type !== TYPE_ATTRIBUTE) && 60 (!blacklist || blacklist.some(pattern => pattern.src.test(src) && (acceptedCookieCategories ? !acceptedCookieCategories.includes(pattern.category) : true))) 61) 62 63const willBeUnblocked = function(script) { 64 const src = script.getAttribute('src') 65 return ( 66 blacklist && blacklist.every(entry => !entry.src.test(src)) 67 ) 68} 69 70const URL_REPLACER_REGEXP = new RegExp('[|\\{}()[\\]^$+*?.]', 'g') 71 72// Unblocks all (or a selection of) blacklisted scripts. 73const unblock = function(...scriptUrlsOrRegexes) { 74 if(scriptUrlsOrRegexes.length < 1) { 75 blacklist = [] 76 } else { 77 if(blacklist) { 78 blacklist = blacklist.filter(pattern => ( 79 scriptUrlsOrRegexes.every(urlOrRegexp => { 80 if(typeof urlOrRegexp === 'string') 81 return !pattern.src.test(urlOrRegexp) 82 else if(urlOrRegexp instanceof RegExp) 83 return pattern.src.toString() !== urlOrRegexp.toString() 84 }) 85 )) 86 } 87 } 88 89 // Parse existing script tags with a marked type 90 const tags = document.querySelectorAll(`script[type="${TYPE_ATTRIBUTE}"]`) 91 for(let i = 0; i < tags.length; i++) { 92 const script = tags[i] 93 if(willBeUnblocked(script)) { 94 backupScripts.blacklisted.push([script, 'application/javascript']) 95 script.parentElement.removeChild(script) 96 } 97 } 98
99 // Exclude 'whitelisted' scripts from the blacklist and append them to <head> 100 let indexOffset = 0; 101 [...backupScripts.blacklisted].forEach(([script, type], index) => { 102 if(willBeUnblocked(script)) { 103 const scriptNode = document.createElement('script') 104 for(let i = 0; i < script.attributes.length; i++) { 105 let attribute = script.attributes[i] 106 if(attribute.name !== 'src' && attribute.name !== 'type') { 107 scriptNode.setAttribute(attribute.name, script.attributes[i].value) 108 } 109 } 110 scriptNode.setAttribute('src', script.src) 111 scriptNode.setAttribute('type', type || 'application/javascript') 112 document.head.appendChild(scriptNode) 113 backupScripts.blacklisted.splice(index - indexOffset, 1) 114 indexOffset++ 115 } 116 }) 117 console.log('UNBLOCKED A SCRIPT') 118 // Disconnect the observer if the blacklist is empty for performance reasons 119 if(blacklist && blacklist.length < 1) { 120 observer.disconnect() 121 } 122} 123 124 125//MONKEYPATCH 126const createElementBackup = document.createElement 127 128const originalDescriptors = { 129 src: Object.getOwnPropertyDescriptor(HTMLScriptElement.prototype, 'src'), 130 type: Object.getOwnPropertyDescriptor(HTMLScriptElement.prototype, 'type') 131} 132 133// Monkey patch the createElement method to prevent dynamic scripts from executing 134document.createElement = function(...args) { 135 136 // If this is not a script tag, bypass 137 if(args[0] && args[0].toLowerCase() !== 'script') 138 return createElementBackup.bind(document)(...args) 139 140 const scriptElt = createElementBackup.bind(document)(...args) 141 142 // Define getters / setters to ensure that the script type is properly set 143 try { 144 Object.defineProperties(scriptElt, { 145 'src': { 146 ...originalDescriptors.src, 147 set(value) { 148 if(isOnBlacklist(value, scriptElt.type)) { 149 originalDescriptors.type.set.call(this, TYPE_ATTRIBUTE) 150 } 151 originalDescriptors.src.set.call(this, value) 152 } 153 }, 154 'type': { 155 ...originalDescriptors.type, 156 get() { 157 const typeValue = originalDescriptors.type.get.call(this); 158 if(typeValue === TYPE_ATTRIBUTE || isOnBlacklist(this.src, typeValue)) { 159 // Prevent script execution. 160 return null 161 } 162 return typeValue 163 }, 164 set(value) { 165 const typeValue = isOnBlacklist(scriptElt.src, scriptElt.type) ? TYPE_ATTRIBUTE : value 166 originalDescriptors.type.set.call(this, typeValue) 167 } 168 } 169 }) 170 171 // Monkey patch the setAttribute function so that the setter is called instead 172 scriptElt.setAttribute = function(name, value) { 173 if(name === 'type' || name === 'src') 174 scriptElt[name] = value 175 else 176 HTMLScriptElement.prototype.setAttribute.call(scriptElt, name, value) 177 } 178 } catch (error) { 179 // eslint-disable-next-line 180 console.warn( 181 'Unable to prevent script execution for script src ', scriptElt.src, '.\n', 182 'A likely cause would be because you are using a third-party browser extension that monkeypatches the "document.createElement" function.' 183 ) 184 } 185 return scriptElt 186} 187 188// Starts the monitoring 189observer.observe(document.documentElement, { 190 childList: true, 191 subtree: true 192}) 193// Event listener for SiteVisions cookiemodule 194document.addEventListener('sv-cookie-consent', (e) => { 195 e.detail.categories.forEach(category => { 196 blacklist.map(script => { 197 if(script.category === category) { 198 unblock(script.src); 199 } 200 }) 201 }) 202 location.reload(); 203});
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.