@el7ven/cookie-kit 0.2.21 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +21 -1
- package/src/core/AnalyticsManager.js +400 -0
- package/src/core/ConsentManager.js +710 -0
- package/src/core/ConsentMode.js +109 -0
- package/src/core/FocusTrap.js +130 -0
- package/src/core/GeoDetector.js +144 -0
- package/src/core/ScriptLoader.js +229 -0
- package/src/core/StorageAdapter.js +179 -0
- package/src/core/analytics.js +101 -10
- package/src/core/index.js +7 -7
- package/src/geo/GeoDetector.js +536 -0
- package/src/geo/RegionRules.js +126 -0
- package/src/geo/index.js +16 -0
- package/src/index.js +56 -18
- package/src/js/CookieConsent.js +0 -1
- package/src/locales/en.js +54 -0
- package/src/locales/index.js +20 -0
- package/src/locales/ro.js +54 -0
- package/src/plugins/CMPPlugin.js +187 -0
- package/src/plugins/PluginManager.js +234 -0
- package/src/plugins/index.js +7 -0
- package/src/providers/GoogleConsentModeProvider.js +278 -0
- package/src/providers/index.js +6 -0
- package/src/rewriting/ScriptRewriter.js +278 -0
- package/src/rewriting/index.js +6 -0
- package/src/scripts/ScriptLoader.js +310 -0
- package/src/scripts/ScriptManager.js +278 -0
- package/src/scripts/ScriptRegistry.js +175 -0
- package/src/scripts/ScriptScanner.js +178 -0
- package/src/scripts/index.js +9 -0
- package/src/trackers/TrackerDetector.js +488 -0
- package/src/trackers/TrackerPatterns.js +307 -0
- package/src/trackers/TrackerRegistry.js +172 -0
- package/src/trackers/index.js +15 -0
- package/src/utils/cookies.js +37 -0
- package/src/utils/dom.js +58 -0
- package/src/utils/helpers.js +89 -0
- package/src/vue/CookieConsent.vue +1 -1
- package/src/vue/CookieDrawer.vue +4 -4
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ScriptLoader — safely injects scripts into DOM
|
|
3
|
+
* @module ScriptLoader
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export class ScriptLoader {
|
|
7
|
+
constructor(config = {}) {
|
|
8
|
+
this.config = {
|
|
9
|
+
timeout: 10000, // 10 seconds
|
|
10
|
+
retryAttempts: 3,
|
|
11
|
+
retryDelay: 1000,
|
|
12
|
+
...config
|
|
13
|
+
}
|
|
14
|
+
this.loadedScripts = new Set() // Track by src
|
|
15
|
+
this.loadedHashes = new Set() // Track by content hash
|
|
16
|
+
this.loadedIds = new Set() // Track by id
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Load external script
|
|
21
|
+
* @param {Object} scriptDescriptor - Script descriptor from registry
|
|
22
|
+
* @returns {Promise<boolean>}
|
|
23
|
+
*/
|
|
24
|
+
async loadExternal(scriptDescriptor) {
|
|
25
|
+
const { src, cookieSrc, attributes, id } = scriptDescriptor
|
|
26
|
+
const actualSrc = cookieSrc || src
|
|
27
|
+
|
|
28
|
+
if (!actualSrc) {
|
|
29
|
+
console.warn('[ScriptLoader] No src provided for external script')
|
|
30
|
+
return false
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Deduplication: check if already loaded
|
|
34
|
+
if (this.loadedScripts.has(actualSrc) || this.loadedIds.has(id)) {
|
|
35
|
+
console.log('[ScriptLoader] Script already loaded:', actualSrc)
|
|
36
|
+
return true
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return new Promise((resolve) => {
|
|
40
|
+
const script = document.createElement('script')
|
|
41
|
+
|
|
42
|
+
// Set src from data-cookie-src (pre-blocking)
|
|
43
|
+
script.src = actualSrc
|
|
44
|
+
|
|
45
|
+
// Apply attributes
|
|
46
|
+
Object.entries(attributes || {}).forEach(([key, value]) => {
|
|
47
|
+
if (key !== 'src') {
|
|
48
|
+
script.setAttribute(key, value)
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
// Set proper type
|
|
53
|
+
script.type = 'text/javascript'
|
|
54
|
+
|
|
55
|
+
// Add data attribute for tracking
|
|
56
|
+
script.setAttribute('data-cookie-loaded', 'true')
|
|
57
|
+
script.setAttribute('data-cookie-id', id)
|
|
58
|
+
|
|
59
|
+
let timeoutId = null
|
|
60
|
+
|
|
61
|
+
const cleanup = () => {
|
|
62
|
+
if (timeoutId) clearTimeout(timeoutId)
|
|
63
|
+
script.removeEventListener('load', onLoad)
|
|
64
|
+
script.removeEventListener('error', onError)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const onLoad = () => {
|
|
68
|
+
cleanup()
|
|
69
|
+
this.loadedScripts.add(actualSrc)
|
|
70
|
+
this.loadedIds.add(id)
|
|
71
|
+
resolve(true)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const onError = (error) => {
|
|
75
|
+
cleanup()
|
|
76
|
+
console.error('[ScriptLoader] Failed to load script:', actualSrc, error)
|
|
77
|
+
resolve(false)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Timeout
|
|
81
|
+
timeoutId = setTimeout(() => {
|
|
82
|
+
cleanup()
|
|
83
|
+
console.warn('[ScriptLoader] Script load timeout:', actualSrc)
|
|
84
|
+
resolve(false)
|
|
85
|
+
}, this.config.timeout)
|
|
86
|
+
|
|
87
|
+
script.addEventListener('load', onLoad)
|
|
88
|
+
script.addEventListener('error', onError)
|
|
89
|
+
|
|
90
|
+
// Inject into DOM
|
|
91
|
+
const target = document.head || document.documentElement
|
|
92
|
+
target.appendChild(script)
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Load inline script
|
|
98
|
+
* @param {Object} scriptDescriptor - Script descriptor from registry
|
|
99
|
+
* @returns {Promise<boolean>}
|
|
100
|
+
*/
|
|
101
|
+
async loadInline(scriptDescriptor) {
|
|
102
|
+
const { content, attributes, id } = scriptDescriptor
|
|
103
|
+
|
|
104
|
+
if (!content) {
|
|
105
|
+
console.warn('[ScriptLoader] No content provided for inline script')
|
|
106
|
+
return false
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Deduplication: check by content hash
|
|
110
|
+
const contentHash = this._hashContent(content)
|
|
111
|
+
if (this.loadedHashes.has(contentHash) || this.loadedIds.has(id)) {
|
|
112
|
+
console.log('[ScriptLoader] Inline script already loaded')
|
|
113
|
+
return true
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
const script = document.createElement('script')
|
|
118
|
+
|
|
119
|
+
// Set content
|
|
120
|
+
script.textContent = content
|
|
121
|
+
|
|
122
|
+
// Apply attributes
|
|
123
|
+
Object.entries(attributes || {}).forEach(([key, value]) => {
|
|
124
|
+
script.setAttribute(key, value)
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
// Set proper type
|
|
128
|
+
script.type = 'text/javascript'
|
|
129
|
+
|
|
130
|
+
// Add data attribute for tracking
|
|
131
|
+
script.setAttribute('data-cookie-loaded', 'true')
|
|
132
|
+
script.setAttribute('data-cookie-id', id)
|
|
133
|
+
|
|
134
|
+
// Inject into DOM
|
|
135
|
+
const target = document.head || document.documentElement
|
|
136
|
+
target.appendChild(script)
|
|
137
|
+
|
|
138
|
+
// Mark as loaded
|
|
139
|
+
this.loadedHashes.add(contentHash)
|
|
140
|
+
this.loadedIds.add(id)
|
|
141
|
+
|
|
142
|
+
return true
|
|
143
|
+
} catch (error) {
|
|
144
|
+
console.error('[ScriptLoader] Failed to load inline script:', error)
|
|
145
|
+
return false
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Load script (auto-detect inline vs external)
|
|
151
|
+
* @param {Object} scriptDescriptor - Script descriptor
|
|
152
|
+
* @returns {Promise<boolean>}
|
|
153
|
+
*/
|
|
154
|
+
async load(scriptDescriptor) {
|
|
155
|
+
const { elementType } = scriptDescriptor
|
|
156
|
+
|
|
157
|
+
// Handle iframes
|
|
158
|
+
if (elementType === 'iframe') {
|
|
159
|
+
return this.loadIframe(scriptDescriptor)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Handle scripts
|
|
163
|
+
if (scriptDescriptor.inline) {
|
|
164
|
+
return this.loadInline(scriptDescriptor)
|
|
165
|
+
} else {
|
|
166
|
+
return this.loadExternal(scriptDescriptor)
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Load iframe (YouTube, Maps, etc)
|
|
172
|
+
* @param {Object} iframeDescriptor - Iframe descriptor
|
|
173
|
+
* @returns {Promise<boolean>}
|
|
174
|
+
*/
|
|
175
|
+
async loadIframe(iframeDescriptor) {
|
|
176
|
+
const { src, cookieSrc, attributes, id, element } = iframeDescriptor
|
|
177
|
+
const actualSrc = cookieSrc || src
|
|
178
|
+
|
|
179
|
+
if (!actualSrc) {
|
|
180
|
+
console.warn('[ScriptLoader] No src provided for iframe')
|
|
181
|
+
return false
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Deduplication
|
|
185
|
+
if (this.loadedScripts.has(actualSrc) || this.loadedIds.has(id)) {
|
|
186
|
+
console.log('[ScriptLoader] Iframe already loaded:', actualSrc)
|
|
187
|
+
return true
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
// Update existing iframe or create new one
|
|
192
|
+
if (element && element.parentNode) {
|
|
193
|
+
// Update src on existing element
|
|
194
|
+
element.src = actualSrc
|
|
195
|
+
|
|
196
|
+
// Remove data-cookie-src attribute
|
|
197
|
+
element.removeAttribute('data-cookie-src')
|
|
198
|
+
|
|
199
|
+
this.loadedScripts.add(actualSrc)
|
|
200
|
+
this.loadedIds.add(id)
|
|
201
|
+
return true
|
|
202
|
+
} else {
|
|
203
|
+
// Create new iframe
|
|
204
|
+
const iframe = document.createElement('iframe')
|
|
205
|
+
iframe.src = actualSrc
|
|
206
|
+
|
|
207
|
+
// Apply attributes
|
|
208
|
+
Object.entries(attributes || {}).forEach(([key, value]) => {
|
|
209
|
+
iframe.setAttribute(key, value)
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
iframe.setAttribute('data-cookie-loaded', 'true')
|
|
213
|
+
iframe.setAttribute('data-cookie-id', id)
|
|
214
|
+
|
|
215
|
+
// Find parent or append to body
|
|
216
|
+
const target = element?.parentNode || document.body
|
|
217
|
+
target.appendChild(iframe)
|
|
218
|
+
|
|
219
|
+
this.loadedScripts.add(actualSrc)
|
|
220
|
+
this.loadedIds.add(id)
|
|
221
|
+
return true
|
|
222
|
+
}
|
|
223
|
+
} catch (error) {
|
|
224
|
+
console.error('[ScriptLoader] Failed to load iframe:', error)
|
|
225
|
+
return false
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Load multiple scripts sequentially
|
|
231
|
+
* @param {Array} scriptDescriptors - Array of script descriptors
|
|
232
|
+
* @returns {Promise<Object>} Results object
|
|
233
|
+
*/
|
|
234
|
+
async loadMany(scriptDescriptors) {
|
|
235
|
+
const results = {
|
|
236
|
+
total: scriptDescriptors.length,
|
|
237
|
+
loaded: 0,
|
|
238
|
+
failed: 0,
|
|
239
|
+
scripts: []
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
for (const script of scriptDescriptors) {
|
|
243
|
+
const success = await this.load(script)
|
|
244
|
+
|
|
245
|
+
results.scripts.push({
|
|
246
|
+
id: script.id,
|
|
247
|
+
category: script.category,
|
|
248
|
+
success
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
if (success) {
|
|
252
|
+
results.loaded++
|
|
253
|
+
} else {
|
|
254
|
+
results.failed++
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return results
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Remove original blocked script element
|
|
263
|
+
* @param {HTMLScriptElement} element - Original script element
|
|
264
|
+
*/
|
|
265
|
+
removeBlockedScript(element) {
|
|
266
|
+
if (element && element.parentNode) {
|
|
267
|
+
element.parentNode.removeChild(element)
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Check if script is already loaded
|
|
273
|
+
* @param {string} src - Script src
|
|
274
|
+
* @returns {boolean}
|
|
275
|
+
*/
|
|
276
|
+
isLoaded(src) {
|
|
277
|
+
return this.loadedScripts.has(src)
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Clear loaded scripts cache
|
|
282
|
+
*/
|
|
283
|
+
clear() {
|
|
284
|
+
this.loadedScripts.clear()
|
|
285
|
+
this.loadedHashes.clear()
|
|
286
|
+
this.loadedIds.clear()
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Get loaded scripts count
|
|
291
|
+
* @returns {number}
|
|
292
|
+
*/
|
|
293
|
+
getLoadedCount() {
|
|
294
|
+
return this.loadedScripts.size
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Simple hash function for content deduplication
|
|
299
|
+
* @private
|
|
300
|
+
*/
|
|
301
|
+
_hashContent(content) {
|
|
302
|
+
let hash = 0
|
|
303
|
+
for (let i = 0; i < content.length; i++) {
|
|
304
|
+
const char = content.charCodeAt(i)
|
|
305
|
+
hash = ((hash << 5) - hash) + char
|
|
306
|
+
hash = hash & hash
|
|
307
|
+
}
|
|
308
|
+
return hash.toString(36)
|
|
309
|
+
}
|
|
310
|
+
}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ScriptManager — main controller for consent-based script activation
|
|
3
|
+
* @module ScriptManager
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ScriptScanner } from './ScriptScanner.js'
|
|
7
|
+
import { ScriptRegistry } from './ScriptRegistry.js'
|
|
8
|
+
import { ScriptLoader } from './ScriptLoader.js'
|
|
9
|
+
|
|
10
|
+
export class ScriptManager {
|
|
11
|
+
constructor(consentManager, config = {}) {
|
|
12
|
+
this.consentManager = consentManager
|
|
13
|
+
this.config = {
|
|
14
|
+
autoScan: true,
|
|
15
|
+
observeDOM: true,
|
|
16
|
+
removeBlockedScripts: true,
|
|
17
|
+
debug: false,
|
|
18
|
+
...config
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
this.scanner = new ScriptScanner({
|
|
22
|
+
observeDOM: this.config.observeDOM
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
this.registry = new ScriptRegistry()
|
|
26
|
+
this.loader = new ScriptLoader()
|
|
27
|
+
|
|
28
|
+
this.isInitialized = false
|
|
29
|
+
this._debug('ScriptManager initialized')
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Initialize script management system
|
|
34
|
+
*/
|
|
35
|
+
async initialize() {
|
|
36
|
+
if (this.isInitialized) {
|
|
37
|
+
this._debug('Already initialized')
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Wait for DOM ready
|
|
42
|
+
if (document.readyState === 'loading') {
|
|
43
|
+
await new Promise(resolve => {
|
|
44
|
+
document.addEventListener('DOMContentLoaded', resolve, { once: true })
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Initial scan
|
|
49
|
+
if (this.config.autoScan) {
|
|
50
|
+
await this.scanAndRegister()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Apply current consent
|
|
54
|
+
const consent = this.consentManager.getConsent()
|
|
55
|
+
if (consent) {
|
|
56
|
+
await this.applyConsent(consent.categories)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Listen for consent changes
|
|
60
|
+
this.consentManager.on('consentChanged', async ({ categories }) => {
|
|
61
|
+
await this.applyConsent(categories)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
// Observe DOM for new scripts
|
|
65
|
+
if (this.config.observeDOM) {
|
|
66
|
+
this.scanner.observe((newScripts) => {
|
|
67
|
+
this._debug('New scripts detected:', newScripts.length)
|
|
68
|
+
this.registry.addMany(newScripts)
|
|
69
|
+
|
|
70
|
+
// Auto-activate if consent already given
|
|
71
|
+
const consent = this.consentManager.getConsent()
|
|
72
|
+
if (consent) {
|
|
73
|
+
this.activateScriptsByConsent(newScripts, consent.categories)
|
|
74
|
+
}
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
this.isInitialized = true
|
|
79
|
+
this._debug('ScriptManager ready')
|
|
80
|
+
this._emitEvent('scripts:ready', { stats: this.registry.getStats() })
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Scan DOM and register all scripts
|
|
85
|
+
*/
|
|
86
|
+
async scanAndRegister() {
|
|
87
|
+
this._debug('Scanning DOM for scripts...')
|
|
88
|
+
const scripts = this.scanner.scan()
|
|
89
|
+
this._debug(`Found ${scripts.length} scripts`)
|
|
90
|
+
|
|
91
|
+
this.registry.addMany(scripts)
|
|
92
|
+
|
|
93
|
+
this._emitEvent('scripts:scanned', {
|
|
94
|
+
total: scripts.length,
|
|
95
|
+
stats: this.registry.getStats()
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
return scripts
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Apply consent to scripts
|
|
103
|
+
* @param {Object} categories - Consent categories
|
|
104
|
+
*/
|
|
105
|
+
async applyConsent(categories) {
|
|
106
|
+
this._debug('Applying consent:', categories)
|
|
107
|
+
|
|
108
|
+
const results = {
|
|
109
|
+
activated: [],
|
|
110
|
+
blocked: [],
|
|
111
|
+
failed: []
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Get all blocked scripts
|
|
115
|
+
const blockedScripts = this.registry.getBlocked()
|
|
116
|
+
|
|
117
|
+
for (const script of blockedScripts) {
|
|
118
|
+
const hasConsent = categories[script.category] === true
|
|
119
|
+
|
|
120
|
+
if (hasConsent) {
|
|
121
|
+
// Activate script
|
|
122
|
+
const success = await this.activateScript(script)
|
|
123
|
+
|
|
124
|
+
if (success) {
|
|
125
|
+
results.activated.push(script)
|
|
126
|
+
} else {
|
|
127
|
+
results.failed.push(script)
|
|
128
|
+
}
|
|
129
|
+
} else {
|
|
130
|
+
results.blocked.push(script)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
this._debug('Consent applied:', {
|
|
135
|
+
activated: results.activated.length,
|
|
136
|
+
blocked: results.blocked.length,
|
|
137
|
+
failed: results.failed.length
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
this._emitEvent('scripts:consent-applied', results)
|
|
141
|
+
|
|
142
|
+
return results
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Activate single script
|
|
147
|
+
* @param {Object} script - Script descriptor
|
|
148
|
+
* @returns {Promise<boolean>}
|
|
149
|
+
*/
|
|
150
|
+
async activateScript(script) {
|
|
151
|
+
if (script.isActivated) {
|
|
152
|
+
this._debug('Script already activated:', script.id)
|
|
153
|
+
return true
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
this._debug('Activating script:', script.id, script.category)
|
|
157
|
+
|
|
158
|
+
try {
|
|
159
|
+
// Load script
|
|
160
|
+
const success = await this.loader.load(script)
|
|
161
|
+
|
|
162
|
+
if (success) {
|
|
163
|
+
// Mark as activated
|
|
164
|
+
this.registry.markActivated(script.id)
|
|
165
|
+
|
|
166
|
+
// Remove original blocked element
|
|
167
|
+
if (this.config.removeBlockedScripts && script.element) {
|
|
168
|
+
this.loader.removeBlockedScript(script.element)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
this._emitEvent('script:activated', {
|
|
172
|
+
id: script.id,
|
|
173
|
+
category: script.category,
|
|
174
|
+
src: script.src || 'inline'
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
return true
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return false
|
|
181
|
+
} catch (error) {
|
|
182
|
+
return false
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Activate scripts by consent (helper for new scripts)
|
|
188
|
+
* @param {Array} scripts - Scripts to check
|
|
189
|
+
* @param {Object} categories - Consent categories
|
|
190
|
+
*/
|
|
191
|
+
async activateScriptsByConsent(scripts, categories) {
|
|
192
|
+
const results = []
|
|
193
|
+
|
|
194
|
+
for (const script of scripts) {
|
|
195
|
+
if (script.isBlocked && !script.isActivated) {
|
|
196
|
+
const hasConsent = categories[script.category] === true
|
|
197
|
+
|
|
198
|
+
if (hasConsent) {
|
|
199
|
+
const success = await this.activateScript(script)
|
|
200
|
+
results.push({ script, success })
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return results
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Get scripts by category
|
|
210
|
+
* @param {string} category - Category name
|
|
211
|
+
* @returns {Array}
|
|
212
|
+
*/
|
|
213
|
+
getScriptsByCategory(category) {
|
|
214
|
+
return this.registry.getByCategory(category)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Get all registered scripts
|
|
219
|
+
* @returns {Array}
|
|
220
|
+
*/
|
|
221
|
+
getAllScripts() {
|
|
222
|
+
return this.registry.getAll()
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Get registry stats
|
|
227
|
+
* @returns {Object}
|
|
228
|
+
*/
|
|
229
|
+
getStats() {
|
|
230
|
+
return this.registry.getStats()
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Get blocked scripts count
|
|
235
|
+
* @returns {number}
|
|
236
|
+
*/
|
|
237
|
+
getBlockedCount() {
|
|
238
|
+
return this.registry.getBlocked().length
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Get activated scripts count
|
|
243
|
+
* @returns {number}
|
|
244
|
+
*/
|
|
245
|
+
getActivatedCount() {
|
|
246
|
+
return this.registry.getActivated().length
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Manually trigger rescan
|
|
251
|
+
*/
|
|
252
|
+
async rescan() {
|
|
253
|
+
return this.scanAndRegister()
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Destroy script manager
|
|
258
|
+
*/
|
|
259
|
+
destroy() {
|
|
260
|
+
this.scanner.disconnect()
|
|
261
|
+
this.registry.clear()
|
|
262
|
+
this.loader.clear()
|
|
263
|
+
this.isInitialized = false
|
|
264
|
+
this._debug('ScriptManager destroyed')
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// --- Internal helpers ---
|
|
268
|
+
|
|
269
|
+
_emitEvent(eventName, detail) {
|
|
270
|
+
if (typeof window !== 'undefined') {
|
|
271
|
+
window.dispatchEvent(new CustomEvent(`cookie-consent:${eventName}`, { detail }))
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
_debug(...args) {
|
|
276
|
+
// Debug disabled
|
|
277
|
+
}
|
|
278
|
+
}
|