Skip to content

Instantly share code, notes, and snippets.

@josephrussell-cn
Last active February 28, 2022 19:49
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save josephrussell-cn/908fdba0c80b7d38f18119cbc5263039 to your computer and use it in GitHub Desktop.
Save josephrussell-cn/908fdba0c80b7d38f18119cbc5263039 to your computer and use it in GitHub Desktop.
Provides tcfapi interface to iframe window using amp-consent data
// amp-iframe tcfapi proxy
// The cne video player inside the iframe on amp does not have access to consent data.
// This script is creating a __tcfapi proxy that scripts needing it can call to obtain TCData.
// TCData is sourced from a postMessage with type=send-consent-data to sentinel=amp
// Should only execute on AMP pages where gdpr is enabled
var consentMetadata;
var consentString;
var gdprApplies;
var queue = [];
try {
// only add the proxy if a __tcfapiLocator frame is present
var tcfapiLocatorDetected = false;
try {
if (window.top.frames['__tcfapiLocator']) {
tcfapiLocatorDetected = true;
}
} catch (error) {
tcfapiLocatorDetected = false;
}
if (tcfapiLocatorDetected) {
// create a __tcfapi proxy that can be called by required scripts within the iframe
window.__tcfapi = function (cmd, version, callback, parameter) {
console.log(cmd);
console.log(version);
console.log(callback);
console.log(parameter);
if (!cmd) {
return queue;
} else if (cmd === 'ping') {
if (callback === 'function') {
if (consentString) {
callback({
gdprApplies: gdprApplies,
cmpLoaded: true,
cmpStatus: 'loaded'
});
} else {
callback({
gdprApplies: gdprApplies,
cmpLoaded: false,
cmpStatus: 'stub'
});
}
}
// https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/TCFv2/IAB%20Tech%20Lab%20-%20CMP%20API%20v2.md#gettcdata
} else if (cmd === 'getTCData') {
// https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/TCFv2/IAB%20Tech%20Lab%20-%20CMP%20API%20v2.md#tcdata
if (typeof callback === 'function') {
var tcData = Object.assign({
tcString: consentString
}, consentMetadata);
callback({
tcData: tcData,
success: true
});
}
} else if (cmd === 'addEventListener') {
var listener = {cmd:cmd, version:version, callback:callback, parameter:parameter, id: Math.random() + ''}
queue.push(listener);
if (consentString && typeof listener.callback === 'function') {
var tcData = Object.assign({
tcString: consentString,
eventStatus:'tcloaded',
listenderId:listener.id,
cmpStatus: 'loaded'
}, consentMetadata);
console.log(tcData);
console.log(consentString);
listener.callback(tcData, true);
}
} else if (cmd === 'removeEventListener') {
if (parameter) {
for (var i=0; i < queue.length; i++) {
if (listener.id && listener.id === parameter) {
queue.splice(i, 1);
if (callback === 'function') {
callback(true); // success = true
}
}
}
}
}
}
var processQueue = function () {
if (consentString) {
for (var i=0; i < queue.length; i++) {
var listener = queue[i];
if (listener.callback && typeof listener.callback === 'function') {
console.log('processQueue ' + listener.id);
var tcData = Object.assign({
tcString: consentString,
eventStatus:'tcloaded',
cmpStatus: 'loaded',
listenderId:listener.id
}, consentMetadata);
listener.callback(tcData, true);
}
}
}
}
var isAmpMessage = function isAmpMessage(event, type) {
return (event.source == window.parent && event.origin != window.location.origin && event.data && event.data.sentinel == 'amp' && event.data.type == type);
}
var postMessageHandler = function postMessageHandler(event) {
// response from amp-consent get-consent-data
if (isAmpMessage(event, 'consent-data')) {
// save consent data so it can be returned with 'getTCData'
gdprApplies = true;
consentMetadata = event.data.consentMetadata;
consentString = event.data.consentString;
console.log(consentMetadata);
console.log(consentString);
processQueue();
} else {
// could be any other postMessage on the window here
var msgIsString = typeof event.data === 'string';
var json = {};
if (msgIsString) {
try {
/**
* Try to parse the data from the event. This is important
* to have in a try/catch because often messages may come
* through that are not JSON
*/
json = JSON.parse(event.data);
} catch (ignore) { }
} else {
json = event.data;
}
var payload = (typeof json === 'object') ? json.__tcfapiCall : null;
if (payload) {
// this should be a message with __tcfapiCall asking for a response from __tcfapi
window.__tcfapi(payload.command, payload.version, function (retValue, success) {
var returnMsg = {
__tcfapiReturn: {
returnValue: retValue,
success: success,
callId: payload.callId,
},
};
if (event && event.source && event.source.postMessage) {
event.source.postMessage((msgIsString) ? JSON.stringify(returnMsg) : returnMsg, '*');
}
}, payload.parameter);
}
}
}
window.addEventListener('message', postMessageHandler, false);
// https://github.com/ampproject/amphtml/blob/main/extensions/amp-iframe/0.1/amp-iframe.md#iframe--consent-data
window.parent.postMessage({
sentinel: 'amp',
type: 'send-consent-data'
}, '*');
}
} catch (error) {
console.log(error)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment