"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.hotStaqWebStart = hotStaqWebStart;
const Hot_1 = require("./Hot");
const HotClient_1 = require("./HotClient");
const HotFile_1 = require("./HotFile");
const HotLog_1 = require("./HotLog");
const HotStaq_1 = require("./HotStaq");
/**
 * If running in a browser, this will start HotStaq.
 */
function hotStaqWebStart() {
    // @ts-ignore
    if (typeof (window.hotstaqStartingApp) !== "undefined") {
        // @ts-ignore
        if (window.hotstaqStartingApp === true)
            return;
    }
    // Prevent re-entry from synthetic load events dispatched by useOutput.
    // @ts-ignore
    window.hotstaqStartingApp = true;
    let hotstaqElms = document.getElementsByTagName("hotstaq");
    // Set this to true, just in case...
    HotStaq_1.HotStaq.isWeb = true;
    // @ts-ignore
    if (typeof (HotStaqWeb) !== "undefined") {
        // @ts-ignore
        for (let key in HotStaqWeb) {
            // @ts-ignore
            window[key] = HotStaqWeb[key];
        }
    }
    if (hotstaqElms.length > 0) {
        let hotstaqElm = hotstaqElms[0];
        setTimeout(function () {
            return __awaiter(this, void 0, void 0, function* () {
                let getAttr = (elm, attrNames) => {
                    for (let iIdx = 0; iIdx < attrNames.length; iIdx++) {
                        let attrName = attrNames[iIdx];
                        if (elm.getAttribute(attrName) != null)
                            return (elm.getAttribute(attrName));
                        if (elm.getAttribute(`data-${attrName}`) != null)
                            return (elm.getAttribute(`data-${attrName}`));
                    }
                    return (undefined);
                };
                let loadPage = getAttr(hotstaqElm, ["load-page", "loadPage", "src"]) || "";
                let loggingLevel = getAttr(hotstaqElm, ["logging-level", "loggingLevel"]) || null;
                let startDelay = getAttr(hotstaqElm, ["start-delay", "startDelay"]) || null;
                let router = getAttr(hotstaqElm, ["router"]) || "";
                let spaTarget = getAttr(hotstaqElm, ["spa-target", "spaTarget"]) || null;
                let outlet = getAttr(hotstaqElm, ["outlet", "spa-outlet", "spaOutlet"]) || null;
                // In layout mode the `src` is the shell page (rendered once); the
                // route is resolved separately from the URL. Capture it before the
                // router match below overwrites `loadPage` with the route's src.
                let shellSrc = getAttr(hotstaqElm, ["src"]) || "";
                let name = getAttr(hotstaqElm, ["name"]) || "default";
                let args = getAttr(hotstaqElm, ["args"]) || null;
                let apiJSUrl = getAttr(hotstaqElm, ["api-js-url", "apiJSUrl"]) || null;
                let apiLibrary = getAttr(hotstaqElm, ["api-library", "apiLibrary"]) || null;
                let apiName = getAttr(hotstaqElm, ["api-name", "apiName"]) || null;
                let apiUrl = getAttr(hotstaqElm, ["api-url", "apiUrl"]) || null;
                let testerName = getAttr(hotstaqElm, ["tester-name", "testerName"]) || "HotTesterMochaSelenium";
                let testerMap = getAttr(hotstaqElm, ["tester-map", "testerMap"]) || null;
                let testerApiBaseUrl = getAttr(hotstaqElm, ["tester-api-base-url", "testerApiBaseUrl"]) || null;
                let testerLaunchpadUrl = getAttr(hotstaqElm, ["tester-launchpad-url", "testerLaunchpadUrl"]) || null;
                let dontReuseProcessor = false;
                let passRawUrl = false;
                let htmlSource = hotstaqElm.innerHTML || "";
                let routerManager = {};
                let routerWildcards = [];
                let search = new URLSearchParams(window.location.search);
                if (startDelay != null)
                    yield HotStaq_1.HotStaq.wait(parseInt(startDelay));
                if (getAttr(hotstaqElm, ["src"]) != null)
                    loadPage = getAttr(hotstaqElm, ["src"]);
                if (getAttr(hotstaqElm, ["passRawUrl"]) != null)
                    passRawUrl = true;
                if (getAttr(hotstaqElm, ["dont-reuse-processor", "dontReuseProcessor"]) != null)
                    dontReuseProcessor = true;
                let hstqbaseredirect = search.get("hstqbaseredirect");
                if (hstqbaseredirect != null) {
                    hstqbaseredirect = decodeURI(hstqbaseredirect);
                    window.history.replaceState("", "", hstqbaseredirect);
                    loadPage = hstqbaseredirect;
                }
                let hotstaqErrors = document.getElementsByTagName("hotstaq-error");
                for (let iIdx = 0; iIdx < hotstaqErrors.length; iIdx++) {
                    // @ts-ignore
                    let hotstaqErrorElm = hotstaqErrors[iIdx];
                    let errorStatus = getAttr(hotstaqErrorElm, ["status"]);
                    let unsupportedBrowser = getAttr(hotstaqErrorElm, ["unsupported-browser-redirect"]);
                    if (unsupportedBrowser != null)
                        HotStaq_1.HotStaq.errors["unsupportedBrowser"] = { redirectToUrl: unsupportedBrowser };
                    else
                        HotStaq_1.HotStaq.errors[`${errorStatus}`] = { redirectToUrl: unsupportedBrowser };
                }
                // Check if async/await is available.
                try {
                    eval("async () => {}");
                }
                catch (ex) {
                    HotStaq_1.HotStaq.executeError("unsupportedBrowser");
                }
                if (router !== "") {
                    let hotstaqRouterElms = document.getElementsByTagName("hotstaq-router");
                    for (let iIdx = 0; iIdx < hotstaqRouterElms.length; iIdx++) {
                        // @ts-ignore
                        let hotstaqRouterElm = hotstaqRouterElms[iIdx];
                        let routerName = getAttr(hotstaqRouterElm, ["name"]);
                        let serveLocally = getAttr(hotstaqRouterElm, ["serve-local", "serveLocally"]);
                        // @ts-ignore
                        if (routerName === router) {
                            // Load all routes from the router.
                            for (let iJdx = 0; iJdx < hotstaqRouterElm.childNodes.length; iJdx++) {
                                // @ts-ignore
                                let routerElm = hotstaqRouterElm.childNodes[iJdx];
                                if (routerElm instanceof HTMLElement) {
                                    if (routerElm.tagName.toUpperCase() === "ROUTE") {
                                        let routerPath = getAttr(routerElm, ["path"]);
                                        let redirect = getAttr(routerElm, ["redirect"]);
                                        let baseRedirect = getAttr(routerElm, ["base-redirect", "baseRedirect"]);
                                        let base = getAttr(routerElm, ["base"]);
                                        let routerSrc = getAttr(routerElm, ["src"]);
                                        if (routerPath.indexOf("*") > -1)
                                            routerWildcards.push(routerPath);
                                        routerManager[routerPath] = {
                                            redirect: redirect || undefined,
                                            baseRedirect: baseRedirect || undefined,
                                            base: base || undefined,
                                            src: routerSrc || undefined
                                        };
                                    }
                                }
                            }
                            let checkPath = window.location.pathname;
                            let gotoPath = window.location.pathname;
                            if (serveLocally != null) {
                                const lowerServeLocally = serveLocally.toLowerCase();
                                if ((lowerServeLocally === "true") ||
                                    (lowerServeLocally === "yes") ||
                                    (lowerServeLocally === "1")) {
                                    const lastSlashPos = checkPath.lastIndexOf("/");
                                    if (lastSlashPos > -1) {
                                        checkPath = checkPath.substring(lastSlashPos);
                                        gotoPath = gotoPath.substring(lastSlashPos);
                                    }
                                }
                            }
                            if (routerWildcards.length > 0) {
                                // Serve locally doesn't really work with wildcards
                                /// @fixme This isn't actually working like a wildcard should. This needs to be improved.
                                for (let iJdx = 0; iJdx < routerWildcards.length; iJdx++) {
                                    let routeWildcard = routerWildcards[iJdx];
                                    let tempRouteWildcard = routeWildcard.replace("*", "");
                                    if (checkPath.indexOf(tempRouteWildcard) > -1) {
                                        // This simply returns the key in the routerManager to access.
                                        checkPath = routeWildcard;
                                        break;
                                    }
                                }
                            }
                            // Find the correct route and load it.
                            if (routerManager[checkPath] != null) {
                                if (routerManager[checkPath].redirect != null) {
                                    window.location.href = routerManager[checkPath].redirect;
                                    return;
                                }
                                if (routerManager[checkPath].baseRedirect != null) {
                                    const searchParams = window.location.search;
                                    let modifiedSearchParams = "";
                                    if ((searchParams !== "") && (searchParams !== "?"))
                                        modifiedSearchParams = `&${searchParams.substring(1)}`;
                                    window.location.href = `${routerManager[checkPath].baseRedirect}?hstqbaseredirect=${encodeURI(gotoPath + searchParams)}${modifiedSearchParams}`;
                                    return;
                                }
                                if (routerManager[checkPath].src != null)
                                    loadPage = routerManager[checkPath].src;
                            }
                            break;
                        }
                    }
                }
                // Store router data on HotStaq for SPA navigation.
                HotStaq_1.HotStaq.routerManager = routerManager;
                HotStaq_1.HotStaq.routerWildcards = routerWildcards;
                // Also save on window as a backup — these survive any class
                // re-initialization that may occur during useOutput's DOM replacement.
                // @ts-ignore
                window.__hotstaqRouterManager = routerManager;
                // @ts-ignore
                window.__hotstaqRouterWildcards = routerWildcards;
                if (args != null)
                    args = JSON.parse(args);
                else
                    args = Hot_1.Hot.Arguments;
                let hasHtmlSource = false;
                if (htmlSource !== "") {
                    const htmlSourceCheck = htmlSource.replace(/\s/g, '');
                    if (htmlSourceCheck !== "")
                        hasHtmlSource = true;
                }
                let tempMode = 0;
                // @ts-ignore
                if (window["Hot"] != null)
                    tempMode = Hot_1.Hot.Mode;
                let processor = null;
                if (dontReuseProcessor === false) {
                    if (typeof (Hot_1.Hot) !== "undefined") {
                        if (Hot_1.Hot.CurrentPage != null) {
                            if (Hot_1.Hot.CurrentPage.processor != null)
                                processor = Hot_1.Hot.CurrentPage.processor;
                        }
                    }
                }
                if (processor == null)
                    processor = new HotStaq_1.HotStaq();
                if (loggingLevel != null)
                    processor.logger.logLevel = HotLog_1.HotLog.parse(loggingLevel);
                processor.mode = tempMode;
                let options = {
                    name: name,
                    processor: processor,
                    args: args
                };
                if (loadPage !== "") {
                    if (passRawUrl === false) {
                        if (loadPage.indexOf("hstqserve") < 0)
                            loadPage += "?hstqserve=nahfam";
                    }
                    options.url = loadPage;
                }
                if (testerMap != null) {
                    options.testerMap = testerMap;
                    options.testerName = testerName;
                }
                if (testerName != null)
                    options.testerName = testerName;
                if (testerApiBaseUrl != null)
                    options.testerAPIBaseUrl = testerApiBaseUrl;
                if (testerLaunchpadUrl != null)
                    options.testerLaunchpadUrl = testerLaunchpadUrl;
                if (apiName != null) {
                    let client = new HotClient_1.HotClient(processor);
                    if ((apiUrl == null) || (apiUrl === ""))
                        throw new Error(`api-url was not set!`);
                    if ((apiJSUrl == null) || (apiJSUrl === ""))
                        throw new Error(`api-js-url was not set!`);
                    const apiJSUrlContent = yield HotFile_1.HotFile.httpGet(apiJSUrl);
                    if (apiJSUrlContent === "")
                        throw new Error(`api-js-url content was empty!`);
                    let parentObject = null;
                    /// @todo Require a hash to be passed that matches the content of apiJSUrlContent.
                    if (HotStaq_1.HotStaq.isWeb === true) {
                        if (parentObject == null)
                            parentObject = window;
                    }
                    else {
                        if (parentObject == null)
                            parentObject = global;
                    }
                    eval.apply(parentObject, [apiJSUrlContent]);
                    let parentLib = window;
                    if (apiLibrary != null) {
                        // @ts-ignore
                        parentLib = window[apiLibrary];
                        if (parentLib == null)
                            throw new Error(`Unable to find the API library ${apiLibrary}!`);
                    }
                    let newAPI = new parentLib[apiName](apiUrl, client);
                    if (newAPI.onPreRegister != null)
                        yield newAPI.onPreRegister();
                    newAPI.connection.api = newAPI;
                    processor.api = newAPI;
                }
                // Store the processor for SPA navigation.
                HotStaq_1.HotStaq.spaProcessor = processor;
                // @ts-ignore
                window.__hotstaqSpaProcessor = processor;
                // Layout (app-shell) mode: when an `outlet` is declared, this
                // page IS the shell — it renders once and each route is
                // content-only, injected into the outlet by navigateTo. Set the
                // statics + window backups (the latter survive useOutput's
                // <html>.innerHTML swap so the first navigateTo still takes the
                // layout path).
                if (outlet != null) {
                    HotStaq_1.HotStaq.spaOutlet = outlet;
                    HotStaq_1.HotStaq.layoutEnabled = true;
                    // @ts-ignore
                    window.__hotstaqSpaOutlet = outlet;
                    // @ts-ignore
                    window.__hotstaqLayoutEnabled = true;
                }
                // Enable SPA mode if a spa-target or an outlet was provided. In
                // layout mode the outlet doubles as the link-interception target
                // (navigateTo branches on layoutEnabled, not spaTarget, so this
                // is harmless).
                if (spaTarget != null || outlet != null)
                    HotStaq_1.HotStaq.enableSPA(spaTarget || outlet, processor);
                // Layout (app-shell) mode. The shell (chrome + <hot-outlet>) comes
                // from one of three sources, then the current route's content is
                // injected into the outlet via navigateTo (pushState=false so the
                // initial paint doesn't add a duplicate history entry):
                //   1. a rendered shell .hott (the `src` attribute) — rendered once
                //      via useOutput, for apps whose chrome is dynamic;
                //   2. inline <hotstaq> content — same, rendered via displayContent;
                //   3. a static host-HTML shell (no src) — the outlet is already in
                //      the DOM, so just inject the route.
                // If the current URL matches no route (e.g. opening the shell host
                // directly in a test), nothing is injected yet — navigation will
                // populate the outlet.
                if (outlet != null) {
                    let initialPath = window.location.pathname + window.location.search;
                    let initialCheck = initialPath.split("?")[0];
                    let initialMatched = (routerManager[initialCheck] != null);
                    if (initialMatched === false) {
                        for (let wIdx = 0; wIdx < routerWildcards.length; wIdx++) {
                            if (initialCheck.indexOf(routerWildcards[wIdx].replace("*", "")) > -1) {
                                initialMatched = true;
                                break;
                            }
                        }
                    }
                    let injectInitialRoute = function () {
                        if (initialMatched === true)
                            HotStaq_1.HotStaq.navigateTo(initialPath, false);
                    };
                    if (shellSrc !== "") {
                        // Rendered shell: render the shell page once, then inject
                        // the route into its outlet once that render resolves.
                        let shellUrl = shellSrc;
                        if (shellUrl.indexOf("hstqserve") < 0)
                            shellUrl += "?hstqserve=nahfam";
                        options.url = shellUrl;
                        let shellRender = HotStaq_1.HotStaq.displayUrl(options);
                        if ((shellRender != null) && (typeof (shellRender.then) === "function"))
                            shellRender.then(injectInitialRoute);
                        else
                            injectInitialRoute();
                    }
                    else if (hasHtmlSource === true) {
                        // Shell provided as inline <hotstaq> content.
                        let shellRender = HotStaq_1.HotStaq.displayContent(options);
                        if ((shellRender != null) && (typeof (shellRender.then) === "function"))
                            shellRender.then(injectInitialRoute);
                        else
                            injectInitialRoute();
                    }
                    else {
                        // Static host-HTML shell: the outlet is already present.
                        injectInitialRoute();
                    }
                }
                else if (hasHtmlSource === false) {
                    if (loadPage === "") {
                        // No route matched the current URL. Try, in order:
                        //   1. An explicit `path="404"` route.
                        //   2. An explicit `path="*"` catch-all route. (The
                        //      wildcard scanner above already tries to match
                        //      `*`-containing paths via indexOf — this is a
                        //      belt-and-suspenders direct lookup.)
                        //   3. A user-supplied `HotStaq.onRouteNotFound` hook.
                        //   4. The framework's default "page not found" body
                        //      (no throw, no blank screen).
                        const notFoundPath = window.location.pathname;
                        let fallbackSrc = "";
                        if ((routerManager["404"] != null) && (routerManager["404"].src != null))
                            fallbackSrc = routerManager["404"].src;
                        else if ((routerManager["*"] != null) && (routerManager["*"].src != null))
                            fallbackSrc = routerManager["*"].src;
                        if (fallbackSrc !== "") {
                            loadPage = fallbackSrc;
                        }
                        else if (typeof HotStaq_1.HotStaq.onRouteNotFound === "function") {
                            HotStaq_1.HotStaq.onRouteNotFound(notFoundPath);
                            return;
                        }
                        else {
                            HotStaq_1.HotStaq.displayDefault404(notFoundPath);
                            return;
                        }
                    }
                    if (loadPage.indexOf("hstqserve") < 0)
                        loadPage += "?hstqserve=nahfam";
                    options.url = loadPage;
                    HotStaq_1.HotStaq.displayUrl(options);
                }
                else {
                    HotStaq_1.HotStaq.displayContent(options);
                }
            });
        }, 50);
    }
}
//# sourceMappingURL=HotStaqWebStart.js.map