Skip to content

Instantly share code, notes, and snippets.

@3urdoch
Last active November 25, 2020 10:39
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save 3urdoch/ce855850d94af8fbb4f3f7f27269cb28 to your computer and use it in GitHub Desktop.
Save 3urdoch/ce855850d94af8fbb4f3f7f27269cb28 to your computer and use it in GitHub Desktop.
TCF2.0 Compliant approach to loading Google Ad Manager, Prebid and Amazon APS tags that prevents TCV2.1a errors
<!doctype html>
<html lang="en">
<head>
</head>
<body>
<!-- Quantcast CMP, pasted un-modified from Quantcast interface -->
<script type="text/javascript" async=true>
(function() {
var host = window.location.hostname;
var element = document.createElement('script');
var firstScript = document.getElementsByTagName('script')[0];
var url = 'https://quantcast.mgr.consensu.org'
.concat('/choice/', '<<your quantcast id>>', '/', host, '/choice.js')
var uspTries = 0;
var uspTriesLimit = 3;
element.async = true;
element.type = 'text/javascript';
element.src = url;
firstScript.parentNode.insertBefore(element, firstScript);
function makeStub() {
var TCF_LOCATOR_NAME = '__tcfapiLocator';
var queue = [];
var win = window;
var cmpFrame;
function addFrame() {
var doc = win.document;
var otherCMP = !!(win.frames[TCF_LOCATOR_NAME]);
if (!otherCMP) {
if (doc.body) {
var iframe = doc.createElement('iframe');
iframe.style.cssText = 'display:none';
iframe.name = TCF_LOCATOR_NAME;
doc.body.appendChild(iframe);
} else {
setTimeout(addFrame, 5);
}
}
return !otherCMP;
}
function tcfAPIHandler() {
var gdprApplies;
var args = arguments;
if (!args.length) {
return queue;
} else if (args[0] === 'setGdprApplies') {
if (
args.length > 3 &&
args[2] === 2 &&
typeof args[3] === 'boolean'
) {
gdprApplies = args[3];
if (typeof args[2] === 'function') {
args[2]('set', true);
}
}
} else if (args[0] === 'ping') {
var retr = {
gdprApplies: gdprApplies,
cmpLoaded: false,
cmpStatus: 'stub'
};
if (typeof args[2] === 'function') {
args[2](retr);
}
} else {
queue.push(args);
}
}
function postMessageEventHandler(event) {
var msgIsString = typeof event.data === 'string';
var json = {};
try {
if (msgIsString) {
json = JSON.parse(event.data);
} else {
json = event.data;
}
} catch (ignore) {}
var payload = json.__tcfapiCall;
if (payload) {
window.__tcfapi(
payload.command,
payload.version,
function(retValue, success) {
var returnMsg = {
__tcfapiReturn: {
returnValue: retValue,
success: success,
callId: payload.callId
}
};
if (msgIsString) {
returnMsg = JSON.stringify(returnMsg);
}
event.source.postMessage(returnMsg, '*');
},
payload.parameter
);
}
}
while (win) {
try {
if (win.frames[TCF_LOCATOR_NAME]) {
cmpFrame = win;
break;
}
} catch (ignore) {}
if (win === window.top) {
break;
}
win = win.parent;
}
if (!cmpFrame) {
addFrame();
win.__tcfapi = tcfAPIHandler;
win.addEventListener('message', postMessageEventHandler, false);
}
};
makeStub();
var uspStubFunction = function() {
var arg = arguments;
if (typeof window.__uspapi !== uspStubFunction) {
setTimeout(function() {
if (typeof window.__uspapi !== 'undefined') {
window.__uspapi.apply(window.__uspapi, arg);
}
}, 500);
}
};
var checkIfUspIsReady = function() {
uspTries++;
if (window.__uspapi === uspStubFunction && uspTries < uspTriesLimit) {
console.warn('USP is not accessible');
} else {
clearInterval(uspInterval);
}
};
if (typeof window.__uspapi === 'undefined') {
window.__uspapi = uspStubFunction;
var uspInterval = setInterval(checkIfUspIsReady, 6000);
}
})();
</script>
<!-- Our Prebid.js build, all bidders are IAB registered and the build
includes Consent Management and GDPR Enforcement -->
<script async src="/prebid.js"></script>
<!-- Amazon APS -->
<script>
!function(a9,a,p,s,t,A,g){if(a[a9])return;function q(c,r){a[a9]._Q.push([c,r])}a[a9]={init:function(){q("i",arguments)},fetchBids:function(){q("f",arguments)},setDisplayBids:function(){},targetingKeys:function(){return[]},_Q:[]};A=p.createElement(s);A.async=!0;A.src=t;g=p.getElementsByTagName(s)[0];g.parentNode.insertBefore(A,g)}("apstag",window,document,"script","//c.amazon-adsystem.com/aax2/apstag.js");
</script>
<!-- Our bespoke ad code -->
<script>
window.googletag=window.googletag||{};
window.googletag.cmd=window.googletag.cmd||[];
window.pbjs=window.pbjs||{};
window.pbjs.que=window.pbjs.que||[];
(function() {
// Initiate the whole ad stack, including
// Prebid, amazon and GAM
function initAdStack() {
// a whole load of code in here to initiate your ad stack
// e.g.
// * request bids through pre bid
// * request bids through amazon
// * define & display ad units via googletag
}
// Wait for the signal before touching GAM/Amazon/Prebid
window.__tcfapi('addEventListener', 2, function(tcData, listenerSuccess) {
if (listenerSuccess) {
// Don't initiate ads until after the result from the CMP
// 'useractioncomplete' >> Fired when the user clicks accept/reject
// 'tcloaded' >> Fired when the CMP loads, the user may or may not have made a decision yet
if( tcData.eventStatus === 'useractioncomplete' || tcData.eventStatus === 'tcloaded' ) {
// Don't bother initiating any ad systems unless you have concent for Purpose 1.
if( tcData.purpose.consents[1] ) {
if( !window.adInit ) {
// Make sure we only call this once per page load just in case the user
// changing preferences triggers a 2nd event.
window.adInit = true;
// Iniaite you're ad stack
initAdStack();
}
}
}
}
});
})();
</script>
<!-- Register GAM script after Quantcast but I don't think it matters if its before
or after as it only sets cookies once you interact with it via `googletag` -->
<script async src="//www.googletagservices.com/tag/js/gpt.js"></script>
<!-- ... rest of page ... -->
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment