PageSourceSearch

https://www.mdu.se/download/18.2d62f17617fda4fe01d360cc/1653394914686/script-blocker.js

js mdu.se collected 2026-09-25 08:55:55 UTC 8,191 bytes, 203 lines download raw bytes

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.