My Premium Colony

Unlock Premium

// ==UserScript==
// @name			My Premium Colony
// @namespace		https://greasyfork.org/scripts?set=439787
// @homepage		https://greasyfork.org/scripts/435865-my-premium-colony
// @version			0.2
// @description		Unlock Premium
// @defaulticon		https://market.ape-apps.com/app_icons/1637335496386.png
// @author			V. H.
// @match			http*://*.apewebapps.com/my-colony/*
// @webRequest		[{"selector":"/my-colony/js/script/*/frame-script.js","action":{"redirect":{"to":"https://greasyfork.org/scripts/435946-mycolonyframescript/code/MyColonyFramescript.js"}}}]
// @grant			unsafeWindow
// @grant			GM_log
// @grant			GM_addStyle
// @grant			GM_setValue
// @grant			GM_getValue
// @grant			GM_deleteValue
// @grant			GM_listValues
// @grant			GM_webRequest
// @grant			window.onurlchange
// @grant			window.close
// @compatible		Chrome
// @license			AFL-3.0
// @run-at			document-start
// @antifeature		Be careful not to install any unofficial versions of this script!
// ==/UserScript==

/**
 * THIS IS NOT A SCAM, just a low-profile non-permament hack, the work is done in @webRequest, no script body needed.
 * 
 * FrameScript Version: 2.6.1
 * This is the original code of the game replaced and cracked, only lines marked with comment '//!' are editted.
 */

void async function _script() {
	"use strict";
	
	const _code = `
	//Hacked
	
	/* global adl */
/* global Windows */
/* global featuredHelper */
/* global Android */
/* global io */
/* global Stripe */
/* global FB */
/* global webkit */
/* global MSApp */
/* global analyticsHelper */
/* global amslLogStat */
/* global amslLogError */
/* global YT */
/* global amslLogSale */
/* global html2canvas */
/* global bannerAdHelper */
(function() {
    // keep up on the latest:
    // https://web.dev/blog/
    // https://web.dev/app-like-pwas/

    window.addEventListener("load",onLoad);
    window.addEventListener("resize",onResize);
    window.addEventListener("beforeunload",onUnload);
    window.addEventListener("message",onMessage);
    window.addEventListener("devicemotion", onDeviceMotion, false);
    window.addEventListener("appinstalled", onPWAInstalledToHomescreen, false);
    window.addEventListener("beforeinstallprompt", onBeforeInstallPrompt, false);
    window.addEventListener("error",onGeneralError,false);

    let vh = window.innerHeight * 0.01;
    document.documentElement.style.setProperty("--vh", \`\${vh}px\`);

    if(typeof window.FB === "undefined") {
        window.FB = null;
    }

    let usingAppVersion = window.appVersion;

    // fullscreen watchers 
    document.addEventListener("fullscreenchange", onFullscreenChange);
    document.addEventListener("webkitfullscreenchange", onFullscreenChange);
    document.addEventListener("mozfullscreenchange", onFullscreenChange);
    document.addEventListener("MSFullscreenChange", onFullscreenChange);

    const APE_ACCOUNTS_API = "https://accounts.ape-apps.com/api.php";

    // elements
    let splashScreen;
    let appFrame;
    let childWindowObject;
    let appWrapper;
    let titleScreen;
    let titleScreenLogo;
    let titleScreenOptions;
    let titleScreenFeaturedApp;
    let titleScreenNewsfeed;
    let titleScreenApeLogo;
    let titleScreenSubtitle;
    let splashLoadingText;
    let toolbarHolder;
    let bottomToolbarHolder;
    let introFader;
    let chatHolder;
    let chatHolderToolbar;
    let chatHolderFrame;
    let adHolder;
    let fontPreloadArea;

    let completedScan = false;
    let completedScanTimeout = null;

    let appIsPaused = false;
    let premiumAllowed = true;

    let childAppWindows = {};
    let passedInData = null;

    let remoteLPTPrinters = {};
    let remoteScanners = {};

    let friendsSignalingChannel = null;
    let sharedResourceSignalingChannel = null;

    // helpers 
    let fh;
    let bah;
    let ah = null;
    let fth = null;
    let rmn = null;

    // electron host vars
    let appFolder = null;
    let BASE_DOCS = null;
    let dialogFileRefes = null;
    let dialogFileCounter = 0;
    let CURRENT_PLATFORM = null;

    // variables
    let friendSelectedCallback = null;
    let customMenu = [];
    let customToolbar = [];
    let databaseFunctional = false;
    let db = null;
    let dbHasRetry = false;
    let sidebarCategory = null;
    let sidebarCategoryIcon = null;
    let platformName = "web";
    let premiumOptionText = "Remove Advertising";
    let currentTitle = window.appName;
    let packSaveTimer = null;
    let apeAppsLoginData = {
        "creds": null,
        "profile": null
    };
    let onGamepadVelocity = null;
    let pendingLoadFileName = null;
    let tmpCLoudOpenFileName = null;
    let titleGoosed = false;
    let windowsUISettings = null;
    let audioArray = [];
    let loopingAudio = null;

    let signalSocket = null;
    let signalSocketConnected = false;

    let accountLevelPremium = true; //! = false
    let stripe;
    let paymentRequest;
    let paymentMethodEvent;
    let processFiles = [];
    let processProgress = 0;
    let remainingProcess = 0;
    let webcamWindow;
    let imageInput = null;
    let scoreServerAchievementData = null;
    let achievementData = null;
    let vibrateInterval = null;
    let supportsVibrate = "vibrate" in navigator;
    let dataTransferManager = null;
    let winShareType = null;
    let textToShare = null;
    let fileToShare = null;
    let startReportingShake = false;
    let lastShakeX = 0;
    let lastShakeY = 0;
    let lastShakeZ = 0;
    let shakeSensitivity = 3;
    let gravity = 9.80665;
    let pendingScoreMode = null;
    let pendingScoreScore = null;
    let scorePending = false;
    let scoresPendingUsername = null;
    let midiPlayer = null;
    let currentMidiCallback = null;
    let currentMidiLoop = false;
    let currentMidi = null;
    let currentMidiVolume = 100;
    let hidden; 
    let visibilityChange;
    let fileImportCallback = null;
    let midiScriptAdded = false;
    let fileSelector = null;
    let workingSubplatform = "none";
    let gamepadScriptLoaded = false;
    let onGamepadUp = null;
    let onGamepadDown = null;
    let currentGamepadElement = null;
    let styleDynamicPrint = null;
    let printArea = null;
    let tmpExportCallback = null;
    let fileSystemFileIdCounter = 0;
    let fileSystemFilesHolder = {};
    let winRingtoneAvailable = false;
    let audioInstances = {};
    let currentChatRoom = null;
    let appFrameLoaded = false;
    let workspaceFolder = null;
    let domainRootStorage = null;
    let openFileCallback = null;
    let androidHostVersion = 0;
    let tmpFileSavCallback = null;
    let saveCallbacks = {};
    let saveCallbackIdNumber = 0;
    let youtubeVideoFinished = false;
    let youtubePlayer = null;
    let filesystemFileSaveCallback = null;


    let shortcutData = [];
    let currentFrameURL = null;
    let systemNavigationManager = null;
    let winAlreadyLaunched = false;
    let launchFile = null;
    let extensionBridgeApplicationId = null;
    let extensionBridgeActive = false;
    let opacitySetting = 0;
    let currentTheme = "#1E88E5";
    let chatiFrame = null;
    let didBeginInit = false;
    let isAutoLoginAttempt = false;
    let iapWindow = null;
    let isInstalledPWA = false;
    let deferredPrompt = null;
    let tmpFriendWindow = null;
    let friendOnlineStatus = {};
    let lastFriendsStatus = null;
    let fileSystemAccessSupported = false;
    let coinBuyWindow = null;
    let coinBuyCallback = null;

    let isApeLauncher = false;
    let isWindowsStore = false;
    let isSteam = false;
    let isApeMarket = false;
    let isApeTvFrame = false;
    let isWacChildWindow = false;
    let isSoundboardCity = false;
    let isApeVRFrame = false;

    let wacUtils = null;

    let usingElectronHost = false;

    let allGamesRef = [];

    let appResultCounter = 1;
    let appResultListeners = {};
    let thisWindowResultCounter = 0;

    if (typeof document.hidden !== "undefined") { 
        hidden = "hidden";
        visibilityChange = "visibilitychange";
    } else if (typeof document.mozHidden !== "undefined") {
        hidden = "mozHidden";
        visibilityChange = "mozvisibilitychange";
    } else if (typeof document.msHidden !== "undefined") {
        hidden = "msHidden";
        visibilityChange = "msvisibilitychange";
    } else if (typeof document.webkitHidden !== "undefined") {
        hidden = "webkitHidden";
        visibilityChange = "webkitvisibilitychange";
    }

    // WAC 4.5.0 hooks 
    window.announceLivestreamStatus = announceLivestreamStatus;
    window.isVR = isVR;
    window.setSplashLogo = setSplashLogo;
    window.exitWithData = exitWithData;
    window.showApeCoinPurchaseFlow = showApeCoinPurchaseFlow;
    window.getApeCoinBalance = getApeCoinBalance;
    window.spendApeCoins = spendApeCoins;
    window.queryCoinPurchases = queryCoinPurchases;
    window.sendApeCoins = sendApeCoins;
    window.refreshCoinBalance = refreshCoinBalance;

    // WAC 4.4.0 hooks
    window.getSafeAreaInset = getSafeAreaInset;
    window.getInstalledApps = getInstalledApps;
    window.launchApplication = launchApplication;
    window.isAppInstalled = isAppInstalled;
    window.uninstallApp = uninstallApp;

    // WAC 4.2.0 hooks
    window.getFileTypeHelper = getFileTypeHelper;

    // WAC v4.1.0 hooks
    window.getIsEmbedded = getIsEmbedded;

    // WAC v4.0.0 hooks
    window.getADLVersion = getADLVersion;
    window.getADL = getADL;
    window.showFriendsList = showFriendsList;
    window.postToSignalServer = postToSignalServer;
    window.setFriendsStatus = setFriendsStatus;
    window.injectStylesheet = injectStylesheet;
    window.removeStylesheet = removeStylesheet;
    window.removeCustomCSS = removeCustomCSS;
    window.injectCustomCSS = injectCustomCSS;
    window.injectFontFace = injectFontFace;
    window.removeFontFace = removeFontFace;

    // Pre-WAC4 Hooks
    window.printRaw = printRaw;
    window.getRawPrinterConfig = getRawPrinterConfig;
    window.supportsRawPrinting = supportsRawPrinting;
    window.createTcpSocket = createTcpSocket;
    window.supportsTcpSockets = supportsTcpSockets;
    window.tvHostMessage = tvHostMessage;
    window.getAppUrlId = getAppUrlId;
    window.notifyImportReady = notifyImportReady;
    window.notifyURL = notifyURL;
    window.getIsBetaMode = getIsBetaMode;
    window.proxyRequest = proxyRequest;
    window.resetCustomMenu = resetCustomMenu;
    window.resetCustomToolbar = resetCustomToolbar;
    window.getBaseURL = getBaseURL;
    window.buildVarPackage = buildVarPackage;
    window.setSidebarCategory = setSidebarCategory;
    window.getPlatform = getPlatform;
    window.setPremiumOptionText = setPremiumOptionText;
    window.setTheme = setTheme;
    window.setTitle = setTitle;
    window.getIsPremium = getIsPremium;
    window.isTV = isTV;
    window.addMenuItem = addMenuItem;
    window.updateVarPackage = updateVarPackage;
    window.loadFile = loadFile;
    window.isApeAppsLoggedIn = isApeAppsLoggedIn;
    window.closeChat = closeChat;
    window.showDialogNew = showDialogNew;
    window.getInterface = getInterface;
    window.clearTitleOptions = resetTitle;
    window.clearSplash = clearSplash;
    window.setTitleVisible = setTitleVisible;
    window.setTitleBackground = setTitleBackground;
    window.setTitleLogo = setTitleLogo;
    window.addTitleOption = addTitleOption;
    window.getAppVersion = getAppVersion;
    window.setTitleSubtitle = setTitleSubtitle;
    window.getExternalParm = getExternalParm;
    window.loadURL = loadURL;
    window.toggleMenu = toggleMenu;
    window.isSystemThemeDark = isSystemThemeDark;
    window.showSplash = showSplash;
    window.getFiles = getFiles;
    window.askForLicenseKey = presentPremium;
    window.newCheckFullscreenWeb = getIsFullscreen;
    window.newToggleFullscreenWeb = toggleFullscreen;
    window.getSystemAccentColor = getSystemAccentColor;
    window.fetchFeaturedImage = fetchFeaturedImage;
    window.playAudio = playAudio;
    window.checkForLaunchFile = checkForLaunchFile;
    window.killLoop = killLoop;
    window.showToast = showToast;
    window.addToolbarItem = addToolbarItem;
    window.inputBox = inputBox;
    window.getFacebookLogged = getFacebookLogged;
    window.showAlert = showAlert;
    window.showChoice = showChoice;
    window.showColorPicker = showColorPicker;
    window.presentApeLogin = presentApeLogin;
    window.getApeAppsProfile = getApeAppsProfile;
    window.getApeAppsCreds = getApeAppsCreds;
    window.getFriendsList = getFriendsList;
    window.importImage = importImage;
    window.showAchievements = showAchievements;
    window.reportAchievement = reportAchievement;
    window.loopAudio = loopAudio;
    window.vibrate = vibrate;
    window.endVibrate = endVibrate;
    window.toggleShareBox = toggleShareBox;
    window.doTTS = doTTS;
    window.stopTTS = stopTTS;
    window.setShakeSensitivity = setShakeSensitivity;
    window.setBrandingLogo = setBrandingLogo;
    window.toggleScores = toggleScores;
    window.submitHighScore = submitHighScore;
    window.wacPlayMidi = playMidi;
    window.wacStopMidi = stopMidi;
    window.importLocalFile = importLocalFile;
    window.logEvent = logEvent;
    window.setGamepadFunctions = setGamepadFunctions;
    window.showInterstitial = showInterstitial;
    window.printContent = printContent;
    window.processScreenshot = processScreenshot;
    window.notifySaveAsDone = notifySaveAsDone;
    window.wacSupportsRingtone = supportsRingtone;
    window.wacPlayAudioInstance = playAudioInstance;
    window.wacStopAudioInstance = stopAudioInstance;
    window.presentNativeList = presentNativeList;
    window.loadChat = loadChat;
    window.sendChatMessage = sendChatMessage;
    window.openPackageListing = openPackageListing;
    window.exportTextFileAsDialog = exportTextFileAsDialog;
    window.sendFile = sendFile;
    window.onAndroidSaveDone = onAndroidSaveDone;
    window.forceCloseMenu = forceCloseMenu;
    window.loadSharedCloudFile = loadSharedCloudFile;
    window.buildCloudSharePathForFile = buildCloudSharePathForFile;
    window.showPopupMenu = showPopupMenu;
    window.isRewardedVideoAvailable = isRewardedVideoAvailable;
    window.onOpenFileReady = onOpenFileReady;
    window.wacPlayRewardedVideo = getRewardedVideo;
    window.showDialog = showDialog;
    window.fileDialogsSupported = fileDialogsSupported;
    window.openFileDialog = openFileDialog;
    window.filesystemFileSave = filesystemFileSave;
    window.notifySaveImportDataDone = notifySaveImportDataDone;
    window.onChatMessageReciever = onChatMessageReciever;
    window.supportsTransparency = supportsTransparency;
    window.getFacebookCredentials = getFacebookCredentials;
    window.requestFacebookLogin = requestFacebookLogin;
    window.addFriend = addFriend;
    window.toggleToolbar = toggleToolbar;
    window.wacCreateShortcut = createShortcut;
    window.endsWith = endsWith;
    window.negotiateKeyContent = negotiateKeyContent;
    window.postForResult = postForResult;
    window.getForResult = getForResult;
    window.getBaseURL = getBaseURL;
    window.saveScreenshot = saveScreenshot;
    window.deleteFile = deleteFile;
    window.onSetBackFunction = onSetBackFunction;
    window.copyToClipboard = copyToClipboard;
    window.getHighScoresUsername = getHighScoresUsername;
    window.getDailyTopScore = getDailyTopScore;
    window.getNewsfeed = getNewsfeed;
    window.changeWorkingDirectory = changeWorkingDirectory;
    window.workspaceChangeCallback = workspaceChangeCallback;
    window.getLaunchFileName = getLaunchFileName;
    window.getLaunchFileContent = getLaunchFileContent;
    window.saveLaunchFile = saveLaunchFile;
    window.awaBridgeExtReciever = awaBridgeExtReciever;
    window.isAWABridgeExtConnected = isAWABridgeExtConnected;
    window.sendChoiceTwo = sendChoiceTwo;
    window.sendChoiceOne = sendChoiceOne;
    window.toastWacBack = toastWacBack;
    window.fadeTitle = fadeTitle;
    window.wacSetRingtone = setRingtone;
    window.updateApeAppsAvatar = updateApeAppsAvatar;
    window.getAppName = getAppName;
    window.isChatOpen = isChatOpen;
    window.sendKongregateStat = sendKongregateStat;
    window.showTryBetaInfo = showTryBetaInfo;
    window.logError = logError;

    finalizePremiumAndRemoveBannerAds(); //!

    function onLoad() {

        initParms();

        setTitle(null);

        linkLayouts();
        getAllGames();

        if(localStorage["awa-lpt-printers"]) {
            try {
                currentLPTPrinters = JSON.parse(localStorage["awa-lpt-printers"]);
            } catch(ex) {
                console.log(ex);
                currentLPTPrinters = {};
            }
        } else {
            currentLPTPrinters =  {};
        }

        premiumAllowed = true;

        if(window.wacIsGame && isWindowsStore) {
            // until they add a way to do MS store
            // purchases in PWAs
            premiumAllowed = false;
        }

        if(window.wacUtils) {
            wacUtils = window.wacUtils;
            platformName = "desktop";
            CURRENT_PLATFORM = wacUtils.getPlatform();
            window.platformOS = getElectronOS();

            wacUtils.setApplicationMenu(null);
        } else {
            isSteam = false;
            isApeMarket = false;
            usingElectronHost = false;
        }

        if ('launchQueue' in window) {
            launchQueue.setConsumer((launchParams) => {
                // Nothing to do when the queue is empty.
                if (!launchParams.files.length) {
                    return;
                }
                for (const fileHandle of launchParams.files) {
                    launchFile = fileHandle;
                }
            });
        }

        let adlScript = document.createElement("script");
        adlScript.onload = function() {
            adl.addADLStyle();
            adl.addIonicons();
            adl.initADL();

            loadPageStyles();
        };
        adlScript.src = "https://design.ape-apps.com/adl/" + window.adlVersion + "/js/adl.js";
        document.head.appendChild(adlScript);
        
        let filetypeHelperScript = document.createElement("script");
        filetypeHelperScript.onload = function(){
            fth = window.apeApps.utilities.fileTypeHelper;
        };
        filetypeHelperScript.src = "https://www.apewebapps.com/js/filetypehelper/" + window.fileTypeHelperVersion + "/filetypehelper.js";
        document.head.appendChild(filetypeHelperScript);

        if(window.platformOS == "ios") {
            document.body.classList.add("awaios");
        }

        let ret = localStorage[window.appId + "_winstore_upret"];

        if(ret && !isNaN(parseInt(ret))) {
            localStorage[window.appId + "_winstore_upret"] = (parseInt(ret) - 1);
        }
    }

    function onResize() {
        let vh = window.innerHeight * 0.01;
        document.documentElement.style.setProperty("--vh", \`\${vh}px\`);

        if(!getIsFullscreen()) {
            let refname = window.appId + "-size";

            let val = window.innerWidth + "x" + window.innerHeight;
            localStorage[refname] = val;
        }
        
    }

    function onUnload() {
        closeSignalSocketConnection();
    }

    function tvHostMessage(data) {
        console.log(data);

        if(data == "resume") {
            doOnResume();
            return;
        }

        if(data == "pause") {
            doOnPause();
            return;
        }
    }

    function onMessage(e) {

        if(e.origin == "https://chat.ape-apps.com") {
            if(e.data.msg && e.data.msg == "xfercred") {
                if(isApeAppsLoggedIn() && apeAppsLoginData && apeAppsLoginData.creds) {
                    let retDat = {
                        "msg": "xfercreddat",
                        "data": {
                            "sa": apeAppsLoginData.creds.sida,
                            "sb": apeAppsLoginData.creds.sidb,
                            "sc": apeAppsLoginData.creds.sidc,
                            "am": window.appId
                        }
                    };

                    chatiFrame.contentWindow.postMessage(retDat,"*");
                }
            }

            if(e.data.msg && e.data.msg == "loadprofile") {
                if(childWindowObject && childWindowObject.webAppCore &&childWindowObject.webAppCore.onChatUsernameSelected) {
                    childWindowObject.webAppCore.onChatUsernameSelected(e.data.data);
                }
            }

            if(e.data.msg && e.data.msg == "newmessage") {
                if(childWindowObject && childWindowObject.webAppCore && childWindowObject.webAppCore.onChatMessage) {
                    childWindowObject.webAppCore.onChatMessage(e.data.data);
                }
            }
        }

        if(e.origin == "https://apps.ape-apps.com") {
            let data = e.data;

            if(data.func) {
                if(data.func == "onPremiumUpgradeComplete") {
                    notifyPremiumActivation(data.data);

                    if(iapWindow) {
                        iapWindow.close();
                    }

                    iapWindow = null;
                }
            }
        }

        if(e.origin == "https://accounts.ape-apps.com") {
            let data = e.data;

            if(data.func) {
                if(data.func == "onCoinsPurchased") {

                    let me = getApeAppsProfile();
        
                    if(me) {
                        me.bal = parseInt(data.data);
                    }

                    let balance = getApeCoinBalance();
        
                    if(coinBuyCallback) {
                        coinBuyCallback(balance);
                    }

                    if(coinBuyWindow) {
                        coinBuyWindow.close();
                    }

                    coinBuyWindow = null;
                    coinBuyCallback = null;
                }
            }
        }

        

        if(e.origin == "https://www.apewebapps.com") {

            let data = e.data;

            

            if(data.func) {

                if(data.func == "returnlaunchdata") {
                    let dat = data.data;

                    passedInData = dat;

                    if(data.res) {
                        thisWindowResultCounter = parseInt(data.res);
                    } else {
                        thisWindowResultCounter = null;
                    }
                }

                if(data.func == "getlaunchdata") {
                    if(data.data.id && childAppWindows[data.data.id]) {
                        let launcher = childAppWindows[data.data.id];

                        if(launcher.data) {
                            launcher.win.postMessage({
                                func: "returnlaunchdata",
                                data: launcher.data
                            });
                        }
                    }
                }

                if(data.func == "exitWithData") {
                    if(data.data.id && childAppWindows[data.data.id]) {
                        let launcher = childAppWindows[data.data.id];

                        if(launcher.win) {
                            if(launcher.res) {
                                let cb = appResultListeners[launcher.res];

                                if(cb) {
                                    cb(data.data.data);
                                }
                            }
                            
                            launcher.win.close();
                        }
                    }
                }
            
                if(data.func == "tvCredsReturn") {
                    if(data.data.r && data.data.x) {
                        attemptAPISignIn(atob(data.data.r),atob(data.data.x));
                    }
                }
            }

            if(e.data.msg) {
                let msg = e.data.msg;
                let data = e.data.data;

                if(msg == "needslogin" && tmpFriendWindow) {
                    let retDat = {
                        "func": "onApeAppsLogin",
                        "data": {
                            "sida": apeAppsLoginData.creds.sida,
                            "sidb": apeAppsLoginData.creds.sidb,
                            "sidc": apeAppsLoginData.creds.sidc,
                            "am": window.appId
                        }
                    };

                    tmpFriendWindow.contentWindow.postMessage(retDat,"*");
                }

                if(msg == "loadprofile" && tmpFriendWindow) {
                    if(friendSelectedCallback) {
                        let cb = friendSelectedCallback;
                        friendSelectedCallback = null;
                        cb(data);
                    }

                    adl.dismissDialogWindow();
                }
            }

            if(e.data.func && e.data.func == "saveFile") {
                saveFile(e.data.data.name,e.data.data.content,e.data.data.blockCloud,e.data.data.useDir,childWindowObject.webAppCore.fileSaveComplete);
            }

            if(e.data.func && e.data.func == "onApeAppsLogin") {
                adl.dismissDialogWindow();

                apeAppsLoginData.creds = e.data.data;

                if (window.PasswordCredential) {
                    let cred = new PasswordCredential({
                        id: atob(apeAppsLoginData.creds.r),
                        password: atob(apeAppsLoginData.creds.x),
                        name: "Ape Apps Account",
                        iconURL: "https://accounts.ape-apps.com/getavatar.php?u=" + atob(apeAppsLoginData.creds.r)
                    });
                    navigator.credentials.store(cred);
                }


                isAutoLoginAttempt = false;
                submitLoginData(apeAppsLoginData.creds.sida,apeAppsLoginData.creds.sidb,apeAppsLoginData.creds.sidc,apeAppsLoginData.creds.app);

                delete apeAppsLoginData.creds.x;
                delete apeAppsLoginData.creds.r;
                
            }
    
            if(e.data.func && e.data.func == "closecamwindow") {
                if(webcamWindow == null) {
                    return;
                }
    
                webcamWindow.close();
            }
    
            if(e.data.func && e.data.func == "onCameraCapture") {
                if(webcamWindow == null) {
                    return;
                }
    
                webcamWindow.close();
    
                var tag = e.data.data;
    
                if(childWindowObject && childWindowObject.webAppCore && childWindowObject.webAppCore.passBackImageFromParent) {
                    childWindowObject.webAppCore.passBackImageFromParent(tag);
                }
            }
        }

        
    }

    function handleVisibilityChange() {
        window.focus();

        if (document[hidden]) {
            doOnPause();
        } else {
            doOnResume();
        }
    }

    function doOnPause() {

        if(childWindowObject && childWindowObject.webAppCore && childWindowObject.webAppCore.onPause) {
            childWindowObject.webAppCore.onPause();

            midiOnPause();
        }

        appIsPaused = true;
    }

    function doOnResume() {

        if(childWindowObject && childWindowObject.webAppCore && childWindowObject.webAppCore.onResume) {
            childWindowObject.webAppCore.onResume();

            midiOnResume();
        }

        appIsPaused = false;
    }

    function loadPageStyles() {
        let linkTag = document.createElement("link");
        linkTag.rel = "stylesheet";
        linkTag.type = "text/css";
        linkTag.onload = function() {
            document.body.style.fontFamily = null;
            initFilesDatabase();
        };
        linkTag.href = "/css/style/" + window.styleVersion + "/style.css";

        document.head.appendChild(linkTag);
    }

    function doBeginInit() {

        if(didBeginInit) {
            return;
        }

        didBeginInit = true;

        if("serviceWorker" in navigator) {
            gooseUpServiceWorker();
        } else {
            continueInit();
        }
    }

    function continueInit() {

        if(window.navigator.onLine) {
            initiateSignIn();
        }

        if(window.webProtocols && window.webProtocols.trim().length > 0 && window.webProtocols.trim() != "n/a" && navigator.registerProtocolHandler) {
            let allProtocols = window.webProtocols.split(",");
            
            for(let i = 0; i < allProtocols.length; i++) {
                try {
                    navigator.registerProtocolHandler(allProtocols[i],window.protocolBase,window.appName);
                } catch(ex) {
                    console.log(ex);
                }
                
            }
        }
    
        
        prepareAchievementData();

        document.addEventListener(visibilityChange, handleVisibilityChange, false);

        imageInput = document.createElement("input");
        imageInput.type = "file";
        imageInput.accept = "image/*";
        imageInput.addEventListener("change",onImageInputChange,false);

        fileSelector = document.createElement("input");
        fileSelector.setAttribute("type", "file");
        fileSelector.addEventListener("change",onInputFileSelected,false);

        if(window.preloadSocketIO) {
            loadSocketIO(null);
        }

        // needed for some legacy WAC calls
        // migrate future references to appId
        window.apeMarket = window.appId;

        let shortcutName = window.appId + "-shortcuts";
        let shortcutDataEnc = getCookie(shortcutName);

        if(shortcutDataEnc && shortcutDataEnc.trim().length > 0) {
            try {
                let dec = atob(shortcutDataEnc);
                shortcutData = JSON.parse(dec);
            } catch(ex) {
                console.log(ex);
                shortcutData = [];
            }
        }        

        setUpApplicationWorkingFolder();

        setPlayReference();

        setInterval(sharedResourcePing,60000);
    }

    function setPlayReference(addTime) {

        if(!isApeAppsLoggedIn()) {
            return;
        }

        let refName = apeAppsLoginData.profile.un + "_playLog";

        let playedTitles = localStorage[refName];

        if(!playedTitles || playedTitles.trim().length < 2) {
            playedTitles = "{}";
        }

        let playedTitlesObj = {};

        try {
            playedTitlesObj = JSON.parse(playedTitles);
        } catch(ex) {
            console.log(ex);
            playedTitlesObj = {};
        }

        if(!playedTitlesObj[window.appId]) {
            playedTitlesObj[window.appId] = {
                plays: 0,
                first: new Date().getTime(),
                last: new Date().getTime(),
                totalMinutes: 0,
                playedOnTv: false
            };
        }

        playedTitlesObj[window.appId].last = new Date().getTime();

        if(addTime) {
            playedTitlesObj[window.appId].totalMinutes += addTime;   
        } else {
            playedTitlesObj[window.appId].plays++;
        }

        if(isTV()) {
            playedTitlesObj[window.appId].playedOnTv = true;
        }

        let saveDat = JSON.stringify(playedTitlesObj);
        localStorage[refName] = saveDat;
    }

    function getPermissionToUseWorkspaceFolder() {
        adl.showDialog({
            title: "Folder Permission",
            message: "You have previously elected to change the location of your workspace folder.  Do you want to continue using the location you specified, or use the systems internal storage location instead?",
            icon: "folder-open-outline",
            buttons: [
                {
                    text: "Internal",
                    func: function() {
                        db.transaction(["files"], "readwrite").objectStore("files").delete("wac_workspace_location.xxx");
                        workspaceFolder = null;
                        setDefaultWorkingFolder();
                    }
                },
                {
                    text: "My Location",
                    func: function() {
                        workspaceFolder.requestPermission({
                            mode: "readwrite"
                        }).then(function(perms){
                            if(!perms || perms != "granted") {
                                db.transaction(["files"], "readwrite").objectStore("files").delete("wac_workspace_location.xxx");
                                workspaceFolder = null;
                                setDefaultWorkingFolder();
                            } else {
                                loadAppFrame();
                            }
                        });
                    }
                }
            ]
        });
    }

    function linkLayouts() {
        fontPreloadArea = document.getElementById("fontPreloadArea");

        splashScreen = document.getElementById("splashScreen");
        splashLoadingText = document.getElementById("splashLoadingText");

        appWrapper = document.getElementById("appWrapper");
        toolbarHolder = document.getElementById("toolbarHolder");
        bottomToolbarHolder = document.getElementById("bottomToolbarHolder");
        chatHolder = document.getElementById("chatHolder");
        chatHolderToolbar = document.getElementById("chatHolderToolbar");
        chatHolderFrame = document.getElementById("chatHolderFrame");
        adHolder = document.getElementById("adHolder");
        appFrame = document.getElementById("appFrame");

        titleScreen = document.getElementById("titleScreen");
        titleScreenLogo = document.getElementById("titleScreenLogo");
        titleScreenOptions = document.getElementById("titleScreenOptions");
        titleScreenFeaturedApp = document.getElementById("titleScreenFeaturedApp");
        titleScreenNewsfeed = document.getElementById("titleScreenNewsfeed");
        titleScreenApeLogo = document.getElementById("titleScreenApeLogo");
        titleScreenSubtitle = document.getElementById("titleScreenSubtitle");

        introFader = document.getElementById("introFader");

        titleScreen.addEventListener("focus",autoCloseChatIfNeeded);
    }

    function showSplash(text) {
        if(!text) {
            text = "Loading...";
        }

        appWrapper.style.position = "fixed";
        splashScreen.style.display = "block";
        splashLoadingText.innerHTML = text;
    }

    function clearSplash() {
        appWrapper.style.position = "initial";
        splashScreen.style.display = "none";
    }

    function initFilesDatabase() {
        let dbName = "files" + window.appId;
        let request;

        try {
            request = window.indexedDB.open(dbName, 1);
        } catch(err) {
            console.log(err);
            retryDb();
            return;
        }

        request.onerror = function() {
            retryDb();
        };

        request.onsuccess = function() {
            databaseFunctional = true;
            db = request.result;
            doBeginInit();
        };

        request.onupgradeneeded = function() {
            databaseFunctional = true;
            
            db = request.result;
            db.createObjectStore("files", {keyPath: "name"});
            
            setTimeout(doBeginInit,500);
        };
    }

    function retryDb() {
        if(dbHasRetry) {
            doBeginInit();
            return;
        }
        
        dbHasRetry = true;
        setTimeout(initFilesDatabase,500);
    }

    function loadAppFrame() {

        appFrame.title = window.appName;

        appFrame.addEventListener("focus",autoCloseChatIfNeeded);
        
        appFrame.onload = function(){

            if(appFrameLoaded) {
                return;
            }
            
            appFrameLoaded = true;

            childWindowObject.addEventListener("focus",autoCloseChatIfNeeded);

            rebuildToolbar();
        };

        appFrame.src = window.appURL; 
        childWindowObject = appFrame.contentWindow;

        appWrapper.style.display = null;

        logStatistics();
        setTimeout(initAdvertising,1000);

        setTimeout(checkForLicenseLaunch,500);

        setInterval(function(){
            setPlayReference(1);
        },60000);

        // load rateme nagger
        setTimeout(function() {

            if(!isWindowsStore) {
                return;
            }

            if(window.windowsAppId && window.windowsAppId.length > 5) {
                let rmnScript = document.createElement("script");
                rmnScript.onload = function() {
                    setTimeout(function(){
                        if(window.RateMeNagger) {

                            let rateURL = "ms-windows-store://review/?ProductId=" + window.windowsAppId;

                            rmn = new window.RateMeNagger(
                                window.appName,
                                rateURL,
                                window.appId,
                                "in the Windows Store",
                                function(rated) {
                                    if(ah) {
                                        if(rated) {
                                            ah.trackEvent("Rate Me Nagger", "Accepted", window.appName, true);
                                        } else {
                                            ah.trackEvent("Rate Me Nagger", "Rejected", window.appName, true);
                                        }
                                    }
                                    
                                },
                                false
                            );

                            console.log(rmn);

                            rmn.pingForRating();

                        }
                    },100);
                };
                rmnScript.src = "/js/ratemenagger/" + window.rateMeNaggerVersion + "/ratemenagger.js";
                document.head.appendChild(rmnScript);
            }

            
        },1200);
    }

    function checkForLicenseLaunch() {
        if(launchFile && launchFile.name && (launchFile.name.endsWith(".amk") || launchFile.name.endsWith(".avk"))) {
            openLaunchFileLicense();
        }
    }

    

    function autoCloseChatIfNeeded() {
        if(window.innerWidth <= 500 && isChatOpen()) {
            closeChat();
        }
    }

    function notifyURL(url) {
        startReportingShake = false;
        currentFrameURL = url;
    }

    function getIsBetaMode() {
        return window.betaMode;
    }

    function proxyRequest(url,callback,data,post,forceProxy) {

        if(!navigator.onLine) {
            if(callback.fromWAC) {
                if(childWindowObject.webAppCore) {
                    childWindowObject.webAppCore.wacProxyReturn(callback.id,null);
                }
            } else {
                callback(null);
            }

            return;
        }

        let xmlhttp = new XMLHttpRequest();
        xmlhttp.callbackFunction = callback;
        
        let m = "get";
        
        if(post) {
            m = "post";
        }
        
        let params = new FormData();
        params = "";
        
        if(data && data != null) {
            if(Array.isArray(data)) {
                for(let i = 0;i < data.length; i++) {
                    
                    
                    if(i > 0) {
                        params = params + "&";
                    }

                    params = params + data[i].key + "=" + data[i].value;
                }
            } else {
                if (typeof data === "string" || data instanceof String) {
                    params = data;
                } else {
                    params = data.key + "=" + data.value;
                }
            }
        }

        xmlhttp.addEventListener("timeout",function(){
            
            if(this.callbackFunction) {
                if(this.callbackFunction.fromWAC) {
                    if(childWindowObject && childWindowObject.webAppCore) {
                        childWindowObject.webAppCore.wacProxyReturn(this.callbackFunction.id,null);
                    }
                    
                } else {
                    this.callbackFunction(null);
                }
            }
            
        });
        
        xmlhttp.addEventListener("load",function(e){

            if(this.callbackFunction) {
                if(this.callbackFunction.fromWAC) {
                    if(childWindowObject && childWindowObject.webAppCore) {
                        childWindowObject.webAppCore.wacProxyReturn(this.callbackFunction.id,e.target.responseText);
                    }
                    
                } else {
                    this.callbackFunction(e.target.responseText);
                }
            }
            

        });
        
        xmlhttp.addEventListener("error",function() {

            if(this.callbackFunction) {
                if(this.callbackFunction.fromWAC) {
                    if(childWindowObject && childWindowObject.webAppCore) {
                        childWindowObject.webAppCore.wacProxyReturn(this.callbackFunction.id,null);
                    }
                    
                } else {
                    this.callbackFunction(null);
                }
            }
            
        });

        let useThis = "/awaproxy.php?m=" + m + "&url=" + encodeURIComponent(url);
        let useMethod = "POST";
        
        if(url.indexOf("https:") == 0 && !forceProxy) {
            useThis = url;

            if (post) {
                useMethod = "POST";
            } else {
                useMethod = "GET";

                if (params.length > 0) {
                    useThis += ("?" + params);
                }
            }

        }
        
        if(url.indexOf("?") == -1) {
            url = url + "?zxwaccb=" + Math.floor(Math.random() * 10000); 
        } else {
            url = url + "&zxwaccb=" + Math.floor(Math.random() * 10000); 
        }

        xmlhttp.open(useMethod,useThis,true);
        xmlhttp.timeout = 15000;

        if (useMethod == "POST") {
            xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
            xmlhttp.send(params);

        } else {
            xmlhttp.send();
        }

    }

    function resetCustomMenu() {
        customMenu = [];
        rebuildToolbar();
    }

    function resetCustomToolbar() {
        customToolbar = [];
        rebuildToolbar();
    }

    function rebuildToolbar() {

        if(!toolbarHolder) {
            return;
        }

        toolbarHolder.innerHTML = "";
        bottomToolbarHolder.innerHTML = "";

        let tbBackground = null;
        let tbForeground = null;

        if(window.themeStyle == "2" || window.themeStyle == "3") {
            tbBackground = currentTheme;

            if(adl.isHexColorDark(tbBackground)) {
                tbForeground = "#ffffff";
            } else {
                tbForeground = "#000000";
            }
        }

        if(!window.useToolbar || getIsEmbedded()) {
            return;
        }

        let secondaryToolbarItems = [];
        let primaryToolbarItems = [];

        for(let i = 0; i < customToolbar.length; i++) {
            let item = customToolbar[i];

            if(item.wacInSecondary) {
                secondaryToolbarItems.push(item);
            } else {
                primaryToolbarItems.push(item);
            }
        }

        let accountButton = null;
        let premiumOption = null;
        let categoryOption = null;

        if(sidebarCategory) {

            let sbIcon = "fluent.&#xE179;";

            if(sidebarCategoryIcon) {
                sbIcon = getCleanIcon(sidebarCategoryIcon);
            }

            categoryOption = {
                title: sidebarCategory,
                icon: sbIcon,
                func: showCategoriesFromSidebar
            };
        } else {
            let sbIcon = "fluent.&#xE179;";

            categoryOption = {
                title: "Latest Updates",
                icon: sbIcon,
                func: showCategoriesFromSidebar
            };
        }

        
        if(!getIsPremium() && premiumAllowed) {
            premiumOption = {
                title: premiumOptionText,
                icon: "fluent.&#xE14D;",
                func: presentPremium
            };
        }

        if(!isSoundboardCity) {
            if(isApeAppsLoggedIn()) {
                accountButton = {
                    type: "button",
                    label: apeAppsLoginData.profile.un,
                    icon: "https://accounts.ape-apps.com/getavatar.php?u=" + apeAppsLoginData.profile.un,
                    circleIcon: true,
                    func: showAccountInfo
                };
            } else {
                accountButton = {
                    type: "button",
                    label: "Sign In",
                    icon: "fluent.&#xE13D;",
                    func: presentApeLogin
                };
            }
        }
        


        let toolbarOptions = {
            element: toolbarHolder,
            items: [],
            background: tbBackground,
            foreground: tbForeground
        };

        let customHelpOptions = 0;

        if(window.innerWidth > 500) {

            let customMenuCategories = [];
            let customMenuItems = {};

            customMenuCategories.push("File");
            customMenuItems.File = [];

            for(let i = 0; i < customMenu.length; i++) {
                let cm = customMenu[i];

                if(cm.wacSubmenu) {
                    if(customMenuCategories.indexOf(cm.wacSubmenu) == -1) {
                        if(cm.wacSubmenu != "View" && cm.wacSubmenu != "Help") {
                            customMenuCategories.push(cm.wacSubmenu);
                        }
                        
                    }  
                } else {
                    cm.wacSubmenu = "File";
                }

                if(!customMenuItems[cm.wacSubmenu]) {
                    customMenuItems[cm.wacSubmenu] = [];
                }

                customMenuItems[cm.wacSubmenu].push(cm);

                if(cm.wacSubmenu == "Help") {
                    customHelpOptions++;
                }
            }

            if(customMenuCategories.indexOf("View") == -1) {

                customMenuCategories.push("View");

                if(!customMenuItems.View) {
                    customMenuItems.View = [];
                }
                
            }

            if(customMenuCategories.indexOf("Help") == -1) {
                customMenuCategories.push("Help");

                if(!customMenuItems.Help) {
                    customMenuItems.Help = [];
                }
            }

            if(window.platform != "ios" && window.platform != "android" && !isSoundboardCity) {
                if(customMenuItems.View.length > 0) {
                    customMenuItems.View.push({
                        type: "separator"
                    });
                }

                let fsText = "Enter Fullscreen";
                let fsIcon = "fluent.&#xE1D9;";

                if(getIsFullscreen()) {
                    fsText = "Exit Fullscreen";
                    fsIcon = "fluent.&#xE1D8;";
                }

                customMenuItems.View.push({
                    title: fsText,
                    func: toggleFullscreen,
                    icon: fsIcon
                });
            }
            
            if(premiumOptionText && !getIsPremium() && premiumAllowed) {

                if(customMenuItems.Help.length > 0) {
                    customMenuItems.Help.unshift({
                        type: "separator"
                    });
                    customMenuItems.Help.unshift(premiumOption);
                } else {
                    customMenuItems.Help.push(premiumOption);
                }

            }

            if(customHelpOptions > 0) {
                customMenuItems.Help.push({
                    type: "separator"
                });
            }
            
            if(categoryOption) {
                customMenuItems.Help.push(categoryOption);
            }

            if(!usingElectronHost && "share" in navigator) {
                customMenuItems.Help.push({
                    title: "Share " + window.appName,
                    func: performStandardShare,
                    icon: "fluent.&#xF003;"
                });
            }
            

            customMenuItems.Help.push({
                title: "Send Feedback",
                func: presentFeedback,
                icon: "fluent.&#xE119;"
            });

            if(!isWindowsStore && !isSteam && !usingElectronHost && !isApeTvFrame && !isSoundboardCity) {

                if(deferredPrompt && !isInstalledPWA) {
                    customMenuItems.Help.push({
                        title: "Install " + window.appName,
                        func: function() {
                            if(ah) {
                                ah.trackEvent("Menu Install", "Selected", window.appName, true);
                            }
                            
                            if(deferredPrompt) {
                                deferredPrompt.prompt();
                            }
                        },
                        icon: window.shareImage,
                    });
                }

                if(!isApeLauncher) {
                    customMenuItems.Help.push({
                        title: "Ape Apps Launcher",
                        func: function(){
                            loadURL("https://market.ape-apps.com/ape-apps-launcher.html");
                        },
                        icon: "https://www.apewebapps.com/ape-apps-launcher-114.png",
                    });
                }
                
            }

            customMenuItems.Help.push({
                title: "About " + window.appName,
                func: presentAbout,
                icon: "fluent.&#xE783;"
            });

            if(customMenuItems.File.length > 0) {
                customMenuItems.File.push({
                    type: "separator"
                });
            }

            if(!usingElectronHost && !isApeLauncher && !isSoundboardCity) {
                customMenuItems.File.push({
                    title: "More Free Apps",
                    func: showMoreApps,
                    icon: "fluent.&#xE80A;"
                });
            }

            if(isSoundboardCity) {
                customMenuItems.File.push({
                    title: "More Free Soundboards",
                    func: showMoreSoundboards,
                    icon: "/sc-114.png"
                });
            }
            
            if(usingElectronHost || isApeLauncher) {

                let exitText = "Quit " + window.appName;

                if(usingElectronHost) {
                    exitText = "Exit to Desktop";
                }

                customMenuItems.File.push({
                    title: exitText,
                    func: doQuitElectron,
                    icon: "fluent.&#xF3B1;"
                });
            }

            for(let i = 0; i < customMenuCategories.length; i++) {
                let cat = customMenuCategories[i];

                if(!customMenuItems[cat] || customMenuItems[cat].length == 0) {
                    continue;
                }

                toolbarOptions.items.push({
                    type: "button",
                    label: cat,
                    menu: {
                        items: customMenuItems[cat]
                    }
                });
            }

            toolbarOptions.items.push({
                type: "spacer"
            });

            

            if(primaryToolbarItems) {

                let added = 0;
                let totalToAdd = 1;

                if(window.innerWidth > 620) {
                    totalToAdd = 2;
                }

                if(window.platform != "ios" && window.platform != "android") {
                    if(window.innerWidth > 740) {
                        totalToAdd = 3;
                    }

                    if(window.innerWidth > 860) {
                        totalToAdd = 4;
                    }

                    if(window.innerWidth > 980) {
                        totalToAdd = 5;
                    }
                }

                while(primaryToolbarItems.length > 0) {

                    let item = primaryToolbarItems.shift();

                    if(added < totalToAdd) {
                        toolbarOptions.items.push({
                            type: "button",
                            label: item.title,
                            icon: item.icon,
                            func: item.func
                        });
                    } else {
                        secondaryToolbarItems.push(item);
                    }

                    added++;
                }
            }

            if(accountButton) {
                toolbarOptions.items.push(accountButton);
            }

            if(secondaryToolbarItems && secondaryToolbarItems.length > 0) {
                toolbarOptions.items.push({
                    type: "button",
                    special: "threedots",
                    menu: {
                        items: secondaryToolbarItems
                    }
                });
            }
            
        } else {

            if(accountButton) {
                toolbarOptions.items.push(accountButton);
            }
            
            let dropdownOptions = [];
            let needsSep = true;

            if(customMenu) {
                for(let i = 0; i < customMenu.length; i++) {
                    dropdownOptions.push(customMenu[i]);
                }

                if(dropdownOptions.length > 0) {
                    dropdownOptions.push({
                        type: "separator"
                    });

                    needsSep = false;
                }
            }

            if(secondaryToolbarItems.length > 0) {

                while(secondaryToolbarItems.length > 0) {
                    dropdownOptions.push(secondaryToolbarItems.shift());
                }
                

                dropdownOptions.push({
                    type: "separator"
                });

                needsSep = false;
            }

            if(dropdownOptions.length > 0 && needsSep) {
                dropdownOptions.push({
                    type: "separator"
                });
            }
    
            if(premiumOptionText && premiumOption && premiumAllowed) {
                dropdownOptions.push(premiumOption);
            }

            // default menu items 
            if(sidebarCategory) {
                let sbIcon = "fluent.&#xE179;";

                if(sidebarCategoryIcon) {
                    sbIcon = getCleanIcon(sidebarCategoryIcon);
                }

                dropdownOptions.push({
                    title: sidebarCategory,
                    icon: sbIcon,
                    func: showCategoriesFromSidebar
                });
            } else {
                let sbIcon = "fluent.&#xE179;";

                dropdownOptions.push({
                    title: "Latest Updates",
                    icon: sbIcon,
                    func: showCategoriesFromSidebar
                });

            }

            if(!usingElectronHost && "share" in navigator) {
                dropdownOptions.push({
                    title: "Share " + window.appName,
                    func: performStandardShare,
                    icon: "fluent.&#xF003;"
                });
            }
            

            dropdownOptions.push({
                title: "Send Feedback",
                func: presentFeedback,
                icon: "fluent.&#xE119;"
            });

            dropdownOptions.push({
                title: "About " + window.appName,
                func: presentAbout,
                icon: "fluent.&#xE783;"
            });

            if(usingElectronHost || isApeLauncher) {
                let exitText = "Quit " + window.appName;

                if(usingElectronHost) {
                    exitText = "Exit to Desktop";
                }

                dropdownOptions.push({
                    title: exitText,
                    icon: "fluent.&#xF3B1;",
                    func: doQuitElectron
                });
            }

            let text = currentTitle;


            toolbarOptions.items.push({
                type: "title",
                label: text
            });

            toolbarOptions.items.push({
                type: "button",
                special: "threedots",
                menu: {
                    items: dropdownOptions
                }
            });

            
        }

        adl.addToolbar(toolbarOptions);

        if(primaryToolbarItems && primaryToolbarItems.length > 0) {

            let bottomToolbarOptions = {
                element: bottomToolbarHolder,
                items: [],
                background: tbBackground,
                foreground: tbForeground
            };

            if(window.innerWidth > 500 || primaryToolbarItems.length > 5) {
                bottomToolbarOptions.items.push({
                    type: "spacer"
                });
            }

            let added = 0;

            while(primaryToolbarItems.length > 0) {

                let item = primaryToolbarItems.shift();

                if(added > 5) {
                    secondaryToolbarItems.push(item);
                } else {
                    bottomToolbarOptions.items.push({
                        type: "button",
                        label: item.title,
                        icon: item.icon,
                        func: item.func
                    });
                }
    
                


    
            }

            if(secondaryToolbarItems && secondaryToolbarItems.length > 0) {
                bottomToolbarOptions.items.push({
                    type: "button",
                    special: "threedots",
                    menu: {
                        items: secondaryToolbarItems
                    }
                });
            }

            adl.addToolbar(bottomToolbarOptions);
        }
        
    }

    function getBaseURL() {
        return window.appBase;
    }

    function buildVarPackage() {
        let fileName = window.appId + "_var_package.wvp";

        openFile(fileName,function(data){
            if(!data || data == "") {
                childWindowObject.webAppCore.recieveVariablePackage({});
            } else {
                try {
                    let obj = JSON.parse(data);
                    childWindowObject.webAppCore.recieveVariablePackage(obj);
                } catch(ex) {
                    childWindowObject.webAppCore.recieveVariablePackage({});
                }
                
            }
        });
    }

    function openFile(name,callback,useDir) {

        if(!useDir) {
            useDir = null;
        }

        if(usingElectronHost) {
            let openDir = workspaceFolder;

            if(useDir) {
                openDir = BASE_DOCS + "/" + useDir;
            }
    
            let filePath = openDir + "/" + name;
            
            if(wacUtils.doesFileExist(filePath)) {
                let fileContents = wacUtils.openFile(filePath);
                callback(fileContents);
            } else {
                callback(null);
            }

            return;
        }

        if(fileSystemAccessSupported && workspaceFolder) {

            if(useDir && domainRootStorage) {
                domainRootStorage.getDirectoryHandle(useDir, { create: true }).then(function(adr){
                    openFileDromAPIStorageDir(adr,name,callback);
                });
            } else {
                openFileDromAPIStorageDir(workspaceFolder,name,callback);
            }

            return;
        }

        tryOpenFromDatabase(name,callback);
    }

    function tryOpenFromDatabase(name,callback) {
        if(databaseFunctional) {
            let transaction = db.transaction(["files"]);
            let objectStore = transaction.objectStore("files");
            let request = objectStore.get(name);
            request.onerror = function(event) {
                callback("");
            };
            request.onsuccess = function() {
                let returnData = "";
                if(request.result) {
                    returnData = request.result.content;
                }
                
                callback(returnData);
            };
        } else {
            let fileData = fetchFallbackFile(name);

            if(fileData) {
                callback(fileData);
            } else {
                callback("");
            }
        }
    }

    function onOpenFileReady() {
        if(openFileCallback != null) {
            openFileCallback(Android.getOpenedFile());
        }
    }

    function fetchFallbackFile(fileName) {
        let storagevar = "file-" + window.appId + "-" + fileName;
        
        if(localStorage[storagevar]) {
            return localStorage[storagevar];
        }
    }

    function setSidebarCategory(category,icon) {
        sidebarCategory = category;
        
        if(icon) {
            sidebarCategoryIcon = icon;
        } else {
            sidebarCategoryIcon = null;
        }
        
        rebuildToolbar();
    }

    function sendKongregateStat() {
        // does nothing
    }

    function onAndroidSaveDone() {
        // does nothing
    }

    function getPlatform() {
        let market = "apewebapps";

        if(isSteam) {
            market = "steam";
        }

        if(isApeLauncher) {
            market = "apemarket";
        }

        if(isApeMarket) {
            market = "apemarket";
        }

        if(isWindowsStore) {
            market = "windowsstore";
        }

        let platform = {
            platform: platformName,
            market: market,
            os: window.platformOS
        };

        return platform;
    }

    function setPremiumOptionText(text) {
        premiumOptionText = text;

        if(!text || text.trim().length == 0) {
            premiumOptionText = "Remove Advertising";
        }

        rebuildToolbar();
    }

    function setTheme(color) {

        let forceTheme = "dark";

        if(window.useSysDefaultTheme) {
            if(isSystemThemeDark()) {
                forceTheme = "dark";
            } else {
                forceTheme = "light";
            }

        } else {
            if(window.lightTheme) {
                forceTheme = "light";
            }
        }

        let useTitleColor = "#ffffff";

        if(forceTheme == "dark") {
            useTitleColor = "#222222";

            chatHolder.style.backgroundColor = "#222222";
        } else {
            chatHolder.style.backgroundColor = "#ffffff";
        }

        if(window.themeStyle == "2" || window.themeStyle == "3") {
            useTitleColor = color;
        }

        let useDarkenedTitleHex = useTitleColor;

        let adBackground = "#222222";

        if(window.themeStyle == "2") {
            adBackground = color;
        }

        adHolder.style.backgroundColor = adBackground;

        if(window.awaEmbedded) {
            appWrapper.style.backgroundColor = "transparent";
            document.body.style.backgroundColor = "transparent";
        } else {
            appWrapper.style.backgroundColor = useTitleColor;
        }
        
        
        let isTitleDark = adl.isHexColorDark(useDarkenedTitleHex);



        currentTheme = color;

        adl.setTheme(color,forceTheme);

        let metaThemeColor = document.querySelector("meta[name=theme-color]");

        if(metaThemeColor) {
            metaThemeColor.setAttribute("content", useDarkenedTitleHex);
        }

        rebuildToolbar();
    }

    function setTitle(title) {

        if(!title) {
            title = window.appName;
        }

        currentTitle = title.trim();
        

        let text = currentTitle;

        if(currentTitle != window.appName) {
            text = currentTitle + " - " + window.appName;
        }

        if(getIsBetaMode()) {
            text += " Beta";
        }

        document.title = text;

        rebuildToolbar();

    }

    function postForResult(url,data) {
        proxyRequest(url,postForResultResponse,data,true);
    }
    
    function getForResult(url,data) {
        proxyRequest(url,postForResultResponse,data,false);
    }

    function postForResultResponse(data) {
        childWindowObject.webAppCore.resultFunction(data);
    }

    function getIsPremium() {
        if (accountLevelPremium = true) { //! accountLevelPremium
            document.body.classList.add("premium");
            document.body.classList.remove("notpremium");
            return true;
        }

        if (isSteam && usingElectronHost) {
            document.body.classList.add("premium");
            document.body.classList.remove("notpremium");
            return true;
        }

        if(isWindowsStore && window.awauwppo) {
            document.body.classList.add("premium");
            document.body.classList.remove("notpremium");
            localStorage[window.appId + "_winstore_upret"] = 5;
            return true;
        }

        let amkFileActivation = localStorage[window.appId + "_premium_upgrade"];

        if(amkFileActivation && amkFileActivation == "1") {
            document.body.classList.add("premium");
            document.body.classList.remove("notpremium");
            return true;
        }

        document.body.classList.remove("premium");
        document.body.classList.add("notpremium");

        let ret = localStorage[window.appId + "_winstore_upret"];

        if(ret && !isNaN(parseInt(ret)) && parseInt(ret) > 0) {
            return true;
        }

        return false;
    }

    function isTV() {
        return window.wacIsTv;
    }

    function addMenuItem(name,id,icon,color,tag,subMenu,isAd) {

        let menuItem = {};

        if(name) {
            menuItem.title = name;
        }

        if(icon) {
            menuItem.icon = icon;
        }

        if(color) {
            menuItem.iconColor = color;
        }

        if(subMenu) {
            menuItem.wacSubmenu = subMenu;
        }

        if(isAd) {
            menuItem.wacIsAd = isAd;
        }

        if(tag) {
            menuItem.wacTag = tag;
        }

        menuItem.func = function(){
            childWindowObject.webAppCore.executeMenuFunction(id);
        };

        customMenu.push(menuItem);

        rebuildToolbar();
    }

    function updateVarPackage(pkg) {
        if(packSaveTimer) {
            clearTimeout(packSaveTimer);
        }
        
        let packContent = JSON.stringify(pkg);
        let packName = window.appId + "_var_package.wvp";
        
        packSaveTimer = setTimeout(function() {
            packSaveTimer = null;
            saveFile(packName,packContent,true,null,null);
        },150);
    }

    function deleteFile(filename,useDir) {
        if(!useDir) {
            useDir = null;
        }


        doLocalFileDelete(filename,function(){
            if(isApeAppsLoggedIn()) {
                let cloudData = new CloudRequest("d",filename,null);
                sendCloudRequest(cloudData,null,useDir);
            }

            childWindowObject.webAppCore.fileDeleted();
        },useDir);
    }

    function saveFile(filename,data,blockCloud,useDir,callback) {

        if(!useDir) {
            useDir = null;
        }

        doLocalFileSave(filename,data,useDir,function(){
            if(isApeAppsLoggedIn() && !blockCloud) {
                let cloudData = new CloudRequest("s",filename,data);
                sendCloudRequest(cloudData,function(data) {
                    tagCloudSave(data,useDir);
                    
                    if(callback) {
                        callback();
                    }
                },useDir);
            } else {
                if(callback) {
                    callback();
                }
            }
        });
    }

    function tagCloudSave(data,useDir) {
        if(data) {
            try {
                let dataobj = JSON.parse(data);

                if(dataobj.result) {
                    if(dataobj.result == "success") {

                        let saveTime = dataobj.time;
                        let saveName = dataobj.data;

                        if(useDir) {
                            saveName += "." + useDir;
                        }

                        localStorage["aacloud-" + saveName + "-last-save"] = saveTime;
                    } else {
                        console.log("cloud fail");
                        console.log(dataobj.reason);
                    } 
                } else {
                    console.log("bad result back from cloud!");
                }
            } catch(ex) {
                console.log(ex);
            }
        }
    }

    function doLocalFileDelete(name,callback,useDir) {

        if(!useDir) {
            useDir = null;
        }

        if(usingElectronHost) {
            let openDir = workspaceFolder;

            if(useDir) {
                openDir = BASE_DOCS + "/" + useDir;
            }

            let filePath = openDir + "/" + name;
            wacUtils.deleteFile(filePath);
            callback();
            return;
        }
        
        if(fileSystemAccessSupported && workspaceFolder) {

            if(useDir && domainRootStorage) {

                domainRootStorage.getDirectoryHandle(useDir, { create: true }).then(function(adr){
                    adr.removeEntry(name);

                    if(callback) {
                        callback();
                    }
                });

            } else {
                workspaceFolder.removeEntry(name);

                if(callback) {
                    callback();
                }

                callback = placeholder;
            }

        }

        deleteLocalFileDatabaseMethod(name,callback);
    }

    function placeholder(){}

    function deleteLocalFileDatabaseMethod(name,callback) {
        if(databaseFunctional) {
            db.transaction(["files"], "readwrite").objectStore("files").delete(name).onsuccess = function() {

                if(callback) {
                    callback();
                }
                
            };
        } else {
            deleteFallbackFile(name);
            
            if(callback) {
                callback();
            }
        }
    }

    function deleteFallbackFile(fileName) {
        let storagevar = "file-" + window.appId + "-" + fileName;
        
        if(localStorage[storagevar]) {
            localStorage.removeItem(storagevar);
        }
    }

    function doLocalFileSave(name,content,useDir,callback) {
        if(!useDir) {
            useDir = null;
        }

        if(usingElectronHost) {

            let openDir = workspaceFolder;

            if(useDir) {
                openDir = BASE_DOCS + "/" + useDir;
            }

            let filePath = openDir + "/" + name;

            let enc = "utf8";
            let data = content;
            let binaryData;
            
            if(name.endsWith(".png")) {
                enc = "binary";
                
                let base64Data  = data.replace(/^data:image\/png;base64,/, "");
                base64Data += base64Data.replace("+", " ");
                binaryData = wacUtils.getBinaryData(base64Data,"base64");
                
                data = binaryData;
            }
            
            if(name.endsWith(".pdf")) {
                enc = "binary";
                
                binaryData = wacUtils.getBinaryData(data,"base64");
                
                data = binaryData;
            }
      
            wacUtils.writeFile(filePath,data,enc);

            callback();

            return;
        }

        if (navigator.storage && navigator.storage.persist) {
            navigator.storage.persist().then(granted => {
                if (granted) {
                    ah.trackEvent("Persistent Storage","Accepted",window.appName,true);
                } else {
                    ah.trackEvent("Persistent Storage","Rejected",window.appName,true);
                }
            });
        }

        if(fileSystemAccessSupported && workspaceFolder) {


            if(useDir && domainRootStorage) {

                domainRootStorage.getDirectoryHandle(useDir, { create: true }).then(function(adr){
                    adr.getFileHandle(name,{
                        create: true
                    }).then(function(fileHandle){
                        saveFileToAppLocationHandle(fileHandle,content,callback);
                    });
                });

            } else {
                workspaceFolder.getFileHandle(name,{
                    create: true
                }).then(function(fileHandle){
                    saveFileToAppLocationHandle(fileHandle,content,callback);
                    deleteLocalFileDatabaseMethod(name);
                });
            }


            return;
        }

        if(databaseFunctional) {
            db.transaction(["files"], "readwrite").objectStore("files").put({ name: name, content: content });

            if(callback) {
                callback();
            } 
        } else {
            saveFallbackFile(name,content);

            if(callback) {
                callback();
            } 
        }
    }

    function saveFileToAppLocationHandle(handle,content,callback) {
        handle.createWritable().then(function(writable){
            writable.write(content).then(function(){
                writable.close();

                if(callback) {
                    callback();
                }
                
            });
        });
    }

    function saveFallbackFile(fileName,content) {
        let storagevar = "file-" + window.appId + "-" + fileName;
        
        try {
            localStorage[storagevar] = content;
        } catch(ex) {
            console.log(ex);
        }
    }

    function loadFile(filename,useDir) {
        if(!useDir) {
            useDir = null;
        }

        pendingLoadFileName = filename;

        if(isApeAppsLoggedIn() && navigator.onLine) {
            tmpCLoudOpenFileName = filename;

            if(useDir) {
                tmpCLoudOpenFileName += "." + useDir;
            }

            let cloudData = new CloudRequest("o",filename,null);
            sendCloudRequest(cloudData,function(data){
                cloudFileReturn(data,useDir);
            },useDir);
        } else {
            loadFileLocalStyle(filename,null,useDir);
        }
    }

    function isApeAppsLoggedIn() {
        if(apeAppsLoginData.creds == null || apeAppsLoginData.profile == null) {
            return false;
        } else {
            return true;
        }
    }

    function getApeAppsProfile() {
        if(isApeAppsLoggedIn()) {
            return apeAppsLoginData.profile;
        } else {
            return null;
        }
    }

    function getApeAppsCreds() {
        if(isApeAppsLoggedIn()) {

            return {
                "a": apeAppsLoginData.creds.sida,
                "b": apeAppsLoginData.creds.sidb,
                "c": apeAppsLoginData.creds.sidc
            };
            
        } else {
            return null;
        }
    }

    function CloudRequest(method,filename,content) {
        if(method) {
            this.m = method;
        } else {
            this.m = null;
        }

        if(filename) {
            this.n = filename;
        } else {
            this.n = null;
        }

        if(content) {
            this.c = btoa(content);
        } else {
            this.c = null;
        }
    }

    function sendCloudRequest(request,callback,useDir) {

        let cloudString = JSON.stringify(request);
        let cloudEnc = btoa(cloudString);

        let saveData = [];

        let useAppName = window.appName;

        if(useDir && useDir.trim().length > 0) {
            useAppName = useDir;
        }        

        saveData.push({key:"newaccount",value:"1"});
        saveData.push({key:"a",value:apeAppsLoginData.creds.sida});
        saveData.push({key:"b",value:apeAppsLoginData.creds.sidb});
        saveData.push({key:"c",value:apeAppsLoginData.creds.sidc});
        saveData.push({key:"m",value:window.appId});
        saveData.push({key:"n",value:useAppName});
        saveData.push({key:"r",value:cloudEnc});

        let cloudRequestUrl = "https://cloud.ape-apps.com:2728";
        proxyRequest(cloudRequestUrl,callback,saveData,true);
    }

    function cloudFileReturn(data,useDir) {

        if(!useDir) {
            useDir = null;
        }

        let cloudSuccess = false;
        let cloudNewer = false;
        let cloudDeleted = false;
        
        let fileContentFromCloud = null;

        if(data) {
            try {
                let dataobj = JSON.parse(data);

                if(dataobj.result) {
                    if(dataobj.result == "success") {

                        let localSaveTime = localStorage["aacloud-" + tmpCLoudOpenFileName + "-last-save"];

                        let decoded = atob(dataobj.data);
                        let decodedObj = JSON.parse(decoded);

                        let saveTime = decodedObj.t;

                        fileContentFromCloud = atob(decodedObj.c);
                        cloudSuccess = true;

                        cloudDeleted = decodedObj.d;

                        if(!localSaveTime || saveTime >= localSaveTime) {
                            cloudNewer = true;
                        } else {
                            cloudNewer = false;
                        }

                    } else {
                        cloudSuccess = false;
                        cloudNewer = false;
                        cloudDeleted = false;
                    } 
                } else {
                    cloudSuccess = false;
                    cloudNewer = false;
                    cloudDeleted = false;
                }
            } catch(ex) {
                console.log(ex);
                cloudSuccess = false;
                cloudNewer = false;
                cloudDeleted = false;
                fileContentFromCloud = null;
            }
        }

        if(cloudSuccess && !cloudDeleted && fileContentFromCloud != null) {
            if(cloudNewer) {
                childWindowObject.webAppCore.fileLoaded(fileContentFromCloud,cloudNewer);
            } else {
                loadFileLocalStyle(pendingLoadFileName,fileContentFromCloud,useDir);
            }
            
        } else {
            loadFileLocalStyle(pendingLoadFileName,null,useDir);
        }

    }

    function loadFileLocalStyle(filename,pendingCloudContent,useDir) {

        openFile(filename,function(data) {

            if(pendingCloudContent && (!data || data.trim().length == "")) {
                childWindowObject.webAppCore.fileLoaded(pendingCloudContent,false);
            } else {
                childWindowObject.webAppCore.fileLoaded(data,false);
            }
            
        },useDir);
    }

    function showDialog(title,message,op1,op2,icon) {
        let options = {};

        if(title) {
            options.title = title;
        }

        if(message) {
            options.message = message;
        }

        let buttons = null;

        if(op1) {
            if(!buttons) {
                buttons = [];
            }

            if(!op1.color) {
                op1.color = null;
            }

            buttons.push({
                text: op1.text,
                func: op1.func,
                color: op1.color
            });
        }

        if(op2) {
            if(!buttons) {
                buttons = [];
            }

            if(!op2.color) {
                op2.color = null;
            }

            buttons.push({
                text: op2.text,
                func: op2.func,
                color: op2.color
            });
        }

        if(buttons) {
            options.buttons = buttons;
        }

        if(icon) {
            options.icon = icon;
        }

        showDialogNew(options);
    }

    function showDialogNew(options) {

        if(options.icon) {
            options.icon = getCleanIcon(options.icon);
        }

        if(options.sliderCallback) {
            options.slider = {
                min: options.sliderMin,
                max: options.sliderMax,
                value: options.sliderValue,
                status: options.sliderStatusText,
                onChange: options.sliderCallback
            };
        }

        if(options.listItems) {

            // clean up items 
            for(let i = 0; i < options.listItems.length; i++) {
                let item = options.listItems[i];

                if(item["status-color"]) {
                    item.statusColor = item["status-color"];
                }

                item.icon = getCleanIcon(item.icon);

                if(item.overflow) {
                    for(let j = 0; j < item.overflow.length; j++) {
                        let of = item.overflow[j];
                        if(of.icon) {
                            of.icon = getCleanIcon(of.icon);
                        }
                    }
                }
            }
            
            let listOptions = {
                title: options.title,
                options: options.listItems,
                onSelection: function(tag) {
                    options.listCallback(tag);
                }
            };
            
            if(options.dt) {
                listOptions.cancelTest = options.dt;
            }

            adl.showList(listOptions);

            return;
        }

        adl.showDialog(options);
    }

    function getCleanIcon(icon) {
        if(icon && (icon.endsWith(".png") || icon.indexOf("data:") > -1 || icon.indexOf("https:") > -1)) {
            if(icon.indexOf("http") != 0 && icon.indexOf("data:") == -1) {
                icon = window.appBase + icon;
            }
        }

        return icon;
    }

    function getInterface() {
        return platformName;
    }

    function resetTitle() {
        titleScreenOptions.innerHTML = "";
    }

    function doInitialTitleGoose() {
        if(titleGoosed) {
            return;
        }

        titleScreenApeLogo.onload = function(){
            titleScreenApeLogo.style.display = "block";
        };
        titleScreenApeLogo.src = "/images/ape_logo.webp";
        titleScreenApeLogo.onclick = function(){
            loadURL("https://www.ape-apps.com");
        };

        fetchFeaturedImage(titleScreenFeaturedApp);

        if(!getIsPremium()) {
            getNewsfeed(titleScreenNewsfeed);
        }

        titleGoosed = true;
    }

    function setTitleVisible(visible) {
        if(visible) {
            doInitialTitleGoose();
            titleScreen.style.display = "grid";
        } else {
            titleScreen.style.display = "none";
        }
    }

    function setTitleBackground(bgHTML) {
        let hasbg = bgHTML.indexOf("url('");
        if(hasbg > -1) {
            let n = bgHTML.indexOf("://");
            if(n == -1 && bgHTML.indexOf("data:") == -1) {
                bgHTML = bgHTML.replace("url('","url('" + window.appBase);
            }
        }

        titleScreen.style.background = bgHTML;
    }

    function setTitleLogo(logo) {

        if(logo.indexOf("://") > -1 || logo.indexOf("data:") == 0) {
            titleScreenLogo.src = logo;
        } else {
            titleScreenLogo.src = window.appBase + logo;
        }

    }

    function setBrandingLogo(logo) {

        if(logo == null) {
            titleScreenApeLogo.style.display = "none";
            return;
        }

        titleScreenApeLogo.style.display = null;

        let n = logo.indexOf("://");
        if(n == -1) {
            titleScreenApeLogo.src = window.appBase + logo;
        } else {
            titleScreenApeLogo.src = logo;
        }
    }

    function addTitleOption(name,tag,color,selectedColor) {
        let titleOption = document.createElement("div");
        titleOption.className = "titleOption adlGamepadSelectable";

        titleOption.innerHTML = name;

        if(color) {
            titleOption.style.color = color;
        }

        if(selectedColor) {
            titleOption.selectedColorRef = selectedColor;
        }

        titleOption.onclick = function(){
            childWindowObject.webAppCore.titleOptionSelected(tag);
        };

        titleScreenOptions.appendChild(titleOption);
    }

    function getAppVersion() {
        return usingAppVersion;
    }

    function setTitleSubtitle(text) {
        if(text.trim().length == 0) {
            titleScreenSubtitle.style.display = "none";
            return;
        }
        
        titleScreenSubtitle.style.display = "block";
        titleScreenSubtitle.innerHTML = text;
    }

    function getExternalParm() {

        if(passedInData) {
            return passedInData;
        }

        return window.launchParm;
    }

    function loadURL(url) {
        if (url == -1) {
            return;
        }

        if(usingElectronHost) {
            try {
                wacUtils.openURIWithOS(url);
            } catch(ex) {
                console.log(ex);
            }

            return;
        }

        window.open(url,"_blank");
    }

    function toggleToolbar() {
        window.useToolbar = !window.useToolbar;
        rebuildToolbar();
    }

    function toggleMenu(fromToolbar) {

        if(window.useToolbar && !fromToolbar) {
            return;
        }

        let listOptions = [];

        if(!isSoundboardCity) {
            if(isApeAppsLoggedIn()) {

                listOptions.push({
                    title: apeAppsLoginData.profile.un,
                    icon: "https://accounts.ape-apps.com/getavatar.php?u=" + apeAppsLoginData.profile.un,
                    tag: {
                        wacTag: null,
                        func: showAccountInfo
                    },
                    compactItem: true
                });

            } else {
                listOptions.push({
                    title: "Sign In",
                    icon: "fluent.&#xE13D;",
                    tag: {
                        wacTag: null,
                        func: presentApeLogin
                    },
                    compactItem: true
                });

            }
        }

        for(let i = 0; i < customMenu.length; i++) {
            let item = customMenu[i];

            let itemOptions = {
                title: item.title,
                icon: getCleanIcon(item.icon),
                tag: item,
                compactItem: true
            };

            if(item.iconColor) {
                itemOptions.iconColor = item.iconColor;
            }

            listOptions.push(itemOptions);
        }

        let categoryOption = null;

        // default menu items 
        if(sidebarCategory) {
            let sbIcon = "fluent.&#xE179;";

            if(sidebarCategoryIcon) {
                sbIcon = getCleanIcon(sidebarCategoryIcon);
            }

            categoryOption = {
                title: sidebarCategory,
                icon: sbIcon,
                tag: "sidebarCategory",
                compactItem: true
            };
        } else {
            let sbIcon = "fluent.&#xE179;";

            categoryOption = {
                title: "Latest Updates",
                icon: sbIcon,
                tag: "sidebarCategory",
                compactItem: true
            };

        }

        if(categoryOption && !isTV() && !isSteam) {
            listOptions.push(categoryOption);
        }

        if(!getIsPremium() && premiumAllowed) {
            listOptions.push({
                title: premiumOptionText,
                icon: "fluent.&#xE14D;",
                tag: "gopremium",
                compactItem: true
            });
        }

        if(!isTV() && !usingElectronHost && !isApeLauncher) {
            listOptions.push({
                title: "More Free Apps",
                icon: "fluent.&#xE80A;",
                tag: "moregames",
                compactItem: true
            });
        }
        

        if(!isTV() && !isWindowsStore && !isSteam && !usingElectronHost && !isSoundboardCity) {

            if(deferredPrompt && !isInstalledPWA) {
                listOptions.push({
                    title: "Install " + window.appName,
                    icon: window.shareImage,
                    tag: "installpwa",
                    compactItem: true
                });
            }

            if(!isApeLauncher) {
                listOptions.push({
                    title: "Ape Apps Launcher",
                    icon: "https://www.apewebapps.com/ape-apps-launcher-114.png",
                    tag: "launcher",
                    compactItem: true
                });
            }
            
        }

        if(!isTV()) {
            if(window.innerWidth > 500 && !isSoundboardCity) {
                let fsText = "Enter Fullscreen";
                let fsIcon = "fluent.&#xE1D9;";
        
                if(getIsFullscreen()) {
                    fsText = "Exit Fullscreen";
                    fsIcon = "fluent.&#xE1D8;";
                }
    
                listOptions.push({
                    title: fsText,
                    icon: fsIcon,
                    tag: "fullscreen",
                    compactItem: true
                });
            }

            if(!usingElectronHost && "share" in navigator) {
                listOptions.push({
                    title: "Share " + window.appName,
                    icon: "fluent.&#xF003;",
                    tag: "share",
                    compactItem: true
                });
            }
            
        }
        
        
        

        listOptions.push({
            title: "Send Feedback",
            icon: "fluent.&#xE119;",
            tag: "feedback",
            compactItem: true
        });

        listOptions.push({
            title: "About " + window.appName,
            icon: "fluent.&#xE783;",
            tag: "about",
            compactItem: true
        });

        if(isApeTvFrame) {
            listOptions.push({
                title: "Quit " + window.appName,
                icon: "fluent.&#xF3B1;",
                tag: "awatvquit",
                compactItem: true
            });
        } else {
            if(usingElectronHost || isApeLauncher) {
                let exitText = "Quit " + window.appName;

                if(usingElectronHost) {
                    exitText = "Exit to Desktop";
                }

                listOptions.push({
                    title: exitText,
                    icon: "fluent.&#xF3B1;",
                    tag: "electronquit",
                    compactItem: true
                });
            }
        }

        let flyoutPosition = "left";

        // bottom position on iOS
        if(window.innerWidth < 500 && window.platformId == "4") {
            flyoutPosition = "bottom";
        }

        let menuTitle = window.appName;

        if(getIsBetaMode()) {
            menuTitle = window.appName + " - Beta";
        }

        if(getIsEmbedded()) {
            menuTitle = null;
        }

        adl.showList({
            title: menuTitle,
            options: listOptions,
            onSelection: function(tag) {

                if(tag) {

                    if(tag == "electronquit") {
                        doQuitElectron();
                        return;
                    }

                    if(tag == "awatvquit") {
                        parent.tvChildMessage(replaceAll(window.appName," ","-").toLowerCase(),"fullquit");
                        return;
                    }

                    if(tag == "gopremium") {
                        presentPremium();
                        return;
                    }

                    if(tag == "about") {
                        presentAbout();
                        return;
                    }

                    if(tag == "feedback") {
                        presentFeedback();
                        return;
                    }

                    if(tag == "share") {
                        performStandardShare();
                        return;
                    }

                    if(tag == "fullscreen") {
                        toggleFullscreen();
                        return;
                    }

                    if(tag == "sidebarCategory") {
                        showCategoriesFromSidebar();
                        return;
                    }

                    if(tag == "moregames") {
                        showMoreApps();
                        return;
                    }

                    if(tag == "launcher") {
                        loadURL("https://market.ape-apps.com/ape-apps-launcher.html");
                        return;
                    }

                    if(tag == "installpwa") {
                        if(ah) {
                            ah.trackEvent("Menu Install", "Selected", window.appName, true);
                        }

                        if(deferredPrompt) {
                            deferredPrompt.prompt();
                        }

                        return;
                    }
                }

                if(tag && tag.func) {
                    tag.func(tag.wacTag);
                }
            },
            flyout: {
                position: flyoutPosition
            }
        });

    }

    function showMoreSoundboards() {
        parent.postMessage({
            func: "showMoreSoundboards"
        },"*");
    }

    function showMoreApps() {
        let moreURL = "https://www.apewebapps.com";

        if(window.winTenEdge || isWindowsStore) {
            moreURL = "ms-windows-store://publisher/?name=Ape Apps";
        }

        if(isSteam) {
            moreURL = "https://store.steampowered.com/search/?developer=Ape%20Apps";
        }

        loadURL(moreURL);
    }

    function performStandardShare() {
        toggleShareBox("Check out " + window.appName + "! #appurl");
    }


    function isSystemThemeDark() {
        if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) {
            return true;
        } else {
            return false;
        }
    }

    function getFiles(useDir,callback) {

        if(!callback) {
            callback = childWindowObject.webAppCore.listingLoaded;
        }

        if(!useDir) {
            useDir = null;
        }

        if(isApeAppsLoggedIn() && useDir && useDir.trim().length > 0) {

            let cloudData = new CloudRequest("l","","");
            sendCloudRequest(cloudData,function(data){
                if(!data || data == null) {
                    getFilesLocal(useDir,callback);
                    return;
                }

                try {
                    let responseData = JSON.parse(data);
        
                    if(responseData.result == "success") {
                        getFilesLocal(useDir,callback,responseData.data);
                    } else {
                        getFilesLocal(useDir,callback);
                    }
                } catch(ex) {
                    console.log(ex);
                    getFilesLocal(useDir,callback);
                }
            },useDir);

            return;
        }

        getFilesLocal(useDir,callback);
    }

    function getFilesLocal(useDir,callback,mergeInFromCloud) {
        if(!useDir) {
            useDir = null;
        }

        if(!mergeInFromCloud) {
            mergeInFromCloud = null;
        }

        if(usingElectronHost) {

            let openDir = workspaceFolder;

            if(useDir) {
                openDir = BASE_DOCS + "/" + useDir;
            }

            let returnData = [];

            let dir = wacUtils.getDirectory(openDir);

            if(dir) {
                for(let i = 0, l = dir.length; i < l; i++) {
                    let fileName = dir[i];

                    if(fileName.endsWith(".aacfi")) {
                        continue;
                    }
                    
                    let checkPath = openDir + "/" + fileName;

                    if(wacUtils.isPathFile(checkPath)) {
                        returnData.push(fileName);
                    }
                }

            }

            callback(returnData);

            return;
        }

        if(fileSystemAccessSupported && workspaceFolder) {

            if(useDir && domainRootStorage) {
                domainRootStorage.getDirectoryHandle(useDir, { create: true }).then(function(adr){
                    getDirectoryListingFileAPIStyle(adr,callback,mergeInFromCloud);
                });
            } else {
                getDirectoryListingFileAPIStyle(workspaceFolder,callback,mergeInFromCloud);
            }

            return;

        }

        getReturnListingFromDatabase(callback,mergeInFromCloud);
    }

    function getReturnListingFromDatabase(callback,mergeInFromCloud) {
        let returnData = [];

        if(!mergeInFromCloud) {
            mergeInFromCloud = null;
        }

        if(databaseFunctional) {

            try {
                let objectStore = db.transaction("files").objectStore("files");

                objectStore.openCursor().onsuccess = function(event) {
                    let cursor = event.target.result;

                    if (cursor) {
                        returnData.push(cursor.key);
                        cursor.continue();
                    } else {

                        if(mergeInFromCloud && mergeInFromCloud.length > 0) {
                            for(let i = 0; i < mergeInFromCloud.length; i++) {
                                let mergeFile = mergeInFromCloud[i];
                
                                if(returnData.indexOf(mergeFile) == -1) {
                                    returnData.push(mergeFile);
                                }
                            }
                        }

                        callback(returnData);
                    }
                };
            } catch(ex) {
                console.log(ex);

                if(mergeInFromCloud && mergeInFromCloud.length > 0) {
                    for(let i = 0; i < mergeInFromCloud.length; i++) {
                        let mergeFile = mergeInFromCloud[i];
        
                        if(returnData.indexOf(mergeFile) == -1) {
                            returnData.push(mergeFile);
                        }
                    }
                }

                callback(returnData);
            }
        } else {
            for (let i = 0; i < localStorage.length; i++) {
                if(localStorage.key(i).indexOf("file-" + window.appId + "-") == 0) {
                    returnData.push(localStorage.key(i).replace("file-" + window.appId + "-",""));
                }
            }

            if(mergeInFromCloud && mergeInFromCloud.length > 0) {
                for(let i = 0; i < mergeInFromCloud.length; i++) {
                    let mergeFile = mergeInFromCloud[i];
    
                    if(returnData.indexOf(mergeFile) == -1) {
                        returnData.push(mergeFile);
                    }
                }
            }

            callback(returnData);
        }
    }

    async function openFileDromAPIStorageDir(dirHandle,filename,callback) {

        let found = false;

        for await (let [name, handle] of dirHandle.entries()) {
            if(name == filename) {
                found = true;

                handle.getFile().then(function(file) {
                    let fileData = fth.getFileTypeData(file.name);

                    if(fileData && fileData.prefBinary) {
                        file.arrayBuffer().then(function(content){
                            callback(content);
                        });
                    } else {
                        file.text().then(function(content){
                            callback(content);
                        });
                    }
                });

                return;
            }
        }

        if(!found) {
            tryOpenFromDatabase(filename,callback);
        }
    }

    async function getDirectoryListingFileAPIStyle(dirHandle,callback,mergeInFromCloud) {

        let returnData = [];

        if(!mergeInFromCloud) {
            mergeInFromCloud = null;
        }

        for await (let [name, handle] of dirHandle.entries()) {
            returnData.push(name);
        }

        if(!returnData || returnData.length == 0) {

            getReturnListingFromDatabase(callback,mergeInFromCloud);

            return;
        }

        if(mergeInFromCloud && mergeInFromCloud.length > 0) {
            for(let i = 0; i < mergeInFromCloud.length; i++) {
                let mergeFile = mergeInFromCloud[i];

                if(returnData.indexOf(mergeFile) == -1) {
                    returnData.push(mergeFile);
                }
            }
        }

        callback(returnData);
    }

    function presentPremium() {


        showPaymentMenu();
    }

    function getIsFullscreen() {
        if(document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement) {
            return true;
        } else {
            return false;
        }
    }

    function toggleFullscreen() {
        if(getIsFullscreen()) {
            if (document.exitFullscreen) {
                document.exitFullscreen();
            } else if (document.msExitFullscreen) {
                document.msExitFullscreen();
            } else if (document.mozCancelFullScreen) {
                document.mozCancelFullScreen();
            } else if (document.webkitExitFullscreen) {
                document.webkitExitFullscreen();
            }
        } else {
            if (document.body.requestFullscreen) {
                document.body.requestFullscreen();
            } else if (document.body.msRequestFullscreen) {
                document.body.msRequestFullscreen();
            } else if (document.body.mozRequestFullScreen) {
                document.body.mozRequestFullScreen();
            } else if (document.body.webkitRequestFullscreen) {
                document.body.webkitRequestFullscreen();
            }
        }


    }

    function onFullscreenChange() {
        if(childWindowObject && childWindowObject.webAppCore && childWindowObject.webAppCore.onFullscreen) {
            childWindowObject.webAppCore.onFullscreen(getIsFullscreen());
        }

        rebuildToolbar();
    }

    function showCategoriesFromSidebar() {
        let data = [];

        if(sidebarCategory != null) {
            data.push({
                "key":  "cat",
                "value": sidebarCategory
            });
        }

        proxyRequest("https://market.ape-apps.com/app_resources/get-category.php",onCatDataBack,data,true,false);
    }

    function onCatDataBack(data) {
        if(!data || data.trim().length == 0) {
            return;
        }

        try {
            let catData = JSON.parse(data);

            let varItems = [];

            for(let i = 0; i < catData.length; i++) {
                if(sidebarCategory == null && varItems.length >= 6) {
                    break;
                }

                let useDesc = catData[i].description.split("<br />")[0];

                let useLink = null;

                if(sidebarCategory == null) {
                    useLink = "https://market.ape-apps.com/" + replaceAll(catData[i].app_name.toLowerCase()," ","-") + ".html";
                }

                if(catData[i].webapp_url && catData[i].webapp_url.trim().length > 4) {
                    useLink = catData[i].webapp_url;
                }

                if(isWindowsStore) {
                    if(catData[i].win_store_app.trim().length > 4) {
                        useLink = catData[i].win_store_app.replace("https://www.microsoft.com/store/apps/","ms-windows-store://pdp/?ProductId=");
                    } else {
                        continue;
                    }
                }

                if(isSteam) {
                    if(catData[i]["steam-full-url"].trim().length > 4) {
                        useLink = catData[i]["steam-full-url"];
                    } else {
                        continue;
                    }
                }

                if(useLink == null) {
                    continue;
                }

                varItems.push({
                    "title": catData[i].app_name,
                    "description": useDesc,
                    "icon": "https://market.ape-apps.com/app_icons/" + catData[i].icon,
                    "tag": useLink
                });
            }

            let catTitle = "Recently Updated Apps";

            if(sidebarCategory != null) {
                catTitle = sidebarCategory;
            }

            let position = "right";

            if(window.innerWidth <= 500) {
                position = "bottom";
            }

            adl.showList({
                title: catTitle,
                options: varItems,
                onSelection: function(tag) {

                    if(tag) {
                        onCatItemSelected(tag);
                    }
                },
                flyout: {
                    position: position
                }
            });

        } catch(ex) {
            console.log(ex);
        }
    }

    function onCatItemSelected(item) {
        if(!item || item.trim().length < 4) {
            return;
        }

        loadURL(item);
    }

    function replaceAll(str, find, replace) {
        return str.replace(new RegExp(find, "g"), replace);
    }

    function getSystemAccentColor() {

        return null;
    }

    function initAnalytics(callback) {

        if(ah != null) {
            callback();
            return;
        }

        let ahScript = document.createElement("script");
        ahScript.onload = function() {
            setTimeout(function(){
                ah = new analyticsHelper(window.appName,usingAppVersion,window.analytics);

                if(callback) {
                    callback();
                }
            },100);
        };
        ahScript.src = "/js/analyticshelper/" + window.analyticsHelperVersion + "/analyticshelper.js";
        document.head.appendChild(ahScript);
    }

    function initAMSL(callback) {
        if(typeof amslLogStat !== "undefined") {
            callback();
            return;
        }

        let amslScript = document.createElement("script");
        amslScript.onload = function() {
            setTimeout(function(){
                if(callback) {
                    callback();
                }
            },100);
        };
        amslScript.src = "/js/statlogger/" + window.statloggerVersion + "/statlogger.js";
        document.head.appendChild(amslScript);
    }

    function fetchFeaturedImage(element) {
        if(!element) {
            return;
        }

        if(getIsPremium()) {
            element.style.display = "none";
            return;
        }

        if(!window.featuredHelper) {

            let featureScript = document.createElement("script");
            featureScript.onload = function(){
                setTimeout(function(){
                    fetchFeaturedImage(element);
                },100);
            };
            featureScript.src = "/js/featuredhelper/" + window.featuredHelperVersion + "/featuredhelper.js";

            

            document.head.appendChild(featureScript);

            return;
        }

        if(!fh) {
            fh = new featuredHelper(window.appId,window.platformId);
        }

        if(!fh) {
            element.style.display = "none";
            return;
        }

        element.style.display = "inline";
        fh.fetchFeaturedApp(element);
    }
    
    function playAudio(soundname,volume) {
        let soundFileURL = window.appBase + "audio/mp3/" + soundname + ".mp3";

        if(volume === undefined) {
            volume = 1;
        } else {
            if(volume > 1) {
                volume = volume / 100;
            }
        }

        playAudioChannel(soundFileURL,0,volume);
    }

    function playAudioChannel(soundName,channel,volume) {

        if(!audioArray) {
            audioArray = [];
        }
        
        
        
        if(!audioArray[channel]) {
            audioArray[channel] = [];
        }

        let arrayToUse = audioArray[channel];

        let audioObject;
        
        for(let i = 0; i < arrayToUse.length; i++) {
            if(arrayToUse[i].name == soundName) {
                audioObject = arrayToUse[i];
            }
        }
        
        if(audioObject) {
            if(audioObject.audObj.currentTime > 0) {
                playAudioChannel(soundName,(channel + 1),volume);
                return;
            }
            audioObject.audObj.volume = volume;
            audioObject.audObj.play();
        } else {
            let myAudio = document.createElement("audio");
            myAudio.src = soundName;
            myAudio.addEventListener("ended",function() {
                this.currentTime = 0;
            },false);
            myAudio.volume = volume;
            myAudio.play();
        
            audioObject = { name:soundName,audObj:myAudio };
            
            arrayToUse.push(audioObject);
        }
    }

    function checkForLaunchFile() {
        if(childWindowObject.webAppCore.launchCheckCallback) {
            childWindowObject.webAppCore.launchCheckCallback(getLaunchFileExists());
        }
    }

    function getLaunchFileExists() {
        if(launchFile) {
            return true;
        }

        return false;
    }

    function loopAudio(soundname,volume) {
        var soundFileURL = window.appBase + "audio/mp3/" + soundname + ".mp3";
        
        if(volume == undefined || volume == null) {
            volume = 1;
        }

        if(!loopingAudio) {
            loopingAudio = document.createElement("audio");
        }

        loopingAudio.src = soundFileURL;
        loopingAudio.loop = true;
        loopingAudio.volume = volume;
        loopingAudio.play();
    }

    function killLoop() {

        if(loopingAudio != null) {
            try {
                loopingAudio.pause();
            } catch(ex) {
                console.log(ex);
            }
            
            loopingAudio.src = "";
            try {
                loopingAudio.currentTime = 0;
            } catch(err) {
                console.log(err);
            }
        }
    }

    function showToast(message,icon,actionButton) {

        let action = null;

        if(actionButton) {

            action = {
                text: actionButton.title,
                func: actionButton.callback
            };
        }

        adl.showToast({
            message: message,
            icon: getCleanIcon(icon),
            action: action
        });
    }

    function addToolbarItem(name,id,inSecondarySection,icon) {

        customToolbar.push({
            title: name,
            func: function() {
                childWindowObject.webAppCore.executeToolbarFunction(id);
            },
            icon: getCleanIcon(icon),
            wacInSecondary: inSecondarySection
        });

        rebuildToolbar();
    }

    function inputBox(message,funct,title,password,multiline,placeholder) {

        if(!funct) {
            funct = childWindowObject.webAppCore.inputReturn;
        }

        adl.inputBox({
            title:title,
            message: message,
            multiline: multiline,
            password: password,
            placeholder: placeholder,
            func: function(res) {
                funct(res);
            }
        });
    }

    function getFacebookLogged() {
        return false;
    }

    function getFacebookCredentials() {
        return null;
    }

    function requestFacebookLogin() {
        return null;
    }

    function showAlert(text,title) {
        if(title) {
            adl.showAlertWithTitle(text,title);
        } else {
            adl.showAlert(text);
        }
    }

    function showChoice(message,option1,option2) {
        adl.showChoice(message, option1, option2, sendChoiceOne, sendChoiceTwo);
    }

    function sendChoiceOne() {
        childWindowObject.webAppCore.choice1();
    }
    
    function sendChoiceTwo() {
        childWindowObject.webAppCore.choice2();
    }

    function showColorPicker(defaultColor) {

        adl.pickColor({
            callback: function(color){
                childWindowObject.webAppCore.colorSelected(color);
            },
            withInput: true,
            default: defaultColor
        });    
    }

    function presentApeLogin() {

        if(isSoundboardCity) {
            parent.postMessage({
                func: "presentLogin"
            },"*");

            return;
        }

        let useForground = "ffffff";

        if(adl.getOverallThemeLight()) {
            useForground = "000000";
        }

        let tvFlag = 0;

        if(isTV()) {
            tvFlag = 1;
        }

        let loginFrame = document.createElement("iframe");
        loginFrame.seamless = true;
        loginFrame.className = "sidebarFrame";
        loginFrame.src = "/login2.php?app=" + window.appId + "&fg=" + useForground + "&tv=" + tvFlag + "&plat=" + window.statPlatform;

        let position = "left";

        if(window.innerWidth > 500) {
            position = "right";
        }

        adl.showDialog({
            customElement: loginFrame,
            buttons: [],
            flyout: {
                position: position
            }
        });
    }

    function onAALoginDataBack(data) {

        try {
            let result = JSON.parse(data);

            apeAppsLoginData.profile = result[0];

            let saveLoginData = JSON.stringify(apeAppsLoginData);

            localStorage[window.appId + "_aaa_login"] = saveLoginData;

            if(apeAppsLoginData.profile.pre == "1") {
                accountLevelPremium = true;
                
                if (bah) {
                    bah.goPremium();
                }

            }

            if(childWindowObject && childWindowObject.webAppCore && childWindowObject.webAppCore.onApeAppsStatusChanged) {
                childWindowObject.webAppCore.onApeAppsStatusChanged(true);
                syncAchievementsWithServer();
            }

            let cloudData = new CloudRequest("l","","");

            sendCloudRequest(cloudData,apeAppsCloudListingBack);

            if(!isApeTvFrame && !getIsEmbedded()) {
                showToast("Welcome, " + result[0].un,"https://accounts.ape-apps.com/images/" + result[0].pp);
            }

            friendsSignalingChannel = "awa-friends-signaling-" + apeAppsLoginData.profile.un;
            sharedResourceSignalingChannel = "awa-shared-resources-" + apeAppsLoginData.profile.un;
            
            connectToFriendsSignalingServer();
            loadStripe();

            rebuildToolbar();

            setPlayReference();

            finalActivationCheck();

        } catch(ex) {
            console.log(ex);

            if(isAutoLoginAttempt) {
                isAutoLoginAttempt = false;
                attemptCredentialSignin();
            } else {
                signOutOfApeApps();
            }
            
        }
        
    }

    function signOutOfApeApps() {
        closeSignalSocketConnection();

        isAutoLoginAttempt = false;

        localStorage.removeItem(window.appId + "_aaa_login");
        apeAppsLoginData = {
            "creds": null,
            "profile": null
        };

        sharedResourceSignalingChannel = null;
        friendsSignalingChannel = null;

        if(childWindowObject && childWindowObject.webAppCore && childWindowObject.webAppCore.onApeAppsStatusChanged) {
            childWindowObject.webAppCore.onApeAppsStatusChanged(false);
        }

        if (navigator.credentials && navigator.credentials.preventSilentAccess) {
            navigator.credentials.preventSilentAccess();
        }

        rebuildToolbar(); 
    }

    function loadSocketIO(callback) {

        let sioScript = document.createElement("script");
        sioScript.onload = function(){
            if(callback) {
                setTimeout(function(){
                    callback();
                },100);
            }
            
        };
        sioScript.src = "/js/socketio/" + window.socketioVersion + "/socket.io.min.js";
        document.head.appendChild(sioScript);
    }

    function connectToFriendsSignalingServer() {
        if(!isApeAppsLoggedIn() || signalSocketConnected) {
            return;
        }

        if(!window.io) {

            //window.socketioVersion
            //<script src="/js/socketio/<?php echo $socketioVersion; ?>/socket.io.slim.js" async></script>
            loadSocketIO(connectToFriendsSignalingServer);
            

            return;
        }

        signalSocket = io("https://signaling.ape-apps.com:2736",{
            secure: true,
            rejectUnauthorized: false
        });

        signalSocket.on("connect",onSignalSocketConnect);
        signalSocket.on("message",onSignalSocketMessage);
        signalSocket.on("disconnect",onSignalSocketDisconnect);
    }

    function closeSignalSocketConnection() {
        if(!isApeAppsLoggedIn()) {
            
            if(signalSocket) {
                signalSocket.emit("closeconnection","");
                signalSocket.close();
            }

            signalSocket = null;
            signalSocketConnected = false;
            
            return;
        }
        
        if(signalSocket) {
            signalSocket.emit("gamemessage",{
                "destination": friendsSignalingChannel,
                "msg": {
                    "type": "friendsignal",
                    "code": "goingoffline",
                    "un": apeAppsLoginData.profile.un,
                    "for": "all"
                }
            });
            
            signalSocket.emit("closeconnection","");
            signalSocket.close();
        }

        signalSocket = null;
        signalSocketConnected = false;
    }

    function onSignalSocketDisconnect() {
        signalSocketConnected = false;
    }

    function onSignalSocketConnect() {

        if(!signalSocket) {
            return;
        }

        signalSocketConnected = true;

        if(friendsSignalingChannel) {
            signalSocket.emit("joinroom",friendsSignalingChannel);
        }
        
        if(sharedResourceSignalingChannel) {
            signalSocket.emit("joinroom",sharedResourceSignalingChannel);
        }

        announceOnlineToSignalServer();
        sharedResourcePing();
    }

    function sharedResourcePing() {
        if(!signalSocketConnected || !signalSocket || !isApeAppsLoggedIn()) {
            return;
        }

        remoteLPTPrinters = {};
        remoteScanners = {};

        signalSocket.emit("gamemessage",{
            "destination": sharedResourceSignalingChannel,
            "msg": {
                "type": "resourcesignal",
                "code": "pingresources",
                "un": apeAppsLoginData.profile.un,
                "for": "all"
            }
        });
    }

    function announceOnlineToSignalServer() {
        if(!signalSocket) {
            return;
        }

        announceCurrentOnlineStatus();
        updateFriendsNetworkStatus(null);
        joinFriendsSignalChannels();
    }

    function announceCurrentOnlineStatus() {
        if(!signalSocket) {
            return;
        }

        signalSocket.emit("gamemessage",{
            "destination": friendsSignalingChannel,
            "msg": {
                "type": "friendsignal",
                "code": "iamonline",
                "un": apeAppsLoginData.profile.un,
                "for": "all"
            }
        });
    }

    function updateFriendsNetworkStatus(status) {
        if(!signalSocket) {
            return;
        }

        signalSocket.emit("gamemessage",{
            "destination": friendsSignalingChannel,
            "msg": {
                "type": "friendsignal",
                "code": "iamplaying",
                "un": apeAppsLoginData.profile.un,
                "extra": {
                    "app": window.appName,
                    "plat": window.statPlatform,
                    "stat": status
                },
                "for": "all"
            }
        });
    }

    function buildAccountsAPICreds(func) {

        let creds = getApeAppsCreds();

        if(!creds) {
            return null;
        }

        let data = [];

        if(func) {
            data.push({
                "key": "f",
                "value": func
            });
        }

        data.push({
            "key": "s1",
            "value": creds.a
        });

        data.push({
            "key": "s2",
            "value": creds.b
        });

        data.push({
            "key": "ak",
            "value": creds.c
        });

        data.push({
            "key": "apikey",
            "value": creds.c
        });

        data.push({
            "key": "am",
            "value": window.appId
        });

        return data;
    }

    function joinFriendsSignalChannels() {

        if(!signalSocket) {
            return;
        }

        let statusData = buildAccountsAPICreds("9");

        if(!statusData) {
            return;
        }

        proxyRequest(APE_ACCOUNTS_API,function(data){
            try {
                let friendsObj = JSON.parse(data);

                if(friendsObj.confirmed && friendsObj.confirmed.length > 0) {
                    for(let i = 0, l = friendsObj.confirmed.length; i < l; i++) {
                        let friend = friendsObj.confirmed[i];

                        let chRef = "awa-friends-signaling-" + friend.un;
                        
                        if(signalSocket) {
                            signalSocket.emit("joinroom",chRef);
                        }
                    }
                }
            } catch(ex) {
                console.log(ex);
            }

            let me = getApeAppsProfile();

            if(signalSocket && me) {
                signalSocket.emit("gamemessage",{
                    "destination": friendsSignalingChannel,
                    "msg": {
                        "type": "friendsignal",
                        "code": "onlinecheck",
                        "for": "all"
                    }
                });
            }
            
        },statusData,true,true);
    }

    function handleResourceSignalMessage(message) {

        if(!isApeAppsLoggedIn()) {
            return;
        }

        let me = getApeAppsProfile();

        if(message.for && message.for != me.un && message.for != "all") {
            return;
        }

        if(message.code == "scancomplete") {
            let result = message.data;

            if(result.trim().length > 5) {
                if(childWindowObject && childWindowObject.webAppCore && childWindowObject.webAppCore.passBackImageFromParent) {
                    childWindowObject.webAppCore.passBackImageFromParent([result]);
                }
            }

            completedScan = true;
            clearCompletedScanTimeout();

            adl.dismissProgress();
        }

        if(message.code == "resourceinfo") {
            let sender = message.session;

            if(message.printers) {
                remoteLPTPrinters[sender] = message.printers;
            }

            if(message.scanners) {
                remoteScanners[sender] = message.scanners;
            }

        }

        if(message.code == "printerinfo") {
            if(message.printers) {
                let sender = message.session;

                remoteLPTPrinters[sender] = message.printers;
            }

        }
    }

    function supportsRawPrinting() {
        for(let prop in remoteLPTPrinters) {
            let server = remoteLPTPrinters[prop];

            for(let prntr in server) {
                return true;
            }
        }

        return false;
    }

    function onSignalSocketMessage(message) {

        if(message && message.type && message.type == "resourcesignal") {

            handleResourceSignalMessage(message);

            return;
        }

        if(!message || !message.type || message.type != "friendsignal") {
            return;
        }

        if(!isApeAppsLoggedIn()) {
            return;
        }

        let me = getApeAppsProfile();

        if(message.for && message.for != me.un && message.for != "all") {
            return;
        }

        if(message.code == "customsignal") {
            if(childWindowObject && childWindowObject.webAppCore && childWindowObject.webAppCore.onCustomSignalMessage) {
                childWindowObject.webAppCore.onCustomSignalMessage(message.from,message.data);
            }
        }

        if(message.code == "onlinecheck") {
            updateFriendsNetworkStatus(lastFriendsStatus);
        }

        if(message.code == "iamonline" && message.un) {
            setFriendStatus(message.un,true,null);
        }

        if(message.code == "iamplaying" && message.un) {
            setFriendStatus(message.un,true,message.extra);
        }

        if(message.code == "goingoffline" && message.un) {
            setFriendStatus(message.un,false,null);
        }
    }

    function setFriendStatus(friend,online,extra) {

        let previouslyOnline = false;
        let onThisApp = false;

        if(!friendOnlineStatus[friend]) {
            friendOnlineStatus[friend] = {};
        }

        if(friendOnlineStatus[friend].ex && friendOnlineStatus[friend].ex.app == window.appName) {
            onThisApp = true;
        }

        if(friendOnlineStatus[friend].ol) {
            previouslyOnline = true;
        }

        if(!extra) {
            extra = null;
        }

        if(extra && extra.app == window.appName) {
            onThisApp = true;
        }

        friendOnlineStatus[friend].un = friend;
        friendOnlineStatus[friend].ol = online;

        if(!online || extra || !friendOnlineStatus[friend].ex) {
            friendOnlineStatus[friend].ex = extra;
        }
        
        if(!previouslyOnline) {
            let me = getApeAppsProfile();

            if(friend != me.un) {
                console.log(friend + " is online");
            }
        }

        if(onThisApp) {
            let sendStatus = null;
            
            if(online) {
                sendStatus = "online";

                if(extra && extra.stat) {
                    sendStatus = extra.stat;
                }
            } else {
                sendStatus = "offline";
            }

            if(sendStatus) {
                if(childWindowObject && childWindowObject.webAppCore && childWindowObject.webAppCore.onFriendStatusUpdate) {
                    childWindowObject.webAppCore.onFriendStatusUpdate(friend,sendStatus);
                }
            }
        }
    }

    function submitLoginData(s1,s2,s3,a) {
        let loginData = [];
        
        loginData.push({
            "key": "f",
            "value": "1"
        });

        loginData.push({
            "key": "s1",
            "value": s1
        });

        loginData.push({
            "key": "s2",
            "value": s2
        });

        loginData.push({
            "key": "ak",
            "value": s3
        });

        loginData.push({
            "key": "am",
            "value": a
        });

        proxyRequest(APE_ACCOUNTS_API,onAALoginDataBack,loginData,true,true);
    }

    function syncAchievementsWithServer() {

        if(!window.scoreServerId || window.scoreServerId == "0") {
            return;
        }

        if(isApeAppsLoggedIn()) {
            let reqUri = "https://www.ape-apps.com/highscores/app_resource.php";
            let reqOptions = [];

            reqOptions.push({
                key: "f",
                value: "11"
            });

            reqOptions.push({
                key: "g",
                value: window.scoreServerId
            });

            reqOptions.push({
                key: "u",
                value: encodeURI(apeAppsLoginData.profile.un)
            });

            proxyRequest(reqUri, function (data) {

                try {
                    let parsed = JSON.parse(data);

                    for (let i = 0; i < parsed.length; i++) {

                        let gameMode = parsed[i].achid;
                        let unlockTime = parsed[i].time;
                        //let unlockRef = parsed[i].unlockref;
                        let serverProgress = parseInt(parsed[i].progress);

                        let unlockDate = localStorage["wac_ach_" + gameMode + "_uld"];

                        if(!unlockDate) {
                            unlockDate = "LOCKED";
                        }

                        let progress = localStorage["wac_ach_" + gameMode + "_progress"];

                        if(!progress) {
                            progress = "0";
                        }

                        let currentProgress = parseInt(progress);

                        if (serverProgress > currentProgress) {
                            localStorage["wac_ach_" + gameMode + "_progress"] = serverProgress.toString();
                        }

                        if (unlockDate !== "LOCKED") {
                            continue;
                        }

                        let t = unlockTime.split(/[- :]/);
                        let d = new Date(Date.UTC(t[0], t[1] - 1, t[2], t[3], t[4], t[5]));

                        let ud = generateDate(d);

                        localStorage["wac_ach_" + gameMode + "_progress"] = "1";
                        localStorage["wac_ach_" + gameMode + "_uld"] = ud;

                    }



                } catch (ex) {
                    console.log(ex);
                }
            }, reqOptions, false);
        }
    }

    function generateDate(useDate) {
        let today = new Date();

        if(useDate) {
            today = useDate;
        }

        let dd = today.getDate();
        let mm = today.getMonth()+1; //January is 0!

        let yyyy = today.getFullYear();
        if(dd<10){
            dd="0"+dd;
        } 
        if(mm<10){
            mm="0"+mm;
        } 
        return mm+"/"+dd+"/"+yyyy;
    }

    function apeAppsCloudListingBack(data) {
        if(!data || data == null) {

            return;
        }

        try {
            let responseData = JSON.parse(data);

            if(responseData.result == "success") {
                createCloudPlaceholders(responseData.data);
            } else {
                console.log("cloud listing failure: " + responseData.reason);
            }
        } catch(ex) {
            console.log("error parsing cloud listing response!");
            console.log(ex);
        }
    }

    function createCloudPlaceholders(listing) {
        getFiles(null,function(existing) {
            for(let c = 0; c < listing.length; c++) {

                let fileExists = false;

                for(let e = 0; e < existing.length; e++) {
                    if(listing[c] == existing[e]) {
                        fileExists = true;
                        break;
                    }
                }

                if(!fileExists) {
                    if(!listing[c].endsWith(".aacfi")) {
                        saveFile(listing[c],"",true,null,null);
                    }
                }
            }
        });
    }

    function loadStripe() {

        if(window.Stripe) {
            return;
        }

        if(getIsPremium()) {
            return;
        }

        if(!isApeAppsLoggedIn()) {
            return;
        }

        let stripeSrcHold = document.createElement("script");
        stripeSrcHold.onload = function(){
            setTimeout(initStripe,500);
        };
        stripeSrcHold.src = "https://js.stripe.com/v3/";
        document.head.appendChild(stripeSrcHold);
    }

    function initStripe() {

        if(!window.Stripe) {
            return;
        }

        stripe = Stripe("pk_live_NMjE6y47CFe86ZOw19UkE6gA");

        paymentRequest = stripe.paymentRequest({
            country: "US",
            currency: "usd",
            total: {
                label: window.appName + " Premium",
                amount: parseInt(parseFloat(window.upgradePrice) * 100),
            },
            requestPayerName: false,
            requestPayerEmail: false,
            requestShipping: false
        });


        paymentRequest.on("paymentmethod",function(event) {
            paymentMethodEvent = event;
            fetchPaymentClientSecret();
        });

    }

    function fetchPaymentClientSecret() {
        let reqUrl = "/stripeconfirm.php?app=" + window.appurlid;

        let xhr = new XMLHttpRequest();
        xhr.open("get",reqUrl);
        xhr.onload = function(res) {

            let fullRes = res.target.responseText;

            if(!fullRes || fullRes == "fail") {
                // do something
                return;
            }

            try {
                let cleaned = fullRes.trim().replace("Stripe\\PaymentIntent JSON: ","");

                let obj = JSON.parse(cleaned);

                if(obj && obj.client_secret) {
                    processConfirmForStripe(obj.client_secret);
                }
            } catch(ex) {
                console.log(ex);
            }
        };
        xhr.send();
    }

    function processConfirmForStripe(clientSecret) {

        stripe.confirmCardPayment(
            clientSecret,
            {
                payment_method: paymentMethodEvent.paymentMethod.id
            },
            {
                handleActions: false
            }
        ).then(function(confirmResult) {
            if (confirmResult.error) {
                paymentMethodEvent.complete("fail");
            } else {
                paymentMethodEvent.complete("success");

                // Let Stripe.js handle the rest of the payment flow.
                stripe.confirmCardPayment(clientSecret).then(function(result) {
                    if (result.error) {
                        // The payment failed -- ask your customer for a new payment method.
                    } else {

                        if(isApeAppsLoggedIn() && apeAppsLoginData) {
                            let verificationData = [];

                            verificationData.push({key:"a",value:apeAppsLoginData.creds.sida});
                            verificationData.push({key:"b",value:apeAppsLoginData.creds.sidb});
                            verificationData.push({key:"c",value:apeAppsLoginData.creds.sidc});
                            verificationData.push({key:"m",value:window.appId});
                            verificationData.push({key:"n",value:window.appName});
                            verificationData.push({key:"cs",value:clientSecret});
                            verificationData.push({key:"pr",value:window.upgradePrice});

                            let verificationURL = "https://www.apewebapps.com/stripeverify.php";
                            proxyRequest(verificationURL,onVerificationResult,verificationData,true);
                        } else {
                            notifyPremiumActivation(true);
                            showToast("Thank you for your support!");
                        }
                    }
                });
            }
        });
    }

    function onVerificationResult(result) {
        if(result && result.trim() == "complete") {
            notifyPremiumActivation(true);
            showToast("Thank you for your support!");
        }
    }

    function notifyPremiumActivation(success) {
        if(success) {
            finalizePremiumAndRemoveBannerAds();

            if(childWindowObject && childWindowObject.webAppCore.onPremiumUpgraded) {
                childWindowObject.webAppCore.onPremiumUpgraded();
            }
        }
    }

    function finalizePremiumAndRemoveBannerAds() {
        localStorage[window.appId + "_premium_upgrade"] = "1";
        
        if (bah) {
            bah.goPremium();
        }

        
        if (adHolder) {
            adHolder.innerHTML = "";
            adHolder.style.display = "none";
        }

        rebuildToolbar();
    }

    function showAccountInfo() {

        let profile = getApeAppsProfile();

        let slideoutPosition = "right";

        if(window.useToolbar && window.innerWidth <= 500) {
            slideoutPosition = "left";
        }
        
        let accountOptions = [];

        accountOptions.push({
            title: "Sign Out",
            icon: "https://accounts.ape-apps.com/getavatar.php?u=" + profile.un,
            description: "You are currently signed in to " + window.appName + " as " + profile.un,
            tag: "signout",
            circleIcon: true
        });

        let premiumStatus = "Not Premium";
        let premiumColor = "#f44336";
        let premiumDesc = "You are using the free ad-support trial of " + window.appName + ".  Upgrade to the full version to unlock all content, support development, and remove advertising!";

        if(getIsPremium()) {
            premiumStatus = "Premium";
            premiumColor = "#4CAF50";
            premiumDesc = "You are using the premium version of " + window.appName + "!  Thank you for your support!";
        }

        accountOptions.push({
            title: "Premium Upgrade",
            icon: "fluent.&#xE14D;",
            iconColor: premiumColor,
            description: premiumDesc,
            tag: "premium",
            status: premiumStatus,
            statusColor: premiumColor
        });

        
        if(shouldShowApeCoins()) {
            accountOptions.push({
                title: "Ape Coins",
                icon: "https://www.apewebapps.com/images/ape-coin.png",
                description: "Ape Coins can be used in some Ape Apps games and applications to unlock additional content or transact between players.",
                tag: "apecoin",
                status: "Balance: " + numberWithCommas(getApeCoinBalance()),
                statusColor: "#FFA000"
            });
        }
        

        accountOptions.push({
            title: "Manage Account",
            icon: "fluent.&#xE13D;",
            iconColor: "#FF9800",
            description: "Manage all of your Ape Apps account settings.  You will be sent to the account management portal.",
            tag: "manage",
            status: "https://accounts.ape-apps.com",
            statusColor: "#1E88E5"
        });

        adl.showList({
            title: profile.un,
            options: accountOptions,
            onSelection: function(tag) {

                if(tag == "apecoin") {
                    showApeCoinPurchaseFlow();
                }

                if(tag == "manage") {
                    loadURL("https://accounts.ape-apps.com");
                }

                if(tag == "premium") {
                    if(!getIsPremium()) {
                        presentPremium();
                    }
                }

                if(tag == "signout") {
                    signOutOfApeApps();
                }
            },
            flyout: {
                position: slideoutPosition
            }
        });
    }

    function getFriendsList() {
        let statusData = buildAccountsAPICreds("9");

        if(!statusData) {
            return;
        }

        proxyRequest(APE_ACCOUNTS_API,onFriendsListBack,statusData,true,true);
    }

    function onFriendsListBack(data) {

        if(childWindowObject && childWindowObject.webAppCore && childWindowObject.webAppCore.onFriendsListReturn) {
            
            if(data) {
                try {
                    let datObj = JSON.parse(data);
                    childWindowObject.webAppCore.onFriendsListReturn(datObj);
                } catch(ex) {
                    childWindowObject.webAppCore.onFriendsListReturn(null);
                }
            } else {
                childWindowObject.webAppCore.onFriendsListReturn(null);
            }
            
            
        }
    }

    function importImage(desiredResolution) {
        if(!desiredResolution) {
            desiredResolution = 240;
        }

        if(desiredResolution < 50) {
            desiredResolution = 50;
        }

        let retVal = {
            options: [],
            callback: onImageImportOptionSelected
        };

        let test = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia || navigator.oGetUserMedia;
        

        if(window.platformOS == "android" || window.platformOS == "ios") {
            test = false;
        }

        if(test) {
            retVal.options.push({
                title: "Camera",
                description: "Capture an image with an attached camera device.",
                icon: "camera-outline",
                iconColor: "#607D8B",
                tag: {
                    "device": "camera",
                    "id": null
                }
            });
        }

        retVal.options.push({
            title: "File",
            description: "Import an image from your filesystem.",
            icon: "image-outline",
            iconColor: "#2196F3",
            tag: {
                "device": "file",
                "id": null
            }
        });

        if(remoteScanners) {
            for(let sessionId in remoteScanners) {
                let ses = remoteScanners[sessionId];
    
                for(let i = 0; i < ses.length; i++) {
                    let scanner = ses[i];
    
                    retVal.options.push({
                        title: "Scanner",
                        description: "Capture an image from a scanner device.",
                        icon: "winicon.&#xE294;",
                        iconColor: "#303F9F",
                        status: scanner.name,
                        tag: {
                            "device": "remotescanner",
                            "id": scanner.id,
                            "session": sessionId,
                            "resolution": desiredResolution
                        }
                    });
                }
            }
        }
        

        showImageImportOptions(retVal);
    }

    function showImageImportOptions(importOptions) {
        if(importOptions.options.length == 1) {
            importOptions.callback(importOptions.options[0].tag);
            return;
        }

        adl.showList({
            title: "Import From",
            options: importOptions.options,
            onSelection: function(tag) {
                if(tag) {
                    importOptions.callback(tag);
                }
            },
            flyout: {
                position: "right"
            }
        });
    }

    function onImageImportOptionSelected(tag) {
        
        if(!tag) {
            return;
        }

        if(tag.device == "remotescanner") {
            requestRemoteScan(tag.session,tag.id,tag.resolution);
        }

        if(tag.device == "camera") {
            launchImageImportFromWebcam();
        }

        if(tag.device == "file") {
            launchImageImportFromFile();
        }

    }

    function requestRemoteScan(session,scanner,resolution) {
        if(!isApeAppsLoggedIn() || !sharedResourceSignalingChannel || !signalSocketConnected) {
            return;
        }

        clearCompletedScanTimeout();

        signalSocket.emit("gamemessage",{
            "destination": sharedResourceSignalingChannel,
            "msg": {
                "type": "resourcesignal",
                "code": "scanrequest",
                "un": apeAppsLoginData.profile.un,
                "for": "all",
                "session": session,
                "scanner": scanner,
                "resolution": resolution
            }
        });

        completedScan = false;

        adl.showProgress({
            title: "Scanning...",
            indeterminate: true
        });

        completedScanTimeout = setTimeout(function(){
            adl.dismissProgress();
            showToast("Scanner Timeout!");
            clearCompletedScanTimeout();
        },60000);
    }

    function clearCompletedScanTimeout() {
        if(completedScanTimeout) {
            clearTimeout(completedScanTimeout);
        }

        completedScanTimeout = null;
    }

    function launchImageImportFromWebcam() {
        let posString = "";
        
        if(localStorage.wcwwinx) {
            posString = ",top=" + localStorage.wcwwiny + ",left=" + localStorage.wcwwinx;
        }

        let sizeString = "height=480,width=640";
        
        if(localStorage.wcwWidth) {
            sizeString = "height=" + localStorage.wcwHeight + ",width=" + localStorage.wcwWidth;
        }

        webcamWindow = window.open("/webcam.html","webcamWindow",sizeString + posString + ",status=no,titlebar=no,modal=yes,resizable=yes,menubar=no");
    }

    function imageImportCheckin(e) {

        processFiles.push(e.target.result);

        processProgress++;

        if(processProgress == remainingProcess) {
            if(childWindowObject && childWindowObject.webAppCore && childWindowObject.webAppCore.passBackImageFromParent) {
                childWindowObject.webAppCore.passBackImageFromParent(processFiles);
            }
        }

        
    }

    function launchImageImportFromFile() {
        imageInput.value = null;
        imageInput.click();
    }

    function onImageInputChange(e) {
        let ourFiles = [];
        let i,file;

        if(e.target.files) {
            for(i = 0; i < e.target.files.length; i++) {
                file = e.target.files[i];

                if (file.type.indexOf("image") == 0) {
                    ourFiles.push(file);
                }
            }
        }

        if(ourFiles.length  > 0) {
            processFiles = [];
            processProgress = 0;
            remainingProcess = ourFiles.length;

            for(i = 0; i < ourFiles.length; i++) {

                file = ourFiles[i];

                let reader = new FileReader();
                reader.onload = imageImportCheckin;
                reader.readAsDataURL(file);
            }
        }

        imageInput.value = null;
    }

    function showAchievements() {

        if(!scoreServerAchievementData || scoreServerAchievementData.length == 0) {
            showToast("There are no achievements for this app!");
            return;
        }

        achievementData = [];

        let totalPoints = 0;
        let unlockedPoints = 0;

        for (let i = 0; i < scoreServerAchievementData.length; i++) {

            let progress = localStorage["wac_ach_" + scoreServerAchievementData[i].trophyid + "_progress"];

            if(!progress) {
                progress = "0";
            }

            let unlocked = "0";
            let icon = "WINSQUARE";

            let useStatus = "LOCKED";
            let useStatusColor = "#607D8B";
            let useOpacity = "0.5";
            let iconColor = null;

            if (scoreServerAchievementData[i].trophyicon !== "n/a" && scoreServerAchievementData[i].trophyicon !== "0") {
                icon = "https://www.ape-apps.com/highscores/gameicons/" + scoreServerAchievementData[i].trophyicon;
            } else {
                icon = "trophy-outline";
                iconColor = "#FFC107";
            }

            let progressInt = parseInt(progress);
            let counterInt = parseInt(scoreServerAchievementData[i].counter);

            if (counterInt === 0 && progressInt >= 1) {
                unlocked = "1";
            }

            if (counterInt > 0 && progressInt >= counterInt) {
                unlocked = "1";
            }

            let unlockDate = localStorage["wac_ach_" + scoreServerAchievementData[i].trophyid + "_uld"];

            if(!unlockDate) {
                unlockDate = "LOCKED";
            }

            let useProgress = null;

            if (counterInt > 0 && progressInt > 0) {
                let prgs = progressInt / counterInt;
                let big = prgs * 100;
                useProgress = Math.round(big);

                if (useProgress > 100) {
                    useProgress = 100;
                }
            }

            let useOverflow = null;

            if (scoreServerAchievementData[i].value && scoreServerAchievementData[i].value > 0) {

                totalPoints += parseInt(scoreServerAchievementData[i].value);

                if (unlocked == "1") {
                    unlockedPoints += parseInt(scoreServerAchievementData[i].value);
                }

                useOverflow = [{
                    pre: "Point Value",
                    icon: "trophy-outline",
                    iconColor: "#FFC107",
                    post: scoreServerAchievementData[i].value
                }];
            }

            if (unlocked == "1") {
                if (unlockDate != "LOCKED") {
                    unlockDate = "Unlocked on " + unlockDate;
                } else {
                    let ud = generateDate();
                    unlockDate = "Unlocked on " + ud;
                    localStorage["wac_ach_" + scoreServerAchievementData[i].trophyid + "_uld"] = ud;
                }

                useStatus = unlockDate;
                useStatusColor = null;
                useOpacity = null;

                
                

            } else {
                icon = "lock-closed-outline";
                iconColor = "#607D8B";
            }

            let ach = {
                title: scoreServerAchievementData[i].trophyname,
                description: scoreServerAchievementData[i].description,
                icon: icon,
                iconColor: iconColor,
                status: useStatus,
                statusColor: useStatusColor,
                opacity: useOpacity,
                progress: useProgress,
                overflow: useOverflow
            };

            achievementData.push(ach);
        }

        if (totalPoints > 0) {

            let progPercent = unlockedPoints / totalPoints;
            let prog100 = progPercent * 100;
            let progRound = Math.round(prog100);

            if (progRound > 100) {
                progRound = 100;
            }

            achievementData.unshift({
                title: "Total Progress",
                status: unlockedPoints + "/" + totalPoints,
                progress: progRound
            });
        }

        adl.showList({
            title: "Achievements",
            options: achievementData,
            onSelection: function(tag) {
                console.log(tag);
            },
            flyout: {
                position: "right"
            }
        });
    }

    function prepareAchievementData() {
        if(!window.scoreServerId || window.scoreServerId == "0") {
            return;
        }

        proxyRequest("https://www.ape-apps.com/highscores/app_resource.php?f=7&g=" + window.scoreServerId,parseAchievementData,null,false);
    }

    function parseAchievementData(data) {
        
        if(!data) {
            return;
        }
        
        achievementData = [];
        //let jsonData;
        
        try {
            //jsonData = JSON.parse(data);
            scoreServerAchievementData = JSON.parse(data);

            syncAchievementsWithServer();
        } catch(err) {
            if(ah != null) {
                ah.trackEvent("Achievement Parse Error", err.message, data, false);
            }
            return;
        }

    }

    function reportAchievement(trophy,increment) {
        if(!trophy) {
            return;
        }

        if(!increment) {
            increment = 1;
        }
        
        let unlockDate = localStorage["wac_ach_" + trophy + "_uld"];

        if(unlockDate) {
            return;
        }

        if(!scoreServerAchievementData) {
            return;
        }
        
        let ach = null;

        for(let i = 0; i < scoreServerAchievementData.length; i++) {
            if(scoreServerAchievementData[i].trophyid == trophy) {
                ach = scoreServerAchievementData[i];
                break;
            }
        }

        if(!ach) {
            return;
        }
        
        let progress = localStorage["wac_ach_" + trophy + "_progress"];

        if(!progress) {
            progress = "0";
        }

        let counter = parseInt(ach.counter);

        let currentProgress = parseInt(progress);
        currentProgress += parseInt(increment);

        localStorage["wac_ach_" + trophy + "_progress"] = currentProgress;

        if (counter === 0 || currentProgress >= counter) {
            let ud = generateDate();
            localStorage["wac_ach_" + trophy + "_uld"] = ud;

            let trophIcon = ach.trophyicon;
            let useIcon = "https://www.ape-apps.com/highscores/gameicons/" + trophIcon;

            if (trophIcon == "0" || trophIcon == "n/a") {
                useIcon = "trophy-outline";
            }

            adl.showToast({
                message: "Achievement Unlocked: " + ach.trophyname,
                icon: getCleanIcon(useIcon),
                iconColor: "#FFC107",
                action: {
                    text: "Show",
                    func: showAchievements
                }
            });

            if(isApeAppsLoggedIn()) {
                let scoreData = [];

                scoreData.push({key:"t",value:trophy});
                scoreData.push({key:"g",value:window.scoreServerId});
                scoreData.push({key:"n",value:apeAppsLoginData.profile.un});
            
                proxyRequest("https://www.ape-apps.com/highscores/app_resource.php?f=10",null,scoreData,true);
            }
        }

    }

    function vibrate(duration) {
        if(supportsVibrate) {
            if(duration == 0) {
                vibrateInterval = setInterval(function() {
                    doVibrate(4000);
                }, 4000);
                doVibrate(4000);
            } else {
                doVibrate(duration);
            }
            
        }
    }

    function doVibrate(length) {
        navigator.vibrate(length);
    }

    function endVibrate() {
        if(supportsVibrate) {
            if(vibrateInterval) {
                clearInterval(vibrateInterval);
            }
            
            navigator.vibrate(0);
        }
    }

    function toggleShareBox(value) {

        let useShareUrl = window.shareURL;

        textToShare = value.replace("#appurl",useShareUrl);

        var shareObject = {
            title: window.appName,
            text: textToShare
        };

        if(textToShare.indexOf(window.shareURL) == -1) {
            shareObject.url = window.shareURL;
        }

        navigator.share(shareObject).then(function() {
            // share success
        }).catch(function(error) {
            console.log("Error sharing", error);
        });
        
        
    }

    function stopTTS() {
        window.speechSynthesis.cancel();
    }
    
    function doTTS(text) {

        if ("speechSynthesis" in window) {
            let msg = new SpeechSynthesisUtterance(text);

            msg.addEventListener("end",function() {

                if(childWindowObject && childWindowObject.webAppCore && childWindowObject.webAppCore.onTTSdone) {
                    childWindowObject.webAppCore.onTTSdone(text);
                }
            });

            window.speechSynthesis.speak(msg);
        }
    }

    function setShakeSensitivity(sensitivity) {
        startReportingShake = true;
        shakeSensitivity = sensitivity;
    }

    function onDeviceMotion(e) {
        if(!e || !e.acceleration) {
            return;
        }
        
        let acclX = e.acceleration.x;
        let acclY = e.acceleration.y;
        let acclZ = e.acceleration.z;
        
        let gAcclX = e.accelerationIncludingGravity.x;
        let gAcclY = e.accelerationIncludingGravity.y;
        let gAcclZ = e.accelerationIncludingGravity.z;
        
        if(!acclX) {
            acclX = gAcclX;
        }
        
        if(!acclY) {
            acclY = gAcclY;
        }
        
        if(!acclZ) {
            acclZ = (gAcclZ - gravity);
        }
        
        let changeX = Math.abs(lastShakeX - acclX);
        let changeY = Math.abs(lastShakeY - acclY);
        let changeZ = Math.abs(lastShakeZ - acclZ);
        
        let totalChange = Math.round(changeX + changeY + changeZ);
        totalChange = Math.round(totalChange * 0.25);
        
        lastShakeX = acclX;
        lastShakeY = acclY;
        lastShakeZ = acclZ;
        
        if(!startReportingShake) {
            return;
        }
        
        if(totalChange > shakeSensitivity) {
            if(childWindowObject.webAppCore.shakeCallback) {
                childWindowObject.webAppCore.shakeCallback();
            }   
        }
    }

    function toggleScores(mode) {

        if(!window.scoreServerId || window.scoreServerId == "0") {
            return;
        }
        
        callStandardScores(mode);
    }

    function callStandardScores(mode) {
        let scoreData = [];
        scoreData[scoreData.length] = {key:"g",value:window.scoreServerId};
        scoreData[scoreData.length] = {key:"m",value:mode};
        scoreData[scoreData.length] = {key:"i",value:"0"};

        let requestURI = "https://www.ape-apps.com/highscores/app_resource.php?f=5";

        if (isApeAppsLoggedIn()) {
            requestURI = "https://www.ape-apps.com/highscores/app_resource.php?f=12";
        }
    
        proxyRequest(requestURI,parseScoreWindow,scoreData,true);
    }

    function parseScoreWindow(data) {

        if (data.indexOf(":") == -1) {
            return;
        }

        let scoreArray = data.split("&");
        let formattedScoreArray = [];
        
        for(let i = 0; i < scoreArray.length; i++) {
            if (scoreArray[i].indexOf(":") != -1) {
                let scoreParts = scoreArray[i].split(":");
                let scorePlayer = scoreParts[0];
                let scoreScore = scoreParts[1];
                
                let formattedScore = { title: scorePlayer,status: scoreScore};

                if(isApeAppsLoggedIn()) {
                    formattedScore.icon = "https://accounts.ape-apps.com/getavatar.php?u=" + encodeURI(scoreParts[0]);
                } else {
                    formattedScore.icon = "person-outline";
                    formattedScore.iconColor = "#FFC107";
                }

                formattedScore.compactItem = true;

                formattedScoreArray.push(formattedScore);
            }
        }
        
        adl.showList({
            title: "High Scores",
            options: formattedScoreArray,
            onSelection: function(tag) {
                console.log(tag);
            },
            flyout: {
                position: "right"
            }
        });

    }

    function submitHighScore(modeid,score) {

        if(!window.scoreServerId || window.scoreServerId == "0") {
            return;
        }
        
        pendingScoreMode = modeid;
        pendingScoreScore = score;
        scorePending = true;
        
        scoresPendingUsername = localStorage.wac_hs_username;

        if(isApeAppsLoggedIn()) {
            finishApeAppsScoreSubmission();
            return;
        }

        if(!scoresPendingUsername || scoresPendingUsername == "0" || scoresPendingUsername.length < 1) {
            setTimeout(setHighScoresUsername,500);
            return;
        }

        finishStandardScoreSubmission();
    }

    function finishApeAppsScoreSubmission() {
        let scoreData = [];

        scoreData.push({key:"g",value:window.scoreServerId});
        scoreData.push({key:"m",value:pendingScoreMode});
        scoreData.push({key:"s",value:pendingScoreScore});
        scoreData.push({key:"n",value:apeAppsLoginData.profile.un});
    
        proxyRequest("https://www.ape-apps.com/highscores/app_resource.php?f=9",scoreSubmissionResult,scoreData,true);
    }

    function setHighScoresUsername(message) {
        
        let display = "Choose a username for the High Scores server.";
        
        if(message) {
            display = message;
        }
        
        inputBox(display,saveScoresUsername);
    }

    function finishStandardScoreSubmission() {
        let scoreData = [];
        scoreData[scoreData.length] = {key:"g",value:window.scoreServerId};
        scoreData[scoreData.length] = {key:"m",value:pendingScoreMode};
        scoreData[scoreData.length] = {key:"s",value:pendingScoreScore};
        scoreData[scoreData.length] = {key:"n",value:scoresPendingUsername};
    
        proxyRequest("https://www.ape-apps.com/highscores/app_resource.php?f=6",scoreSubmissionResult,scoreData,true);
    }

    function saveScoresUsername(username) {
        localStorage.wac_hs_username = username;
        
        if(scorePending) {
            submitHighScore(pendingScoreMode,pendingScoreScore);
        }
    }

    function getHighScoresUsername() {
        if(!window.scoreServerId || window.scoreServerId == "0") {
            return;
        }
        
        let username = localStorage.wac_hs_username;

        if(!username) {
            return "n/a";
        }
        
        if(username == "0") {
            return "n/a";
        }
        
        if(username.length < 1) {
            return "n/a";
        }
        
        return username;
    }

    function scoreSubmissionResult(data) {
        if(childWindowObject.webAppCore && childWindowObject.webAppCore.pendingScoreFunction) {
            childWindowObject.webAppCore.pendingScoreFunction(data);
        }
    }

    function playMidi(song,loop,callback,volume) {
        if(!midiPlayer) {
            midiInit(function(){
                playMidi(song,loop,callback,volume);
            });

            return;
        }

        if(volume == undefined) {
            volume = 100;
            currentMidiVolume = 100;
        } else {
            currentMidiVolume = volume;
        }

        if(callback) {
            currentMidiCallback = callback;
        } else {
            currentMidiCallback = null;
        }

        if(loop) {
            currentMidiLoop = loop;
        } else {
            currentMidiLoop = false;
        }

        let url = window.appBase + "audio/midi/" + song + ".mid";
        currentMidi = song;

        if(typeof song != "string") {console.log("arr");
            midiPlayer.play({
                arrayBuffer: song
            });
        } else {
            midiPlayer.play({
                url: url
            });
        }

       
        
        setTimeout(function(){
            try {
                midiPlayer.setVolume({ volume: currentMidiVolume });
            } catch(ex) {
                console.log(ex);
            }
        },100);
        
    }

    function stopMidi() {
        if(!midiPlayer) {
            return;
        }

        currentMidi = null;
        currentMidiLoop = false;

        if(currentMidiCallback) {
            currentMidiCallback = null;
        }

        currentMidi = null;
        midiPlayer.stop();
    }

    function midiInit(callback) {

        if(!midiScriptAdded) {
            let midiScript = document.createElement("script");
            midiScript.onload = function() {
                midiScriptAdded = true;

                setTimeout(function(){
                    midiInit(callback);
                },100);
            };
            midiScript.src = "/js/webmidiplayer/" + window.webMidiPlayerVersion + "/webmidiplayer.js";
            document.head.appendChild(midiScript);

            return;
        }

        const { "web-midi-player": { default: MidiPlayer } } = window;
        
        midiPlayer = new MidiPlayer({
            patchUrl: "/midipat/",
            eventLogger: onMidiEvent,
            volume: currentMidiVolume
        });

        setTimeout(function(){
            callback();
        },100);
    }

    function onMidiEvent(e) {

        if(e.event && e.event == "MIDI_END") {

            if(currentMidiLoop && currentMidi) {
                playMidi(currentMidi,currentMidiLoop,currentMidiCallback,currentMidiVolume);
                return;
            }

            let cb = null;

            if(currentMidiCallback) {
                cb = currentMidiCallback;
            }

            currentMidiCallback = null;

            if(cb) {
                cb("done");
            }
        }
    }

    function midiOnPause() {
        
        if(currentMidi != null) {
            midiPlayer.pause();
        }
    }

    function midiOnResume() {
        if(currentMidi != null) {
            midiPlayer.resume();
        }
    }

    function importLocalFile(type,asBinary,callback) {

        if(!callback) {
            callback = onImportFileReturn;
        }

        if(usingElectronHost || window.showOpenFilePicker) {
            let typeAr = [];

            if(Array.isArray(type)) {
                typeAr = type;
            } else {
                typeAr = type.split(",");
            }

            openFileDialog(typeAr,callback,asBinary);

            return;
        }


        let acceptValue = "";
        let typeAr = [];

        if(Array.isArray(type)) {
            typeAr = type;
        } else {
            typeAr = type.split(",");
        }

        for(let i = 0; i < typeAr.length; i++) {

            if(i > 0) {
                acceptValue = acceptValue + ",";
            }

            let t = typeAr[i];

            acceptValue = acceptValue + "." + t;
        }

        fileSelector.asBinaryRef = asBinary;
        fileSelector.callbackRef = callback;
        fileSelector.setAttribute("accept", acceptValue);
        fileSelector.value = null;
        fileSelector.click();
    }

    function onImportFileReturn(name,data) {
        if(!name) {
            showToast("Unable to open file!");
            return;
        }

        childWindowObject.webAppCore.fileImportComplete(name,data);
    }

    // android callback
    function notifyImportReady(fileId) {
        let name = Android.getImportName(fileId);
        let content = Android.getImportContent(fileId);
    
        if(fileImportCallback) {
            fileImportCallback(name,content,fileId);
        }
    }

    function endsWith(str, suffix) {
        return str.indexOf(suffix, str.length - suffix.length) !== -1;
    }

    function getFileTypeHelper() {
        return fth;
    }

    function onInputFileSelected(e) {
        let f = e.target.files[0]; 

        if (f) {
            let r = new FileReader();
            r.onload = function(e) { 
                let contents = e.target.result;
                let name = f.name.replace("'","");
                
                fileSelector.callbackRef(name,contents);
            };

            let fileData = fth.getFileTypeData(f.name);

            let asBinary = false;
            let asArrayBuffer = false;

            if(fileSelector.asBinaryRef) {
                asBinary = true;
            }

            if(fileData) {
                if(fileData.prefBinary) {
                    asBinary = true;
                }

                if(fileData.prefArrayBuffer) {
                    asArrayBuffer = true;
                }
            }

            if(asBinary) {
                if(asArrayBuffer) {
                    r.readAsArrayBuffer(f);
                } else {
                    r.readAsBinaryString(f);
                }
                
                
            } else {
                r.readAsText(f);
            }
        } else { 
            fileSelector.callbackRef(false);
        }

        fileSelector.value = null;
    }

    function logEvent(category,action,label) {
        if(ah == null) {
            initAnalytics(function(){
                logEvent(category,action,label);
            });
        } else {
            ah.trackEvent(category,action,label, false);
        }
    }

    function logStatistics() {
        if(ah == null) {
            initAnalytics(logAnalytics);
        } else {
            logAnalytics();
        }

        if(window.amslLogStat) {
            logApeMarket();
        } else {
            initAMSL(logApeMarket);
        }
    }

    function logAnalytics() {

        let totalLaunches = 0;

        if(localStorage[window.appId + "-total-launches"]) {
            totalLaunches = parseInt(localStorage[window.appId + "-total-launches"]);
        }

        totalLaunches++;

        localStorage[window.appId + "-total-launches"] = totalLaunches;

        isInstalledPWA = false;

        if (window.matchMedia && window.matchMedia("(display-mode: standalone)").matches) {
            isInstalledPWA = true;
        } else {
            if(navigator.standalone) {
                isInstalledPWA = true;
            }
        }

        if(usingElectronHost) {
            ah.trackCustomScreen("UA-75642810-7", window.appName);

            if(!isSteam) {
                ah.trackEvent("Ape Web Apps Hosted for Desktop","Launched",window.appName,true);

                if(isApeLauncher) {
                    ah.trackInstall("Ape Apps Launcher");
                } else {
                    ah.trackInstall("Ape Market");
                }

            }

            let arch = wacUtils.getArchitecture();

            ah.trackCustomEvent("UA-75642810-7","Desktop Architecture", CURRENT_PLATFORM, arch);
        } else {

            if(isSoundboardCity) {
                ah.trackScreen("Soundboard City App Frame", true);
                ah.trackInstall("Soundboard City");
            } else {
                ah.trackScreen("Ape Web Apps Online Player", true);
                ah.trackInstall("Ape Web Apps");
            }

            

            if(isInstalledPWA) {
                ah.trackEvent("Ape Web App", "Launched", "PWA Installed", true);
            } else {
                ah.trackEvent("Ape Web App", "Launched", "PWA Not Installed", true);
            }
        }

        if(isTV()) {
            ah.trackEvent("TV App", "Opened", window.appName, true);
            ah.trackEvent("Ape Web Apps Online TV App", "Opened", window.appName, true);

            if(isWindowsStore) {
                ah.trackEvent("Xbox App", "Opened", window.appName, true);
            }
        }
        
        
        if (window.analytics2 != "0" && window.analytics2 != "n/a") {
            ah.trackCustomScreen(window.analytics2, window.appName);
        }
        
        if (window.analytics3 != "0" && window.analytics3 != "n/a") {
            ah.trackCustomScreen(window.analytics3, window.appName);
        }

        if(isWindowsStore) {
            ah.trackEvent("Windows Store PWA","Launched",window.appName,true);

            let forWinPlatform = "Desktop";

            if(isTV()) {
                forWinPlatform = "Xbox";
            }

            ah.trackScreen("Ape Web Apps Hosted for " + forWinPlatform, true);
            ah.trackInstall("Windows Store");
            ah.trackCustomScreen("UA-75642810-22", window.appName);

            ah.trackEvent("Hosted App",window.appName,"Windows",true);
        }

        if(isSteam) {
            ah.trackEvent("Ape Web Apps Hosted for Steam","Launched",window.appName,true);
            ah.trackInstall("Steam");
            ah.trackCustomScreen("UA-75642810-34", window.appName);
        }

        if(isApeLauncher) {
            ah.trackEvent("Ape Apps Launcher PWA","Launched",window.appName,true);
            ah.trackInstall("Ape Apps Launcher");
        }

        if(isApeMarket) {
            ah.trackEvent("Ape Market Standalone PWA","Launched",window.appName,true);
            ah.trackInstall("Ape Market");
        }

        // install nag
        if(!isApeLauncher && !isWindowsStore && !isSteam && !usingElectronHost && !isInstalledPWA && !isSoundboardCity && !isApeTvFrame) {
            if(totalLaunches == 6) {
                setTimeout(doInstallNag,500);
            }
        }
    }

    function logApeMarket() {
        let tv = 0;
    
        if(isTV()) {
            tv = 1;
        }

        workingSubplatform = "desktop";
			
        if(window.innerWidth <= 500) {
            workingSubplatform = "phone";
        } else {
            let isiPad = navigator.userAgent.match(/iPad/i) != null;
            let isAndroid = navigator.userAgent.match(/Android/i) != null;
            
            if(isiPad || isAndroid) {
                workingSubplatform = "tablet";
            }
        }
        
        if(tv == 1) {
            workingSubplatform = "tv";
        }

        amslLogStat(window.appId,window.statPlatform,usingAppVersion,tv,workingSubplatform);
    }

    function setGamepadFunctions(down,up,velocity) {
        onGamepadDown = down;
        onGamepadUp = up;
        onGamepadVelocity = velocity;

        if(!gamepadScriptLoaded) {
            gamepadScriptLoaded = true;

            let gamepadTag = document.createElement("script");
            gamepadTag.onload = function() {
                setTimeout(function(){
                    window.setGamepadListeners(onGamepadDownGlobal,onGamepadUpGlobal,function(id,stick,vel){
                        if(appIsPaused) {
                            return;
                        }

                        if(onGamepadVelocity) {
                            onGamepadVelocity(id,stick,vel);
                        }
                        
                    });
                },100);
            };
            gamepadTag.src = "/js/gamepadhelper/" + window.gamepadHelperVersion + "/gamepadhelper.js";
            document.head.appendChild(gamepadTag);
        }
    }

    function forceCloseMenu() {
        adl.dismissDialogWindow();
    }

    function onGamepadDownGlobal(id,key) {

        if(appIsPaused) {
            return;
        }

        if(key == "up" || key == "down" || key == "left" || key == "right") {
            let selectedElement = adl.gamepadXYCheck(key,currentGamepadElement);

            if(currentGamepadElement) {
                currentGamepadElement.classList.remove("adlGamepadSelected");
            }

            if(selectedElement) {
                currentGamepadElement = selectedElement;
                currentGamepadElement.classList.add("adlGamepadSelected");
                currentGamepadElement.scrollIntoView(false);
    
                return;
            }
            
        }

        if(key == "a" && currentGamepadElement) {
            if(adl.canElementBeGamepadSelected(currentGamepadElement)) {
                currentGamepadElement.click();
                return;
            } else {
                currentGamepadElement = null;
            }
        }

        if(key == "b") {

            if(currentGamepadElement) {
                currentGamepadElement.classList.remove("adlGamepadSelected");
            }

            currentGamepadElement = null;

            if(adl.dismissDialogWindow()) {
                return;
            }
        }

        if(onGamepadDown) {
            onGamepadDown(id,key);
        }
    }

    function onGamepadUpGlobal(id,key) {

        if(appIsPaused) {
            return;
        }

        if(currentGamepadElement) {
            if(adl.canElementBeGamepadSelected(currentGamepadElement)) {
                return;
            }
        }

        if(onGamepadUp) {
            onGamepadUp(id,key);
        }
    }

    function showInterstitial() {
        if(getIsPremium()) {
            return;
        }

        proxyRequest("https://market.ape-apps.com/app_resources/get_daily_feature.php",function(res) {
            try {
                let resultData = JSON.parse(res);

                if(resultData && resultData.name) {
                    let linkName = replaceAll(resultData.name," ","-").toLowerCase();
                    let appURL = "https://apps.ape-apps.com/" + linkName + "/";
                    let featureURL = "https://market.ape-apps.com/app_resources/getfeature.php?a=" + linkName;

                    let holder = document.createElement("div");

                    let img = document.createElement("img");
                    img.style.width = "100%";
                    img.draggable = false;
                    img.alt = resultData.name;
                    img.src = featureURL;
                    holder.appendChild(img);

                    let p = document.createElement("p");
                    p.className = "adl";
                    p.innerHTML = "Try " + resultData.name + " for free today!";
                    p.style.textAlign = "center";
                    holder.appendChild(p);

                    adl.showDialog({
                        title: resultData.name, 
                        customElement: holder,
                        buttons: [
                            {
                                text: "Dismiss"
                            },
                            {
                                text: "Download",
                                color: "#4CAF50",
                                func: function() {
                                    loadURL(appURL);
                                }
                            }
                        ]
                    });
                }
            } catch(ex) {
                console.log(ex);
            }
            

        },[],false,false);
    }

    function printContent(content,margin) {

        if(!margin) {
            margin = "1in 0.75in";
        }

        if(!styleDynamicPrint) {
            styleDynamicPrint = document.createElement("style");
            document.head.appendChild(styleDynamicPrint);
        }

        if(!printArea) {
            printArea = document.createElement("div");
            printArea.className = "printArea";
            document.body.appendChild(printArea);
        }

        printArea.innerHTML = content;
        styleDynamicPrint.innerHTML = "@page { margin: " + margin + " }";

        setTimeout(function() {
            window.print();
        },500);
    }

    function saveScreenshot(element,send,filename) {
        if(!filename) {
            filename = null;
        }

        if(!element) {
            element = childWindowObject.document.body;
        }

        if (typeof html2canvas !== "undefined") {
            html2canvas(element, {
                removeContainer: false,
                allowTaint: true
            }).then(function(canvas) {
                processScreenshot(canvas,send,filename);
            });
        } else {
            var htc = document.createElement("script");
            htc.onload = function () {

                setTimeout(function(){
                    html2canvas(element, {
                        removeContainer: false,
                        allowTaint: true
                    }).then(function(canvas) {
                        processScreenshot(canvas,send,filename);
                    });
                },100);

                
            };
            htc.src = "/js/html2canvas/" + window.html2canvasversion + "/html2canvas.min.js";
            document.head.appendChild(htc);
        }
    }

    function processScreenshot(canvas,send,filename) {

        if(!send) {
            send = false;
        }

        let previewURL = null;

        if(canvas.nodeName.toLowerCase() == "canvas") {
            previewURL = canvas.toDataURL();
        }

        if(canvas.nodeName.toLowerCase() == "img") {
            previewURL = canvas.src;
        }

        if(!previewURL) {
            console.log("no iamge!");
            return;
        }

        let n = Math.round((new Date()).getTime() / 1000);

        if(!filename) {
            filename = "img_" + n + ".png";
        }

        if(!filename.toLowerCase().endsWith(".png")) {
            filename = filename + ".png";
        }

        if(usingElectronHost) {
            saveElectronFileAs(previewURL,filename,null,placeholder);
            return;
        }

        let blob = dataURLtoBlob(previewURL);

        if(!send) {
            saveFileAs(blob,filename);
            return;
        }

        let myDomElement = document.createElement("img");
        myDomElement.draggable = false;
        myDomElement.style.width = "100%";
        myDomElement.src = previewURL;

        let useBtns = [{
            text: "Save",
            func: function(){
                saveFileAs(blob,filename);
            }
        }];

        if (navigator.share !== undefined) {
            useBtns.push( {
                text: "Share",
                func: function() {
                    shareFile(blob,filename,"image/png");
                }
            });
        }

        useBtns.push( {
            text: "Dismiss"
        });

        adl.showDialog({ 
            title: filename, 
            customElement: myDomElement,
            buttons: useBtns
        });
    }

    function shareFile(blob,filename,mime) {
        let file = new File([blob], filename, {type: mime});

        let shareObject = {
            title: filename,
            text: "Created with " + window.appName,
            files: [file]
        };

        navigator.share(shareObject).then(function() {
            console.log("Successful share");
        }).catch(function(error) {
            console.log("Error sharing", error);
        });
    }

    function dataURLtoBlob(dataurl) {
        let arr = dataurl.split(","), mime = arr[0].match(/:(.*?);/)[1],
            bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
        while(n--){
            u8arr[n] = bstr.charCodeAt(n);
        }
        return new Blob([u8arr], {type:mime});
    }

    function saveElectronFileAs(data,filename,noencode,callback) {
        if(!filename) {
            filename = "";
        }

        let filters = [];
        let enc = "utf8";
        let base64Data;
        let binaryData;

        if(noencode) {
            enc = null;
        }

        let filetypeDefinition = fth.getFileTypeData(filename);

        if(filetypeDefinition) {
            if(filetypeDefinition.display && filetypeDefinition.ext) {
                filters.push({name: filetypeDefinition.display,extensions: [filetypeDefinition.ext]});
            }

            if(filetypeDefinition.enc) {
                enc = filetypeDefinition.enc;
            }
        }

        if(enc && enc == "binary") {
            if(filetypeDefinition && filetypeDefinition.ext == "png") {
                base64Data = data.replace(/^data:image\/png;base64,/, "");
                base64Data += base64Data.replace("+", " ");
                binaryData = wacUtils.getBinaryData(base64Data,"base64");
                
                data = binaryData;
            } else {
                binaryData = wacUtils.getBinaryData(data,"base64");
                data = binaryData;
            }
        }

        if(filters.length == 0) {
            
            let ext = filename.split(".").pop();
            
            if(ext && ext.length > 0) {
                filters.push({name: "." + ext + " File",extensions: [ext]});
            }
            
            filters.push({name: "All Files",extensions: ["*"]});
        }

        wacUtils.showSaveDialog({ title: "Save As...",filters:filters},data,enc,function(filePath) {
            if(callback) {
                if(!dialogFileRefes) {
                    dialogFileRefes = {};
                }
    
                let id = dialogFileCounter++;
    
                dialogFileRefes[id] = {
                    file: filePath
                };
    
                callback(id,filenameFromPath(filePath));
            }
        });
    }

    function filenameFromPath(path) {
        let sep = "/";

        if(path.indexOf(sep) == -1) {
            sep = "\\";
        }

        if(path.indexOf(sep) == -1) {
            return path;
        }

        let parts = path.split(sep);

        return parts[parts.length - 1];
    }

    function saveFileAs(data,name,isXML,callback) {

        if(usingElectronHost) {
            saveElectronFileAs(data,name,null,callback);
            return;
        }

        if(!isXML) {
            isXML = false;
        }

        if(callback) {
            tmpExportCallback = callback;
        } else {
            callback = null;
        }

        let suggestedName = null;

        if(name) {
            suggestedName = name;
        }
        let filetypeData = fth.getFileTypeData(suggestedName);

        if(window.showOpenFilePicker) {

            let saveAsOptions = {};

            saveAsOptions.suggestedName = suggestedName;

            if(filetypeData && filetypeData.display && filetypeData.mime && filetypeData.dotExt) {
                let acceptDat = {};

                acceptDat[filetypeData.mime] = [
                    filetypeData.dotExt
                ];
    
                saveAsOptions.types = [
                    {
                        description: filetypeData.display,
                        accept: acceptDat
                    }
                ];

                // maybe at some point, file type helper can specify
                // if the exclude all option should be enabled for
                // a specific type!

                //saveAsOptions.excludeAcceptAllOption = true;
            }

            window.showSaveFilePicker(saveAsOptions).then(function(handle){
                fileSystemFileIdCounter++;

                fileSystemFilesHolder[fileSystemFileIdCounter] = {};
                fileSystemFilesHolder[fileSystemFileIdCounter].file = null;
                fileSystemFilesHolder[fileSystemFileIdCounter].handle = handle;
                fileSystemFilesHolder[fileSystemFileIdCounter].binary = false;

                doNativeHandleSave(fileSystemFileIdCounter,handle,data,callback);
            });

            return;
        }

        let blob = new Blob([data], {type: "octet/stream"});
        let url = window.URL.createObjectURL(blob);

        if(!suggestedName) {
            suggestedName = "file";
        }
        
        let downloadLink = document.createElement("a");
        downloadLink.href = url;
        downloadLink.download = suggestedName;
        
        if( navigator.userAgent.toLowerCase().indexOf("firefox") > -1 ){
            document.body.appendChild(downloadLink);
        }

        downloadLink.click();
        
        window.URL.revokeObjectURL(url);

        if( navigator.userAgent.toLowerCase().indexOf("firefox") > -1 ){
            document.body.removeChild(downloadLink);
        }
    }

    /*
    function getFileTypesArrayFromFilenames(filename) {

        let types = [];
        let fromArray = [];

        if(Array.isArray(filename)) {
            fromArray = filename;
        } else {
            fromArray = [filename];
        }

        for(let i = 0; i < fromArray.length; i++) {
            let type = fromArray[i].toLowerCase();

            if(type.endsWith("txt")) {
                types.push({
                    description: "Text Files",
                    accept: {
                        "text/plain": [".txt"]
                    }
                });

                continue;
            }

            if(type.endsWith("nnp")) {
                types.push({
                    description: "Noteastic Note",
                    accept: {
                        "application/noteastic": [".nnp"]
                    }
                });

                continue;
            }

            if(type.endsWith("vpp")) {
                types.push({
                    description: "Voxel Paint Files",
                    accept: {
                        "application/voxel-paint": [".vpp"]
                    }
                });

                continue;
            }

            if(type.endsWith("m2m")) {
                types.push({
                    description: "My Business Empire 2 Map",
                    accept: {
                        "application/my-business-empire-2-map": [".m2m"]
                    }
                });

                continue;
            }

            if(type.endsWith("m2s")) {
                types.push({
                    description: "My Business Empire 2 Save",
                    accept: {
                        "application/my-business-empire-2-save": [".m2s"]
                    }
                });

                continue;
            }
            
            if(type.endsWith("ppp")) {
                types.push({
                    description: "Pixel Paint Files",
                    accept: {
                        "application/pixel-paint": [".ppp"]
                    }
                });

                continue;
            }
            
            if(type.endsWith("fpp")) {
                types.push({
                    description: "Finger Paint Paintings",
                    accept: {
                        "application/finger-paint": [".fpp"]
                    }
                });

                continue;
            }

            if(type.endsWith("pdf")) {
                types.push({
                    description: "Portable Document Format",
                    accept: {
                        "application/pdf": [".pdf"]
                    }
                });

                continue;
            }

            if(type.endsWith("pds")) {
                types.push({
                    description: "PDF Document Scanner Project",
                    accept: {
                        "application/pdf-document-scanner": [".pds"]
                    }
                });

                continue;
            }

            // if we got here, make a generic entry

            let splitUp = type.split(".");
            let lastType = splitUp[splitUp.length - 1];

            let acceptitem = {};
            acceptitem[getMime("." + lastType)] = ["." + lastType];

            types.push({
                description: getFileAppTypeReadable("." + lastType) + " Files",
                accept: acceptitem
            });
        }

        return types;
    }
    */

    function notifySaveAsDone(id) {
        if(tmpExportCallback) {
            let tmpCallback = tmpExportCallback;
            tmpExportCallback = null;
            tmpCallback(id);
        }
    }

    function supportsRingtone() {
        return false;
    }

    function playAudioInstance(soundname,volume,instanceName) {
        
        if(volume == undefined || volume == null) {
            volume = 1;
        }

        if(!instanceName) {
            instanceName = soundname;
        }


        if(audioInstances[instanceName]) {
            engageAudioInstance(instanceName,volume);
        } else {
            let soundFileURL = null;

            if(soundname instanceof ArrayBuffer) {
                let blob = new Blob([soundname]);
    
                soundFileURL = URL.createObjectURL(blob);

            } else {
                soundFileURL = window.appBase + "audio/mp3/" + soundname + ".mp3";
            }

            audioInstances[instanceName] = document.createElement("audio");
            audioInstances[instanceName].nameRef = instanceName;
            audioInstances[instanceName].src = soundFileURL;
            audioInstances[instanceName].volume = volume;
            audioInstances[instanceName].addEventListener("ended",function() {
                this.currentTime = 0;
                if(childWindowObject.webAppCore) {
                    childWindowObject.webAppCore.audioInstanceCallbackListener(this.nameRef);
                }
            },false);

            engageAudioInstance(instanceName,volume);
        }
    }

    function stopAudioInstance(soundname) {

        if(!audioInstances[soundname]) {
            return;
        }

        try {
            audioInstances[soundname].currentTime = 0;
        } catch(err) {
            console.log(err);
        }

        audioInstances[soundname].pause();
        childWindowObject.webAppCore.audioInstanceCallbackListener(soundname);
 
    }

    function engageAudioInstance(soundname,volume) {
        audioInstances[soundname].volume = volume;
        audioInstances[soundname].play();
    }

    function presentNativeList(title,items,dismissText,callback) {

        if(!callback) {
            if(childWindowObject && childWindowObject.webAppCore) {
                callback = childWindowObject.webAppCore.nativeListOptionSelected;
            }
        }

        let buttons = [];

        if(dismissText) {

            buttons.push({
                text: dismissText,
                func: function(){
                    callback(null);
                }
            });

        }

        showDialogNew({
            title: title,
            listItems: items,
            listCallback: function(tag) {
                callback(tag);
            },
            buttons: buttons
        });

    }

    function closeChat() {
        // DONT FORGET TO CHANGE THIS!!!
        chatHolder.style.width = "0px";
    }

    function isChatOpen() {
        if(chatHolder.style.width == "290px") {
            return true;
        }

        return false;
    }

    function loadChat(room,nametag,silent) {
        if(!silent) {
            silent = false;
        }

        if(!isApeAppsLoggedIn()) {
            return;
        }

        if(!chatiFrame) {
            chatiFrame = document.createElement("iframe");
            chatiFrame.seamless = true;
            chatiFrame.className = "chatIframe";
            chatHolderFrame.appendChild(chatiFrame);

            chatHolderToolbar.innerHTML = "";

            let toolbarItems = [];

            toolbarItems.push({
                type: "button",
                special: "hamburger",
                menu: {
                    items: [
                        {
                            title: "Help",
                            func: function(){
                                chatiFrame.contentWindow.postMessage({
                                    "msg": "/help",
                                    "data": "postchat"
                                },"*");
                            }
                        },
                        {
                            title: "Text Color",
                            func: function(){
                                adl.pickColor({
                                    callback: function(color){ 
                                        if(color && color.trim().length == 7) {
                                            chatiFrame.contentWindow.postMessage({
                                                "msg": "/color " + color,
                                                "data": "postchat"
                                            },"*");
                                        }
                                    } 
                                });
                            }
                        }
                    ]
                }
            });

            toolbarItems.push({
                type: "spacer"
            });

            if(window.innerWidth > 500) {
                toolbarItems.push({
                    type: "button",
                    label: "Detach",
                    icon: "resize-outline",
                    func: function(){
                        closeChat();
                        window.open("https://chat.ape-apps.com/?channel=" + currentChatRoom,"apechat","resizable,height=800,width=1280");
                    }
                });
            }

            toolbarItems.push({
                type: "button",
                label: "Dismiss",
                icon: "close-outline",
                func: function(){
                    closeChat();
                }
            });

            let chatToolbarOptions = {
                element: chatHolderToolbar,
                forceLabel: true,
                items: toolbarItems
            };

            adl.addToolbar(chatToolbarOptions);
        }

        if(room != currentChatRoom) {
            let fg = "000000";
            let forceTheme = "dark";

            if(window.useSysDefaultTheme) {
                if(isSystemThemeDark()) {
                    forceTheme = "dark";
                } else {
                    forceTheme = "light";
                }

            } else {
                if(window.lightTheme) {
                    forceTheme = "light";
                }
            }

            if(forceTheme == "dark") {
                fg = "ffffff";
            }

            currentChatRoom = room;

            if(nametag && nametag != "0" && nametag.trim().length > 1) {
                chatiFrame.src = "https://chat.ape-apps.com/?embed=1&channel=" + currentChatRoom + "&nametag=" + encodeURIComponent(nametag) + "&fh=" + fg;
            } else {
                chatiFrame.src = "https://chat.ape-apps.com/?embed=1&channel=" + currentChatRoom + "&fh=" + fg;
            }
        }
        

        if(!silent) {
            chatHolder.style.width = "290px";
        }
    }

    function sendChatMessage(message) {

        if(!isApeAppsLoggedIn()) {
            return;
        }

        if(message && message.trim().length > 0) {
            chatiFrame.contentWindow.postMessage({
                "msg": message,
                "data": "postchat"
            },"*");

        }
    }

    function openPackageListing(directory,callback) {
        if(!directory || directory.trim().length == 0) {
            directory = "/";
        }

        proxyRequest("https://www.apewebapps.com/getpackagelisting.php",function(listing){

            if(!listing || listing.trim().length == 0) {
                callback([]);
                return;
            }

            try {
                let ret = JSON.parse(listing);
                callback(ret);
            } catch(ex) {
                console.log(ex);
                callback([]);
            }
        },[
            {
                "key": "an",
                "value": window.appurlid
            },
            {
                "key": "av",
                "value": usingAppVersion
            },
            {
                "key": "sd",
                "value": directory
            }
        ],true,false);
    }

    function exportTextFileAsDialog(filename,content,isXML,callback) {
        saveFileAs(content,filename,isXML,callback);
    }

    function sendFile(filename) {
        openFile(filename,function(data){

            let mime = getMime(filename);

            let blob = new Blob([data],{ type: mime });
            shareFile(blob,filename,mime);
        }); 
    }

    function setUpApplicationWorkingFolder() {

        if(usingElectronHost) {
            BASE_DOCS = wacUtils.getMakeSpecialFolder("documents");
            appFolder = BASE_DOCS + "/" + window.appName;
            wacUtils.createDirectoryIfNotExist(appFolder);
    
            workspaceFolder = appFolder;

            if(localStorage[window.appId + "-desktop-workspace-location"] && localStorage[window.appId + "-desktop-workspace-location"].length > 1) {
                workspaceFolder = localStorage[window.appId + "-desktop-workspace-location"];
            }
    
            if(!wacUtils.doesFileExist(workspaceFolder)) {
                workspaceFolder = appFolder;
            }
    
            picsFolder = null;

            try {
                picsFolder = wacUtils.getMakeSpecialFolder("pictures") + "/" + window.appName;
                wacUtils.createDirectoryIfNotExist(picsFolder);
            } catch(ex) {
                console.log(ex);
                // one drive generally messes everything up
                picsFolder = appFolder + "/Pictures";
                wacUtils.createDirectoryIfNotExist(picsFolder);
            }
            
    
            wacUtils.deleteFile(workspaceFolder + "/Setup.exe");

            loadAppFrame();

            return;
        }

        if(window.showOpenFilePicker && navigator.storage && navigator.storage.getDirectory) {

            fileSystemAccessSupported = true;

            let transaction = db.transaction(["files"]);
            let objectStore = transaction.objectStore("files");
            let request = objectStore.get("wac_workspace_location.xxx");

            request.onerror = function(e) {
                console.log(e);
                setDefaultWorkingFolder();
            };
            request.onsuccess = function() {

                if(request.result) {
                    workspaceFolder = request.result.content;

                    workspaceFolder.queryPermission({
                        mode: "readwrite"
                    }).then(function(perm){
                        if(!perm || perm != "granted") {
                            getPermissionToUseWorkspaceFolder();
                        } else {
                            loadAppFrame();
                        }
                    });
                } else {
                    setDefaultWorkingFolder();
                }
                

            };

        

        } else {
            loadAppFrame();
        }
    }

    function setDefaultWorkingFolder() {
        navigator.storage.getDirectory().then(function(dir) {
            domainRootStorage = dir;

            domainRootStorage.getDirectoryHandle(window.appName, { create: true }).then(function(adr){
                workspaceFolder = adr;

                loadAppFrame();
            });

        });
    }

    function loadSharedCloudFile(path) {
        let cloudRequestUrl = "https://cloud.ape-apps.com:2728/" + path;
        proxyRequest(cloudRequestUrl,function(data){
            childWindowObject.webAppCore.sharedFileLoaded(data);
        },null,false);
    }
    
    function buildCloudSharePathForFile(filename) {

        if(!isApeAppsLoggedIn()) {
            return "";
        }

        let un = getApeAppsProfile().un;

        let cloudURL = "share/" + un + "/" + window.appName + "/" + encodeURIComponent(filename);
        return cloudURL;
    }

    function showPopupMenu(options) {


        for(let i = 0; i < options.items.length; i++) {
            let item = options.items[i];

            if(item.type && item.type == "separator") {
                continue;
            }

            if(item.icon) {
                item.icon = getCleanIcon(item.icon);
            }

            addPopupCallback(item,options.callback);
        }

        let title = null;

        if(options.title) {
            title = options.title;
        }

        adl.popupMenu({
            items: options.items,
            title: title
        },options.x,options.y);
    }

    function addPopupCallback(item,callback) {
        item.func = function(){
            callback(item.tag);
        };
    }

    function isRewardedVideoAvailable() {
        return true;
    }

    function getRewardedVideo() {
        youtubeVideoFinished = false;

        let requestURL = "https://market.ape-apps.com/app_resources/videoad.php";

        let params = [];
        params.push({"key":"a","value":window.appId});
        params.push({"key":"p","value":window.platformId});

        proxyRequest(requestURL,onVideoApiBack,params,false);
    }

    function onVideoApiBack(result) {
        if(!result) {
            return;
        }
        
        let resJSON = null;
        
        try {
            resJSON = JSON.parse(result);
        } catch(ex) {
            // whatevs
            resJSON = null;
        }
        
        if(resJSON == null) {
            return;
        }

        let playerWidth = "550";
        let playerHeight = "344";

        if(window.innerWidth < 670 || window.innerHeight < 490) {
            playerWidth = "288";
            playerHeight = "176";
        }

        if(window.YT && window.YT.Player) {
            setupYoutubePlayer(resJSON,playerHeight,playerWidth);
        } else {
            let ytPlayerHold = document.createElement("script");
            ytPlayerHold.onload = function() {
                setTimeout(function(){
                    setupYoutubePlayer(resJSON,playerHeight,playerWidth);
                },250);
                
            };
            ytPlayerHold.src = "https://www.youtube.com/iframe_api";
            document.head.appendChild(ytPlayerHold);
        }
    }

    function setupYoutubePlayer(resJSON,playerHeight,playerWidth) {
        if(youtubePlayer) {
            youtubePlayer.loadVideoById(resJSON.youtube);
        } else {
            if(YT) {
                let holder = document.createElement("div");
                holder.id = "youtubeVideoHolder";

                adl.showDialog({
                    title: resJSON.appname, 
                    customElement: holder,
                    buttons: [
                        {
                            text: "Dismiss",
                            func: function() {
                                finalizeYoutubePlayer();
                            }
                        },
                        {
                            text: "Download",
                            color: "#4CAF50",
                            func: function() {
                                youtubeVideoFinished = true;

                                finalizeYoutubePlayer();

                                loadURL("https://apps.ape-apps.com/" + resJSON.appname.replace(/\s+/g, "-").toLowerCase() + "/");
                            }
                        }
                    ]
                });

                setTimeout(function() {
                    youtubePlayer = new YT.Player("youtubeVideoHolder", {
                        height: playerHeight,
                        width: playerWidth,
                        videoId: resJSON.youtube,
                        events: {
                            "onReady": onYoutubePlayerReady,
                            "onStateChange": onYoutubePlayerStateChange
                        },
                        playerVars: {
                            "autoplay": 1,
                            "controls": 0,
                            "modestbranding": 1,
                            "showinfo": 0,
                            "rel": 0
                        }
                    });
                },100);

                
            } else {
                youtubePlayer = null;
            }
        }
    }

    function finalizeYoutubePlayer() {
        if(youtubePlayer && youtubePlayer.stopVideo) {
            youtubePlayer.stopVideo();
        }

        if(youtubePlayer) {
            youtubePlayer = null;
        }

        if(youtubeVideoFinished) {
            childWindowObject.webAppCore.rewardedVideoDone(true);
        } else {
            childWindowObject.webAppCore.rewardedVideoDone(false);
        }
    }

    function onYoutubePlayerReady(event) {
        event.target.playVideo();
    }

    function onYoutubePlayerStateChange(event) {
        if(event.data == 0) {
            youtubeVideoFinished = true;
        }
    }

    function fileDialogsSupported(withHeliosNag) {
        if(!withHeliosNag) {
            withHeliosNag = false;
        }

        if(window.showOpenFilePicker) {
            return true;
        } else {
            return false;
        }
    }

    function openFileDialog(type,callback,asBinary) {

        if(!Array.isArray(type)) {
            type = [type];
        }

        if(usingElectronHost) {

            let filters = [];

            for(let i = 0; i < type.length; i++) {
                let sfn = "." + type[i];

                let typeDat = fth.getFileTypeData(sfn);

                if(typeDat) {
                    filters.push({name: typeDat.display,extensions: [typeDat.ext]});
                }
            }

            let options = {
                title: "Open File",
                filters: filters,
                properties: [
                    "openFile"
                ]
            };

            wacUtils.showOpenDialog(options,function(filename,content,fullPath) {
                if(!dialogFileRefes) {
                    dialogFileRefes = {};
                }

                let id = dialogFileCounter++;

                dialogFileRefes[id] = {
                    file: fullPath
                };

                callback(filename,content,id);
            },asBinary);

            return;
        }

        if(window.showOpenFilePicker) {

            let pickerOptions = {};


            pickerOptions.types = [];
            pickerOptions.excludeAcceptAllOption = false;

            for(let i = 0; i < type.length; i++) {
                let sfn = "." + type[i];

                let useDat = {
                    description: sfn + " File",
                    accept: {
                        "*/*": [sfn]
                    }
                };

                let typeDat = fth.getFileTypeData(sfn);

                if(typeDat) {
                    pickerOptions.excludeAcceptAllOption = true;

                    if(typeDat.display) {
                        useDat.description = typeDat.display;
                    } else {
                        if(typeDat.type) {
                            useDat.description = typeDat.type;
                        }
                    }

                    if(typeDat.mime) {
                        useDat.accept = {};

                        useDat.accept[typeDat.mime] = [sfn];
                    }
                }

                pickerOptions.types.push(useDat);
            }

            window.showOpenFilePicker(pickerOptions).then(function(data){

                if(data && data.length > 0) {
                    let fileHandle = data[0];

                    fileHandle.getFile().then(function(file){
                        fileSystemFileIdCounter++;

                        let showName = file.name.replace("'","");

                        let finTypeDat = fth.getFileTypeData(showName);

                        asBinary = false;

                        if(finTypeDat && (finTypeDat.prefBinary || finTypeDat.prefArrayBuffer)) {
                            asBinary = true;
                        }

                        fileSystemFilesHolder[fileSystemFileIdCounter] = {};
                        fileSystemFilesHolder[fileSystemFileIdCounter].file = file;
                        fileSystemFilesHolder[fileSystemFileIdCounter].handle = fileHandle;
                        fileSystemFilesHolder[fileSystemFileIdCounter].binary = asBinary;

                        if(asBinary) {
                            file.arrayBuffer().then(function(content){
                                callback(showName, content, fileSystemFileIdCounter);
                            });
                        } else {
                            file.text().then(function(content){
                                callback(showName, content, fileSystemFileIdCounter);
                            });
                        }
                        
                    });
                } else {
                    callback(false);
                }
            });
        } else {
            callback(false);
        }
    }

    function filesystemFileSave(data,fileId,callback) {

        if(usingElectronHost) {

            if(!dialogFileRefes) {
                callback(false);
                return;
            }
    
            if(!dialogFileRefes[fileId]) {
                callback(false);
                return;
            }
    
            let filePath = dialogFileRefes[fileId].file;
    
            if(!filePath) {
                callback(false);
                return;
            }
    
            let enc = "utf8";
            let binaryData;
            
            if(filePath.endsWith(".png")) {
                enc = "binary";
                
                let base64Data  = data.replace(/^data:image\/png;base64,/, "");
                base64Data += base64Data.replace("+", " ");
                binaryData = wacUtils.getBinaryData(base64Data,"base64");
                
                data = binaryData;
            }
            
            if(filePath.endsWith(".pdf")) {
                enc = "binary";
                
                binaryData = wacUtils.getBinaryData(data,"base64");
                
                data = binaryData;
            }
      
            wacUtils.writeFile(filePath,data,enc);
    
            callback(fileId);

            return;
        }

        if(window.showOpenFilePicker) {
            if(fileSystemFilesHolder[fileSystemFileIdCounter] && fileSystemFilesHolder[fileSystemFileIdCounter].handle) {
                let holder = fileSystemFilesHolder[fileSystemFileIdCounter];
                let handle = holder.handle;

                doNativeHandleSave(fileId,handle,data,callback);
            } else {
                callback(false);
            }
        } else {
            callback(false);
        }
    }

    async function doNativeHandleSave(fileId,handle,data,callback) {


        await writeToFileHandle(handle, data);

        let name = null;

        if(handle && handle.name) {
            name = handle.name;
        }

        if(callback) {
            callback(fileId,name);
        }

    }

    async function writeToFileHandle(handle, data) {
        const writable = await handle.createWritable();
        await writable.write(data);
        await writable.close();
    }

    function notifySaveImportDataDone(result) {
        if(filesystemFileSaveCallback) {
            filesystemFileSaveCallback(result);
        }
    }

    function onChatMessageReciever(dataString) {

        let obj = JSON.parse(dataString);

        if(childWindowObject && childWindowObject.webAppCore && childWindowObject.webAppCore.onChatMessage) {
            childWindowObject.webAppCore.onChatMessage(obj);
        }
    }

    function supportsTransparency() {
        // it may be, that in the coming years, browser windows will support this
        return false;
    }

    function notifyWindowsStorePremiumStatus(status,premiumPlatformCode) {

        let statusData = buildAccountsAPICreds("4");

        if(!statusData) {
            return;
        }

        statusData.push({
            "key": "lp",
            "value": "4"
        });

        statusData.push({
            "key": "returl",
            "value": "app"
        });

        statusData.push({
            "key": "ls",
            "value": status
        });

        proxyRequest(APE_ACCOUNTS_API,null,statusData,true,true);
    }

    function addFriend(username) {

        let statusData = buildAccountsAPICreds("8");

        if(!statusData) {
            return;
        }

        statusData.push({
            "key": "un",
            "value": username
        });

        proxyRequest(APE_ACCOUNTS_API,onFriendAddedCallback,statusData,true,true);
    }

    function onFriendAddedCallback(data) {

        if(childWindowObject && childWindowObject.webAppCore && childWindowObject.webAppCore.onFriendAdded) {
            if(data) {
                childWindowObject.webAppCore.onFriendAdded(data);
            } else {
                childWindowObject.webAppCore.onFriendAdded("-1");
            }
        }
    }

    function createShortcut(title,launchParm,icon,desc) {

        let shortcutName = window.appId + "-shortcuts";

        let shortcutObject = {
            title: title,
            parm: launchParm,
            icon: getCleanIcon(icon),
            desc: desc
        };

        let killCuts = [];

        for(let i = 0; i < shortcutData.length; i++) {
            let sc = shortcutData[i];

            if(sc.parm == shortcutObject.parm) {
                killCuts.push(sc);
            }
        }

        while(killCuts.length > 0) {
            removeFromArray(shortcutData,killCuts.pop(),false);
        }

        shortcutData.push(shortcutObject);

        let rawSC = JSON.stringify(shortcutData);
        let enc = btoa(rawSC);
        setCookie(shortcutName,enc,999);
    }

    function getCookie(cname) {
        let name = cname + "=";
        let decodedCookie = decodeURIComponent(document.cookie);
        let ca = decodedCookie.split(";");

        for(let i = 0; i <ca.length; i++) {
            let c = ca[i];

            while (c.charAt(0) == " ") {
                c = c.substring(1);
            }

            if (c.indexOf(name) == 0) {
                return c.substring(name.length, c.length);
            }
        }

        return "";
    }

    function setCookie(cname, cvalue, exdays) {
        let d = new Date();
        d.setTime(d.getTime() + (exdays*24*60*60*1000));

        let expires = "expires="+ d.toUTCString();
        document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
    }

    function removeFromArray(arr, item, showarray) {

        if(arr == null || item == null) {
            if(showarray !== undefined) {
                console.log("COULDNT remove from: " + showarray);
            }
            return;
        }

        var wasRemoved = false;

        for(var i = arr.length; i--;) {
            if(arr[i] === item) {
                arr.splice(i, 1);
                wasRemoved = true;
            }
        }

        return wasRemoved;
    }

    function onSetBackFunction(fun) {
        
    }

    function negotiateKeyContent(contents) {

        if(getIsPremium()) {
            showDialog("Activation Error","This product has already been activated.  There is no need to use your license key right now.  Each time you activate your product using this key, you use up one of your activation allowances!");
            return;
        }

        let keyContent = null;
        let keyObject = null;

        try {
            keyContent = atob(contents);
            keyObject = JSON.parse(keyContent);
        } catch(e) {
            showDialog("License Key Error","Unable to read this license key file.  It may have become damaged or corrupt.");
            return;
        }

        if (keyObject.appid != window.appId) {
            showDialog("Incorrect License Key","This license key is not for " + window.appName + ".  Ape Market License Keys can only be used with the product that they were generated for.");
            return;
        }
        
        let requestURL = "https://market.ape-apps.com/app_resources/activate_product.php";

        let params = [];
        params.push({"key":"id","value":keyObject.appid});
        params.push({"key":"key","value":keyObject.keyid});
        
        if(keyObject.license) {
            params.push({"key":"license","value":keyObject.license});
        } else {
            if(keyObject.vlicense) {
                requestURL = "https://market.ape-apps.com/app_resources/volume_activation.php";
                params.push({"key":"license","value":keyObject.vlicense});
                params.push({"key":"phrase","value":keyObject.phraseid});
            }
        }

        proxyRequest(requestURL,keyActivationCallback,params,true);
    }

    function finalActivationCheck() {
        if(isSteam && usingElectronHost) {
            notifyWindowsStorePremiumStatus(true,"6");
        }

        if(isWindowsStore && window.awauwppo) {
            notifyWindowsStorePremiumStatus(true,"4");
        }
    }

    function keyActivationCallback(data) {
        if(data == "success") {
            showDialog("Activation Successful!","Your product has been activated.  Thank you for your support!");
            notifyPremiumActivation(true);

            if(isApeAppsLoggedIn() && navigator.onLine) {
                notifyWindowsStorePremiumStatus(true,"1");
            }
        } else {
            showDialog("Activation Error",data);
        }
    }

    function getMime(filename) {

        if(filename.toLowerCase().endsWith(".png")) {
            return "image/png";
        }

        if(filename.toLowerCase().endsWith(".nnp")) {
            return "application/noteastic";
        }

        if(filename.toLowerCase().endsWith(".vpp")) {
            return "application/voxel-paint";
        }

        if(filename.toLowerCase().endsWith(".m2m")) {
            return "application/my-business-empire-2-map";
        }

        if(filename.toLowerCase().endsWith(".m2s")) {
            return "application/my-business-empire-2-save";
        }
        
        if(filename.toLowerCase().endsWith(".ppp")) {
            return "application/pixel-paint";
        }
        
        if(filename.toLowerCase().endsWith(".fpp")) {
            return "application/finger-paint";
        }

        if(filename.toLowerCase().endsWith(".vox")) {
            return "image/voxel";
        }

        if(filename.toLowerCase().endsWith(".gb")) {
            return "application/gameboy-rom";
        }

        if(filename.toLowerCase().endsWith(".gb")) {
            return "application/gameboy-color-rom";
        }

        if(filename.toLowerCase().endsWith(".act")) {
            return "application/ape-city";
        }

        if(filename.toLowerCase().endsWith(".ezl")) {
            return "application/ez-letter-maker";
        }

        if(filename.toLowerCase().endsWith(".vcf")) {
            return "text/x-vcard";
        }

        if(filename.toLowerCase().endsWith(".ezo")) {
            return "application/ez-outliner";
        }

        if(filename.toLowerCase().endsWith(".mks")) {
            return "application/musical-kittens";
        }

        if(filename.toLowerCase().endsWith(".mlx")) {
            return "application/my-land";
        }

        if(filename.toLowerCase().endsWith(".pds")) {
            return "application/pdf-document-scanner";
        }

        if(filename.toLowerCase().endsWith(".ezs")) {
            return "application/ez-sheets";
        }

        if(filename.toLowerCase().endsWith(".ezr")) {
            return "application/ez-register";
        }

        if(filename.toLowerCase().endsWith(".c2d")) {
            return "application/colony2-engine";
        }

        if(filename.toLowerCase().endsWith(".c2m")) {
            return "application/colony2-engine";
        }

        if(filename.toLowerCase().endsWith(".pdf")) {
            return "application/pdf";
        }

        return "text/plain";
    }

    function getFileAppTypeReadable(filename) {

        if(filename.toLowerCase().endsWith(".pdf")) {
            return "Portable Document Format";
        }

        if(filename.toLowerCase().endsWith(".png")) {
            return "Portable Network Graphic";
        }

        if(filename.toLowerCase().endsWith(".nnp")) {
            return "Noteastic";
        }

        if(filename.toLowerCase().endsWith(".vpp")) {
            return "Voxel Paint";
        }

        if(filename.toLowerCase().endsWith(".m2s")) {
            return "My Business Empire 2";
        }

        if(filename.toLowerCase().endsWith(".m2m")) {
            return "My Business Empire 2";
        }
        
        if(filename.toLowerCase().endsWith(".ppp")) {
            return "Pixel Paint";
        }

        if(filename.toLowerCase().endsWith(".vox")) {
            return "MagicaVoxel";
        }

        if(filename.toLowerCase().endsWith(".gb")) {
            return "Gameboy ROM";
        }

        if(filename.toLowerCase().endsWith(".gbc")) {
            return "Gameboy Color ROM";
        }

        if(filename.toLowerCase().endsWith(".act")) {
            return "Ape City";
        }

        if(filename.toLowerCase().endsWith(".ezl")) {
            return "EZ Letter Maker";
        }

        if(filename.toLowerCase().endsWith(".vcf")) {
            return "vCard";
        }

        if(filename.toLowerCase().endsWith(".ezo")) {
            return "EZ Outliner";
        }

        if(filename.toLowerCase().endsWith(".mks")) {
            return "Musical Kittens";
        }

        if(filename.toLowerCase().endsWith(".mlx")) {
            return "My Land";
        }

        if(filename.toLowerCase().endsWith(".pds")) {
            return "PDF Document Scanner";
        }

        if(filename.toLowerCase().endsWith(".ezs")) {
            return "EZ Sheets";
        }

        if(filename.toLowerCase().endsWith(".ezr")) {
            return "EZ Register";
        }

        if(filename.toLowerCase().endsWith(".c2d")) {
            return "Colony 2 Engine Data";
        }
        
        if(filename.toLowerCase().endsWith(".fpp")) {
            return "Finger Paint";
        }

        if(filename.toLowerCase().endsWith(".c2m")) {
            return "Colony 2 Engine Total Conversion";
        }

        return "Text";
    }

    function copyToClipboard(text) {
        if(navigator.clipboard) {
            navigator.clipboard.writeText(text);
        } else {
            let copyDiv = document.createElement("pre");
            copyDiv.contentEditable = true;
            document.body.appendChild(copyDiv);
            copyDiv.innerHTML = text;
            copyDiv.unselectable = "off";
            copyDiv.focus();
            document.execCommand("SelectAll");
            document.execCommand("Copy", false, null);
            document.body.removeChild(copyDiv);
        }
    }

    function getDailyTopScore(mode) {
        if(!window.scoreServerId || window.scoreServerId == "0") {
            return;
        }
        
        let scoreData = [];
        scoreData[scoreData.length] = {key:"g",value:window.scoreServerId};
        scoreData[scoreData.length] = {key:"m",value:mode};
        
        proxyRequest("https://www.ape-apps.com/highscores/app_resource.php?f=2",processTopScoreResult,scoreData,true);
    }

    function processTopScoreResult(data) {
        if (data.indexOf("###") == -1) {
            return;
        }
        
        let topScore = {};
        topScore.player = data.split("###")[0];
        topScore.score = data.split("###")[1];

        childWindowObject.webAppCore.setDailyReadout(topScore);
    }

    function getNewsfeed(output) {
        if(!output) {
            return;
        }
        
        proxyRequest("https://market.ape-apps.com/app_resources/title-newsfeed.php",function(data){
            if(!data) {
                return;
            }
            
            if(!output || output == null) {
                return;
            }
    
            if (data.indexOf("###") == -1) {
                output.innerHTML = data;
            } else {
                let newsParts = [];
                newsParts = data.split("###");
                output.innerHTML = newsParts[0];
                output.addEventListener("click",function(event) {
                    event.preventDefault();
                    loadURL(newsParts[1]);
                },false);
                output.style.cursor = "pointer";
            }
    
            setTimeout(function() {
                if(output && output.start) {
                    output.start();
                }
                output = null;
            },1000);
        },null,false);
    }

    function changeWorkingDirectory() {
        window.showDirectoryPicker().then(function(handle){
            db.transaction(["files"], "readwrite").objectStore("files").put({ name: "wac_workspace_location.xxx", content: handle });

            workspaceFolder = handle;
        });
    }

    function workspaceChangeCallback(success) {
        if(success) {
            childWindowObject.webAppCore.workspaceChanged(true);
        } else {
            childWindowObject.webAppCore.workspaceChanged(false);
        }
    }

    async function getLaunchFileName() {

        let filename = null;

        if(launchFile) {
            filename = launchFile.name;
        }

        if(childWindowObject.webAppCore.launchNameCallback) {
            childWindowObject.webAppCore.launchNameCallback(filename);
        }
    }

    async function getLaunchFileContent() {

        if(!launchFile) {
            if(childWindowObject.webAppCore.launchContentCallback) {
                childWindowObject.webAppCore.launchContentCallback(null);
            }

            return;
        }

        // .text might not be the right option based on file type!
        // check and handle other methods at some point (such as gameboy games?)

        let rawFile = await launchFile.getFile();
        let content = await rawFile.text();
        let name = rawFile.name;

        let typeinfo = fth.getFileTypeData(name);

        if(childWindowObject.webAppCore.launchContentCallback) {
            childWindowObject.webAppCore.launchContentCallback(content);
        }
    }

    async function openLaunchFileLicense() {

        if(!launchFile) {
            return;
        }

        const file = await launchFile.getFile();
        const contents = await file.text();

        negotiateKeyContent(contents);
    }

    async function saveLaunchFile(content) {

        if(!launchFile) {
            return;
        }

        let options = {
            mode: "readwrite"
        };

        if ((await launchFile.queryPermission(options)) === 'granted') {
            writeToFileHandle(launchFile, content);
        } else {
            if ((await launchFile.requestPermission(options)) === 'granted') {
                writeToFileHandle(launchFile, content);
            }
        }
    }

    function awaBridgeExtReciever(data) {}

    function isAWABridgeExtConnected() {
        return false;
    }

    function toastWacBack() {
        childWindowObject.webAppCore.toastBack();
    }

    function fadeTitle(color) {
        opacitySetting = 0;

        introFader.style.backgroundColor = color;
        introFader.style.opacity = 0;
        introFader.style.display = "block";

        doFade();
    }

    function doFade() {
        
        opacitySetting += 0.05;
        
        introFader.style.opacity = opacitySetting;

        if(opacitySetting >= 1) {
            if(childWindowObject.webAppCore.titleFadeCallback != null) {
                childWindowObject.webAppCore.titleFadeCallback();
            }
            
            introFader.style.display = "none";
        } else {
            setTimeout(doFade,50);
        }
    }

    function setRingtone(filename) {
        // nothing on other platforms, doubtful there ever will be :-/
    }

    function updateApeAppsAvatar(dataurl,callback) {
        if(isApeAppsLoggedIn()) {
            let uploadurl = "https://accounts.ape-apps.com/avatarfromdataurl.php";

            let data = [];

            data.push({
                "key": "picdat",
                "value": dataurl
            });

            data.push({
                "key": "sa",
                "value": apeAppsLoginData.creds.sida
            });

            data.push({
                "key": "sb",
                "value": apeAppsLoginData.creds.sidb
            });

            data.push({
                "key": "sc",
                "value": apeAppsLoginData.creds.sidc
            });

            proxyRequest(uploadurl,callback,data,true,false);
        } else {
            return callback("fail");
        }
    }

    function getAppName() {
        return window.appName;
    }

    function showTryBetaInfo() {
        let betaUrl = "https://www.apewebapps.com/" + window.appurlid + "/beta/";

        showDialogNew({
            title: "Beta Available",
            message: "There is a beta version of " + window.appName + " currently in development and ready for testing.  Beta releases contain the latest features and improvements but may be buggy and unstable.  Do you wish to try " + window.appName + " Beta?",
            buttons: [
                {
                    text: "Try Beta",
                    func: function(){
                        
                        loadURL(betaUrl);
                        
                    }
                },
                {
                    text: "Nevermind"
                }
            ]
        });
    }

    function gooseUpServiceWorker() {
        if(getIsBetaMode()) {
            continueInit();
            return;
        }

        let serviceWorkerUrl = "sw.js";

        navigator.serviceWorker.register(serviceWorkerUrl).then(function(reg) {
            continueInit();
        }).catch(function(err) {
            continueInit();
        });
    }

    function presentFeedback() {
        adl.inputBox({
            title: "Send Feedback",
            message: window.appName + " is developed with you in mind, and I am always looking for ways to expand and improve the app.  Please leave me a suggestion/feedback/tip/bug report in the box below.  Updates are made based on user suggestions, and your feedback goes straight to my inbox and will help me make improvements to " + window.appName + " in the coming releases!",
            multiline: true,
            password: false,
            placeholder: "Enter feedback here!",
            func: function(result) { 
                if(result && result.trim().length > 0) {
                    let feedbackText = result.trim();

                    let ape = "";

                    if(isApeAppsLoggedIn()) {
                        ape = window.getApeAppsProfile().un;
                    }

                    let device = navigator.appVersion;

                    device = "awa";

                    let feedbackData = [
                        { key: "a", value: window.appId },
                        { key: "p", value: window.statPlatform },
                        { key: "c", value: feedbackText },
                        { key: "v", value: usingAppVersion },
                        { key: "d", value: device },
                        { key: "ape", value: ape }
                    ];

                    proxyRequest("https://market.ape-apps.com/app_resources/feedback.php",function(){
                        showToast("Thank you for your feedback!");
                    },feedbackData,true,false);
                }
            }
        });
    }

    function presentAbout() {
        let data = [];

        data.push({
            "key": "f",
            "value": "3"
        });

        data.push({
            "key": "un",
            "value": "bastecklein"
        });

        proxyRequest("https://tokens.ape-apps.com/api.php",finishABoutPresentation,data,false,false);
    }

    function finishABoutPresentation(data) {
        let resultObject = null;

        try {
            let tmpResult = JSON.parse(data);
            resultObject = tmpResult[0];
        } catch(ex) {
            console.log(ex);
            resultObject = null;
        }

        if(resultObject == null) {
            return;
        }

        let urlBuild = "https://tokens.ape-apps.com/gettoken.php?s=72&f=37&p=" + resultObject.token;


        let aboutData = [];

        aboutData.push({
            "title": window.appName + " v" + usingAppVersion,
            "icon": window.shareImage,
            "description": window.appDescription,
            "tag": "metainfo"
        });

        aboutData.push({
            "title": "Privacy Policy",
            "icon": "https://www.apewebapps.com/images/privacy.png",
            "description": "Ape Apps cares about your privacy.  Read the " + window.appName + " Privacy Policy online.",
            "status": "https://apps.ape-apps.com/" + window.appurlid + "/privacy.html",
            "statusColor": "#1976D2",
            "tag": "privacy"
        });

        aboutData.push({
            "title": "Developer",
            "icon": urlBuild,
            "description": window.appName + " was created and developed by Brandon Stecklein, doing business as Ape Apps.  Operating out of eastern Kansas, Brandon founded Ape Apps in the summer of 2010 and has been running the business full-time since 2012.  He has published games and apps for most major platforms, including Android, iOS, Windows, Mac, Linux, and the Web.",
            "tag": "developer"
        });

        aboutData.push({
            "title": "Support Community",
            "icon": "https://www.apewebapps.com/images/ape-apps-128.png",
            "description": "Ape Apps operates a global support forum community where you can get answers to many issues and questions you may have, or share your knowledge with others!  Check it out!",
            "status": "https://www.ape-apps.com",
            "statusColor": "#1976D2",
            "tag": "supportforum"
        });

        if(!isSteam && !isWindowsStore) {
            aboutData.push({
                "title": "Become a Patron",
                "icon": "https://www.apewebapps.com/images/Patreon_Mark_Primary.png",
                "description": "If you like what Ape Apps is doing and would like to see more awesome entertainment like " + window.appName + ", consider becoming a patron with Patreon!",
                "status": "https://www.patreon.com/apeapps",
                "statusColor": "#F96855",
                "tag": "patreon"
            });
        }
       

        aboutData.push({
            "title": "Follow Ape Apps",
            "icon": "https://www.apewebapps.com/images/twitter.png",
            "description": "Follow Ape Apps on Twitter.  You know you want to!",
            "status": "@apeapps",
            "statusColor": "#03A9F4",
            "tag": "twitter"
        });

        aboutData.push({
            "title": "Follow the Developer",
            "icon": "https://www.apewebapps.com/images/twitter.png",
            "description": "Why not also follow the developer himself?  All the cool kids have Brandon Stecklein on their twitter.",
            "status": "@bastecklein",
            "status-color": "#03A9F4",
            "tag": "twitterdev"
        });

        aboutData.push({
            "title": "Watch Ape Apps",
            "icon": "https://www.apewebapps.com/images/youtube.png",
            "description": "Keep up with the best videos on the entire internet by subscribing to the Ape Apps channel on YouTube!",
            "status": "https://www.youtube.com/user/apeapps",
            "statusColor": "#F44336",
            "tag": "youtube"
        });

        aboutData.push({
            "title": "Cooperative Gameplay",
            "icon": "https://www.apewebapps.com/images/cg-ty-lionk.png",
            "description": "Follow the Dev's gaming YouTube channel, featuring his family and other guests.",
            "status": "https://www.youtube.com/channel/UCvyCNummQyRDN-dTMBaDJGQ",
            "statusColor": "#F44336",
            "tag": "copgameplay"
        });

        aboutData.push({
            "title": "Watch the Developer",
            "icon": "https://www.apewebapps.com/images/twitch-logo.png",
            "description": "Watch and follow the developer on Twitch!  Always family friendly.",
            "status": "https://www.twitch.tv/bastecklein",
            "statusColor": "#6441a5",
            "tag": "twitch"
        });

        aboutData.push({
            "title": "Contact",
            "icon": "https://www.apewebapps.com/images/ind-mail.png",
            "description": "If you want to contact the developer the old fashioned way, you can do that too.  Send your awesome letters to the following address.  If you leave a return address, you will get a reply too!<br /><br /><strong style=\"text-align:center;\">Attn: Brandon Stecklein<br />Ape Apps<br />PO Box 30631<br />Columbia, MO 65205<br />United States of America</strong>",
            "tag": "snailmail"
        });

        if(!isSteam && !isWindowsStore) {
            aboutData.push({
                "title": "Donation",
                "icon": "https://www.apewebapps.com/images/ind-money.png",
                "description": "If you want to see more glorious low cost entertainment from Ape Apps in the future, consider making a small donation to support future development!",
                "tag": "donation"
            });
        }

        if("serviceWorker" in navigator) {
            aboutData.push({
                "title": "Clear App Cache",
                "icon": "https://www.apewebapps.com/images/ape-cache.png",
                "description": "Unregister the " + window.appName + " service worker and refresh app data from the server on next load.  May help resolve some issues if you have some bad or incomplete data cached.",
                "tag": "clearcache"
            });
        }
        
        let flyoutPosition = "right";

        // bottom position on iOS
        if(window.innerWidth < 500 && window.platformId == "4") {
            flyoutPosition = "bottom";
        }

        adl.showList({
            title: "About " + window.appName,
            options: aboutData,
            onSelection: function(tag) {
                if(tag) {
                    onAboutItemSelected(tag);
                }
                
            },
            flyout: {
                position: flyoutPosition
            }
        });
        
    }

    function doClearAppCache() {
        if("serviceWorker" in navigator) {
            navigator.serviceWorker.getRegistrations().then(function(registrations) {
                for(let registration of registrations) {

                    let scope = registration.scope;

                    if(scope == "https://www.apewebapps.com" || scope == "https://www.apewebapps.com/" || scope == "https://www.apewebapps.com/" + window.appurlid + "/") {
                        registration.unregister();
                    }

                }

                showDialog(
                    "Clear App Cache",
                    "Service worker cache has been cleared.  Would you like to reload the application now?",
                    {"text":"Yes","func":function(){
                        window.location.reload(true);
                    }},{"text":"No","func":null}
                );
            });
        }
    }

    function onAboutItemSelected(tag) {
        if(!tag) {
            return;
        }

        if(tag == "clearcache") {
            doClearAppCache();
        }

        if(tag == "metainfo") {
            loadURL(window.shareURL);
        }

        if(tag == "privacy") {
            loadURL("https://apps.ape-apps.com/" + window.appurlid + "/privacy.html");
        }

        if(tag == "patreon") {
            loadURL("https://www.patreon.com/apeapps");
        }

        if(tag == "supportforum") {
            loadURL("https://www.ape-apps.com/viewpage.php?p=4");
        }

        if(tag == "developer") {
            loadURL("https://www.ape-apps.com/profile.php?u=bastecklein");
        }

        if(tag == "twitter") {
            loadURL("https://twitter.com/apeapps");
        }

        if(tag == "twitterdev") {
            loadURL("https://twitter.com/bastecklein");
        }

        if(tag == "youtube") {
            loadURL("https://www.youtube.com/user/apeapps");
        }

        if(tag == "copgameplay") {
            loadURL("https://www.youtube.com/channel/UCvyCNummQyRDN-dTMBaDJGQ");
        }

        if(tag == "snailmail") {
            loadURL("http://www.mailaletter.com");
        }

        if(tag == "donation") {
            loadURL("https://www.ape-apps.com/donation/");
        }
        
        if(tag == "twitch") {
            loadURL("https://www.twitch.tv/bastecklein");
        }
    }

    function getAppUrlId() {
        return window.appurlid;
    }

    function attemptAPISignIn(username,password) {
        var formData = new FormData();
        formData.append("f","3");
        formData.append("ak",window.appLoginId);
        formData.append("apikey",window.appLoginId);
        formData.append("returl","api");
        formData.append("am", window.appId);
        formData.append("plat", window.statPlatform);
        formData.append("r", btoa(username));
        formData.append("x", btoa(password));

        let apiURL = APE_ACCOUNTS_API;

        let xhr = new XMLHttpRequest();
        xhr.open("post",apiURL,true);

        xhr.addEventListener("load",function(e) {
            let resData = e.target.responseText;

            console.log(resData);

            try {
                let resObj = JSON.parse(resData);

                console.log(resObj);

                if(resObj.success) {

                    if (window.PasswordCredential) {
                        let cred = new PasswordCredential({
                            id: username,
                            password: password,
                            name: "Ape Apps Account",
                            iconURL: "https://accounts.ape-apps.com/getavatar.php?u=" + username
                        });
                        navigator.credentials.store(cred);
                    }

                    apeAppsLoginData.creds = {
                        sida: resObj.sida,
                        sidb: resObj.sidb,
                        sidc: window.appLoginId,
                        app: window.appId
                    };

                    isAutoLoginAttempt = false;
                    submitLoginData(resObj.sida,resObj.sidb,window.appLoginId,window.appId);
                }


            } catch(ex) {
                console.log(ex);
            }
        });

        xhr.send(formData);

        console.log("sent");
    }

    function attemptCredentialSignin() {
        if(window.PasswordCredential) {
            navigator.credentials.get({
                password: true,
                mediation: "optional"
            }).then(function(res){
                if(res && res.id && res.password) {
                    attemptAPISignIn(res.id,res.password);
                } else {
                    signOutOfApeApps();
                }
            });
        } else {
            signOutOfApeApps();
        }
    }

    function initAdvertising() {
        if(getIsPremium() || isTV() || (isSteam && usingElectronHost)) {
            return;
        }

        if(isSoundboardCity || window.awadb) {
            return;
        }

        if(usingElectronHost) {

            let bahHolder = document.createElement("script");
            bahHolder.onload = function(){

                setTimeout(function(){

                    if(!window.bannerAdHelper || getIsPremium()) {
                        return;
                    }

                    bah = new bannerAdHelper(adHolder, window.appId, window.statPlatform);

                    bah.showBanner();
                },100);
            };
            bahHolder.src = "/js/banneradhelper/" + window.bannerAdHelperVersion + "/banneradhelper.js";
            document.head.appendChild(bahHolder);

            return;
        }

        let adsenseHolder = document.createElement("script");
        adsenseHolder.onload = function(){

            setTimeout(function() {

                let bahHolder = document.createElement("script");
                bahHolder.onload = function(){

                    setTimeout(function(){

                        if(!window.bannerAdHelper || getIsPremium()) {
                            return;
                        }

                        bah = new bannerAdHelper(adHolder, window.appId);

                        bah.setAdsenseInfo("ca-pub-2626348133280455","5148829467","8102295867","5426090664");

                        bah.showBanner();
                    },100);
                };
                bahHolder.src = "/js/banneradhelper/" + window.bannerAdHelperVersion + "/banneradhelper.js";
                document.head.appendChild(bahHolder);



                
            },100);
        };
        adsenseHolder.src = "https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js";
        document.head.appendChild(adsenseHolder);
    }

    function getApeCoinPremiumOption() {
        if(!isApeAppsLoggedIn()) {
            return null;
        }

        let coinCost = Math.round(parseFloat(window.upgradePrice) * 100);

        let balColor = "#43A047";

        let bal = getApeCoinBalance();

        if(coinCost > bal) {
            balColor = "#F44336";
        }

        let coinOpt = {
            title: "Ape Coins",
            description: "Upgrade " + window.appName + " for " + numberWithCommas(coinCost) + " Ape Coins using your available Ape Coin balance.",
            tag: "apecoinpayment",
            icon: "https://www.apewebapps.com/images/ape-coin.png",
            status: "Balance: " + numberWithCommas(bal),
            statusColor: balColor
        };

        return coinOpt;
    }

    function showPaymentMenu() {
        if(isApeAppsLoggedIn() && navigator.onLine) {

            let apeCoinOption = getApeCoinPremiumOption();

            let premiumOptions = [];

            if(apeCoinOption && apeCoinOption.statusColor == "#43A047") {
                premiumOptions.push(apeCoinOption);
            }

            if(paymentRequest && paymentRequest.canMakePayment()) {
                premiumOptions.push({
                    title: "Secure Payment",
                    description: "Upgrade " + window.appName + " using your credit or debit card, or by using Apple or Google Pay (where available).  Transactions are handled securely by Stripe and your information is NOT stored by Ape Apps.  Your purchase will be added to your Ape Apps Account and will work across all devices you sign in to.",
                    tag: "webpayment",
                    icon: "https://www.apewebapps.com/images/creditcard-flat.png"
                });
            } else {
                premiumOptions.push({
                    title: "Credit Card",
                    description: "Upgrade " + window.appName + " using your credit or debit card.  Transactions are handled securely by Stripe and your information is NOT stored by Ape Apps.  Your purchase will be added to your Ape Apps Account and will work across all devices you sign in to.",
                    tag: "stripe",
                    icon: "https://www.apewebapps.com/images/creditcard-flat.png"
                });
            }

            

            premiumOptions.push({
                title: "PayPal",
                description: "Upgrade " + window.appName + " using PayPal, the fast and secure way to pay money online.  Your purchase will be added to your Ape Apps Account and will work across all devices you sign in to.",
                tag: "paypal",
                icon: "https://www.apewebapps.com/images/paypal.png"
            });

            premiumOptions.push({
                title: "Mail Order",
                description: "Upgrade " + window.appName + " the old fashioned way by printing the order form and sending cash.  U.S. currency only.  Please allow one business week for your product to activate.",
                tag: "cash",
                icon: "https://www.apewebapps.com/images/paycash.png"
            });

            premiumOptions.push({
                title: "License Key Activation",
                description: "If you have previously purchased " + window.appName + " and have an Ape Market License Key (*.amk) or an Ape Market Volume License key (*.avk) file, you can upload it now to activate your premium upgrade.",
                tag: "license",
                icon: "https://www.apewebapps.com/images/am.png"
            });

            if(apeCoinOption && apeCoinOption.statusColor == "#F44336") {
                premiumOptions.push(apeCoinOption);
            }


            adl.showList({
                title: premiumOptionText + " $" + window.upgradePrice,
                flyout: {
                    position: "right"
                },
                options: premiumOptions,
                onSelection: onUpgradeOptionSelected
            });



            //presentNativeList(premiumOptionText + " $" + window.upgradePrice, premiumOptions, "Nevermind", onUpgradeOptionSelected);
            
        } else {
            showDialog(premiumOptionText,"Upgrading " + window.appName + " requires an authentic license key file from Ape Apps.  Do you already have a " + window.appName + " License Key (.amk file)?",{"text":"Yes","func":browseForKey},{"text":"No","func":initNonActPurchase});
        }
    }

    function initNonActPurchase() {
        let urlName = replaceAll(window.appName," ","-").toLowerCase();
        let goURL = "https://apps.ape-apps.com/" + urlName + "/purchase.html";

        loadURL(goURL);
    }

    function onUpgradeOptionSelected(tag) {
        if(!tag) {
            return;
        }

        if(tag == "apecoinpayment") {
            buyPremiumWithApeCoins();
        }

        if(tag == "webpayment") {
            showPaymentRequestAPIUpgrade();
        }

        if(tag == "license") {
            browseForKey();
        }

        if(tag == "paypal") {
            let creds = getApeAppsCreds();
            let iapURL = "https://apps.ape-apps.com/purchaseact.php?id=" + window.appId + "&a=" + creds.a + "&b=" + creds.b + "&c=" + creds.c;
            iapWindow = window.open(iapURL,"purchase","height=620,width=500,status=no,titlebar=no,modal=yes,resizable=yes,menubar=no,frame=true");
        }

        if(tag == "cash") {
            let orderFormURL = "https://apps.ape-apps.com/orderform.php?id=" + window.appId;

            if(isApeAppsLoggedIn()) {
                orderFormURL += ("&user=" + getApeAppsProfile().un);
            }

            window.open(orderFormURL,"cashform","height=560,width=640,status=no,titlebar=no,modal=yes,resizable=yes,menubar=no,frame=true");
        }

        if(tag == "stripe") {
            launchTraditionalStripePayment();
        }
    }

    function launchTraditionalStripePayment() {
        let creds2 = getApeAppsCreds();
        let iapURL2 = "https://apps.ape-apps.com/awastripepurchase.php?id=" + window.appId + "&a=" + creds2.a + "&b=" + creds2.b + "&c=" + creds2.c;
        iapWindow = window.open(iapURL2,"purchasestripe","height=740,width=1020,status=no,titlebar=no,modal=yes,resizable=yes,menubar=no,frame=true");
    }

    function showPaymentRequestAPIUpgrade() {

        if(!paymentRequest) {
            launchTraditionalStripePayment();
            return;
        }

        paymentRequest.canMakePayment().then(function(result) {
            if(result) {
                paymentRequest.show();
            } else {
                launchTraditionalStripePayment();
            }
        });
    }

    function browseForKey() {

        importLocalFile("amk, .avk",false,function(name,data) {
            if(!name) {
                showToast("Unable to open file!");
                return;
            }

            negotiateKeyContent(data);
        },false);

    }

    function onPWAInstalledToHomescreen() {
        ah.trackEvent("Ape Web App", "Installed to Homescreen", window.platformOS, true);
    }

    function trackEventDefered(cat,act,lbl,global) {
        setTimeout(function(){
            if(!ah) {
                return;
            }

            ah.trackEvent(cat, act, lbl, global);
        },5000);
    }

    function onBeforeInstallPrompt(e) {
        deferredPrompt = e;
    }

    function doInstallNag() {

        if(!deferredPrompt) {
            return;
        }

        adl.showDialog({
            title: "Install " + window.appName,
            message: "Did you know that you can install the " + window.appName + " Progressive Web App (PWA) to your homescreen?  Installing does not require any additional downloads or space, and it gives " + window.appName + " it's own standalone window.  It also allows " + window.appName + " more storage space to save your content and better integration with your device, for a more native app-like feel.  Do you want to give it a try?",
            icon: "bulb-outline",
            iconColor: "#FFCA28",
            buttons: [
                {
                    text: "Install",
                    color: "#4CAF50",
                    func: function() {
                        ah.trackEvent("PWA Install Nag", "Accepted", window.appName, true);
                        deferredPrompt.prompt();
                    }
                },
                {
                    text: "No Thanks",
                    color: "#f44336",
                    func: function(){
                        ah.trackEvent("PWA Install Nag", "Rejected", window.appName, true);
                    }
                }
            ]
        });
    }

    function getADLVersion() {
        return adl.getADLVersion();
    }

    function getADL() {
        return adl;
    }

    function showFriendsList(callback) {

        friendSelectedCallback = callback;

        if(!isApeAppsLoggedIn()) {
            friendSelectedCallback = null;
            return;
        }

        let useForground = "ffffff";

        if(adl.getOverallThemeLight()) {
            useForground = "000000";
        }

        tmpFriendWindow = document.createElement("iframe");
        tmpFriendWindow.seamless = true;
        tmpFriendWindow.className = "sidebarFrame";
        tmpFriendWindow.src = "/friends.php?embed=1&fg=" + useForground + "&nm=1";

        let position = "right";

        adl.showDialog({
            customElement: tmpFriendWindow,
            buttons: [],
            flyout: {
                position: position
            }
        });

    }

    function postToSignalServer(forUser,data) {
        if(!isApeAppsLoggedIn()) {
            return;
        }

        if(!signalSocket) {
            return;
        }

        let signalMessage = {
            "type": "friendsignal",
            "code": "customsignal",
            "for": forUser,
            "from": apeAppsLoginData.profile.un,
            "data": data
        };

        let signalPayload = {
            "destination": friendsSignalingChannel,
            "msg": signalMessage
        };

        signalSocket.emit("gamemessage",signalPayload);
    }

    function setFriendsStatus(statusObj) {
        lastFriendsStatus = statusObj;
        updateFriendsNetworkStatus(statusObj);
    }

    function injectStylesheet(sheet) {
        let fullURL = window.appBase + sheet;

        let theTag = document.getElementById("cust-sheet-" + sheet);

        if(!theTag) {
            theTag = document.createElement("link");
            theTag.rel = "stylesheet";
            theTag.type = "text/css";
            theTag.id = "cust-sheet-" + sheet;
            document.head.appendChild(theTag);
        }

        theTag.href = fullURL;
    }

    function removeStylesheet(sheet) {
        let theTag = document.getElementById("cust-sheet-" + sheet);

        if(theTag) {
            document.head.removeChild(theTag);
        }
    }

    function injectCustomCSS(tag,rawCSS) {
        let theTag = document.getElementById("cust-rawcss-" + tag);

        if(!theTag) {
            theTag = document.createElement("style");
            theTag.id = "cust-rawcss-" + tag;
            document.head.appendChild(theTag);
        }

        theTag.innerHTML = rawCSS;
    }

    function removeCustomCSS(tag) {
        let theTag = document.getElementById("cust-rawcss-" + tag);

        if(theTag) {
            document.head.removeChild(theTag);
        }
    }

    function initParms() {
        const urlSearchParams = new URLSearchParams(window.location.search);
        const params = Object.fromEntries(urlSearchParams.entries());

        if(document.referrer && document.referrer.trim().length > 0) {
            trackEventDefered("PWA Referrer (Client)",document.referrer,window.appName,true);
        }

        if(window.srvRef && window.srvRef.trim().length > 0) {
            trackEventDefered("PWA Referrer (Server)",window.srvRef,window.appName,true);
        }

        // see referrer info here:  https://github.com/pwa-builder/pwabuilder-windows-chromium-docs/issues/9
        // and update here:         https://docs.microsoft.com/en-us/microsoft-edge/progressive-web-apps-chromium/microsoft-store
        let referrer = document.referrer;

        if(!referrer || referrer.trim().length == 0 || (window.srvRef && window.srvRef.indexOf("microsoft-store") > -1)) {
            referrer = window.srvRef;
        }

        // preferred method?
        if(referrer && referrer.trim().length > 0) {

            if(referrer.indexOf("platform/microsoft-store") > -1) {
                isWindowsStore = true;
                window.statPlatform = "6";
                window.platformId = "6";
            }
            
        }

        if(params) {

            if(params.beta && params.beta == "1") {
                window.betaMode = true;

                if(window.betaAppVersion && window.betaAppVersion != "0") {
                    usingAppVersion = window.betaAppVersion;
                }

                window.appURL = "https://www.apewebapps.com/apps/" + window.appurlid + "/" + usingAppVersion + "/index.html";
                window.appBase = "https://www.apewebapps.com/apps/" + window.appurlid + "/" + usingAppVersion + "/";
            }

            if(params.ep) {
                if(!window.launchParm || window.launchParm == "0") {
                    window.launchParm = params.ep;
                }
            }

            if(params["app-install-source"]) {

                trackEventDefered("PWA Install Source",params["app-install-source"],window.appName,true);

                let source = params["app-install-source"];

                if(source == "microsoft-store") {
                    isWindowsStore = true;
                    window.statPlatform = "6";
                    window.platformId = "6";
                }

                if(source == "steam-host") {
                    if(params["app-key"] && params["app-key"] == window.appLoginId) {
                        isSteam = true;
                        usingElectronHost = true;
                        window.statPlatform = "15";
                    }
                }

                if(source == "ape-launcher") {
                    if(params["app-key"] && params["app-key"] == window.appLoginId) {
                        isApeLauncher = true;
                        usingElectronHost = true;
                        window.statPlatform = "16";

                        window.opener.postMessage({
                            func: "getlaunchdata",
                            data: {
                                id: window.appId
                            }
                        });
                    }
                }

                if(source == "ape-market") {
                    if(params["app-key"] && params["app-key"] == window.appLoginId) {
                        isApeMarket = true;
                        usingElectronHost = true;
                        window.statPlatform = "1";
                    }
                }

                if(source == "awa-tv") {
                    window.useToolbar = false;
                    isApeTvFrame = true;
                    window.wacIsTv = true;
                }

                if(source == "awa-vr") {
                    window.useToolbar = false;
                    isApeTvFrame = true;
                }

                if(source == "wac-launch") {
                    isWacChildWindow = true;

                    window.opener.postMessage({
                        func: "getlaunchdata",
                        data: {
                            id: window.appId
                        }
                    });
                }

                if(source == "soundboard-city") {
                    window.awaSBC = true;
                    isSoundboardCity = true;
                    window.statPlatform = "16";
                }
            }
        }

        if(!isSoundboardCity && window.awaSBC) {
            window.awaSBC = true;
            isSoundboardCity = true;
            window.statPlatform = "16";
        }

        // log status of digital goods API for reference
        // https://github.com/WICG/digital-goods/blob/main/explainer.md
        if (window.getDigitalGoodsService === undefined) {
            trackEventDefered("Digital Goods API","Unsupported",window.platformOS,true);
        } else {
            trackEventDefered("Digital Goods API","Supported",window.platformOS,true);
        }
    }

    function initiateSignIn() {

        if(isSoundboardCity) {
            parent.postMessage({
                func: "reqSBCCreds"
            },"*");

            return;
        }

        if(isApeTvFrame) {

            parent.postMessage({
                func: "reqTVCreds"
            });

            return;
        }

        if(isApeLauncher) {

            window.opener.postMessage({
                func: "reqLauncherCreds"
            });

            return;
        }

        if(getIsEmbedded()) {
            parent.postMessage({
                func: "reqApeAppsCreds"
            },"*");
        }

        let storageName = window.appId + "_aaa_login";
        let lli = localStorage[storageName];

        if(lli && lli != null && lli != "null") {
            apeAppsLoginData = JSON.parse(lli);
            isAutoLoginAttempt = true;
            submitLoginData(apeAppsLoginData.creds.sida,apeAppsLoginData.creds.sidb,apeAppsLoginData.creds.sidc,apeAppsLoginData.creds.app);
        } else {
            attemptCredentialSignin();
        }
    }

    function injectFontFace(family,url,display,weight,style) {
        let tag = "cust-fontface-" + family;
        let fullURL = window.appBase + url;
        let fontContent = "@font-face { font-family: '" + family + "'; src: url('" + fullURL + "'); ";

        if(display) {
            tag += "-disp" + display;
            fontContent += "font-display: " + display + "; ";
        }

        if(weight) {
            tag += "-weight" + weight;
            fontContent += "font-weight: " + weight + "; ";
        }

        if(style) {
            tag += "-style" + style;
            fontContent += "font-style: " + style + "; ";
        }

        fontContent += "}";

        let theTag = document.getElementById(tag);

        if(!theTag) {
            theTag = document.createElement("style");
            theTag.id = tag;
            document.head.appendChild(theTag);
        }

        theTag.innerHTML = fontContent;

        if(fontPreloadArea) {
            console.log("preload " + family);

            let peload = document.createElement("div");
            peload.style.fontFamily = family;
            peload.innerHTML = "load";
            fontPreloadArea.appendChild(peload);
        }
    }

    function removeFontFace(family,display,weight,style) {
        let tag = "cust-fontface-" + family;

        if(display) {
            tag += "-disp" + display;
        }

        if(weight) {
            tag += "-weight" + weight;
        }

        if(style) {
            tag += "-style" + style;
        }

        let theTag = document.getElementById("cust-rawcss-" + tag);

        if(theTag) {
            document.head.removeChild(theTag);
        }
    }

    function getIsEmbedded() {
        return window.awaEmbedded;
    }

    function doQuitElectron() {
        if(isApeLauncher) {
            if(wacUtils && usingElectronHost) {
                wacUtils.closeCurrentWindow();
            } else {
                window.close('','_parent','');
            }
            
        } else {
            wacUtils.doQuitApp();
        }
    }

    function getElectronOS() {
        let platform = "";
        
        if(CURRENT_PLATFORM == "linux") {
            platform = "linux";
        }
        
        if(CURRENT_PLATFORM == "darwin") {
            platform = "mac";
        }
        
        if(CURRENT_PLATFORM == "win32") {
            platform = "windows";
        }
        
        return platform;
    }

    function supportsTcpSockets() {
        if(usingElectronHost) {
            return false;
        }

        return false;
    }

    function createTcpSocket(port,callback) {
        // nothing yet
    }

    function onGeneralError(e) {
        let appVersion = usingAppVersion;
        let scrpt = e.filename.substr(e.filename.lastIndexOf("/") + 1);

        logError(scrpt,e.lineno,e.message);

        if(ah) {
            ah.trackEvent("AWA Web Frame Error " + appVersion,scrpt + ":" + e.lineno,e.message, false);
            ah.trackEvent("Web Host Frame Error",scrpt + ":" + e.lineno,e.message, true);
        }
  
    }

    function logError(script,line,message) {
        if(typeof amslLogError !== "undefined") {
            amslLogError(window.appId,window.statPlatform,usingAppVersion,workingSubplatform,script,line,message);
        }
    }

    function getSafeAreaInset(offset) {

        let prop = "";

        if(offset == "top") {
            prop = "--sait";
        }
    
        if(offset == "left") {
            prop = "--sail";
        }
    
        if(offset == "bottom") {
            prop = "--saib";
        }
    
        if(offset == "right") {
            prop = "--sair";
        }

        let val = getComputedStyle(document.documentElement).getPropertyValue(prop);

        if(val && val.trim().length > 0) {
            return val;
        }

        return "0px";
    }

    function isAppInstalled(appid,callback) {

        let currentPlayReference = {};

        if(isApeAppsLoggedIn()) {
            let refName = apeAppsLoginData.profile.un + "_playLog";
            let playedTitles = localStorage[refName];

            if(!playedTitles || playedTitles.trim().length < 2) {
                playedTitles = "{}";
            }

            try {
                currentPlayReference = JSON.parse(playedTitles);
            } catch(ex) {
                console.log(ex);
                currentPlayReference = {};
            }

        }

        if(currentPlayReference[appid]) {
            callback(true);
            return;
        }

        callback(false);
    }

    function uninstallApp(appid) {
        if(isApeAppsLoggedIn()) {
            let refName = apeAppsLoginData.profile.un + "_playLog";
            let playedTitles = localStorage[refName];

            if(!playedTitles || playedTitles.trim().length < 2) {
                playedTitles = "{}";
            }

            try {
                currentPlayReference = JSON.parse(playedTitles);
            } catch(ex) {
                console.log(ex);
                currentPlayReference = {};
            }

            delete currentPlayReference[appid];

            let saveDat = JSON.stringify(currentPlayReference);
            localStorage[refName] = saveDat;
        }
    }

    function getInstalledApps(callback) {

        if(!allGamesRef || allGamesRef.length == 0) {
            callback([]);
            return;
        }

        let currentPlayReference = {};

        if(isApeAppsLoggedIn()) {
            let refName = apeAppsLoginData.profile.un + "_playLog";
            let playedTitles = localStorage[refName];

            if(!playedTitles || playedTitles.trim().length < 2) {
                playedTitles = "{}";
            }

            try {
                currentPlayReference = JSON.parse(playedTitles);
            } catch(ex) {
                console.log(ex);
                currentPlayReference = {};
            }

        }

        let list = [];

        // FULLBLEED BELOW NEEDS TO BE UPDATED WITH A GETTILE FUNCTION THAT I NEED TO
        // ADD TO THE APE MARKET!@!

        for(let i = 0; i < allGamesRef.length; i++) {
            let title = allGamesRef[i];

            if(currentPlayReference[title.appid]) {
                list.push({
                    name: title.app_name,
                    package: title.appid,
                    icon: "https://market.ape-apps.com/app_resources/geticon.php?a=" + title.appid,
                    regIcon: "https://market.ape-apps.com/app_resources/geticon.php?a=" + title.appid,
                    fullbleed: "https://market.ape-apps.com/app_resources/geticon.php?a=" + title.appid
                });
            }
        }

        callback(list);
    }

    function getAllGames() {
        let xhr = new XMLHttpRequest();
        xhr.onload = function() {

            try {
                allGamesRef = JSON.parse(xhr.responseText);
            } catch(ex) {
                console.log(ex);
                allGamesRef = [];
            }
        };
        xhr.open("get","/fetchgameslist.php");
        xhr.send();
    }

    function launchApplication(appid,data,resultlistener) {

        let useResCt = null;

        if(resultlistener) {
            appResultCounter++;
            appResultListeners[appResultCounter] = resultlistener;
            useResCt = appResultCounter;
        }

        if(isApeLauncher) {

            window.opener.postMessage({
                func: "doAppLaunch",
                data: {
                    app: appid,
                    data: data,
                    res: useResCt
                }
            });

            return;
        }

        if(!allGamesRef || allGamesRef.length == 0) {
            return;
        }

        let tgt = null;

        for(let i = 0; i < allGamesRef.length; i++) {
            let title = allGamesRef[i];

            if(title.appid == appid) {
                tgt = title;
                break;
            }
        }

        if(!tgt) {
            return;
        }

        let appTarget = replaceAll(tgt.app_name," ","-").toLowerCase();

        let url = "https://www.apewebapps.com/" + appTarget + "/?app-install-source=wac-launch";

        let win = window.open(url,"wac" + tgt.appid);

        childAppWindows[tgt.appid] = {
            win: win,
            data: data,
            res: useResCt
        };

    }

    function getRawPrinterConfig() {
        return remoteLPTPrinters;
    }

    function printRaw(options) {

        if(options.server && options.printer) {
            doPrintRaw(options,{
                server: options.server,
                printer: options.printer
            });

            return;
        }

        let allPrinters = [];

        for(let prop in remoteLPTPrinters) {
            let server = remoteLPTPrinters[prop];

            for(let prntr in server) {
                let printer = server[prntr];

                let tag = {
                    server: prop,
                    printer: prntr
                };

                allPrinters.push({
                    title: printer.name,
                    description: printer.location,
                    satus: printer.address,
                    icon: "print-outline",
                    tag: tag
                });
            }
        }

        if(options.unformatted) {
            allPrinters.push({
                title: "System Print",
                description: "Print using your default system print dialog.",
                icon: "document-text-outline",
                tag: "unformatted"
            });
        }

        if(allPrinters.length == 0) {
            return;
        }

        if(allPrinters.length == 1) {
            doPrintRaw(options,allPrinters[0].tag);
            return;
        }

        adl.showList({
            title: "Select Printer",
            options: allPrinters,
            onSelection: function(tag) {

                if(!tag) {
                    return;
                }

                if(tag == "unformatted") {

                    margin = "0.75in";

                    if(options.margin) {
                        margin = options.margin + "in";
                    }

                    let textSize = "14.0";
                        
                    let text = replaceAll(options.unformatted,"\n","<br />");
            
                    let html = "<div style=\"font-family: Consolas, monaco, monospace; font-size: " + textSize + "px\">" + text + "</div>";
            
                    printContent(html,margin);

                    return;
                }

                doPrintRaw(options,tag);
            },
            flyout: {
                position: "right"
            }
        });
    }

    function doPrintRaw(options,printer) {

        if(!isApeAppsLoggedIn() || !sharedResourceSignalingChannel || !signalSocketConnected) {
            return;
        }

        if(printer.server && printer.printer) {
            signalSocket.emit("gamemessage",{
                "destination": sharedResourceSignalingChannel,
                "msg": {
                    "type": "resourcesignal",
                    "code": "printrequest",
                    "un": apeAppsLoginData.profile.un,
                    "for": "all",
                    "session": printer.server,
                    "printer": printer.printer,
                    "job": options
                }
            });
        }
    }

    // WAC 4.5.0

    function announceLivestreamStatus(status) {
        if(!signalSocket || !apeAppsLoginData || !apeAppsLoginData.profile) {
            return;
        }

        signalSocket.emit("gamemessage",{
            "destination": friendsSignalingChannel,
            "msg": {
                "type": "friendsignal",
                "code": "iamstreaming",
                "un": apeAppsLoginData.profile.un,
                "extra": {
                    "app": window.appName,
                    "plat": window.statPlatform,
                    "stat": status
                },
                "for": "all"
            }
        });
    }

    function isVR() {
        return isApeVRFrame;
    }

    function numberWithCommas(x) {

        if(!x) {
            x = 0;
        }

        x = parseFloat(x);

        let formattedNumber = x.toFixed(2).replace(/[.,]00$/, "");

        return formattedNumber.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
    }

    function getApeCoinBalance() {
        let apeCoinBalanace = 0;

        if(isApeAppsLoggedIn()) {
            let me = getApeAppsProfile();

            if(me) {
                apeCoinBalanace = parseInt(me.bal);
            }

            if(isNaN(apeCoinBalanace)) {
                apeCoinBalanace = 0;
            }
        }

        

        return apeCoinBalanace;
    }

    function shouldShowApeCoins() {
        if(isSteam) {
            return false;
        }

        return true;
    }

    function setSplashLogo(logo) {
        let imSplashLogo = document.getElementById("imSplashLogo");

        if(!logo) {
            imSplashLogo.src = window.defSplashLogo;
        } else {
            if(logo.indexOf("://") > -1 || logo.indexOf("data:") == 0) {
                imSplashLogo.src = logo;
            } else {
                imSplashLogo.src = window.appBase + logo;
            }
        }
    }

    function exitWithData(data) {
        // launchApplication now has resultlistener
        // returnlaunchdata now has a result code id

        if(window && window.opener) {
            window.opener.postMessage({
                func: "exitWithData",
                data: {
                    id: window.appId,
                    data: data
                }
            });
        } else {
            console.log("no opener!");

            try {
                window.close();
            } catch(ex) {
                console.log(ex);
            }
        }
        
    }

    function showApeCoinPurchaseFlow(callback) {
        if(!callback) {
            callback = placeholder;
        }

        if(!isApeAppsLoggedIn()) {
            return;
        }

        let useval = 5;

        adl.showDialog({
            title: "Purchase Ape Coins",
            message: "You may add Ape Coins to your account at a rate of 100 coins per $1.  How many Ape Coins would you like to add?",
            icon: "https://www.apewebapps.com/images/ape-coin.png",
            slider: {
                min: 1,
                max: 100,
                value: 5,
                status: "Purchase " + (useval * 100) + " = $" + useval + ".00",
                onChange: function(value,update) {
                    useval = parseInt(value);
                    update("Purchase " + (useval * 100) + " = $" + useval + ".00");
                },
            },
            buttons: [
                {
                    text: "Dismiss",
                },
                {
                    text: "Payment",
                    color: "#4CAF50",
                    sliderResult: true,
                    func: function(result) {
                        if(result && !isNaN(result) && parseInt(result) > 0) {
                            let coins = Math.round(parseInt(result) * 100);

                            console.log("buy " + coins);

                            let creds = getApeAppsCreds();
                            let iapURL = "https://accounts.ape-apps.com/buycoinsext.php?id=" + window.appId + "&a=" + creds.a + "&b=" + creds.b + "&c=" + creds.c + "&amt=" + coins;
                            coinBuyWindow = window.open(iapURL,"coinbuy","height=620,width=500,status=no,titlebar=no,modal=yes,resizable=yes,menubar=no,frame=true");
                            coinBuyCallback = callback;
                        }
                    }
                }
            ],
        });
    }

    function buyPremiumWithApeCoins() {
        if(!isApeAppsLoggedIn()) {
            return;
        }

        let bal = getApeCoinBalance();

        let coinCost = Math.round(parseFloat(window.upgradePrice) * 100);

        console.log(coinCost);

        if(coinCost > bal) {
            adl.showDialog({
                title: "Insufficient Ape Coins",
                message: "Upgrading " + window.appName + " requires " + numberWithCommas(coinCost) + " Ape Coins, but your account balance is " + numberWithCommas(bal) + ".  Either add more Ape Coins to your account, or select a different purchase option.",
                icon: "https://www.apewebapps.com/images/ape-coin.png",
                buttons: [
                    {
                        text: "Purchase Ape Coins",
                        func: showApeCoinPurchaseFlow
                    },
                    {
                        text: "Dismiss",
                        func: placeholder
                    }
                ]
            });

            return;
        }

        adl.showDialog({
            title: premiumOptionText,
            message: "You are about to upgrade " + window.appName + " for " + numberWithCommas(coinCost) + " Ape Coins.",
            icon: "https://www.apewebapps.com/images/ape-coin.png",
            buttons: [
                {
                    text: "Confirm",
                    func: confirmApeCoinUpgrade,
                    color: "#4CAF50"
                },
                {
                    text: "Cancel",
                    func: placeholder,
                    color: "#F44336"
                }
            ]
        });
    }

    function confirmApeCoinUpgrade() {
        let upgradeData = buildAccountsAPICreds("17");

        if(!upgradeData) {
            return;
        }

        proxyRequest(APE_ACCOUNTS_API,function(res) {
            try {
                let resDat = JSON.parse(res);

                if(resDat) {
                    if(resDat.success) {
                        notifyPremiumActivation(true);
                        showToast("Thank you for your support!");

                        if(resDat.balance != undefined) {
                            let me = getApeAppsProfile();

                            if(me) {
                                me.bal = parseInt(resDat.balance);
                            }
                        }
                    } else {
                        adl.showDialog({
                            title: "Purchase Failed!",
                            message: "Your Ape Coin purchase failed with error: " + resDat.reason,
                            icon: "https://www.apewebapps.com/images/ape-coin.png",
                        });
                    }
                } else {
                    showToast("Purchase Error!!");
                }
            } catch(ex) {
                console.log(ex);
                showToast("Purchase Error!");
            }
            
        },upgradeData,true,true);
    }

    function spendApeCoins(amount,itemid,callback) {
        let upgradeData = buildAccountsAPICreds("18");

        if(!upgradeData) {
            return;
        }

        if(!itemid) {
            itemid = "noid";
        }

        if(!amount) {
            return;
        }

        if(!callback) {
            callback = placeholder;
        }

        upgradeData.push({
            "key": "amt",
            "value": amount
        });

        upgradeData.push({
            "key": "item",
            "value": itemid
        });

        proxyRequest(APE_ACCOUNTS_API,function(res) {
            try {
                let resDat = JSON.parse(res);

                if(resDat) {
                    if(resDat.success) {
                        let retBal = 0;

                        if(resDat.balance != undefined) {
                            let me = getApeAppsProfile();

                            if(me) {
                                me.bal = parseInt(resDat.balance);
                                retBal = me.bal;
                            }
                        }

                        callback(true,retBal);
                    } else {
                        showToast("Purchase Error!!!");
                        callback(false,0);
                    }
                } else {
                    showToast("Purchase Error!!");
                    callback(false,0);
                }
            } catch(ex) {
                console.log(ex);
                showToast("Purchase Error!");
                callback(false,0);
            }
            
        },upgradeData,true,true);
    }

    function queryCoinPurchases(callback) {
        let upgradeData = buildAccountsAPICreds("19");

        if(!upgradeData) {
            callback([]);
            return;
        }

        proxyRequest(APE_ACCOUNTS_API,function(res) {
            try {
                let resDat = JSON.parse(res);

                if(resDat) {
                    callback(resDat);
                } else {
                    callback([]);
                }
            } catch(ex) {
                console.log(ex);
                callback([]);
            }
            
        },upgradeData,true,true);
    }

    function sendApeCoins(username,callback) {

        if(!username || username.trim().length == 0) {
            return;
        }

        if(!callback) {
            callback = placeholder;
        }

        console.log("show send dialog to " + username);

        if(!isApeAppsLoggedIn()) {
            return;
        }

        let bal = getApeCoinBalance();

        if(bal <= 0) {
            webAppCore.showToast("Zero balance!");
            return;
        }

        let useval = 100;

        if(useval > bal) {
            useval = bal;
        }

        adl.showDialog({
            title: "Send Ape Coins",
            message: "Choose how many Ape Coins you want to send to " + username + ":",
            slider: {
                min: 1,
                max: bal,
                value: useval,
                status: useval,
                onChange: function(value,update) {
                    update(value);
                },
            },
            buttons: [
                {
                    text: "Dismiss",
                },
                {
                    text: "Send",
                    color: "#4CAF50",
                    sliderResult: true,
                    func: function(result) {
                        if(result && !isNaN(result) && parseInt(result) > 0) {
                            let upgradeData = buildAccountsAPICreds("20");

                            if(!upgradeData) {
                                return;
                            }

                            upgradeData.push({
                                "key": "amt",
                                "value": result
                            });
                    
                            upgradeData.push({
                                "key": "un",
                                "value": username
                            });

                            proxyRequest(APE_ACCOUNTS_API,function(res) {
                                try {
                                    let resDat = JSON.parse(res);
                    
                                    if(resDat) {
                                        if(resDat.success) {
                                            let retBal = 0;
                    
                                            if(resDat.balance != undefined) {
                                                let me = getApeAppsProfile();
                    
                                                if(me) {
                                                    me.bal = parseInt(resDat.balance);
                                                    retBal = me.bal;
                                                }
                                            }
                    
                                            callback(true,retBal);

                                            showToast("Sent " + numberWithCommas(result) + " to " + username,"https://www.apewebapps.com/images/ape-coin.png");
                                        } else {
                                            showToast("Send Error!!!");
                                            callback(false,0);
                                        }
                                    } else {
                                        showToast("Send Error!!");
                                        callback(false,0);
                                    }
                                } catch(ex) {
                                    console.log(ex);
                                    showToast("Send Error!");
                                    callback(false,0);
                                }
                                
                            },upgradeData,true,true);
                        }
                    }
                }
            ],
        });
    }

    function refreshCoinBalance(callback) {
        if(!callback) {
            callback = placeholder;
        }

        let upgradeData = buildAccountsAPICreds("21");

        if(!upgradeData) {
            callback(0);
            return;
        }

        proxyRequest(APE_ACCOUNTS_API,function(res) {

            let balance = parseInt(res);

            if(balance < 0) {
                balance = 0;
            }

            let me = getApeAppsProfile();

            if(me) {
                me.bal = balance;
            }

            callback(balance);
            
        },upgradeData,true,true);
    }
})();
	`;
	
	GM_webRequest([
		{
			selector: "js/script/(.+?)/(frame-script\\.js)",
			action: { redirect: `data:text/javascript;base64,${btoa(unescape(encodeURIComponent(_code)))}` }
		},
	], (...rest) => GM_log("Premium Unlocked", ...rest));
	
	localStorage.setItem("351_premium_upgrade", 1);
	localStorage.setItem("undefined_premium_upgrade", 1);
	
	return true;
}();