// SDK Production Version - Uses hardcoded configuration // Designed to work with index.html // Production configuration const installProviders = [ "adinplay", "cpmstar", "local" ]; const videoAdPriorities = [ "cpmstar", "adinplay" ]; const bannerAdPriorities = [ "adinplay", "cpmstar", "local" ]; // Global configuration const DEBUG_MODE = window.location.href.includes('test'); const IS_MOBILE = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); const MIN_REFRESH_INTERVAL = 24_000; // Minimum showtime before refresh const BANNER_DEBOUNCE_TIME = 100; const MAX_AIPTAG_WAIT_TIME = 1800; const SHOWTIME_CHECK_INTERVAL = 1000; // Check showtime every 1.6 seconds // Banner position names for logging const POSITION_NAMES = { 0: 'Hidden', 1: 'TopCenter', 2: 'TopRight', 3: 'TopLeft', 4: 'BottomCenter', 5: 'BottomRight', 6: 'BottomLeft', 7: 'MiddleCenter', 8: 'MiddleLeft', 9: 'MiddleRight', 10: 'BelowTopLeft', 11: 'BelowTopRight' }; // Banner mappings and dimensions window.bannerMapping = { 0: '300x250', 1: '728x90', 2: '300x600' }; window.bannerDimensions = { 0: { width: '300px', height: '250px', scale: 1.3, enableForMobile: true, ratioBoostStops: [ { ratio: 1.0, boost: 1.8 }, { ratio: 1.62, boost: 1.2 }, { ratio: 1.78, boost: 1.1 } ] }, 1: { width: '728px', height: '90px', scale: 1.19, enableForMobile: false, ratioBoostStops: [ { ratio: 0, boost: 1.0 }, { ratio: Infinity, boost: 1.0 } ] }, 2: { width: '300px', height: '600px', scale: 1.06, enableForMobile: false, ratioBoostStops: [ { ratio: 1.0, boost: 1.9 }, { ratio: 1.62, boost: 1.15 }, { ratio: 1.78, boost: 1.0 } ] } }; // Sort ratioBoostStops Object.keys(window.bannerDimensions).forEach(key => { window.bannerDimensions[key].ratioBoostStops.sort((a, b) => a.ratio - b.ratio); }); // Helper functions function debounce(func, wait) { let timeout; return function (...args) { clearTimeout(timeout); timeout = setTimeout(() => func.apply(this, args), wait); }; } // Create promise for AdinPlay availability window.waitForAdinPlay = function () { return new Promise((resolve) => { if (typeof aipDisplayTag !== "undefined") { resolve(true); return; } const timeoutId = setTimeout(() => { resolve(false); }, MAX_AIPTAG_WAIT_TIME); const checkInterval = setInterval(() => { if (typeof aipDisplayTag !== "undefined") { clearInterval(checkInterval); clearTimeout(timeoutId); resolve(true); } }, 100); }); }; // Load provider scripts dynamically function loadProviderScript(provider) { return new Promise((resolve, reject) => { const script = document.createElement('script'); script.src = `ads/adapters/${provider}-ads.js`; script.onload = () => { resolve(); }; script.onerror = () => { reject(); }; document.head.appendChild(script); }); } // Load all configured providers async function loadProviders() { for (const provider of installProviders) { try { await loadProviderScript(provider); } catch (e) { console.error(`Failed to load ${provider}:`, e); } } } // Main SDK object window.SDK = { _isInitialized: false, _pendingBannerQueue: [], gameplayStart() { if (DEBUG_MODE) console.log("[sdk.js] Gameplay started"); }, loadingStart() { if (DEBUG_MODE) console.log("[sdk.js] Loading started"); onWindowResize(); }, loadingEnd() { if (DEBUG_MODE) console.log("[sdk.js] Loading finished"); onWindowResize(); }, gameplayEnd() { if (DEBUG_MODE) console.log("[sdk.js] Gameplay ended"); }, showMidroll() { if (window.ENABLE_ADS) { showAd('midroll'); } else { if (typeof unityInstance !== 'undefined') { unityInstance.SendMessage("SDKManager", "OnVideoAdEnded", "true"); } } }, showRewarded() { if (window.ENABLE_ADS) { showAd('rewarded'); } else { if (typeof unityInstance !== 'undefined') { unityInstance.SendMessage("SDKManager", "OnVideoAdEnded", "true"); } } }, _bannerAds: {}, _occupiedPositions: {}, _lastRefreshed: {}, _pendingBannerOps: {}, _bannerShowtime: {}, // Tracks accumulated visible time per banner _bannerVisibleSince: {}, // Tracks when banner became visible _showtimeInterval: null, // Interval for checking showtime _startTrackingShowtime(adTag) { const now = Date.now(); if (!this._bannerVisibleSince[adTag]) { this._bannerVisibleSince[adTag] = now; if (DEBUG_MODE) console.log(`[sdk.js] Started tracking showtime for ${adTag}`); } }, _stopTrackingShowtime(adTag) { if (this._bannerVisibleSince[adTag]) { const now = Date.now(); const visibleDuration = now - this._bannerVisibleSince[adTag]; this._bannerShowtime[adTag] = (this._bannerShowtime[adTag] || 0) + visibleDuration; delete this._bannerVisibleSince[adTag]; if (DEBUG_MODE) console.log(`[sdk.js] Stopped tracking showtime for ${adTag}. Total showtime: ${Math.floor(this._bannerShowtime[adTag] / 1000)}s`); } }, _resetShowtime(adTag) { this._bannerShowtime[adTag] = 0; delete this._bannerVisibleSince[adTag]; if (DEBUG_MODE) console.log(`[sdk.js] Reset showtime for ${adTag}`); }, _getCurrentShowtime(adTag) { let totalShowtime = this._bannerShowtime[adTag] || 0; if (this._bannerVisibleSince[adTag]) { const now = Date.now(); totalShowtime += (now - this._bannerVisibleSince[adTag]); } return totalShowtime; }, _isBannerVisible(banner) { return banner && banner.position !== 0 && banner.container && banner.container.style.display !== 'none' && banner.container.offsetParent !== null && !document.hidden; }, async _originalSetBanner(bannerType, bannerPosition) { const adTag = window.bannerMapping[bannerType]; const dims = window.bannerDimensions[bannerType]; const now = Date.now(); if (!adTag || !dims) { if (DEBUG_MODE) console.log(`[sdk.js] SetBanner: Invalid banner type ${bannerType}`); return; } if (IS_MOBILE && !dims.enableForMobile) { if (DEBUG_MODE) console.log(`[sdk.js] SetBanner: Banner ${adTag} disabled on mobile`); return; } // Get position name for logging const posName = POSITION_NAMES[bannerPosition] || `Position${bannerPosition}`; if (DEBUG_MODE) { console.log(`[sdk.js] SetBanner called: ${adTag} at ${posName}`); } this._pendingBannerOps[bannerType] = { bannerType: bannerType, bannerPosition: bannerPosition, timestamp: now }; let existingInstance = this._bannerAds[adTag]; // Handle hiding if (bannerPosition === 0) { if (DEBUG_MODE) console.log(`[sdk.js] Hiding banner ${adTag}`); if (existingInstance && existingInstance.container) { // Stop tracking showtime when hiding this._stopTrackingShowtime(adTag); existingInstance.container.style.display = "none"; let oldPosKey = existingInstance.position.toString(); if (this._occupiedPositions[oldPosKey] === adTag) { delete this._occupiedPositions[oldPosKey]; } existingInstance.position = 0; // Remove from pending operations since it's hidden delete this._pendingBannerOps[bannerType]; } return; } // Handle existing banner if (existingInstance) { if (existingInstance.position === bannerPosition) { return; } // Move banner let oldPosKey = existingInstance.position.toString(); if (this._occupiedPositions[oldPosKey] === adTag) { delete this._occupiedPositions[oldPosKey]; } let newPosKey = bannerPosition.toString(); if (this._occupiedPositions[newPosKey] && this._occupiedPositions[newPosKey] !== adTag) { let conflictAdTag = this._occupiedPositions[newPosKey]; let conflictInstance = this._bannerAds[conflictAdTag]; if (conflictInstance && conflictInstance.container) { conflictInstance.container.style.display = "none"; } delete this._occupiedPositions[newPosKey]; } existingInstance.container.style.display = "block"; updateContainerPosition(existingInstance.container, bannerPosition); existingInstance.position = bannerPosition; this._occupiedPositions[newPosKey] = adTag; // Start tracking showtime when showing this._startTrackingShowtime(adTag); // Check if we should refresh based on accumulated showtime const currentShowtime = this._getCurrentShowtime(adTag); const shouldRefresh = currentShowtime >= MIN_REFRESH_INTERVAL; if (shouldRefresh) { if (this._pendingBannerOps[bannerType]?.timestamp > now) { return; } // Reset showtime counter before refresh this._resetShowtime(adTag); this._startTrackingShowtime(adTag); this._displayBannerWithProviders(bannerType, adTag, existingInstance.container, now); } } else { // Create new banner let posKey = bannerPosition.toString(); if (this._occupiedPositions[posKey]) { let conflictAdTag = this._occupiedPositions[posKey]; let conflictInstance = this._bannerAds[conflictAdTag]; if (conflictInstance && conflictInstance.container) { conflictInstance.container.style.display = "none"; } delete this._occupiedPositions[posKey]; } const container = document.createElement('div'); container.className = 'banner-container'; container.id = 'banner_' + adTag; container.style.position = 'absolute'; container.style.zIndex = 1000; container.style.userSelect = 'none'; container.style.pointerEvents = 'all'; container.style.width = dims.width; container.style.height = dims.height; container.style.overflow = 'hidden'; if (DEBUG_MODE) { container.style.backgroundColor = 'rgba(255, 0, 0, 0.66)'; } else { container.style.backgroundColor = 'rgba(0, 0, 0, 0.035)'; } const bannerDiv = document.createElement('div'); bannerDiv.id = adTag; bannerDiv.style.width = dims.width; bannerDiv.style.height = dims.height; container.appendChild(bannerDiv); updateContainerPosition(container, bannerPosition); document.body.appendChild(container); this._bannerAds[adTag] = { bannerType: bannerType, adTag: adTag, container: container, position: bannerPosition }; this._occupiedPositions[posKey] = adTag; // Start tracking showtime for new banner this._startTrackingShowtime(adTag); if (this._pendingBannerOps[bannerType]?.timestamp > now) { return; } this._displayBannerWithProviders(bannerType, adTag, container, now); } onWindowResize(); }, async _displayBannerWithProviders(bannerType, adTag, container, now) { let providerIndex = 0; const priorities = bannerAdPriorities; // Get position name for logging const bannerInstance = this._bannerAds[adTag]; const posName = bannerInstance ? (POSITION_NAMES[bannerInstance.position] || `Position${bannerInstance.position}`) : 'Unknown'; const tryNextProvider = async () => { if (providerIndex >= priorities.length) { if (DEBUG_MODE) console.log(`[sdk.js] All providers failed for banner ${adTag} at ${posName}`); return false; } const providerName = priorities[providerIndex]; const provider = window.bannerAdProviders?.[providerName]; if (!provider) { if (DEBUG_MODE) console.log(`[sdk.js] Provider ${providerName} not available for banner ${adTag}`); providerIndex++; return tryNextProvider(); } if (DEBUG_MODE) console.log(`[sdk.js] Trying to show banner ${adTag} at ${posName} with provider: ${providerName}`); try { // Pass bannerType for CPMStar/Nitro/Local, adTag for AdinPlay const param = providerName === 'adinplay' ? adTag : bannerType; const success = await provider.displayBanner(param, container); if (success) { if (DEBUG_MODE) console.log(`[sdk.js] Successfully showing banner ${adTag} at ${posName} with ${providerName}`); this._lastRefreshed[adTag] = now; // Reset showtime after successful refresh this._resetShowtime(adTag); this._startTrackingShowtime(adTag); return true; } else { if (DEBUG_MODE) console.log(`[sdk.js] Showing banner ${adTag} at ${posName} with ${providerName} FAILED, trying next...`); providerIndex++; return tryNextProvider(); } } catch (error) { if (DEBUG_MODE) console.log(`[sdk.js] Error showing banner ${adTag} with ${providerName}:`, error.message || error); providerIndex++; return tryNextProvider(); } }; return tryNextProvider(); } }; // Debounced SetBanner with initialization queue window.SDK.SetBanner = function (bannerType, bannerPosition) { // If not initialized and trying to show banner (not hide), queue it if (!window.SDK._isInitialized && bannerPosition !== 0) { if (DEBUG_MODE) console.log(`[sdk.js] SDK not initialized yet, queueing banner ${window.bannerMapping[bannerType] || bannerType} for position ${bannerPosition}`); // Check if we already have this banner type in queue const existingIndex = window.SDK._pendingBannerQueue.findIndex(item => item.bannerType === bannerType); if (existingIndex >= 0) { // Update existing entry with new position window.SDK._pendingBannerQueue[existingIndex].bannerPosition = bannerPosition; } else { // Add new entry window.SDK._pendingBannerQueue.push({ bannerType, bannerPosition }); } return; } // If hiding banner (position 0), process immediately even if not initialized // This ensures we can remove queued banners if (bannerPosition === 0) { // Remove from queue if present window.SDK._pendingBannerQueue = window.SDK._pendingBannerQueue.filter( item => item.bannerType !== bannerType ); // If initialized, hide the banner if (window.SDK._isInitialized) { window.SDK._originalSetBanner.call(window.SDK, bannerType, bannerPosition); } } else { // Normal debounced call for showing banners when initialized if (!window.SDK._debouncedSetBannerPerType) { window.SDK._debouncedSetBannerPerType = {}; } if (!window.SDK._debouncedSetBannerPerType[bannerType]) { window.SDK._debouncedSetBannerPerType[bannerType] = debounce( (bannerType, bannerPosition) => { window.SDK._originalSetBanner.call(window.SDK, bannerType, bannerPosition); }, BANNER_DEBOUNCE_TIME ); } window.SDK._debouncedSetBannerPerType[bannerType](bannerType, bannerPosition); } }; // Container positioning helper function updateContainerPosition(container, bannerPosition) { container.style.top = ""; container.style.right = ""; container.style.bottom = ""; container.style.left = ""; container.style.transformOrigin = ""; switch (parseInt(bannerPosition)) { case 0: container.style.display = "none"; break; case 1: // TopCenter container.style.display = "block"; container.style.top = "1%"; container.style.left = "0"; container.style.right = "0"; container.style.marginLeft = "auto"; container.style.marginRight = "auto"; container.style.transformOrigin = "top center"; break; case 2: // TopRight container.style.display = "block"; container.style.top = "1%"; container.style.right = "1%"; container.style.transformOrigin = "top right"; break; case 3: // TopLeft container.style.display = "block"; container.style.top = "1%"; container.style.left = "1%"; container.style.transformOrigin = "top left"; break; case 4: // BottomCenter container.style.display = "block"; container.style.bottom = "0.5%"; container.style.left = "0"; container.style.right = "0"; container.style.marginLeft = "auto"; container.style.marginRight = "auto"; container.style.transformOrigin = "bottom center"; break; case 5: // BottomRight container.style.display = "block"; container.style.bottom = "1%"; container.style.right = "1%"; container.style.transformOrigin = "bottom right"; break; case 6: // BottomLeft container.style.display = "block"; container.style.bottom = "1%"; container.style.left = "1%"; container.style.transformOrigin = "bottom left"; break; case 7: // MiddleCenter container.style.display = "block"; container.style.position = "absolute"; container.style.top = "0"; container.style.bottom = "0"; container.style.left = "0"; container.style.right = "0"; container.style.margin = "auto"; break; case 8: // MiddleLeft container.style.display = "block"; container.style.left = "1%"; container.style.top = "0"; container.style.bottom = "0"; container.style.margin = "auto"; container.style.transformOrigin = "center left"; break; case 9: // MiddleRight container.style.display = "block"; container.style.right = "1%"; container.style.top = "0"; container.style.bottom = "0"; container.style.margin = "auto"; container.style.transformOrigin = "center right"; break; case 10: // Below TopLeft container.style.display = "block"; container.style.position = "absolute"; container.style.left = "1%"; container.style.top = "14%"; container.style.transformOrigin = "top left"; break; case 11: // Below TopRight container.style.display = "block"; container.style.position = "absolute"; container.style.top = "10%"; container.style.right = "1%"; container.style.transformOrigin = "top right"; break; } } // Video ad system function showAd(adType) { let providerIndex = 0; const priorities = videoAdPriorities; function tryNextProvider() { if (providerIndex >= priorities.length) { if (typeof unityInstance !== 'undefined') { unityInstance.SendMessage("SDKManager", "OnVideoAdEnded", "false"); } return; } const providerName = priorities[providerIndex]; const provider = window.videoAdProviders?.[providerName]; if (!provider) { providerIndex++; tryNextProvider(); return; } const methodName = adType === 'rewarded' ? 'showRewarded' : 'showMidroll'; provider[methodName]( function () { if (typeof unityInstance !== 'undefined') { unityInstance.SendMessage("SDKManager", "OnVideoAdEnded", "true"); } }, function () { providerIndex++; tryNextProvider(); } ); } tryNextProvider(); } // Window resize handling function getRatioBoost(stops, aspect) { stops.sort((a, b) => a.ratio - b.ratio); if (aspect <= stops[0].ratio) return stops[0].boost; if (aspect >= stops.at(-1).ratio) return stops.at(-1).boost; for (let i = 0; i < stops.length - 1; i++) { const a = stops[i], b = stops[i + 1]; if (aspect >= a.ratio && aspect <= b.ratio) { const t = (aspect - a.ratio) / (b.ratio - a.ratio); return a.boost + (b.boost - a.boost) * t; } } return 1; } function onWindowResize() { const w = window.innerWidth, h = window.innerHeight; const aspect = w / h; const baseScale = Math.min(w / 1920, h / 960); Object.keys(window.bannerMapping).forEach(key => { const tag = window.bannerMapping[key]; const dims = window.bannerDimensions[key]; const ctr = document.getElementById('banner_' + tag); if (!ctr || (IS_MOBILE && !dims.enableForMobile)) return; const boost = getRatioBoost(dims.ratioBoostStops, aspect); const finalScale = baseScale * dims.scale * boost; ctr.style.transform = `scale(${finalScale})`; }); } window.addEventListener("resize", onWindowResize); // Showtime-based refresh system window.SDK._showtimeInterval = setInterval(async function () { if (!window.ENABLE_ADS) return; const now = Date.now(); // Update showtime for all visible banners Object.keys(window.SDK._bannerAds).forEach(adTag => { const banner = window.SDK._bannerAds[adTag]; if (window.SDK._isBannerVisible(banner)) { // Banner is visible - ensure we're tracking it if (!window.SDK._bannerVisibleSince[adTag]) { window.SDK._startTrackingShowtime(adTag); } // Check if it's time to refresh based on showtime const currentShowtime = window.SDK._getCurrentShowtime(adTag); if (currentShowtime >= MIN_REFRESH_INTERVAL) { if (DEBUG_MODE) console.log(`[sdk.js] Banner ${adTag} reached ${Math.floor(currentShowtime / 1000)}s showtime, refreshing...`); const bannerType = Object.keys(window.bannerMapping).find(key => window.bannerMapping[key] === adTag); if (bannerType !== undefined) { // Reset showtime before refresh window.SDK._resetShowtime(adTag); window.SDK._startTrackingShowtime(adTag); window.SDK._displayBannerWithProviders(parseInt(bannerType), adTag, banner.container, now); } } } else { // Banner is not visible - stop tracking if we were if (window.SDK._bannerVisibleSince[adTag]) { window.SDK._stopTrackingShowtime(adTag); } } }); }, SHOWTIME_CHECK_INTERVAL); // Handle page visibility changes for showtime tracking document.addEventListener('visibilitychange', function () { if (document.hidden) { // Page is hidden - stop tracking all visible banners Object.keys(window.SDK._bannerAds).forEach(adTag => { if (window.SDK._bannerVisibleSince[adTag]) { window.SDK._stopTrackingShowtime(adTag); } }); } else if (window.ENABLE_ADS) { // Page is visible again - restart tracking for visible banners Object.keys(window.SDK._bannerAds).forEach(adTag => { const banner = window.SDK._bannerAds[adTag]; if (window.SDK._isBannerVisible(banner)) { window.SDK._startTrackingShowtime(adTag); } }); } }); // Process queued banners after initialization window.SDK._processQueuedBanners = function () { if (DEBUG_MODE && window.SDK._pendingBannerQueue.length > 0) { console.log(`[sdk.js] Processing ${window.SDK._pendingBannerQueue.length} queued banner(s)`); } // Process each queued banner const queue = [...window.SDK._pendingBannerQueue]; window.SDK._pendingBannerQueue = []; queue.forEach(({ bannerType, bannerPosition }) => { if (DEBUG_MODE) { const adTag = window.bannerMapping[bannerType] || bannerType; console.log(`[sdk.js] Processing queued banner: ${adTag} at position ${bannerPosition}`); } // Use SetBanner to ensure debouncing still applies window.SDK.SetBanner(bannerType, bannerPosition); }); }; // Initialize (async function () { if (DEBUG_MODE) console.log("[sdk.js] Starting SDK initialization..."); await loadProviders(); // Mark as initialized window.SDK._isInitialized = true; if (DEBUG_MODE) console.log("[sdk.js] SDK initialized, providers loaded"); // Process any queued banners window.SDK._processQueuedBanners(); window.SDK.loadingStart(); })();