playworks
This commit is contained in:
@@ -95,10 +95,11 @@ export function openPlayworksConverter(_context: vscode.ExtensionContext) {
|
||||
}
|
||||
|
||||
const ZIPPED_NETWORKS = new Set(['gg', 'vu', 'mtg']);
|
||||
const SUPPORTED_NETWORKS = new Set(['al', 'is', 'fb', 'gg', 'mo', 'vu', 'mtg', 'tt']);
|
||||
const MRAID_NETWORKS = new Set(['al', 'is']);
|
||||
const NETWORK_OUTPUT_FOLDERS: Record<string, string> = {
|
||||
al: 'Applovin',
|
||||
is: 'Ironsource',
|
||||
un: 'Unity',
|
||||
fb: 'Facebook',
|
||||
gg: 'GoogleAds',
|
||||
mo: 'Moloco',
|
||||
@@ -120,6 +121,9 @@ function outputBaseName(opts: ConvertOptions): string {
|
||||
}
|
||||
|
||||
async function convertNetwork(html: string, opts: ConvertOptions, network: string): Promise<string> {
|
||||
if (!SUPPORTED_NETWORKS.has(network)) {
|
||||
throw new Error(`Unsupported Playworks target network: ${network}`);
|
||||
}
|
||||
const transformed = transformHtml(html, network);
|
||||
const needsZip = ZIPPED_NETWORKS.has(network);
|
||||
const baseName = outputBaseName(opts);
|
||||
@@ -150,34 +154,68 @@ function transformHtml(html: string, network: string): string {
|
||||
// Inject into </head>: lifecycle stubs only (must register luna:build listener
|
||||
// BEFORE the Playworks bundle so our handler fires first), then any network SDK script.
|
||||
// Console restore stays at end-of-body — it must run AFTER the bundle overrides console.
|
||||
result = removeNetworkSdkScripts(result, network);
|
||||
let headInject = buildLifecycleScript();
|
||||
if (network === 'al' || network === 'is') {
|
||||
if (MRAID_NETWORKS.has(network)) {
|
||||
headInject += '<script src="mraid.js"></script>';
|
||||
} else if (network === 'gg') {
|
||||
headInject += '<script src="exitapi.js"></script>';
|
||||
}
|
||||
result = result.replace('</head>', headInject + '\n</head>');
|
||||
result = injectBeforeHeadClose(result, headInject);
|
||||
|
||||
// Body-level flags
|
||||
if (network === 'vu') {
|
||||
result = result.replace('<body>', '<body>\n<script>window.__VUNGLE__=true;</script>');
|
||||
result = injectAfterBodyOpen(result, '<script>window.__VUNGLE__=true;</script>');
|
||||
} else if (network === 'mtg') {
|
||||
result = result.replace('<body>', '<body onload="gameReady()">');
|
||||
result = addBodyOnload(result, 'gameReady()');
|
||||
} else if (network === 'tt') {
|
||||
result = result.replace('<body>', '<body>\n<script>window.__TIKTOK__=true;</script>');
|
||||
result = injectAfterBodyOpen(result, '<script>window.__TIKTOK__=true;</script>');
|
||||
}
|
||||
|
||||
// Replace the CTA script
|
||||
result = replaceCTAScript(result, network);
|
||||
|
||||
// Unity: rewrite window.top → window.self (Luna static scan requirement)
|
||||
if (network === 'un') {
|
||||
result = result.split('window.top').join('window.self');
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function removeNetworkSdkScripts(html: string, network: string): string {
|
||||
let result = html;
|
||||
if (MRAID_NETWORKS.has(network)) {
|
||||
result = result.replace(/<script\b[^>]*\bsrc\s*=\s*["']mraid\.js["'][^>]*>\s*<\/script>\s*/gi, '');
|
||||
}
|
||||
if (network === 'gg') {
|
||||
result = result.replace(/<script\b[^>]*\bsrc\s*=\s*["']exitapi\.js["'][^>]*>\s*<\/script>\s*/gi, '');
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function injectBeforeHeadClose(html: string, injection: string): string {
|
||||
if (/<\/head>/i.test(html)) {
|
||||
return html.replace(/<\/head>/i, `${injection}\n</head>`);
|
||||
}
|
||||
return `${injection}\n${html}`;
|
||||
}
|
||||
|
||||
function injectAfterBodyOpen(html: string, injection: string): string {
|
||||
if (/<body\b[^>]*>/i.test(html)) {
|
||||
return html.replace(/<body\b[^>]*>/i, match => `${match}\n${injection}`);
|
||||
}
|
||||
return `${injection}\n${html}`;
|
||||
}
|
||||
|
||||
function addBodyOnload(html: string, handler: string): string {
|
||||
if (!/<body\b[^>]*>/i.test(html)) {
|
||||
return `<body onload="${handler}">\n${html}`;
|
||||
}
|
||||
return html.replace(/<body\b([^>]*)>/i, (match, attrs: string) => {
|
||||
const onloadMatch = attrs.match(/\bonload\s*=\s*(["'])(.*?)\1/i);
|
||||
if (!onloadMatch) return `<body${attrs} onload="${handler}">`;
|
||||
if (onloadMatch[2].includes(handler)) return match;
|
||||
const updated = `${onloadMatch[2]};${handler}`;
|
||||
return match.replace(onloadMatch[0], `onload=${onloadMatch[1]}${updated}${onloadMatch[1]}`);
|
||||
});
|
||||
}
|
||||
|
||||
function replaceCTAScript(html: string, network: string): string {
|
||||
const marker = 'Luna.Unity.Playable.InstallFullGame=function';
|
||||
const markerIdx = html.lastIndexOf(marker);
|
||||
@@ -245,11 +283,7 @@ function buildCTAScript(network: string): string {
|
||||
case 'al':
|
||||
case 'is':
|
||||
// MRAID — mraid.js injected in <head>
|
||||
ctaLogic = `${closeCall}if(typeof mraid!=="undefined"&&typeof mraid.open==="function"){if(typeof mraid.getState==="function"&&mraid.getState()==="loading"){mraid.addEventListener("ready",function(){mraid.open(o)});}else{mraid.open(o);}}else{window.open(o,"_blank");}`;
|
||||
break;
|
||||
case 'un':
|
||||
// Unity Ads — MRAID-based; window.top already rewritten separately
|
||||
ctaLogic = `${closeCall}if(typeof mraid!=="undefined"&&typeof mraid.open==="function"){mraid.open(o);}else{window.open(o,"_blank");}`;
|
||||
ctaLogic = `${closeCall}if(typeof mraid!=="undefined"&&typeof mraid.open==="function"){var s=typeof mraid.getState==="function"?mraid.getState():"default";if(s!=="loading"){mraid.open(o);return;}}window.open(o,"_blank");`;
|
||||
break;
|
||||
case 'fb':
|
||||
ctaLogic = `${closeCall}if(typeof FbPlayableAd!=="undefined"&&typeof FbPlayableAd.onCTAClick==="function"){FbPlayableAd.onCTAClick();return;}window.open(o,"_blank");`;
|
||||
@@ -259,7 +293,7 @@ function buildCTAScript(network: string): string {
|
||||
ctaLogic = `${closeCall}if(typeof ExitApi!=="undefined"&&typeof ExitApi.exit==="function"){ExitApi.exit();return;}window.open(o,"_blank");`;
|
||||
break;
|
||||
case 'mo':
|
||||
ctaLogic = `${closeCall}if(typeof window.clickTag==="string"&&window.clickTag){window.open(window.clickTag,"_blank");return;}window.open(o,"_blank");`;
|
||||
ctaLogic = `${closeCall}if(typeof FbPlayableAd!=="undefined"&&typeof FbPlayableAd.onCTAClick==="function"){FbPlayableAd.onCTAClick();return;}if(typeof window.clickTag==="string"&&window.clickTag){window.open(window.clickTag,"_blank");return;}window.open(o,"_blank");`;
|
||||
break;
|
||||
case 'vu':
|
||||
// Vungle — window.__VUNGLE__ flag set in body
|
||||
@@ -314,10 +348,9 @@ function getHtml(): string {
|
||||
const networks = [
|
||||
{ tag: 'al', label: 'Applovin', note: 'HTML + mraid.js in head' },
|
||||
{ tag: 'is', label: 'Ironsource', note: 'HTML + mraid.js in head' },
|
||||
{ tag: 'un', label: 'Unity', note: 'HTML (window.top → self)' },
|
||||
{ tag: 'fb', label: 'Facebook', note: 'HTML' },
|
||||
{ tag: 'gg', label: 'Google Ads', note: 'ZIP + exitapi.js in head' },
|
||||
{ tag: 'mo', label: 'Moloco', note: 'HTML' },
|
||||
{ tag: 'mo', label: 'Moloco', note: 'HTML + FbPlayableAd CTA' },
|
||||
{ tag: 'vu', label: 'Vungle', note: 'ZIP + __VUNGLE__ flag' },
|
||||
{ tag: 'mtg', label: 'Mintegral', note: 'ZIP + onload="gameReady()"' },
|
||||
{ tag: 'tt', label: 'TikTok', note: 'HTML + __TIKTOK__ flag' },
|
||||
|
||||
Reference in New Issue
Block a user