Skip to content

Instantly share code, notes, and snippets.

@Johannestegner
Created May 16, 2021 12:42
Show Gist options
  • Save Johannestegner/1c8c479e367bcbed1a613ae90ff83291 to your computer and use it in GitHub Desktop.
Save Johannestegner/1c8c479e367bcbed1a613ae90ff83291 to your computer and use it in GitHub Desktop.
Revive "real" async js file.
Not built for older browsers, expects to have arrow functions and template strings available to work.
File is derived from the original async.js file in the revive adserver repository, whilst edited to check for new <ins> elements during runtime, i.e., making it more fitting for a SPA with dynamic dom.
function CustomEvent( event, params ) {
params = params || { bubbles: false, cancelable: false, detail: undefined };
const evt = document.createEvent('CustomEvent');
evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
return evt;
}
CustomEvent.prototype = window.Event.prototype;
window.CustomEvent = CustomEvent;
function AsyncRevive () {
const ID = "<?php echo $etag; ?>";
window.reviveAsync = window.reviveAsync || {};
if (ID in window.reviveAsync) {
return;
}
const revive = (window.reviveAsync[ID] = {
id: Object.keys(window.reviveAsync).length,
attr: (z) => 'data-' + revive.name + '-' + z,
eventName: (n) => `${revive.name}-${ID}-${n}`,
name: "<?php echo $product; ?>", // 'revive', //
seq: 0,
});
const start = (e) => {
if (e.detail && 'start' in e.detail && !e.detail.start) {
return;
}
document.removeEventListener(revive.eventName('start'), start);
document.dispatchEvent(new CustomEvent(revive.eventName('refresh')));
};
/**
* @return {string[]}
*/
const encodeForFetch = (toEncode, name) => {
let query = [];
if (!toEncode) {
return query;
}
try {
for (const key in toEncode) {
// eslint-disable-next-line no-prototype-builtins
if (!toEncode.hasOwnProperty(key)) {
continue;
}
const newKey = name ? name + `[${key}]` : key;
if (Array.isArray(toEncode[key]) || toEncode[key] instanceof Object) {
query = [].concat(query, encodeForFetch(toEncode[key], newKey));
} else {
query.push(`${encodeURIComponent(newKey)}=${encodeURIComponent(toEncode[key])}`);
}
}
} catch (ex) {
return [];
}
return query;
};
const refresh = () => {
const elements = document.querySelectorAll(`ins[${revive.attr('id')}="${ID}"]`);
const data = {
zones: [],
prefix: revive.name + '-' + revive.id + '-',
};
elements.forEach((element, index) => {
// Detect.
const zoneId = revive.attr('zoneid');
const seq = revive.attr('seq');
let seqData;
if (element.hasAttribute(seq)) {
seqData = element.getAttribute(seq);
} else {
seqData = revive.seq++;
element.setAttribute(seq, seqData);
element.id = data.prefix + seqData;
}
if (element.hasAttribute(zoneId)) {
const loadedAttribute = revive.attr('loaded');
const loadingAttribute = revive.attr('loading');
if (element.hasAttribute(loadedAttribute)) {
return;
}
if (element.hasAttribute(loadingAttribute) && element.innerHTML.trim() === '') {
const value = parseInt(element.getAttribute(loadingAttribute));
if (value > 2) {
element.removeAttribute(loadingAttribute);
} else {
element.setAttribute(loadingAttribute, (value + 1).toString());
}
return;
}
const regexp = new RegExp('^' + revive.attr('(.*)') + '$');
element.setAttribute(loadingAttribute, '1');
const len = element.attributes.length;
let match = null;
for (let i = 0; i < len; i++) {
match = element.attributes[i].name.match(regexp);
if (!match || match.length < 2) {
continue;
}
if (match[1] === 'zoneid') {
data.zones[seqData] = element.attributes[i].value;
} else if (!/^(id|seq|loaded)$/.test(match[1])) {
data[match[1]] = element.attributes[i].value;
}
}
}
});
// Apply.
if (data.zones.length > 0) {
const url =
document.location.protocol === 'http:'
? "<?php echo MAX_commonConstructDeliveryUrl($GLOBALS['_MAX']['CONF']['file']['asyncspc']); ?>"
: "<?php echo MAX_commonConstructSecureDeliveryUrl($GLOBALS['_MAX']['CONF']['file']['asyncspc']); ?>";
data.zones = data.zones.join('|');
data.loc = document.location.href;
if (document.referer) {
data.referer = document.referer;
}
document.dispatchEvent(new CustomEvent(revive.eventName('send'), data));
fetch(url + '?' + encodeForFetch(data).join('&'), {
credentials: 'include',
})
.then((r) => {
if (r.status === 200) {
return r.json();
} else {
throw new Error(r.statusText);
}
})
.then(resolve)
.catch((x) => {
// eslint-disable-next-line no-console
console.error(x);
});
}
};
const resolve = (json) => {
document.dispatchEvent(new CustomEvent(revive.eventName('receive'), json));
const loadedAttribute = revive.attr('loaded');
const loadingAttribute = revive.attr('loading');
for (const id in json) {
// eslint-disable-next-line no-prototype-builtins
if (!json.hasOwnProperty(id)) {
continue;
}
/** @var {{iframeFriendly: bool, html: string}} */
const data = json[id];
const insElement = document.getElementById(id);
if (insElement) {
const newInsElement = insElement.cloneNode(false);
if (data.iframeFriendly) {
const iframe = createFrame(data);
newInsElement.appendChild(iframe);
loadFrame(iframe, data.html);
} else {
newInsElement.style.textDecoration = 'none';
newInsElement.innerHTML = data.html;
const scripts = newInsElement.getElementsByTagName('SCRIPT');
const length = scripts.length;
for (let i = 0; i < length; i++) {
const script = document.createElement('SCRIPT');
const attributes = scripts[i].attributes;
const attrLen = attributes.length;
for (let j = 0; j < attrLen; j++) {
script[attributes[j].nodeName] = attributes[j].value;
}
if (scripts[i].innerHTML) {
script.text = scripts[i].innerHTML;
}
scripts[i].parentNode.replaceChild(script, scripts[i]);
}
}
newInsElement.setAttribute(loadedAttribute, '1');
if (newInsElement.hasAttribute(loadingAttribute)) {
newInsElement.removeAttribute(loadingAttribute);
}
insElement.parentNode.replaceChild(newInsElement, insElement);
}
}
};
const loadFrame = (iframe, data) => {
const srcDoc = `<!DOCTYPE html>
<html>
<head>
<base target="_top">
<meta charset="UTF-8">
</head>
<body border="0" margin="0" style="margin: 0; padding: 0;">
${data}
</body>
</html>`;
if ('srcdoc' in iframe && iframe.parentElement.getAttribute(revive.attr('srcdoc')) === '1') {
iframe.srcdoc = srcDoc;
} else {
const iframeDoc = iframe.contentWindow.document || iframe.contentDocument;
iframeDoc.open();
iframeDoc.write(srcDoc);
iframeDoc.close();
}
};
const createFrame = (frameData) => {
const element = document.createElement('iframe');
// TODO: Change to none-deprecated.
element.scrolling = 'no';
element.frameBorder = '0';
element.allow = 'autoplay';
element.width = frameData.width > 0 ? frameData.width : 0;
element.height = frameData.height > 0 ? frameData.height : 0;
element.style.border = '0';
element.style.overflow = 'hidden';
return element;
};
setInterval(refresh, 2500);
const callback = () => {
document.removeEventListener('DOMContentLoaded', callback, false);
window.removeEventListener('load', callback, false);
document.addEventListener(revive.eventName('start'), start);
document.addEventListener(revive.eventName('refresh'), refresh);
document.dispatchEvent(new CustomEvent(revive.eventName('start'), { start: true }));
};
document.dispatchEvent(new CustomEvent(revive.eventName('init')));
if (document.readyState === 'complete') {
setTimeout(callback);
} else {
document.addEventListener('DOMContentLoaded', callback, false);
window.addEventListener('load', callback, false);
}
}
AsyncRevive();
function CustomEvent(event,params){params=params||{bubbles:false,cancelable:false,detail:undefined};const evt=document.createEvent("CustomEvent");evt.initCustomEvent(event,params.bubbles,params.cancelable,params.detail);return evt}CustomEvent.prototype=window.Event.prototype;window.CustomEvent=CustomEvent;function AsyncRevive(){const ID="<?php echo $etag; ?>";window.reviveAsync=window.reviveAsync||{};if(ID in window.reviveAsync){return}const revive=window.reviveAsync[ID]={id:Object.keys(window.reviveAsync).length,attr:z=>"data-"+revive.name+"-"+z,eventName:n=>`${revive.name}-${ID}-${n}`,name:"<?php echo $product; ?>",seq:0};const start=e=>{if(e.detail&&"start"in e.detail&&!e.detail.start){return}document.removeEventListener(revive.eventName("start"),start);document.dispatchEvent(new CustomEvent(revive.eventName("refresh")))};const encodeForFetch=(toEncode,name)=>{let query=[];if(!toEncode){return query}try{for(const key in toEncode){if(!toEncode.hasOwnProperty(key)){continue}const newKey=name?name+`[${key}]`:key;if(Array.isArray(toEncode[key])||toEncode[key]instanceof Object){query=[].concat(query,encodeForFetch(toEncode[key],newKey))}else{query.push(`${encodeURIComponent(newKey)}=${encodeURIComponent(toEncode[key])}`)}}}catch(ex){return[]}return query};const refresh=()=>{const elements=document.querySelectorAll(`ins[${revive.attr("id")}="${ID}"]`);const data={zones:[],prefix:revive.name+"-"+revive.id+"-"};elements.forEach((element,index)=>{const zoneId=revive.attr("zoneid");const seq=revive.attr("seq");let seqData;if(element.hasAttribute(seq)){seqData=element.getAttribute(seq)}else{seqData=revive.seq++;element.setAttribute(seq,seqData);element.id=data.prefix+seqData}if(element.hasAttribute(zoneId)){const loadedAttribute=revive.attr("loaded");const loadingAttribute=revive.attr("loading");if(element.hasAttribute(loadedAttribute)){return}if(element.hasAttribute(loadingAttribute)&&element.innerHTML.trim()===""){const value=parseInt(element.getAttribute(loadingAttribute));if(value>2){element.removeAttribute(loadingAttribute)}else{element.setAttribute(loadingAttribute,(value+1).toString())}return}const regexp=new RegExp("^"+revive.attr("(.*)")+"$");element.setAttribute(loadingAttribute,"1");const len=element.attributes.length;let match=null;for(let i=0;i<len;i++){match=element.attributes[i].name.match(regexp);if(!match||match.length<2){continue}if(match[1]==="zoneid"){data.zones[seqData]=element.attributes[i].value}else if(!/^(id|seq|loaded)$/.test(match[1])){data[match[1]]=element.attributes[i].value}}}});if(data.zones.length>0){const url=document.location.protocol==="http:"?"<?php echo MAX_commonConstructDeliveryUrl($GLOBALS['_MAX']['CONF']['file']['asyncspc']); ?>":"<?php echo MAX_commonConstructSecureDeliveryUrl($GLOBALS['_MAX']['CONF']['file']['asyncspc']); ?>";data.zones=data.zones.join("|");data.loc=document.location.href;if(document.referer){data.referer=document.referer}document.dispatchEvent(new CustomEvent(revive.eventName("send"),data));fetch(url+"?"+encodeForFetch(data).join("&"),{credentials:"include"}).then(r=>{if(r.status===200){return r.json()}else{throw new Error(r.statusText)}}).then(resolve).catch(x=>{console.error(x)})}};const resolve=json=>{document.dispatchEvent(new CustomEvent(revive.eventName("receive"),json));const loadedAttribute=revive.attr("loaded");const loadingAttribute=revive.attr("loading");for(const id in json){if(!json.hasOwnProperty(id)){continue}const data=json[id];const insElement=document.getElementById(id);if(insElement){const newInsElement=insElement.cloneNode(false);if(data.iframeFriendly){const iframe=createFrame(data);newInsElement.appendChild(iframe);loadFrame(iframe,data.html)}else{newInsElement.style.textDecoration="none";newInsElement.innerHTML=data.html;const scripts=newInsElement.getElementsByTagName("SCRIPT");const length=scripts.length;for(let i=0;i<length;i++){const script=document.createElement("SCRIPT");const attributes=scripts[i].attributes;const attrLen=attributes.length;for(let j=0;j<attrLen;j++){script[attributes[j].nodeName]=attributes[j].value}if(scripts[i].innerHTML){script.text=scripts[i].innerHTML}scripts[i].parentNode.replaceChild(script,scripts[i])}}newInsElement.setAttribute(loadedAttribute,"1");if(newInsElement.hasAttribute(loadingAttribute)){newInsElement.removeAttribute(loadingAttribute)}insElement.parentNode.replaceChild(newInsElement,insElement)}}};const loadFrame=(iframe,data)=>{const srcDoc=`<!DOCTYPE html>
<html>
<head>
<base target="_top">
<meta charset="UTF-8">
</head>
<body border="0" margin="0" style="margin: 0; padding: 0;">
${data}
</body>
</html>`;if("srcdoc"in iframe&&iframe.parentElement.getAttribute(revive.attr("srcdoc"))==="1"){iframe.srcdoc=srcDoc}else{const iframeDoc=iframe.contentWindow.document||iframe.contentDocument;iframeDoc.open();iframeDoc.write(srcDoc);iframeDoc.close()}};const createFrame=frameData=>{const element=document.createElement("iframe");element.scrolling="no";element.frameBorder="0";element.allow="autoplay";element.width=frameData.width>0?frameData.width:0;element.height=frameData.height>0?frameData.height:0;element.style.border="0";element.style.overflow="hidden";return element};setInterval(refresh,2500);const callback=()=>{document.removeEventListener("DOMContentLoaded",callback,false);window.removeEventListener("load",callback,false);document.addEventListener(revive.eventName("start"),start);document.addEventListener(revive.eventName("refresh"),refresh);document.dispatchEvent(new CustomEvent(revive.eventName("start"),{start:true}))};document.dispatchEvent(new CustomEvent(revive.eventName("init")));if(document.readyState==="complete"){setTimeout(callback)}else{document.addEventListener("DOMContentLoaded",callback,false);window.addEventListener("load",callback,false)}}AsyncRevive();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment