Demo Tracking Services
Important:
All tracking scripts on this page use non-functional demo IDs. No data is actually collected or transmitted. This demonstrates how the consent management system would classify and block real tracking scripts.
This page loads demo tracking scripts that you can test blocking and unblocking:
Analytics (3 services)
- Google Analytics (GA4): gtag/js requests
- Hotjar: hotjar.com script requests
- Custom Analytics: dataLayer events
Demo Test: Enable "Analytics" consent → Check Network tab (requests use demo IDs only)
Marketing (5 services)
- Facebook Pixel: connect.facebook.net
- LinkedIn: snap.licdn.com
- Twitter/X: static.ads-twitter.com
- TikTok: analytics.tiktok.com
- Marketo: munchkin.marketo.net
Demo Test: Enable "Marketing" → Watch for demo script requests (no real tracking)
Embeds (2 services)
- YouTube: Video embed above
- SoundCloud: Audio player above
Demo Test: Enable "Embeds" → Media players will load (real content, not tracking)
Quick Verification Commands
UCLPrivacy.getStats()
// Shows blocked requests, cookies, scripts
UCLPrivacy.updateConsent('analytics', true)
// Demo scripts load but don't collect real data
UCLPrivacy.updateConsent('marketing', true)
// Demo scripts load but don't perform real tracking
Deployment
The modular architecture simplifies deployment whilst maintaining security. Add the following to your page's <head> section.
CONFIGURATION (OPTIONAL)
<script>
window.privacyConfig = {
// Allowlist domains that should never be blocked
allowlist: [
'cdn.yoursite.com',
'api.yoursite.com',
'fonts.googleapis.com'
],
// Reclassify specific domains to different categories
reclassify: {
'internal-analytics.yoursite.com': 'necessary'
},
// Extend built-in domain lists with additional services
extend: {
analytics: ['matomo.yoursite.com'],
marketing: ['custom-tracker.com'],
embeds: ['custom-video-platform.com']
},
// Extend cookie classification patterns
cookiePatterns: {
analytics: ['^custom_analytics_', 'internal_tracking_.*'],
marketing: ['^custom_ads_', 'retargeting_.*']
},
// Extend platform display names for blocked content
platformMapping: {
'custom-video.yoursite.com': 'Your Custom Video Platform',
'internal-media.yoursite.com': 'Internal Media Service'
},
// Extend script content classification patterns
scriptPatterns: {
analytics: [
{ pattern: 'customAnalytics.track', service: 'Custom Analytics' }
],
marketing: [
{ pattern: 'customAds.fire', service: 'Custom Ad Platform' }
]
},
// Settings button position configuration
settingsButton: {
// Default position for all pages
position: {
side: 'right', // 'left' or 'right'
bottom: '20px', // CSS length (px, rem, vh, vw, em, %, pt)
horizontal: '20px', // Distance from left or right edge
zIndex: 999997 // Stacking order (1-999999)
},
// URL-based conditional positioning
urlConditions: [
{
pattern: '/admin/*', // Wildcard pattern (* = any characters)
position: {
side: 'left', // Override side
bottom: '60px' // Other properties inherit from default
}
},
{
pattern: '/checkout/*',
visibility: {
hidden: true // Hide button on checkout pages
}
}
],
// Global visibility toggle
visibility: {
hidden: false // Set true to disable button entirely
}
},
// Configuration options
options: {
// Development and debugging
debug: false, // Enable verbose debug logging
logging: false, // Log blocking actions to console (default: false)
// Blocking behaviour
blockingMode: 'strict', // 'strict' or 'balanced' approach
defaultUnknownAction: 'block', // How to handle unclassified resources
blockFirstPartyInlineScripts: false, // Allow essential CMS patterns (default)
blockFirstPartyProxies: true, // Block oEmbed proxy bypasses (default: true)
// User interface
bannerDelay: 500, // Delay before showing consent banner (ms)
enablePlaceholders: true, // Show placeholders for blocked content
showBanner: true, // Display consent banner to new users
cookiePolicyUrl: './cookie-policy.html' // Link to cookie policy page
}
};
</script>
SETTINGS BUTTON POSITION CONFIGURATION
window.privacyConfig = {
settingsButton: {
// Default position for all pages
position: {
side: 'right', // 'left' or 'right'
bottom: '20px', // CSS length (px, rem, vh, vw, em, %, pt)
horizontal: '20px', // Distance from left or right edge
zIndex: 999997 // Stacking order (1-999999)
},
// URL-based conditional positioning
urlConditions: [
{
pattern: '/admin/*', // Wildcard patterns (* = any characters)
position: {
side: 'left', // Override side
bottom: '60px' // Other properties inherit from default
}
},
{
pattern: '/checkout/*',
visibility: {
hidden: true // Hide button on checkout pages
}
},
{
pattern: '/node/*/edit',
position: {
side: 'right',
bottom: '80px',
horizontal: '30px'
}
}
],
// Global visibility toggle
visibility: {
hidden: false // Set true to disable button entirely
}
}
};
// Patterns match against URL pathname only (not domain or query string)
// Supports * wildcard for any characters (simple glob patterns)
// Case-sensitive matching
// Examples:
'/admin/*' → matches /admin/users, /admin/settings/profile
'/*/edit' → matches /posts/123/edit, /pages/about/edit
'/checkout' → matches /checkout only (exact match)
// Level 1: System defaults
{side: 'right', bottom: '20px', horizontal: '20px', zIndex: 999997}
// Level 2: User defaults (merge with system)
{side: 'left'}
// Result: {side: 'left', bottom: '20px', horizontal: '20px', zIndex: 999997}
// Level 3: Matched rule (merge with user defaults)
{bottom: '60px'}
// Final: {side: 'left', bottom: '60px', horizontal: '20px', zIndex: 999997}
// View effective button configuration for current page
const config = UCLPrivacyUI.getEffectiveButtonConfig();
console.log(config.position.side); // 'left' or 'right'
console.log(config.hidden); // true or false
Important Limitations:
- Single-Page Applications (SPAs): Pattern matching happens once at page load. If your site changes URLs without full page reload (React Router, Vue Router, etc.), the button position will not update automatically.
- Query Strings: Patterns match pathname only.
/search?q=test → pattern matches /search, not query parameters.
- Hash Fragments: Not included in pathname matching.
/page#section → pattern matches /page.
- CSP Requirements: Inline styles with
!important require style-src 'unsafe-inline' or appropriate nonces in your Content Security Policy.
OPTION 1: SINGLE FILE DEPLOYMENT (RECOMMENDED)
<script src="https://cookie-consent.ucl.ac.uk/dist/ucl-consent.blocker.min.js"></script>
<script src="https://cookie-consent.ucl.ac.uk/dist/ucl-consent.ui-full.min.js" defer></script>
OPTION 2: SEPARATE CSS DEPLOYMENT
<script src="https://cookie-consent.ucl.ac.uk/dist/ucl-consent.blocker.min.js"></script>
<script src="https://cookie-consent.ucl.ac.uk/dist/ucl-consent.ui-headless.min.js" defer></script>
<link rel="stylesheet" href="https://cookie-consent.ucl.ac.uk/dist/ucl-consent-ui.css">
PROTECTING ESSENTIAL SCRIPTS
<!-- Configuration scripts (automatically detected) -->
<script>
window.privacyConfig = { /* your config */ };
</script>
<!-- Essential functionality scripts -->
<script data-privacy-necessary="true">
// Critical site functionality
</script>
<!-- Additional configuration scripts -->
<script data-privacy-config="true">
// Setup or configuration code
</script>
INTELLIGENT INLINE SCRIPT HANDLING
// Default behavior (recommended for CMS sites)
window.privacyConfig = {
options: {
blockFirstPartyInlineScripts: false // Allows essential patterns, blocks tracking
}
};
// Detected patterns that get blocked:
// - gtag(), ga(), fbq(), ttq() tracking calls
// - Google Analytics, Facebook Pixel, TikTok Pixel
// - LinkedIn tracking, Twitter Pixel, Hotjar
// Allowed patterns (essential functionality):
// - jQuery fallback scripts
// - CMS configuration (Drupal.settings, WordPress localization)
// - Feature detection and progressive enhancement
// - Basic DOM event handlers
OEMBED PROXY PROTECTION
// Default behaviour (recommended for all sites)
window.privacyConfig = {
options: {
blockFirstPartyProxies: true // Prevents consent bypasses through proxy endpoints
}
};
// Automatically detected proxy patterns for security:
// - WordPress: /wp-json/oembed/1.0/proxy
// - Drupal: /media/oembed
// - Custom CMS: /embed/, /proxy/, /api/oembed
// Example blocked URL demonstrating protection:
// yourdomain.com/media/oembed?url=https://youtube.com/watch?v=...
// Target URL (https://youtube.com) classified as 'embeds' and blocked appropriately
// Prevents circumvention of user consent preferences through proxy mechanisms
STRICT SECURITY MODE
// Strict security (requires manual script marking)
window.privacyConfig = {
options: {
blockFirstPartyInlineScripts: true // Blocks all inline scripts by default
}
};
// Use data attributes to mark essential scripts:
<script data-privacy-necessary="true">
// Only marked scripts will execute
</script>
Console API Documentation
The modular API is split between the blocker (UCLPrivacy) and UI (UCLPrivacyUI) for better separation of concerns.
UCLPrivacy API (from blocker)
UCLPrivacy.getConsent()
// Returns: {necessary: true, analytics: false, ...}
UCLPrivacy.updateConsent('analytics', true)
UCLPrivacy.updateAllConsent({analytics: true, marketing: true, embeds: true})
UCLPrivacy.getStats()
UCLPrivacy.getBlockedElements()
UCLPrivacy.restoreContent('embeds')
// The UI will listen for this and restore placeholders.
UCLPrivacy.getCookiePatterns()
// Returns: {analytics: [...], marketing: [...], embeds: [...]}
UCLPrivacy.getProxyPatterns()
// Returns: ['/wp-json/oembed', '/media/oembed', ...]
UCLPrivacyUI API (from UI)
UCLPrivacyUI.showSettings()
UCLPrivacyUI.hideBanner()
UCLPrivacyUI.getPlatformMapping()
// Returns: {'www.youtube.com': 'YouTube', 'soundcloud.com': 'SoundCloud', ...}
Event Listening
window.addEventListener('ucl-privacy-consent-updated', (e) => console.log(e.detail))