Blocking tags using Guardian
Asking consent is not enough for your website, you need to be sure that cookies or others tracking technologies are not used without the consent of the user.
For this, we propose a script called Guardian.
info
At this point, we assume that you followed the instructions in this section to install the CMP
1. Setup the whitelist/blacklist list in the <head>
In the <head>
section, configure the urls to be blacklisted or whitelisted in the dynamicallyLoadedScripts in the parameter of the configSFBXAppConsent configuration variable like this :
<script type="text/javascript">
const configSFBXAppConsent = {
appKey: 'YOUR_APP_KEY',
dynamicallyLoadedScripts: {
blacklist: [
/facebook/, /youtube/,
],
whitelist: [
/appconsent/,
]
},
}
</script>
info
In the code above, we take control on Facebook and Youtube. If you want to add a new domain, let's say ads-twitter.com, just add this new entry
,/ads-twitter.com/
2. Add the Guardian script
Add the Guardian script in the <head>
section, after the definition of the configSFBXAppConsent variable.
<script>
(function(h,p){typeof exports=="object"&&typeof module<"u"?p(exports):typeof define=="function"&&define.amd?define(["exports"],p):(h=typeof globalThis<"u"?globalThis:h||self,p(h.sfbxguardian={}))})(this,function(h){"use strict";let p=!1;function P(t){function e(i){for(var s=0,r=0;r<i.length;r++)s=i.charCodeAt(r)+((s<<5)-s);return s}function n(i){return"hsl("+e(i)%360+", 100%, 80%)"}return function(i,...s){if(p&&console.log){if(i instanceof Error&&console.error){console.error(i);return}console.log("%c "+t+" %c "+i,"background: "+n(t)+"; color: #000","",...s)}}}function _(t){if(t===void 0)return p;p=!!t}const B=()=>{const t=window.location.search.substr(1).split("&");if(t==="")return{};for(var e={},n=0;n<t.length;++n){var i=t[n].split("=",2);if(i.length===1)e[i[0]]="";else{var s=decodeURIComponent(i[1].replace(/\+/g," "));s==="false"&&(s=!1),s==="0"&&(s=0),s==="true"&&(s=!0),s===""&&(s=null),e[i[0]]=s}}return e},d=P("guardian"),H=()=>{const t=B();(t.ac_cmd&&t.ac_cmd==="debug"||configSFBXAppConsent&&configSFBXAppConsent.debug)&&_(!0)},f="javascript/blocked",F="application/javascript",c={SCRIPT:"SCRIPT",IFRAME:"IFRAME",BLOCKQUOTE:"BLOCKQUOTE"},w=[c.SCRIPT,c.IFRAME,c.BLOCKQUOTE],o={blacklist:configSFBXAppConsent.dynamicallyLoadedScripts.blacklist,whitelist:configSFBXAppConsent.dynamicallyLoadedScripts.whitelist},l={blacklisted:[],hiddenBlacklisted:[]},U="sfbx_guardian_",b={INSTAGRAM:"instagram-media",TWITTER:"twitter-tweet",TIKTOK:"tiktok-embed"},g={};g[b.INSTAGRAM]="instagram.com",g[b.TIKTOK]="tiktok.com",g[b.TWITTER]="twitter.com";const D=[b.INSTAGRAM,b.TWITTER,b.TIKTOK],k=t=>{switch(t.tagName){case c.BLOCKQUOTE:return j(t);case c.IFRAME:case c.SCRIPT:return K(t)}},K=t=>t.src&&(!t.type||t.type!==f)&&E(t.src),j=function(t){return t.className.split(" ").some(n=>D.includes(n)&&W(n))},W=function(t){const e=g[t];return E(e)},E=t=>(!o.blacklist||o.blacklist.some(e=>e.test(t)))&&(!o.whitelist||o.whitelist.every(e=>!e.test(t))),v=t=>{switch(t.tagName){case c.BLOCKQUOTE:return q(t);case c.IFRAME:case c.SCRIPT:return X(t)}},X=function(t){const e=t.getAttribute("src");return A(e)},q=function(t){return t.className.split(" ").some(n=>G(n))},G=function(t){const e=g[t];return A(e)};function A(t){return o.blacklist&&o.blacklist.every(e=>!e.test(t))||o.whitelist&&o.whitelist.some(e=>e.test(t))}const Q=t=>t.offsetHeight>0&&t.offsetWidth>0,u=(t,e)=>{Object.keys(e).forEach(n=>{t.style[n]=e[n]})},$=function(){let t="";const e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";for(let n=0;n<8;n++)t+=e.charAt(Math.floor(Math.random()*e.length));return t},S="#0252B6",V={width:"100%",height:"100%",display:"flex",justifyContent:"center",alignItems:"center"},z={display:"flex",flexDirection:"column",borderRadius:"9px",backgroundColor:"white",padding:"20px",border:`1px solid${S}`,fontFamily:"Montserrat, Roboto, Tahoma, Helvetica, Arial, sans-serif"},Y={display:"flex",justifyContent:"center",marginTop:"20px"},J={display:"flex",justifyContent:"center",alignItems:"center",padding:"10px 20px",borderRadius:"6px",fontStyle:"normal",fontWeight:"500",fontSize:"1.1rem",borderColor:`1px solid ${S}`,cursor:"pointer",backgroundColor:`${S}`,color:"white",boxShadow:"none",letterSpacing:"0.05rem"},Z={fontSize:"1rem",fontWeight:400,color:"#02244F",lineHeight:"1.6rem"};class ee{constructor(e,n,i){this.node=e,this.parentElement=i,this.id=n,this.isVisible=!0,this.wrapper=document.createElement("div"),this.init()}init(){this.isVisible=Q(this.node),this.isVisible||this.addToHiddenBlacklisted(),this.buildHtml()}addToHiddenBlacklisted(){y.observe(this.parentElement),C.observe(this.parentElement),l.hiddenBlacklisted.push(this)}buildHtml(){u(this.wrapper,V),this.wrapper.setAttribute("id",this.id),this.buildContainer()}buildContainer(){const e=document.createElement("div");e.classList.add("message-container"),u(e,z),this.isVisible||u(e,{display:"none"}),e.appendChild(this.buildHeader()),e.appendChild(this.buildMessage()),e.appendChild(this.buildButton()),this.wrapper.appendChild(e)}buildHeader(){return document.createElement("div")}buildMessage(){const e=document.createElement("aside");return u(e,Z),e.innerHTML=this.buildDefaultText(),e}buildDefaultText(){return"<p>Le contenu est bloqué, car vous avez décidé de refuser les cookies et usages nécessaires à son affichage.</p> <p>Usages nécessaires : </p> <ul style='margin-bottom: 16px'><li>Interactions avec les réseaux sociaux : Ces cookies vous permettent de partager des contenus de notre site, d’interagir avec les réseaux sociaux et d'afficher des contenus provenant des réseaux sociaux.</li></ul>"}buildButton(){const e=document.createElement("div"),n=document.createElement("div");return u(e,Y),u(n,J),n.innerHTML="Modifier mon consentement",n.onclick=this.callbackDisplayCMP,e.appendChild(n),e}callbackDisplayCMP(){window.__tcfapi("show",2,()=>{},{})}getMessageContainer(){return this.wrapper.querySelector(".message-container")}}class te{constructor(e){this.node=e,this.oldType=null,this.parentElement=this.node.parentElement,this.message=null,this.id=`${U}${$()}`,this.init()}init(){const{tagName:e}=this.node;switch(e){case c.SCRIPT:this.blockScript();break;case c.IFRAME:case c.BLOCKQUOTE:this.blockElementWithSrc();break}this.removeNodeToDocument()}blockScript(){const{type:e}=this.node;this.oldType=e,this.node.setAttribute("type",f),this.addBeforeScriptListener()}blockElementWithSrc(){this.addWrapper()}addBeforeScriptListener(){const e=function(n){this.node.getAttribute("type")===f&&n.preventDefault(),this.node.removeEventListener("beforescriptexecute",e)};this.node.addEventListener("beforescriptexecute",e)}removeNodeToDocument(){this.parentElement&&this.parentElement.removeChild(this.node)}addWrapper(){d("add wrapper",this.id,this.node,this.parentElement),this.message=new ee(this.node,this.id,this.parentElement),this.parentElement.appendChild(this.message.wrapper)}}const O=new MutationObserver(t=>{for(let e=0;e<t.length;e++){const{addedNodes:n}=t[e];for(let i=0;i<n.length;i++){const s=n[i],{tagName:r,nodeType:a}=s;a===1&&w.includes(r)&&d("checking",k(s),s),a===1&&w.includes(r)&&k(s)&&(L(s),d("observer",l))}}}),y=new ResizeObserver(t=>{t.forEach(e=>{const n=I(e.target);if(n){const i=n.getMessageContainer(),s=e.target.offsetHeight>0?"flex":"none";d("resizeObserver",e.target,n.id,s,e.target.offsetHeight),u(i,{display:s})}})}),C=new IntersectionObserver(t=>{t.forEach(e=>{const n=I(e.target);if(n){const i=n.getMessageContainer(),s=e.isIntersecting&&e.target.offsetHeight>0?"flex":"none";d("intersectionObserver",e.target,n.id,s,e.target.offsetHeight),u(i,{display:s})}})});function I(t){return l.hiddenBlacklisted.find(e=>t&&e.parentElement.isEqualNode(t))}function L(t){const e=new te(t);l.blacklisted.push(e)}const ne=()=>{O.observe(document.documentElement,{childList:!0,subtree:!0})},x=document.createElement,m={src:Object.getOwnPropertyDescriptor(HTMLScriptElement.prototype,"src"),type:Object.getOwnPropertyDescriptor(HTMLScriptElement.prototype,"type")};document.createElement=function(...t){if(t[0].toLowerCase()!=="script")return x.bind(document)(...t);const e=x.bind(document)(...t);try{Object.defineProperties(e,{src:{...m.src,set(n){E(n)&&m.type.set.call(this,f),m.src.set.call(this,n)}},type:{...m.type,get(){const n=m.type.get.call(this);return n===f||E(this.src)?null:n},set(n){const i=k(e.src)?f:n;m.type.set.call(this,i)}}}),e.setAttribute=function(n,i){n==="type"||n==="src"?e[n]=i:HTMLScriptElement.prototype.setAttribute.call(e,n,i)}}catch{console.warn("sfbxguardian: unable to prevent script execution for script src ",e.src,`.
`,'A likely cause would be because you are using a third-party browser extension that monkey patches the "document.createElement" function.')}return e};const se=new RegExp("[|\\{}()[\\]^$+*?.]","g"),ie=function(...t){oe(t),re();let e=0;[...l.blacklisted].forEach((n,i)=>{const{id:s,node:r,oldType:a}=n;if(v(r)){switch(r.tagName){case c.SCRIPT:ce(r,a);break;case c.IFRAME:N(s),le(r,s);break;case c.BLOCKQUOTE:N(s),ae(r,s);break}d("node unblocked",r),l.blacklisted.splice(i-e,1),e++}}),o.blacklist&&o.blacklist.length<1&&(O.disconnect(),y.disconnect(),C.disconnect(),d("observers disconnected"))},N=t=>{const e=l.hiddenBlacklisted.find(n=>n.id===t);e&&(y.unobserve(e.parent),C.unobserve(e.parent),l.hiddenBlacklisted=l.hiddenBlacklisted.filter(n=>n.id!==t))};function oe(t){t.length<1?(o.blacklist=[],o.whitelist=[]):(o.blacklist&&(o.blacklist=o.blacklist.filter(e=>t.every(n=>{if(typeof n=="string")return!e.test(n);if(n instanceof RegExp)return e.toString()!==n.toString()}))),o.whitelist&&(o.whitelist=[...o.whitelist,...t.map(e=>{if(typeof e=="string"){const i=".*"+e.replace(se,"\\$&")+".*";if(o.whitelist.every(s=>s.toString()!==i.toString()))return new RegExp(i)}else if(e instanceof RegExp&&o.whitelist.every(n=>n.toString()!==e.toString()))return e;return null}).filter(Boolean)]))}function re(){const t=document.querySelectorAll(`script[type="${f}"]`);for(let e=0;e<t.length;e++){const n=t[e];v(n)&&L(n)}}function ce(t,e){const n=document.createElement("script");for(let i=0;i<t.attributes.length;i++){let s=t.attributes[i];s.name!=="src"&&s.name!=="type"&&n.setAttribute(s.name,t.attributes[i].value)}n.setAttribute("src",t.src),n.setAttribute("type",e||F),document.head.appendChild(n)}function le(t,e){M(t,"iframe",e).setAttribute("src",t.src)}function ae(t,e){const n=M(t,"blockquote",e),i=t.children;for(let s=0;s<i.length;s++)n.appendChild(i[s].cloneNode(!0))}function M(t,e,n){const i=document.getElementById(n),s=i.parentElement,r=document.createElement(e);for(let a=0;a<t.attributes.length;a++){let T=t.attributes[a];T.name!=="src"&&T.name!=="type"&&r.setAttribute(T.name,t.attributes[a].value)}return s.removeChild(i),s.appendChild(r),r}if(typeof configSFBXAppConsent>"u")throw console.error("SFBX Guardian has not been executed! The configuration (configSFBXAppConsent) must be placed before calling the script "),Error();if(!configSFBXAppConsent.dynamicallyLoadedScripts||!configSFBXAppConsent.dynamicallyLoadedScripts.blacklist&&!configSFBXAppConsent.dynamicallyLoadedScripts.whitelist)throw console.warn("SFBX Guardian: No whitelist or blacklist has been defined"),Error();function de(){H(),d("init"),ne()}const R=B();(!R.ac_cmd||R.ac_cmd!=="no_guardian")&&de(),h.unblock=ie,Object.defineProperty(h,Symbol.toStringTag,{value:"Module"})});
</script>
Important
The Guardian script must be placed after the configSFBXAppConsent variable to work.
That's it - All the tags are now blocked until the user consent. If the user deny cookies in the cmp, the tags will remains blocked.
info
This library is using observer and override CreateElement core JS functionalities. If you expriment tests that are not working, please deactivate Chrome or Firefox extension in your browser.
We will add new capabilities regularly ( more controls, shared database of tags .. )
Any issue or suggestions ? Drop an email at support@sfbx.io