"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var sp_core_library_1 = require("@microsoft/sp-core-library");
var sp_diagnostics_1 = require("@microsoft/sp-diagnostics");
var BaseApplication_1 = tslib_1.__importDefault(require("../BaseApplication"));
var Killswitches_1 = require("../common/Killswitches");
var NavigationDataError_1 = tslib_1.__importDefault(require("./NavigationDataError"));
var NavigationDataProvider_1 = tslib_1.__importDefault(require("./NavigationDataProvider"));
var NavigationOrchestrator_1 = tslib_1.__importDefault(require("./NavigationOrchestrator"));
var PrefetchedDataEventArgs_1 = tslib_1.__importDefault(require("./PrefetchedDataEventArgs"));
var NavigationDataError_2 = require("./NavigationDataError");
var navigateQosScenarioName = 'Navigator.navigate';
var navigateToPreloadedDataQosScenarioName = 'Navigator.navigateToPreloadedData';
var navigateToApplicationQosScenarioName = 'Navigator.navigateToApplication';
var prefetchNavigationDataQosScenarioName = 'Navigator.prefetch';
/**
 * Navigator for SPFx applications.
 * It allows to navigate to a different URL that is backed by an SPFx application.
 *
 * Updates all SPFx-internal data structures with the information from the new URL.
 * This includes the page context, the manifest store, the session, telemetry and the themes.
 *
 * @internal
 */
var Navigator = /** @class */ (function () {
    function Navigator(serviceScope, applicationManager) {
        var _this = this;
        sp_core_library_1.Validate.isNotNullOrUndefined(serviceScope, 'serviceScope');
        this._navigationDataProvider = new NavigationDataProvider_1.default(serviceScope, function (data) {
            _this._validatePreloadedData(data);
        });
        this._navigationOrchestrator = new NavigationOrchestrator_1.default(serviceScope, applicationManager, this);
    }
    /**
     * Raises an event for a page navigation.
     */
    Navigator.raiseNavigatedEvent = function () {
        requestAnimationFrame(function () {
            setTimeout(function () {
                sp_core_library_1._SPEventManager.instance.raiseStickyEvent(BaseApplication_1.default._navigatedEventName, {});
            }, 0);
        });
    };
    /**
     * Clears the queue of navigation event.
     * Needed for in-place navigation.
     */
    Navigator.clearNavigatedEventQueue = function () {
        sp_core_library_1._SPEventManager.instance._clearStickyEventQueue(BaseApplication_1.default._navigatedEventName);
    };
    Object.defineProperty(Navigator.prototype, "preloadedData", {
        /**
         * Returns the preloaded data used by the current page.
         * Throws if it hasn't navigated to a page yet.
         */
        get: function () {
            sp_core_library_1.Validate.isNotNullOrUndefined(this._preloadedData, 'preloadedData');
            return this._preloadedData;
        },
        enumerable: false,
        configurable: true
    });
    /**
     * Navigates to a new page.
     * Requests a JSON to SharePoint server and updates all SPFx related data with the new information.
     * This includes new manifests, page context, theme, telemetry settings.
     *
     * @param url - Destination URL
     * @param props - Optional Navigation properties
     */
    Navigator.prototype.navigate = function (url, props) {
        var _this = this;
        sp_core_library_1.Validate.isNonemptyString(url, 'url');
        var qosMonitor = new sp_diagnostics_1._QosMonitor(navigateQosScenarioName);
        return this._navigationDataProvider
            .getData(url, props)
            .then(function (response) {
            if (!Killswitches_1.Killswitches.monitorClosureKSActivated() && !response.preloadedData) {
                throw new Error('Preloaded Data is not available.');
            }
            return response.preloadedData
                .then(function (preloadedData) {
                if (response.prefetchedData) {
                    _this._raisePrefetchDataEvent(url, preloadedData, response.prefetchedData);
                }
                return _this.navigateToPreloadedData(preloadedData);
            })
                .then(function (navigationResult) {
                if (navigationResult && navigationResult.operation === 'Unsupported') {
                    qosMonitor.writeExpectedFailure('Unsupported');
                }
                else {
                    qosMonitor.writeSuccess();
                }
                return navigationResult;
            });
        })
            .catch(function (error) {
            var isExpected = false;
            if (error instanceof NavigationDataError_1.default) {
                isExpected = error.isExpected;
            }
            isExpected
                ? qosMonitor.writeExpectedFailure(undefined, error)
                : qosMonitor.writeUnexpectedFailure(undefined, error);
            throw error;
        });
    };
    /**
     * Prefetches page navigation data
     *
     * @remarks
     * This is a required optimization that allows applications to pre-emptively fetch navigation data
     * so as to allow faster transitions between spfx-based applications.
     *
     * @param url - Destination URL
     * @param props - Optional Navigation properties
     *
     * @returns A promise
     */
    Navigator.prototype.prefetch = function (url, props) {
        var prefetchProps = tslib_1.__assign(tslib_1.__assign({}, props), { isPrefetchRequest: true });
        var qosMonitor = new sp_diagnostics_1._QosMonitor(prefetchNavigationDataQosScenarioName);
        return this._navigationDataProvider
            .getData(url, prefetchProps)
            .then(function (response) {
            qosMonitor.writeSuccess();
        })
            .catch(function (error) {
            qosMonitor.writeUnexpectedFailure(undefined, error);
        });
    };
    /**
     * Given a preloaded data object, it sets up all SPFx related data with the new information from the preloaded data.
     *
     * @remarks
     * This is necessary because ListView has its own router and will give us only the object itself.
     *
     * @param preloadedData - Preloaded data object
     */
    Navigator.prototype.navigateToPreloadedData = function (preloadedData) {
        var _this = this;
        var qosMonitor = new sp_diagnostics_1._QosMonitor(navigateToPreloadedDataQosScenarioName);
        try {
            this._validatePreloadedData(preloadedData);
            this._preloadedData = preloadedData;
            return this._navigationOrchestrator
                .navigate(preloadedData)
                .then(function (navigationResult) {
                _this._preloadedData = navigationResult.preloadedData;
                qosMonitor.writeSuccess();
                return navigationResult;
            })
                .catch(function (error) {
                qosMonitor.writeUnexpectedFailure('AsyncError', error);
                if (!Killswitches_1.Killswitches.monitorClosureKSActivated()) {
                    throw NavigationDataError_1.default.createNavigationError(NavigationDataError_2.NavigationErrorCode.asyncError, error);
                }
                else {
                    throw error;
                }
            });
        }
        catch (error) {
            if (!Killswitches_1.Killswitches.buildIDMismatchKSActivated()) {
                var navError = void 0;
                if (error && error._errorId === NavigationDataError_2.NavigationErrorCode.buildIdMismatch) {
                    navError = error;
                    qosMonitor.writeExpectedFailure('ValidationError', error, { error: 'BuildIdMismatch' });
                }
                else {
                    navError = NavigationDataError_1.default.createNavigationError(NavigationDataError_2.NavigationErrorCode.syncError, error);
                    qosMonitor.writeUnexpectedFailure('SyncError', error);
                }
                return Promise.reject(navError);
            }
            else {
                error.errorSource === 'BuildIdMismatch'
                    ? qosMonitor.writeExpectedFailure('VaidationError', error, { error: 'BuildIdMismatch' })
                    : qosMonitor.writeUnexpectedFailure('SyncError', error);
                return Promise.reject(error);
            }
        }
    };
    /**
     * Given a preloaded data object, it sets up all SPFx related data with the new information from the preloaded data.
     * It returns a promise with the loaded application.
     *
     * @remarks
     * This is used by the PlatformLoader to bootstrap an application.
     *
     * @param preloadedData - Preloaded data object
     */
    Navigator.prototype.navigateToApplication = function (preloadedData) {
        var _this = this;
        var qosMonitor = new sp_diagnostics_1._QosMonitor(navigateToApplicationQosScenarioName);
        try {
            this._navigationDataProvider.buildId = this._getBuildId(preloadedData);
            this._validatePreloadedData(preloadedData);
            // Starting the application requires a preloaded data object to already be set up.
            this._preloadedData = preloadedData;
            return this._navigationOrchestrator.navigate(preloadedData).then(function (navigationResult) {
                _this._preloadedData = navigationResult.preloadedData;
                qosMonitor.writeSuccess();
                return Promise.resolve(navigationResult.application);
            });
            // TODO fix this
        }
        catch (error) {
            qosMonitor.writeUnexpectedFailure(error);
            throw error;
        }
    };
    /**
     * This is an API used to boot sequence to load application customizers after application has rendered.
     * @internal
     */
    Navigator.prototype._loadApplicationCustomizers = function (preloadedData) {
        return this._navigationOrchestrator._loadApplicationCustomizers(preloadedData);
    };
    /**
     * Invalidates a cached resource by its URL, or a collection of cached resources that match a RegExp.
     * Any subsequent request for the resource(s) will be fetched from its origin and recached.
     *
     * @param url - URL or URL RegExp to invalidate
     */
    Navigator.prototype.invalidate = function (url) {
        return this._navigationDataProvider.invalidate(url);
    };
    Navigator.prototype._validatePreloadedData = function (preloadedData) {
        sp_core_library_1.Validate.isNotNullOrUndefined(preloadedData, 'preloadedData');
        sp_core_library_1.Validate.isNotNullOrUndefined(preloadedData.spPageContextInfo, 'preloadedData.spPageContextInfo');
        sp_core_library_1.Validate.isNonemptyString(preloadedData.clientSideApplicationId, 'preloadedData.clientSideApplicationId');
        try {
            sp_core_library_1.Validate.isTrue(DEBUG || this._navigationDataProvider.buildId === this._getBuildId(preloadedData), 'NavigationDataProvider.buildId');
        }
        catch (error) {
            if (!Killswitches_1.Killswitches.buildIDMismatchKSActivated()) {
                throw NavigationDataError_1.default.createNavigationError(NavigationDataError_2.NavigationErrorCode.buildIdMismatch, error);
            }
            else {
                throw { errorSource: 'BuildIdMismatch', error: error };
            }
        }
    };
    Navigator.prototype._raisePrefetchDataEvent = function (url, preloadedData, prefetchData // tslint:disable-line:no-any
    ) {
        // tslint:disable-next-line:no-any
        prefetchData.then(function (data) {
            if (data) {
                sp_core_library_1._SPEventManager.instance.raiseStickyEvent(BaseApplication_1.default._prefetchedDataEventName, new PrefetchedDataEventArgs_1.default(preloadedData.clientSideApplicationId, url, data));
            }
        });
    };
    /**
     * Extracts the current build id from a string in the first manifest.
     * This is a temporary solution until a better solution is arrived at
     * such as including the build id at the root of the preload data.
     *
     * @param preloadedData - preload data for a given application
     */
    Navigator.prototype._getBuildId = function (preloadedData) {
        try {
            if (preloadedData.buildNumber) {
                return preloadedData.buildNumber;
            }
            var baseUrl = preloadedData.manifests[0].loaderConfig.internalModuleBaseUrls[0];
            return baseUrl.slice(baseUrl.indexOf('sp-client-'), -1);
        }
        catch (error) {
            return '';
        }
    };
    /**
     * DO NOT call this from outside the framework code. SPFx calls this internally during startup.
     *
     * @internal
     */
    // @ts-ignore This gets called by the framework, hiding from the public interface see IPrivateNavigator
    Navigator.prototype._setPreloadedData = function (data) {
        this._preloadedData = data;
    };
    return Navigator;
}());
exports.default = Navigator;
//# sourceMappingURL=Navigator.js.map